
昨天才从DockerCon大会归来,阿里云容器服务团队将为大家奉献一系列深入学习的文章来帮助大家了解Docker 1.12的最新动态。
在DockerCon第一天的Keynote里面,Docker CTO Solomon Hykes宣布Docker将提供内置的编排(Orchestration)能力,从而能使得Docker Engine原生支持集群管理和服务生命周期管理。Solomon把这称为自Docker发布以来最大的一次改变。这个宣布对每个Docker开发者和厂商都有重要影响,也会左右现有Docker编排市场格局。
Docker Swarm模式简介
在Docker 1.12中,其Docker内置编排包含以下功能
Docker Swarm模式架构如下:

集群中的节点可以包括worker和manager两类不同的角色,它们之间可以通过promote和demote操作互相切换。在manager节点上可以进行集群的管理操作,其状态通过一个内部的Raft一致性协议实现进行同步和持久化。manager节点中会选举出一个leader来进行调度和编排。
Swarm模式支持新的Service/Task来进行容器的调度和执行。一个服务(service)描述了任务(task)如何在集群worker节点上的调度执行。Swarm支持自我复制服务(replicated service 指定副本数目)或全局服务(global service 在每个worker上执行)。一个任务(task)是原子化执行和调度单元,对应着Docker容器和其运行配置。manager节点负责根据service描述调度task到特定worker节点执行。同时manager会根据任务当前状态,自动逼近服务指定目标状态,实现最终一致性。
阿里云上创建Docker Swarm模式集群
为了在第一时间尝鲜体验一下Docker内置的编排能力,我们先在阿里云上操练起来。
Docker Machine是一个可以帮助开发者在自己本地电脑或云服务中创建Docker运行环境的的应用工具。使用它的阿里云ECS Driver,可以大大简化在阿里云环境下Docker运行环境的部署。
Docker Machine和阿里云ECS Driver的安装步骤如下, https://help.aliyun.com/document_detail/26088.html
下面,我们来创建一个包含3个ECS节点的Docker集群,首先配置一些共用的环境变量
export DEBUG=true
export ECS_ACCESS_KEY_ID=<您的Access Key ID>
export ECS_ACCESS_KEY_SECRET=<您的Access Key Secret>
export ECS_REGION=cn-beijing
export ECS_ZONE=cn-beijing-b
export ECS_SSH_PASSWORD=<ECS实例SSH密码>
export MACHINE_DOCKER_INSTALL_URL=http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/intranet
export ENGINE_REGISTRY_MIRROR=https://6udu7vtl.mirror.aliyuncs.com
注:
然后我们可以通过如下命令在阿里云上创建3个ECS节点,并安装最新的测试版Docker Engine
docker-machine create -d aliyunecs node1
docker-machine create -d aliyunecs node2
docker-machine create -d aliyunecs node3
我们可以通过如下命令执行如下命令显示node1节点内网地址,并利用docker swarm
命令创建集群和注册manager节点
docker-machine ssh node1 ip addr show eth0
docker-machine ssh node1 docker swarm init --advertise-addr <NODE1_IP>
屏幕输出如下
Swarm initialized: current node (xxxxxxx) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-1xxxxxxx-xxxxxxx \
10.xx.xx.xx:2377
To add a manager to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-1xxxxxxx-xxxxxxx \
10.xx.xx.xx:2377
之后,我们把node-2和node-3作为worker加入Docker Swarm集群,执行docker-machine ssh 之后拷贝上文返回的第一个命令来添加一个worker节点
docker-machine ssh node-2 docker swarm join --token <WORKER_TOKEN> <NODE1_IP>:2377
docker-machine ssh node-3 docker swarm join --token <WORKER_TOKEN> <NODE1_IP>:2377
这样整个Docker集群就已经创建成功了,是不是非常简单 :-)
下面我们将体验Docker的Swarm模式,如需进一步了解请参考Docker Swarm模式官方文档
管理集群节点
我们利用 docker-machine ssh node1
命令登入node1节点,并用下面的命令查看集群节点
root@node1:~# docker node ls
ID NAME MEMBERSHIP STATUS AVAILABILITY MANAGER STATUS
3bj13a80xdltndle20c6rzc55 * node1 Accepted Ready Active Leader
b5x26rpfbqhg18nn11arkxzdz node3 Accepted Ready Active
cqgyh1yv0rn6llckzi1hv84fa node2 Accepted Ready Active
部署管理
下面我们部署一个nginx服务到集群上
root@node1:~# docker service create -p 80 --name nginx nginx:1.11
9wxstra98838kc0hbeeq0hliz
然后我们检查nginx服务状态和它包含的tasks信息
root@node1:~# docker service ls
ID NAME REPLICAS IMAGE COMMAND
9wxstra98838 nginx 1/1 nginx:1.11
root@node1:~# docker service ps nginx
ID NAME SERVICE IMAGE LAST STATE DESIRED STATE NODE
86lnce96t55dempycyp4si0gm nginx.1 nginx nginx:1.11 Running 7 minutes Running node1
我们可以伸缩nginx服务到三个任务
root@node1:~# docker service scale nginx=3
nginx scaled to 3
更新服务
下面我们部署一个redis服务到Swarm集群,采用redis:3.0.6镜像,其初始包含3个任务。我们还定义了服务的更新策略:每次更新1个Redis容器,更新延迟10s
root@node1:~# docker service create --replicas 3 --name redis --update-delay 10s --update-parallelism 1 redis:3.0.6
5dz319epmfflcmfoftn4w1uga
查看Service列表,并检查redis服务细节
root@node1:~# docker service inspect redis --pretty
ID: 5dz319epmfflcmfoftn4w1uga
Name: redis
Mode: Replicated
Replicas: 3
Placement:
Strategy: Spread
UpdateConfig:
Parallelism: 1
Delay: 10s
ContainerSpec:
Image: redis:3.0.6
Resources:
Reservations:
Limits:
下面,我们可以更新redis服务的Docker镜像到redis:3.0.7。Swarm manager将依照之前的更新策略来更新服务:
root@node1:~# docker service update --image redis:3.0.7 redis
redis
再次检查redis服务细节,和相应的task信息
root@node1:~# docker service inspect --pretty redis
ID: 5dz319epmfflcmfoftn4w1uga
Name: redis
Mode: Replicated
Replicas: 3
Placement:
Strategy: Spread
UpdateConfig:
Parallelism: 1
Delay: 10s
ContainerSpec:
Image: redis:3.0.7
Resources:
Reservations:
Limits:
root@node1:~# docker service ps redis
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
agiq4u5b4ttokf95ilgd8ogpt redis.1 redis:3.0.7 node2 Running Running 3 minutes ago
0zc6bsqlhw50er0agzhetyw0q \_ redis.1 redis:3.0.6 node3 Shutdown Shutdown 3 minutes ago
6l5ch8hg2n6dht3oste72n2x0 redis.2 redis:3.0.7 node3 Running Running 2 minutes ago
dfmgh0re6ntspra4yxi5dqj3u \_ redis.2 redis:3.0.6 node1 Shutdown Shutdown 2 minutes ago
515317fk5zfkth66dfop1or5f redis.3 redis:3.0.7 node3 Running Running 2 minutes ago
2taa6br9kdwm5qzz34037lywm \_ redis.3 redis:3.0.6 node1 Shutdown Shutdown 3 minutes ago
当redis更新时,可以看到redis:3.0.6容器会逐渐被基于redis:3.0.7镜像的容器陆续更新
集群可用性验证
我们可以利用下面命令把node2,node3也设置为集群的manager节点
root@node1:~# docker node promote node2
Node node2 promoted to a manager in the swarm.
root@node1:~# docker node ls
ID NAME MEMBERSHIP STATUS AVAILABILITY MANAGER STATUS
3bj13a80xdltndle20c6rzc55 * node1 Accepted Ready Active Leader
b5x26rpfbqhg18nn11arkxzdz node3 Accepted Ready Active
cqgyh1yv0rn6llckzi1hv84fa node2 Accepted Ready Active Reachable
root@node1:~# docker node promote node3
Node node3 promoted to a manager in the swarm.
root@node1:~# docker node ls
ID NAME MEMBERSHIP STATUS AVAILABILITY MANAGER STATUS
3bj13a80xdltndle20c6rzc55 * node1 Accepted Ready Active Leader
b5x26rpfbqhg18nn11arkxzdz node3 Accepted Ready Active
cqgyh1yv0rn6llckzi1hv84fa node2 Accepted Ready Active Reachable
当我们重启Docker服务来模拟Docker Engine故障时,我们可以发现leader已经被自动切换到其他节点上
root@node1:~# service docker restart
docker stop/waiting
docker start/running, process 7077
root@node1:~# docker node ls
ID NAME MEMBERSHIP STATUS AVAILABILITY MANAGER STATUS
3bj13a80xdltndle20c6rzc55 * node1 Accepted Ready Active Reachable
b5x26rpfbqhg18nn11arkxzdz node3 Accepted Ready Active Leader
cqgyh1yv0rn6llckzi1hv84fa node2 Accepted Ready Active Reachable
现在我们来模拟node3下线维护过程,现在node3上运行的节点有如下任务
root@node1:~# docker node tasks node3
ID NAME SERVICE IMAGE LAST STATE DESIRED STATE NODE
88mklyn50dun7telyn97m5grm redis.2 redis redis:3.0.7 Running 5 minutes Running node3
6l612n075rr95ditjdl6x2pe7 redis.3 redis redis:3.0.7 Running 6 minutes Running node3
emxsw4qosd6qkahpyrq8hpx3n nginx.3 nginx nginx:1.11 Running 9 minutes Running node3
我们将node3设置为排水模式,让其下线并将其上运行中容器自动迁移到其他活跃节点
root@node1:~# docker node update --availability drain node3
node3
我们可以看到所有的容器已经迁移到node1和node2上,
root@node1:~# docker node tasks node1
ID NAME SERVICE IMAGE LAST STATE DESIRED STATE NODE
1pm1dyxuwrh57kysyh7j5s48w redis.1 redis redis:3.0.7 Running 5 minutes Running node1
7gstf6nn9ydwm8knbjwb7c0ag nginx.2 nginx nginx:1.11 Running 5 minutes Running node1
46l5e1zmdpbwn2ptcfqtrdpue redis.3 redis redis:3.0.7 Running 9 seconds Running node1
root@node1:~# docker node tasks node2
ID NAME SERVICE IMAGE LAST STATE DESIRED STATE NODE
5tn4bgod6a76k4bjl886vprrb nginx.1 nginx nginx:1.11 Running 11 minutes Running node2
3lj30togrr1j19rya5wc9bxi7 redis.2 redis redis:3.0.7 Running 12 seconds Running node2
1hnvm1kuji5auegexd2u7lm4x nginx.3 nginx nginx:1.11 Running 12 seconds Running node2
root@node1:~# docker node tasks node3
ID NAME SERVICE IMAGE LAST STATE DESIRED STATE NODE
我们可以把node3再修改成为活跃(active)状态,之后系统可以在node3上创建或重新调度容器。
root@node1:~# docker node update --availability active node3
node3
下面我们将测试当一个节点失效时集群的行为。
利用docker-machine我们可以对指定节点强制关机,打开另外一个终端窗口执行下面命令杀死node2节点
docker-machine kill node2
稍等一会儿,我们检查docker的节点和上面的任务状态
root@node1:~# docker node ls
ID NAME MEMBERSHIP STATUS AVAILABILITY MANAGER STATUS
3bj13a80xdltndle20c6rzc55 * node1 Accepted Ready Active Reachable
b5x26rpfbqhg18nn11arkxzdz node3 Accepted Ready Active Leader
cqgyh1yv0rn6llckzi1hv84fa node2 Accepted Down Active Reachable
root@node1:~# docker node tasks node1
ID NAME SERVICE IMAGE LAST STATE DESIRED STATE NODE
1pm1dyxuwrh57kysyh7j5s48w redis.1 redis redis:3.0.7 Running 12 minutes Running node1
7gstf6nn9ydwm8knbjwb7c0ag nginx.2 nginx nginx:1.11 Running 12 minutes Running node1
46l5e1zmdpbwn2ptcfqtrdpue redis.3 redis redis:3.0.7 Running 7 minutes Running node1
root@node1:~# docker node tasks node3
ID NAME SERVICE IMAGE LAST STATE DESIRED STATE NODE
cb99d1ga8dsv2prwvsc3np1fq nginx.1 nginx nginx:1.11 Running 2 minutes Running node3
3r9hniyuq54j12mwtytm9x88k redis.2 redis redis:3.0.7 Running 2 minutes Running node3
9p9jik29urimlruo7alabfcmp nginx.3 nginx nginx:1.11 Running 2 minutes Running node3
我们可以发现这时候,node2节点是"Down"状态,并且所有容器已经被自动迁移到node1和node3之上。
总结
我们利用Docker Machine的ECS driver,可以方便地在阿里云上创建Docker环境。学习并体验了Docker在编排方面的最新特性:节点管理、服务管理和高可用性等。
客观地讲,Docker的Swarm模式确实极大简化了容器编排技术的部署和使用,这体现了Docker公司所强调的“democratize orchestration”的愿景,把容器编排变成为大众化的技术。但是这也会在某种程度上抑制技术的多样性,其他的编排技术的市场空间会受到很大影响。另外作为Docker内置编排方案的第一个版本,其还有很多不足和和可能变化的地方。在DockerCon上我们也和Docker的工程师探讨了是否能够提供扩展机制来定制不同的路由和发布策略等问题。我们期待Docker的进一步发展,同时也希望Docker社区能够更加多元化和健康发展。
阿里云容器服务完全兼容Docker原生的编排技术,目前支持Docker 1.11正式版本。它现在的容器化服务生命周期管理的方式和新的Swarm模式很接近。当Docker 1.12稳定版发布之后,我们会很快推出对新Swarm模式的支持。
想了解更多容器服务内容,请访问 https://www.aliyun.com/product/containerservice