使用Swarm进行Docker集群的部署

目录

作者:杨冬 欢迎转载,也请保留这段声明。谢谢!
出处:https://andyyoung01.github.io/http://andyyoung01.16mb.com/

如果你有很多主机,并且这些主机上都安装了Docker,则需要有一种方法来管理这些主机上的容器。比如说启动一个容器时,如何选择容器运行的主机,容器启动后怎样访问部署在某台主机上的容器。Swarm是为了解决这些问题而开发的。在一个有很多主机的集群中,使用Swarm可以将这些机器看做一个Docker daemon,并且像运行普通Docker命令那样使用。

使用Swarm管理容器集群,可以使你不必关心某个容器应该启动在某台主机上,Swarm通过其调度策略自动选择出最合适的主机来运行容器(当然也可以手动指定约束条件来限制容器启动在某台主机上)。Docker Swarm由三部分组成:agents,a discovery service和master。下图说明了在一个有3个主机节点的docker集群上,这三个部分是如何交互的:
“有3个节点的Docker Swarm”
在每台想要加入Swarm集群的主机上,都运行了一个agent,它是一个程序,发送连接信息到master上的discovery service,从而将该主机变为Docker Swarm集群中的节点。每台安装了agent的主机都需要将Docker daemon的端口开放到网络上(默认的端口号为2375),而不是使用本地的socket。当master启动后,它将与discovery service进行通信,来查找集群中的节点。从那时开始,你可以直接连接到master上,针对集群运行命令,而master会将请求发送到一个通过调度算法选定的agent上。

在Swarm集群中的Docker程序版本最少需要1.4.0版。默认情况下Swarm集群中只有一个master。如果需要集群中的master高可用,可以参考Docker的文档(https://docs.docker.com/swarm/multi-manager-setup/)。
在实验环境下,可以通过sudo /usr/bin/docker daemon -H 0.0.0.0:2375命令将Docker daemon的端口开放到网络上,虽然这样做有严重的安全问题,但为了理解swarm的部署并且为了简单起见,可以暂时这样做一下;在生产环境中禁止这样做,在生产环境中需要启用tls的安全认证。

部署Swarm集群的第一步是选择将要使用的discovery service。有几种方式可以选择:从存储在文件中的IP地址列表到使用ZooKeeper,或者是使用Docker Hub提供的服务发现机制。这里我们选择內建到Docker Hub里的discovery Service,它是通过使用tokens实现的。

Swarm的服务发现机制 - Swarm可以使用很多方法实现服务发现。可以把节点注册到Docker Hub discovery service上,是Swarm提供的一个便利的方式,为了可以快速部署集群。如果你不习惯于将你集群中节点的IP地址存储在一个潜在的公共服务上,可以选择其他的方式。详细信息可以参考https://docs.docker.com/swarm/discovery/

Docker Hub的discovery Service需要你通过命令行取得一个token来标识你的集群。所以在创建集群时,需要有能访问到Docker Hub的外网连接。下面我们就从头搭建一个Docker Swarm集群。本示例使用了三台主机,一个master和两个agent。机器名分别为centos7(master),centos7-A(agent),centos7-B(agent),IP地址分别为192.168.71.131,192.168.71.167,192.168.71.168。
Swarm的可执行程序是以docker镜像的形式提供的,所以在master上可以使用如下命令创建集群:

1
2
3
4
[yangdong@centos7 ~]$ docker pull swarm
...
[yangdong@centos7 ~]$ docker run swarm create
44d0dff23a4d98e3d3db07871b69357e

上面最后swarm create命令返回的长字符串就是用于标识你的集群的标识符。它非常重要,后面其它机器加入集群都需要使用它,请记下来!下面可以检查一下新创建的集群:

1
2
3
[yangdong@centos7 ~]$ docker run swarm list token://44d0dff23a4d98e3d3db07871b69357e
[yangdong@centos7 ~]$ curl https://discovery-stage.hub.docker.com/v1/clusters/44d0dff23a4d98e3d3db07871b69357e
[]

这两条命令是等效的,都用来检查集群中所包括的节点。正如你所看到的,现在还没有机器加入集群,所以命令的结果没有任何内容,swarm list命令没有返回任何数据,curl命令返回了一个空列表。这里curl命令直接查询了Docker Hub的Discovery Service,绕过了swarm提供的方法。
下面在centos7-A主机上启动第一个agent,如下:

1
2
[yangdong@centos7-A ~]$ docker -H tcp://localhost:2375 run -d swarm join --addr=192.168.71.167:2375 token://44d0dff23a4d98e3d3db07871b69357e
47cc2d6130624544c2393e19e2ddb0207891d0ad43811659122350cc6f07236a

在加入集群的命令行参数中,-H参数指定docker命令发送到本机的2375端口上,这是因为前面通过sudo /usr/bin/docker daemon -H 0.0.0.0:2375命令将Docker daemon的端口开放到网络上了; addr参数指定了master用来连接agent的IP地址及端口;token参数指定了集群所使用的标识符。
在master主机上再次查询集群中的节点列表:

1
2
3
4
[yangdong@centos7 ~]$ docker run swarm list token://44d0dff23a4d98e3d3db07871b69357e
192.168.71.167:2375
[yangdong@centos7 ~]$ curl https://discovery-stage.hub.docker.com/v1/clusters/44d0dff23a4d98e3d3db07871b69357e
["192.168.71.167:2375"]

可见centos7-A主机的ip已经加入到了集群中,master会使用此ip地址和端口号连接该主机上的docker daemon。前面在master上创建了集群,但是还没有启动管理容器对集群进行管理,下面就需要在master节点上运行swarm manage来管理集群:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[yangdong@centos7 ~]$ docker run -d -p 2375:2375 swarm manage token://44d0dff23a4d98e3d3db07871b69357e
6e438acc14607b5de7af555bbae49934ae8e1d2eda144d1b63c4897395bbdd02
[yangdong@centos7 ~]$ docker -H tcp://localhost:2375 info
Containers: 8
Running: 1
Paused: 0
Stopped: 7
Images: 10
Server Version: swarm/1.2.5
Role: primary
Strategy: spread
Filters: health, port, containerslots, dependency, affinity, constraint
Nodes: 1
centos7-A: 192.168.71.167:2375
└ ID: JDDQ:ATPE:2OSM:BZJU:EAO7:ZTMV:2ZHC:IA3L:FSUX:JQNA:CAO3:MTN4
└ Status: Healthy
└ Containers: 8 (1 Running, 0 Paused, 7 Stopped)
└ Reserved CPUs: 0 / 1
└ Reserved Memory: 0 B / 1.871 GiB
└ Labels: kernelversion=3.10.0-327.22.2.el7.x86_64, operatingsystem=CentOS Linux 7 (Core), storagedriver=devicemapper
└ UpdatedAt: 2016-09-19T13:53:47Z
└ ServerVersion: 1.11.2
...

上面第1行命令启动了master节点上的swarm manage容器,并且将该容器的端口映射到主机的相同端口上;第3行的命令使用了-H的命令行参数,使得docker命令连接到swarm manage容器中运行info命令,从而得到的是swarm集群中的信息,而不是master主机上的本地docker信息。通过信息可知,现在集群中有一个agent加入了集群,这个agent的主机名是centos7-A,IP是192.168.71.167。
使用同样的方式,将centos7-B主机加入到集群中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[yangdong@centos7-B ~]$ docker -H tcp://localhost:2375 run -d swarm join --addr=192.168.71.168:2375 token://44d0dff23a4d98e3d3db07871b69357e
215b9626b0f8a46b9dda5973134a3537479f40caadf1a61f4c7fcb2048be6d45
[yangdong@centos7-B ~]$ docker -H tcp://192.168.71.131:2375 info
Containers: 16
Running: 2
Paused: 0
Stopped: 14
Images: 20
Server Version: swarm/1.2.5
Role: primary
Strategy: spread
Filters: health, port, containerslots, dependency, affinity, constraint
Nodes: 2
centos7-A: 192.168.71.167:2375
└ ID: JDDQ:ATPE:2OSM:BZJU:EAO7:ZTMV:2ZHC:IA3L:FSUX:JQNA:CAO3:MTN4
└ Status: Healthy
...
centos7-B: 192.168.71.168:2375
└ ID: AQ75:XUEN:DYUD:JDO5:Q67C:LQKO:FOVG:ECRH:RSFM:5F2P:P2NM:AN4G
└ Status: Healthy
...

可见centos-B也加入了集群。注意,第二个命令在agent节点上远程连接到了master上的swarm manage容器中查询到了Swarm集群的信息。
最后,通过Swarm启动一个容器,Swarm根据自己的调度算法自动选择一个主机来运行容器:

1
2
3
4
5
[yangdong@centos7 ~]$ docker -H tcp://192.168.71.131:2375 run -d ubuntu:14.04 sleep 60
47bde0fb0d50da54f9e838044d97d108e088001e2658b0274dbbb27bdf52d14a
[yangdong@centos7 ~]$ docker -H tcp://192.168.71.131:2375 ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
47bde0fb0d50 ubuntu:14.04 "sleep 60" 31 seconds ago Up 30 seconds centos7-B/lonely_goodall

通过上面输出的容器名,可以知道Swarm自动选择了centos7-B主机来启动容器。这样就完成了整个Swarm集群的部署,并且使用Swarm运行了一个容器。