我计划为一款新应用部署一套测试环境,而当时恰逢Docker SwarmKit发布。这绝对是个一探其究竟的好机会,在本篇文章中,我们将共同分享我在EC2服务器上安装SwarmKit的实际体会。
在Replicated,我们编写了一套平台,旨在帮助SaaS企业利用Docker将业务部署至专有环境当中。另外,过去两年中我个人在这款平台的构建中积累到大量调度与编排工具的使用经验。我们甚至专门为Docker容器构建起自己的调度与编排运行时,旨在为部分早期客户提供支持。我设置并运行有Kubernetes与Mesosphere集群,同时也比较熟悉容器化生产环境的运行工作。
在利用swarmctl部署应用的过程当中,我快速分析了SwarmKit的优点,并汇总了其中尚缺少的生产环境必要工具选项——部分可利用其它现有工具替代,部分可能需要自行构建。另外,在撰写本文的过程中,README文档中又新增了一系列评论内容,旨在展示更多用例并指导用户使用内置功能。我个人建议大家首先参阅SwarmKit说明文档的最新版本。
内容摘要
配置
设置集群的过程非常简单,只需要运行几条命令即可同步推进。Docker提供的服务发现机制在这里发挥了巨大作用。相比之下,除非大家决定使用谷歌Container Engine,否则Kubernetes集群的设置则相对更为繁琐。
运行容器
很明显,设置集群的目的在于运行容器。我能够轻松运行容器、对实例进行规划伸缩、编辑环境变量及容器的其它属性,各项工作都可快速完成。即使是由于设置错误而导致容器发生重启,我也能够使用swarmctl service rm与swarmd快速清理已经停止运行的各容器。
上手难度
我使用Docker已经有一段时间,swarmctl也并不会带来夸张的额外学习曲线。当然,在添加更多新功能之后,其使用复杂性也会随之提高。但就目前而言,其仍然算是一套平易近人的方案。
默认安全性
我不需要以手动方式创建或者转移任何TLS证书。这套集群能够轻松实现自动配置,而且效果也还不错。正是因为这种强制配置证书的机制,集群的基本安全性得到了有效保障。我个人也倾向于让一切通信都以TLS加密连接作为载体。
稳定性?
老实说,我的测试时间还不长,所以没法公正地评判其稳定性。SwarmKit才刚刚发布数天,因此生产级别的稳定性恐怕还要过段时间才能实现。不过其立足于相当成熟的技术成果(Docker与Raft),所以稳定性与安全性应该比较扎实。
节点管理
大家可以立足于单一节点对流量进行添加、移除与控制。以此为基础,我可以构建起一套定制化更新策略。如果制定出高度复杂的升级策略,我想我应该可以在此基础上创建起滚动升级功能。
尚不具备的元素
ServiceSpec文件
要实现变更管理、版本历史以及其它一切我所需要的生产环境功能,我需要在文件中定义一套ServiceSpec并将其传递至service create/update命令。相较于swarmctl service create --name redis --image=redis --env KEY=VALUE,我更乐于使用swarmctl service create --file redis.yaml。在运行swarmctl service create --help时,其中列出了这项参数,但在审查代码时我觉得其应该尚未实装。
https://github.com/docker/swarmkit/issues/537上对此进行了探讨,不过讨论内容有些混乱,因为其与代码内容并不相符。我认为可能是某些旧问题导致了这一状况,相信其在不久的将来会得到合理解决。当然,CLI帮助文本中的大部分内容与实际支持情况还是比较一致的。
负载均衡/入口
目前还很难断定Docker在这方面有着怎样的发展规划。我自己也不清楚自己希望如何为这些容器设置入口。默认情况下定义了一套入口网络,但我个人运行在EC2服务器中,因此我更倾向于使用ELB。我的服务器不具备公共IP地址,因此我该如何进行服务公开?我当然希望由swarmd管理这些工作,但目前这一想法还无法实现——或者说还不受支持。
升级策略
虽然可以使用swarmctl service update api ...,但我希望更为具体地定制自己的滚动更新策略。我需要安全关闭容器,停止其运行并重新启动新的版本。另外,我还希望能够在升级出现错误时停止这一流程。我们需要编写一些代码来实现这些目标,但我暂时还没弄清楚要如何在SwarmKit中加以实现。我认为提供一些默认设置与样本能够很好地补充这段学习上的空白。
系统服务
我希望在已知版本中将swarmd作为系统服务运行。我希望能够将这项服务提取出来并通过upstart或者systemd加以管理。当然,我自己也能完成这些操作,因此不必将其纳入服务的核心部分。
远程集群日志
在Kubernetes中,我可以运行kubectl logs <podname>。我希望能够使用swarmctl service <service-name> logs -f以调试或者监控运行中的系统。
专有库凭证
我需要部署存储在Docker Hub以及quay.io上的专有镜像。我认为我可以利用docker-machine对其进行管理,但事实证明目前还无法实现。在以上部署当中,我以手动方式从quay.io处将镜像提取至集群中的每个节点,这种作法在生产环境下显然是不行的。
我的初始设置与部署流程
那么上述结论是如何得出的?这个嘛,我将与大家分享我在SwarmKit中设置并部署一套相对标准的SaaS环境的流程; 下面来看我的SwarmKit集群部署初体验。
这套应用环境的部署并不困难,其中主要涉及以下组件:
- MySQL
- Elasticsearch
- RabbitMQ
- 静态React build站点
- 另一静态React built站点
- 以Golang编写的API
- 以Golang编写的工作程序
我希望确保部署在实例中的一切都被托管在一套可扩展Docker集群当中,另外我会最终将其发布至个人环境当中,从而确保不会涉及任何专利工具。
环境
我决定在us-west-1(北加利福尼亚州)内设置一套全新VPC。我设置了6套子网(2套公共、2套专有、2套db)。现在我已经在us-west-1a与us-west-1c中拥有全部必要的地址。我还设置了一套OpenVPN服务器并进行了配置,这样专有子网内的各服务器尽管不具备公共IP地址,我仍然可以通过该VPN对其进行访问。
但我还需要一套管理器机器。因此,我在一套专有子网内启动了一个t2.medium实例。我将利用该机器管理整套集群、推送更新并对各容器进行故障排查/监控。
安装SwarmKit
终于到了期待的步骤了——最后设置基础设施,现在是时候体验新鲜事物了!
接下来,我在管理节点上安装Docker 1.11.2。这是DOcker的当前版本,我预计SwarmKit应该能够兼容。另外,我需要利用主(惟一分支)repo构建二进制文件。
$ git clone https://github.com/docker/swarmkit.git
Cloning into 'swarmkit'...
remote: Counting objects: 11236, done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 11236 (delta 8), reused 0 (delta 0), pack-reused 11209
Receiving objects: 100% (11236/11236), 6.94 MiB | 1.49 MiB/s, done.
Resolving deltas: 100% (7199/7199), done.
Checking connectivity... done.
$ docker run -it -v `pwd`/swarmkit:/go/src/github.com/docker/swarmkit golang:1.6 /bin/bash
Unable to find image 'golang:1.6' locally
1.6: Pulling from library/golang
51f5c6a04d83: Pull complete
a3ed95caeb02: Pull complete
7004cfc6e122: Pull complete
5f37c8a7cfbd: Pull complete
e0297283ad9f: Pull complete
a7164db3234c: Pull complete
6bb08da223d8: Pull complete
c718b2eba451: Pull complete
Digest: sha256:66618c0274d300e897bcd2cb83584783e66084ea636b88cb49eeffbeb7f9b508
Status: Downloaded newer image for golang:1.6
root@96fa53925bbc:/go# cd /go/src/github.com/docker/swarmkit/
root@96fa53925bbc:/go/src/github.com/docker/swarmkit# make binaries
bin/swarmd
bin/swarmctl
bin/swarm-bench
bin/protoc-gen-gogoswarm
binaries
root@96fa53925bbc:/go/src/github.com/docker/swarmkit# exit
使用这些刚刚构建完成的二进制文件:
$ swarmkit/bin/swarmd -d /tmp/node-mgmt-01 --listen-control-api /tmp/mgmt-01/swarm.sock --hostname mgmt-01 Warning: Specifying a valid address with --listen-remote-api may be necessary for other managers to reach this one. INFO[0000] 4a678cf4eff2b943 became follower at term 2 INFO[0000] newRaft 4a678cf4eff2b943 [peers: [], term: 2, commit: 8, applied: 0, lastindex: 8, lastterm: 2] WARN[0000] ignoring request to join cluster, because raft state already exists INFO[0000] 4a678cf4eff2b943 became follower at term 2 INFO[0000] newRaft 4a678cf4eff2b943 [peers: [], term: 2, commit: 8, applied: 0, lastindex: 8, lastterm: 2] INFO[0000] Listening for local connections addr=/tmp/mgmt-01/swarm.sock proto=unix INFO[0000] Listening for connections addr=[::]:4242 proto=tcp INFO[0005] 4a678cf4eff2b943 is starting a new election at term 2 INFO[0005] 4a678cf4eff2b943 became candidate at term 3 INFO[0005] 4a678cf4eff2b943 received vote from 4a678cf4eff2b943 at term 3 INFO[0005] 4a678cf4eff2b943 became leader at term 3 INFO[0005] raft.node: 4a678cf4eff2b943 elected leader 4a678cf4eff2b943 at term 3 INFO[0005] node is ready
赞!管理节点已经开始运行了!
这套应用暂时不会接收太多流量。我决定先从比较小的t2.medium实例3节点SwarmKit集群入手。我在专有子网内启动这些服务器,而后安装docker-engine。我的集群已经开始运转,但其还不算完整——目前其只能属于内部网络中能够相互通信的3套服务器。
我当然不希望每次都进行SwarmKit二进制文件构建,因此我将各文件复制到3套新服务器中,并利用以下命令将其作为workers 1、2与3进行启动引导:
$ scp ubuntu@<mgmt_01_address>:/home/ubuntu/swarmkit/bin/swarmd . $ scp ubuntu@<mgmt_01_address>:/home/ubuntu/swarmkit/bin/swarmctl . $ swarmd -d /tmp/node --hostname work-<N> --join-addr <mgmt_01_address>:4242 Warning: Specifying a valid address with --listen-remote-api may be necessary for other managers to reach this one. INFO[0000] node is ready
非常简单吧。我想一切应该运转正常,因为我没看到任何连接失败提示,但其目前也没有给出成功连接信息。应该没事,咱们继续进行。
返回管理节点,我运行以下命令:
$ export SWARM_SOCKET=/tmp/mgmt-01/swarm.sock $ ./swarmctl node ls ID Name Membership Status Availability Manager status -- ---- ---------- ------ ------------ -------------- 0fd1wrr78xdld work-1 ACCEPTED READY ACTIVE 14qektqj267gj mgmt-01 ACCEPTED READY ACTIVE REACHABLE * 2mi1lv4edolas work-3 ACCEPTED READY ACTIVE 2rvbyfbhcgi2h work-2 ACCEPTED READY ACTIVE
集群搞定!下面我们开始部署容器!
稍等,README到这里就有些含糊不清了。我当然可以部署Redis,但这实在太没挑战了。我想部署自己的定制化镜像。我有多项服务可供部署,而且我打算创建一项服务来描述自己的API,并将其运行在新集群内的多个工作程序之上。我怀疑swarmctl service create -f <filename>同样会在docker-compose yaml文件内作为服务定义,在实验之后,我发现自己的猜想可能无法实现。总而言之,虽然CLI help中列出,但看起来我无法利用spec文件创建服务:
$ ./swarmctl service create --help Create a service Usage: ./swarmctl service create [flags] Flags: --args value Args (default []) --env value Env (default []) -f, --file string Spec to use --image string Image --instances uint Number of instances for the service Service (default 1) --mode string one of replicated, global (default "replicated") --name string Service name --network string Network name --ports value Ports (default []) Global Flags: -n, --no-resolve Do not try to map IDs to Names when displaying them -s, --socket string Socket to connect to the Swarm manager (default "/tmp/mgmt-01/swarm.sock")
不过这也算不上什么大问题。接下来我手动部署自己的服务:
$ ./swarmctl service create --name api --image quay.io/my_org/api:7dab5f6 --env PROJECT_NAME=api 0icvt9xvf7ja0yspn26yfvvn8
这里我跳过了关于专有镜像提取的手动步骤。虽然只使用了一条env var,但我敢肯定它完全能够支持全部必要的环境变量、分卷与端口。
$ ./swarmctl service ls ID Name Image Instances -- ---- ----- --------- 0icvt9xvf7ja0yspn26yfvvn8 api quay.io/my_org/api:7dab5f6 1
我的容器已经部署完成并开始运行。下面看看swarmctl能做些什么。
对这套集群进行规模扩展:
$ ./swarmctl service update api --instances 2 0icvt9xvf7ja0yspn26yfvvn8 $ ./swarmctl service ls ID Name Image Instances -- ---- ----- --------- 0icvt9xvf7ja0yspn26yfvvn8 api quay.io/my_org/api:7dab5f6 2 $ ./swarmctl service inspect api ID : 0icvt9xvf7ja0yspn26yfvvn8 Name : api Instances : 2 Template Container Image : quay.io/my_org/api:7dab5f6 Env : [PROJECT_NAME=api] Task ID Service Instance Image Desired State Last State Node ------- ------- -------- ----- ------------- ---------- ---- bartp5krui1815paw2srmtd28 api 1 quay.io/my_org/api:7dab5f6 RUNNING RUNNING 3 minutes ago work-1 70kexpn10suulum0hxursil28 api 2 quay.io/my_org/api:7dab5f6 RUNNING RUNNING 41 seconds ago work-2
很好,它还能做点别的么?我能否更新该环境变量?是的,当然可以,但需要重启各容器:
$ ./swarmctl service update api --env PROJECT_NAME=api,TEST=1 0icvt9xvf7ja0yspn26yfvvn8 ubuntu@ip-10-10-5-87:~$ ./swarmctl service inspect api ID : 0icvt9xvf7ja0yspn26yfvvn8 Name : api Instances : 2 Template Container Image : quay.io/my_org/api:7dab5f6 Env : [PROJECT_NAME=api, TEST=1] Task ID Service Instance Image Desired State Last State Node ------- ------- -------- ----- ------------- ---------- ---- 0q3kiohsucrimhcl40xomi47h api 1 quay.io/my_org/api:7dab5f6 RUNNING RUNNING 1 second ago work-1 c1kq8hx4whdbtldb7gtapn0ut api 2 quay.io/my_org/api:7dab5f6 RUNNING ACCEPTED 5 seconds ago work-2
总结陈词
Docker SwarmKit似乎确实是对现有调度及编排生态系统的一种有益补充。截至目前,我们只能利用Kubernetes或者Mesosphere来支持规模合理的生产基础设施,从而实现规模化容器管理。而SwarmKit当前版本的出现则带来了新的选择,意味着我们能够进一步扩展以支持其它环境。目前其功能的丰富程度尚不及其它调度方案,但它拥有扎实的成熟技术成果作为基础,而且似乎并不打算发展为适合任意用户、任意任务的普适型解决方案。我乐于为SwarmKit做出贡献,并将积极为这款新型应用的部署与管理提供我认为有效的功能。