前段时间整理 P4 到软交换机( simple_switch )的编译环境,由于一开始就觉得会有各种坑,就像用某些虚拟化技术来解决最基本的问题:可能要反复重装系统。

因为网易云听说了容器技术、也知道 docker 这个基于 lxc 的工具,就想着拿来用用看,但这个时候其实是把容器当作一个轻量级的虚拟机去看待的。

看到 https://github.com/p4lang/switch 的 docker/Dockerfile 可以直接 build image,就开始尝试:

首先遇到的问题是,网易云的容器构建服务,无法 科学地 访问 github,导致这个 Dockerfile 无法顺利 build。于是就跑到阿里云上面,使用海外构建功能进行尝试,再一次失败。原因是 bmv2 ( P4 的一个平台模块)有个源代码文件有几百K行。构建服务用的内存是 512M 使得 gcc 直接因为内存不足挂掉。接下来自己创建了一个 2G 内存的虚机,再“科学地”拉取代码后进行构建,完毕后第一个感受是镜像好大、第二则是:这玩意儿怎么用?

为了解决第一个问题,找了几篇构建镜像经验方面的文章,同时阅读了 docker 的文档,分清了 Dockerfile 和 shell 脚本的区别。将自己建立的 Dockerfile 构建完毕后,着手解决第二个问题。p4lang/switch 的 docker/Dockerfile 构建完毕后,运行就自行退出,推送镜像到网易云后,启动就报告运行失败,参照了一下网易云公共镜像的 Dockerfile,原来是 要加一句话启动 ssh -D 。至此,docker 被我用成了一个 虚拟机。我可以 ssh 进去,在容器里面起桌面、编译自己的代码、跑 sample 等。同时,研究了特权模式对 host 设备操作的权限、 host kernel 的 capabilities 配置、卷的挂载等( https://github.com/lylandris/p4env )。但是,docker 究竟和 虚拟机 有什么区别呢?难道只是因为轻?同时,要加一句话启动 ssh -D 也困惑着我,为什么 p4lang/switch 项目仓库中的 Dockerfile 没有这句话?

再去阅读 docker 文档,和一些 blog ,看到一句话,大意是:容器存在的生命周期就是主程序的生命周期。理解一下就是:任何镜像中的打包的文件都是为了这个主程序服务的,否则就不需要打包到镜像中(也是精简镜像大小的原则);一个镜像应该只干主程序这一件事。回头再研读 ENTRYPOINT/CMD 两个命令的使用方式,逐渐开始明朗了。网易云在使用我之前构建的镜像启动容器的时候,因为 Dockerfile 中没有指定 ENTRYPOINT/CMD,启动的网页上也没去填写 CMD 参数,所以其不知道起这个容器的目的是什么,只好报告启动失败了。要加一句话启动 ssh -D 是因为网易构建好的基础镜像认为大家需要远程上去做一些基本的配置等,因此将 ENTRYPOINT 设置为 ssh -D。而我们在正常使用容器的时候,根本目的是期望其不会因为安装 App 引入安装一堆库,使得多个 App 所依赖的库出现打架、或者一个 App 卸载或升级的时候,不知道是否因为清理其原先依赖的库版本导致其他 App 失效。也就是说,容器为每一个 App 打包了其运行环境,App 之间互不影响,App 升级也很简便。同时,推导出 App 的运行环境对于开发和运维变成了同一套,这也是为什么容器和 DevOps 的发展相辅相成。

按照这个理解,p4lang/switch 的 Dockerfile 是将 switch 作为一个 App 交付给使用者。用户看到的就是一个 switch。而对于我的需求,则是应该将 p4lang/p4c 这个编译器打包成 App,用这个 App 编译代码后,在调用 p4lang/bmv2 这个 App 运行试验,而不是一股脑在一个镜像中包含这两个 App,然后把 ssh 打包成 App,远程进去在容器内写代码运行 sample。

想了一下以前曾经弄过的几个需求,pandoc + latex 自动编译文档的工具可以通过 docker 来解决当时配置环境的焦头烂额。当然,先看看是不是已经有人把这个轮子在 hub.docker.com 公开了,要是有,就直接拿来主义了。