【编者的话】要在生产环境中发挥Docker的威力,离不开编排工具的支持,否则将深陷容器监控与管理困难的泥沼。目前市面上有不少此类工具,如何选择最合适的一个,本文给出了一些参考。
这是关于我们如何在 IIIEPE 的生产环境中使用Docker的系列文章的第三部分。如果你还没看过 第一 (译文)和 第二 (译文)部分,请先前往阅读再继续。本文中,我将讨论我们测试过的编排工具,我们的选择及原因。我也会说明我们如何使用Jenkins来处理繁重的工作,以及它的组织方式。
使用Docker真的非常酷,它解决了我们工作流中的多个问题,但也制造了一些问题。管理容器的工作量与管理虚拟机相比只多不少,如果你没使用编排工具来完成这件事,那你就错了。一旦容器数量开始增长,将真的很难管理。
我们在 Docker Hub 上开源的所有基础镜像都使用了Supervisor。Supervisor是个管理进程的美妙工具,如果一个进程死亡,supervisor就会重启它。因为Docker工作的方式,容器需要有一个进程处于运行状态,以保持容器存活。进程无法后台化,如果进程死亡,容器也会死亡,你就不得不想办法重启容器。这大概是你一开始就想避免的问题。Supervisor最棒的一点是它可以处理多个进程,因此一个运行PHP应用的容器将运行PHP-FPM、Nginx和Sendmail。
我们大概花了两周时间针对一系列的编排方案进行了测试。包括:
我不想对每个方案做详尽回顾,这里只是简述一下。
使用Docker的PasS方案。基本上就是Heroku加上Docker。安装简单,但在存储方面不够灵活。这是最有吸引力的方案,但由于我们使用了Drupal,它不适合我们。而且,它没提供UI。
我们最终使用了Shipyard,但只是作为一个查看器。Shipyard还在不断发展,它最大的问题是没有办法简单的自动管理容器。正如我所说,我们仅仅把它作为一个查看器,用来监控所有容器和Docker服务的状态。如果一个容器崩溃了,我们只需要使用Shipyard重启死亡的容器,而不用重启该应用的所有容器。
它很有前途,但在我们需要时还不完善。它还大量依赖于某些我个人很不喜欢的模板。缺少代理是我们测试时的一个主要障碍。基本上,没有代理我们就只能在每台服务器上安装Panamax。
PaaS方案,列表中最难安装和配置的一个。它拥有很多超过我们要求的功能,但缺少一个我们需要的功能:Kubernetes不处理存储。
PaaS方案,它声称:
... Amazon S3 ... 是保存内容文件到tsuru的正确方法。
读完这段话,我们都连安装的欲望都没了。
作为Fig的替代品之一,没有多主机能力是最大的问题。
Maestro-NG在几个方面胜出:容易使用、有一个带简单命令的命令行界面、支持多主机、所有东西都在一个YAML文件中描述。
我们安装了一台服务器,并在上面安装了Maestro-NG,因为我们需要在每个web节点上打开Docker端口,为安全着想,只有Maestro-NG可以连接Docker。然后,我们将所有maestro文件组织进一个单独的git项目。项目下是一些以应用的正式域名(FQDN)命名的目录,在每个目录里有一个单独的文件,一个maestro.yaml文件:
/subdomain.example.com/maestro.yaml
/another-example.example.com/maestro.yaml
如果我们需要测试某个具体项目(我们并不会做这类测试),只需要创建一个maestro文件并推送上去,然后我们就可以像其他项目一样对待它。
使用Maestro-NG,我们的持续交付流程缩减成两个单独命令:
maestro pull ; maestro restart
由于做这个有点费时,我们就让Jenkins来替我们完成。
此前我们没使用Jenkins,因此我们也没实践过持续集成(CI)和持续交付(CD),一切都是手工的,非常容易出错。创建一个新的工作流是将其加入的绝好机会。
我们所有项目都具有相同的工作流:
maestro pull ; maestro restart
对于未经测试的小项目,整个过程持续不到2分钟,有些项目甚至更快,在25到35秒之间。最大的一个项目,是一个会被推送到Docker Hub的公共项目,花费了大概6分钟。确实有个例外,项目构建花了18-20分钟,这是一个老旧的HTML网站,具有大量的视频和大尺寸文件,整个项目大概有1.8GB大小,因此构建时间非常久。
在开始配置所有需要的虚拟机时,我们决定将Jenkins和Docker Registry安装在同一台虚拟机上。这么做有两个原因:一是这台虚拟机拥有大量空间,完全满足二者要求,我们的web节点都比较小,这台虚拟机相对而言大得多;二是将Docker registry和Jenkins安装在同一台虚拟机上可以减少推送镜像到registry的传输时间。这对我们来说很有效。
对于正常的、未经测试的应用,Jenkins运行以下脚本任务:
docker build —tag=our-private-docker-registry/application_name --no-cache .
docker login --username="someusername" --password="somepassword" --email="someemail" https://our-private-docker-registry.fqdn
docker push our-private-docker-registry/application_name
对于测试过的应用,Jenkins会进行如下操作:
make prepare-test
sleep 90
make install
make test
make clean-test
docker build --tag=iiieperobot/dashi3 .
docker login --username="iiieperobot" --email="someemail" --password="somepassword"
docker push iiieperobot/dashi3
在上述示例中,我们直接推送到Docker Hub,但99%的任务是推送到私有Docker Registry。
脚本运行后,Jenkins会通过SSH连接Maestro-NG服务器,并运行:
cd /path_to_maestro/application_fqdn ; maestro pull ; maestro restart
当新的基础镜像被重新构建,我们需要重新构建依赖该基础镜像的所有镜像,因此对于每个基础镜像还有这样一个任务:
docker pull iiiepe/nginx-drupal
我们有一个构建后的动作来构建依赖此镜像的所有项目。
在写作中途,我被问及是否在测试项目时使用Docker。猜的没错,需要时我们就会用。有时我们不使用模拟方式(mocking)而是对数据库进行测试,在进行这类测试时,我们会使用我上面描述的步骤,同时为了简化操作,我们使用了Makefiles,因此Jenkins和开发人员都可以通过 make test
来运行测试。
本文先到此为止,在下一篇同时是最后一篇文章中,我将讲述服务发现和负载均衡。
原文链接: A production ready Docker workflow. Part 3: Orchestration tools (翻译: 梁晓勇 校对:郭蕾)