《精通Docker第三版》完整目录:
第一章 Docker概览
第二章 创建容器镜像
第三章 存储和发布镜像
第四章 管理容器
第五章 Docker Compose
第六章 Windows容器
第七章 Docker Machine
第八章 Docker Swarm
第九章 Docker和Kubernetes
第十章 在公有云上运行Docker
第十一章 Portainer – 一个Docker的GUI
第十二章 Docker安全
第十三章 Docker工作流
第十四章 Docker进阶
本章中,我们一起来看看Docker Swarm。使用Docker Swarm,我们可以创建和管理Docker集群。Swarm可用于在多个主机上分发容器并具备对容器扩容的能力。本章涵盖的内容主要有:
Docker Swarm的介绍
Docker Swarm集群的不同角色
创建和管理Swarm
Docker Swarm服务和栈
Docker Swarm负载均衡和调度
技术准备
和此前章节一样,我们将继续使用本地安装的Docker。还有,本章中的截图均来自我个人偏好的操作系统macOS。
同样,我们将要运行的Docker命令在读者安装Docker所使用的三种操作系统上都可以使用。但其中有一些极少数的支持命令可能仅能用于macOS和Linux操作系统。
观看如下视频来查看代码的实时操作:
VIDEO
Docker Swarm简介
在进一步讲解之前,我需要说明一下有两种不同版本的Docker Swarm。一种是脱机版的Docker Swarm,仅支持到Docker 1.12并不再进行开发,但你可能会在一些老的文档中看到它。不推荐安装脱机版的Docker Swarm,因为Docker已于2017停止了对1.11.x版本的支持。
Docker1.12版引入了Docker Swarm模式。它将单机版Docker Swarm中的功能引入到了核心Docker引擎中,并添加了大量其它功能。因本书中讲解的是Docker 18.06及以上版本,我们将使用Docker Swarm模式,并在本章后续内容中将其直接称为Docker Swarm。
大家所运行的Docker版本已经内置了对Docker Swarm的支持,无需再进行Docker Swarm的安装,可通过运行如下命令来验证是否已经安装了Docker Swarm:
运行该命令后可以在Terminal中看到类似如下输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ docker swarm --help
Usage : docker swarm COMMAND
Manage Swarm
Commands :
ca Display and rotate the root CA
init Initialize a swarm
join Join a swarm as a node and /or manager
join -token Manage join tokens
leave Leave the swarm
unlock Unlock swarm
unlock -key Manage the unlock key
update Update the swarm
Run 'docker swarm COMMAND --help' for more information on a command .
如果有报错,请确保所运行的Docker为18.06或以上版本,我们在第一章 Docker概览 中讲解了Docker的安装。既然我们的Docker客户支持Docker Swarm,那么Swarm的意思究竟是什么呢?
Swarm是一个运行Docker主机的集合,被设置为在集群配置中相互进行交互。完成配置后你将可以对单个主机运行我们此前所学的所有命令,并让Docker Swarm来决定容器的位置,通过使用一个部署策略来决定启动容器的最合适的主机。
Docker Swarm由两类主机组成。下面我们来一起学习吧。
Docker Swarm集群中的角色
Docker Swarm中包含哪些角色呢?我们一起来看一个Docker Swarm集群中主机可承担的两种角色。
Swarm manager
Swarm manager是个所有Swarm主机的中央管理节点。Swarm manager节点用于发布所有命令来控制其它节点。你可以在节点间切换、加入节点、删除节点以及操作其它节点。
每个集群上可运行多个Swarm manager。对生产环境,建议至少运行5个Swarm manager,这表示集群要有超过两个Swarm manager节点发生故障时才会出现问题。Swarm manager使用Raft共识算法(参见扩展阅读部分来查看更多信息)来保持多个manager节点上的一致性状态。
Swarm worker
Swarm worker,此前我们称之为Docker主机,用于运行Docker容器。Swarm worker由Swarm manager来进行管理:
以上是Docker Swarm构成的图示。我们可以看到Docker Swarm manager与角色为worker的各个Swarm主机间进行通讯。worker节点有一些连接级别,一会儿我们会说到。
创建和管理Swarm
下面我们来看Swarm的使用以及如何完成如下任务:
创建集群
加入worker节点
列出节点
管理集群
创建集群
我们先来创建一个集群,从Swarm manager开始。因为我们将要在本地机器上创建一个多节点集群,要通过运行如下命令来使用Docker Machine来运行一个主机:
$ docker -machine create \
-d virtualbox \
swarm -manager
输出的删节版如下所示:
( swarm -manager ) Creating VirtualBox VM . . .
( swarm -manager ) Creating SSH key . . .
( swarm -manager ) Starting the VM . . .
( swarm -manager ) Check network to re -create if needed . . .
( swarm -manager ) Waiting for an IP . . .
Waiting for machine to be running , this may take a few minutes . . .
Checking connection to Docker . . .
Docker is up and running !
To see how to connect your Docker Client to the Docker Engine running on this virtual machine , run : docker -machine env swarm -manager
此时Swarm manager节点就启动并使用VirtualBox运行了。我们可以通过运行如下命令来进一步确认:
这时会看到类似如下输出:
$ docker -machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
swarm -manager - virtualbox Running tcp : //192.168.99.100:2376 v18.09.5
下面让我们将Docker Machine指向新的Swarm manager。从前面创建Swarm manager的输出中可以看到提示如何指向该节点:
$ docker -machine env swarm -manager
这会显示需要配置本地Docker客户端亚与新启动的Docker主机对话的命令。以下代码块显示我运行该命令时返回的配置:
export DOCKER_TLS_VERIFY ="1"
export DOCKER_HOST ="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH ="/Users/alan/.docker/machine/machines/swarm-manager"
export DOCKER_MACHINE_NAME ="swarm-manager"
# Run this command to configure your shell:
# eval $(docker-machine env swarm-manager)
运行以上命令时,提示我们运行如下命令来指向Swarm manager节点:
$ eval $ ( docker -machine env swarm -manager )
现在如果如果再来看我们的主机上有哪些机器,可以看到我们有Swarm master主机,现在被置为ACTIVE,表示现在我们可以在上面运行命令了:
会显示类似如下信息:
$ docker -machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
swarm -manager * virtualbox Running tcp : //192.168.99.100:2376 v18.09.5
现在我们的第一个主机已经开启并运行了,是时候添加两个worker节点。只需运行如下命令来再启动两个Docker主机:
$ docker -machine create \
-d virtualbox \
swarm -worker01
$ docker -machine create \
-d virtualbox \
swarm -worker02
启动了这两个主机之后,你可以使用如下命令来获取主机列表:
应该会显示类似如下内容:
$ docker -machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
swarm -manager * virtualbox Running tcp : //192.168.99.100:2376 v18.09.5
swarm -worker01 - virtualbox Running tcp : //192.168.99.101:2376 v18.09.5
swarm -worker02 - virtualbox Running tcp : //192.168.99.102:2376 v18.09.5
值得指出的是,到此为止,我们还没有创建Swarm集群,只是启动了Swarm 会运行的一些主机。
ℹ️你可能注意到了运行docker-machine ls时有一列为SWARM。它只有在使用Docker Machine内置的脱机Docker Swarm命令启动Docker主机时才会包含信息。
向集群添加Swarm管理节点
我们来启动Swarm manager节点。这需要将Docker Machine命令的一些结果传递给我们的主机。创建管理节点所需运行的命令如下:
$ docker $ ( docker -machine config swarm -manager ) swarm init \
--advertise -addr $ ( docker -machine ip swarm -manager ) : 2377 \
--listen -addr $ ( docker -machine ip swarm -manager ) : 2377
你应该会类似如下的信息:
Swarm initialized : current node ( mzglz35ns4vaxz7i999q1lhvk ) is now a manager .
To add a worker to this swarm , run the following command :
docker swarm join --token SWMTKN -1 -29300vs05qz0k1mk5vzs31vpmnjelr3w8bfb2nf8jq0dg9uw3r -7fegze0zipgqz4zyr52twap2s 192.168.99.100 : 2377
To add a manager to this swarm , run 'docker swarm join-token manager' and follow the instructions .
可以从输出中看到,初始化manager节点,会给到一个独立的令牌(token)。上例中,完整的令牌为SWMTKN-1-29300vs05qz0k1mk5vzs31vpmnjelr3w8bfb2nf8jq0dg9uw3r-7fegze0zipgqz4zyr52twap2s。该令牌在worker进行认证和加入我们的集群时需要用到。
向集群添加Swarm的worker节点
要将两个worker节点加入到集群中,运行如下的命令。首先,我们设置环境变量来存储令牌,读者请确保将令牌替换为自己在初始化manager节点所获得的那个:
$ SWARM_TOKEN =SWMTKN -1 -29300vs05qz0k1mk5vzs31vpmnjelr3w8bfb2nf8jq0dg9uw3r -7fegze0zipgqz4zyr52twap2s
下面我们就可惟运行如下命令来添加swarm-worker01到节点了:
$ docker $ ( docker -machine config swarm -worker01 ) swarm join \
--token $ SWARM_TOKEN \
$ ( docker -machine ip swarm -manager ) : 2377
运行如下命令来添加swarm-worker02:
$ docker $ ( docker -machine config swarm -worker02 ) swarm join \
--token $ SWARM_TOKEN \
$ ( docker -machine ip swarm -manager ) : 2377
以上两条命令运行后都会收到节点成功加入集群的确认信息:
This node joined a swarm as a worker .
列出节点
可以通过运行如下命令来查看Swarm:
查看本地Docker客户是否仍配置为连接到Swarm manager节点,如果没有,重新运行如下命令:
$ eval $ ( docker -machine env swarm -manager )
这时我们已连接到了Swarm manager节点,你可以运行如下命令:
这会连接到Swarm master主机并查看形成我们集群的所有节点。你应该会看到所有的三个节点被列出:
$ docker -machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
swarm -manager * virtualbox Running tcp : //192.168.99.100:2376 v18.09.5
swarm -worker01 - virtualbox Running tcp : //192.168.99.101:2376 v18.09.5
swarm -worker02 - virtualbox Running tcp : //192.168.99.102:2376 v18.09.5
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
mzglz35ns4vaxz7i999q1lhvk * swarm -manager Ready Active Leader 18.09.5
f2pezx3uyntagav2fe3amiy34 swarm -worker01 Ready Active 18.09.5
ek2yyqg1bs6vwlp8lbgy58nhb swarm -worker02 Ready Active 18.09.5
管理集群
我们来看如何对所创建的所有集群节点为执行管理。
只有两种方式来管理这些Swarm主机和每个主机上所创建的容器,但首先你需要知道有关它们的一些信息。
在集群上查找信息
我们已经看到可以使用本地Docker客户端来列出集群中的节点,因为它已配置为连接到Swarm manager主机。我们只需输入如下命令:
这会显示有关主机的大量信息,可以从如下输出中看到,我做了部分的截取:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
Containers : 0
Running : 0
Paused : 0
Stopped : 0
Images : 0
Server Version : 18.09.5
Storage Driver : overlay2
Backing Filesystem : extfs
Supports d_type : true
Native Overlay Diff : true
Logging Driver : json -file
Cgroup Driver : cgroupfs
Plugins :
Volume : local
Network : bridge host macvlan null overlay
Log : awslogs fluentd gcplogs gelf journald json -file local logentries splunk syslog
Swarm : active
NodeID : mzglz35ns4vaxz7i999q1lhvk
Is Manager : true
ClusterID : oc05h3ryhqdwzwt6vj1bdbw7b
Managers : 1
Nodes : 3
Default Address Pool : 10.0.0.0 /8
SubnetSize : 24
Orchestration :
Task History Retention Limit : 5
Raft :
Snapshot Interval : 10000
Number of Old Snapshots to Retain : 0
Heartbeat Tick : 1
Election Tick : 10
Dispatcher :
Heartbeat Period : 5 seconds
CA Configuration :
Expiry Duration : 3 months
Force Rotate : 0
Autolock Managers : false
Root Rotation In Progress : false
Node Address : 192.168.99.100
Manager Addresses :
192.168.99.100 : 2377
Runtimes : runc
Default Runtime : runc
Init Binary : docker -init
containerd version : bb71b10fd8f58240ca47fbb579b9d1028eea7c84
runc version : 2b18fe1d885ee5083ef9f0838fee39b62d653e30
init version : fec3683
Security Options :
seccomp
Profile : default
Kernel Version : 4.14.111 -boot2docker
Operating System : Boot2Docker 18.09.5 ( TCL 8.2.1 )
OSType : linux
Architecture : x86_64
CPUs : 1
Total Memory : 989.4MiB
Name : swarm -manager
ID : TWTI : PMGO : M7RQ : J6OT : XMAT : SHLU : RI4Y : TYHX : TBF3 : 6LBR : X37D : AS3D
可以看到,输出中有Swarm部分为集群相关信息,但我们只能对客户端当前配置连接的主机运行docker info命令。所幸docker node命令也对集群可用,因此我们可以获取集群中各节点的信息,例如运行如下命令:
$ docker node inspect swarm -manager --pretty
小贴士: 对docker node inspect 命令使用–pretty标记会像下面那样将输出渲染为易读的格式。如果不添加–pretty,会返回包含inspect对集群运行查看结果的原生JSON对象。
我们的Swarm manager:节点应该会返回如下信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ID : mzglz35ns4vaxz7i999q1lhvk
Hostname : swarm -manager
Joined at : 2019 -04 -21 13 : 47 : 55.676699053 +0000 utc
Status :
State : Ready
Availability : Active
Address : 192.168.99.100
Manager Status :
Address : 192.168.99.100 : 2377
Raft Status : Reachable
Leader : Yes
Platform :
Operating System : linux
Architecture : x86_64
Resources :
CPUs : 1
Memory : 989.4MiB
Plugins :
Log : awslogs , fluentd , gcplogs , gelf , journald , json -file , local , logentries , splunk , syslog
Network : bridge , host , macvlan , null , overlay
Volume : local
Engine Version : 18.09.5
Engine Labels :
- provider =virtualbox
运行相同的命令,但这次针对其中的一个worker节点:
$ docker node inspect swarm -worker01 --pretty
会得到类似如下信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ID : f2pezx3uyntagav2fe3amiy34
Hostname : swarm -worker01
Joined at : 2019 -04 -21 13 : 57 : 01.514666254 +0000 utc
Status :
State : Ready
Availability : Active
Address : 192.168.99.101
Platform :
Operating System : linux
Architecture : x86_64
Resources :
CPUs : 1
Memory : 989.4MiB
Plugins :
Log : awslogs , fluentd , gcplogs , gelf , journald , json -file , local , logentries , splunk , syslog
Network : bridge , host , macvlan , null , overlay
Volume : local
Engine Version : 18.09.5
Engine Labels :
- provider =virtualbox
但是可以看到,这里缺失了manager功能状态的相关信息。这是因为worker节点无需知道manager节点的状态,它们只需知道它们允许从manager节点接收指令。
通过这种方式,我们可以看到该主机的相关信息,比如容器的个数、主机上镜像的个数以及CPU和内存的相关信息,还有其它的一些信息。
升级worker节点
假设你需要对单manager节点进行一些维护,但同时又想维持集群的可用性。完全没有问题,你可以将一个worker升级为manager节点。
我们本地启动和运行的是一个三节点集群,那么我们来将swarm-worker01升级为新的manager节点。通过运行如下命令来实现升级:
$ docker node promote swarm -worker01
在执行该命令后应该会收到一条确定节点已完成升级的消息:
Node swarm -worker01 promoted to a manager in the swarm .
通过运行如下命令列出节点:
这是会显示MANAGER STATUS列有两个节点:
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
mzglz35ns4vaxz7i999q1lhvk * swarm -manager Ready Active Leader 18.09.5
f2pezx3uyntagav2fe3amiy34 swarm -worker01 Ready Active Reachable 18.09.5
ek2yyqg1bs6vwlp8lbgy58nhb swarm -worker02 Ready Active 18.09.5
但我们的swarm-manager节点仍是主管理节点。下面就来进行相关的操作。
降级manager节点
你可能建立了一个二对二的集群,但将manager节点降级为worker节点,你只需运行这条命令:
$ docker node demote swarm -manager
你会再次得到一条反馈信息:
Manager swarm -manager demoted in the swarm .
此时已对节点进行了降级,你可以在集群内通过运行如下命令来查看各节点状态:
因我们的本地Docker客户端仍然指向刚刚降级的节点,我们会收到如下信息:
Error response from daemon : This node is not a swarm manager . Worker nodes can 't be used to view or modify cluster state . Please run this command on a manager node or promote the current node to a manager .
我们已经学习到,更新本地客户端配置来使用Docker Machine与其它节点进行通讯非常的容易。运行如下命令来将本地客户端指向新的管理节点:
$ eval $ ( docker -machine env swarm -worker01 )
译者注: 在执行以上语句时若出现报错,需按提示先重新生成证书:docker-machine regenerate-certs swarm-worker01
此时我们的客户端与manager节点再次建立的对话,重新运行如下命令:
不出所料应该会列出所有节点:
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
mzglz35ns4vaxz7i999q1lhvk swarm -manager Ready Active 18.09.5
f2pezx3uyntagav2fe3amiy34 * swarm -worker01 Ready Active Leader 18.09.5
ek2yyqg1bs6vwlp8lbgy58nhb swarm -worker02 Ready Active 18.09.5
清空(Drain)节点
临时将节点从节点中进行删除来进行维护,我们需要将节点的状态设置为Drain。我们来清空之前的管理节点。通过运行如下命令来实现清空:
$ docker node update --availability drain swarm -manager
在进行清空操作时会停止该节点的所有新任务,比如新容器的启动或执行的操作。在阻止新任务的执行之后,所有运行中的任务会从正在执行清空操作的节点迁移到其它处于ACTIVE状态的节点上。
通过如下的Terminal输出可以看到,列出节点会显示 swarm-manager在AVAILABILITY一列为Drain:
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
mzglz35ns4vaxz7i999q1lhvk swarm -manager Ready Drain 18.09.5
f2pezx3uyntagav2fe3amiy34 * swarm -worker01 Ready Active Leader 18.09.5
ek2yyqg1bs6vwlp8lbgy58nhb swarm -worker02 Ready Active 18.09.5
此时这个节点已不再接收新的任务了,所有的任务已迁移到其它两个节点上,我们可以放心地执行维护,比如重启该主机。要重启Swarm manager,运行如下两条命令,确保你已连接到该Docker主机(你应该会看到boot2docker的欢迎消息,在命令下进行显示):
译者注: 在新的版本中并没有出现boot2docker的欢迎消息,只有一条 TC的欢迎信息
$ docker -machine ssh swarm -manager
$ sudo reboot
$ docker -machine ssh swarm -manager
( '>' )
/) TC ( \ Core is distributed with ABSOLUTELY NO WARRANTY .
( /-_ --_ -\ ) www . tinycorelinux . net
docker @ swarm -manager : ~ $ sudo reboot
docker @ swarm -manager : ~ $ Connection to 127.0.0.1 closed by remote host .
exit status 255
在该主机进行重启之后,运行这条命令:
它应该会显示该节点的AVAILABILITY为Drain。将该节点两次添加到集群中,只需运行下述命令将AVAILABILITY修改为active即可:
$ docker node update --availability active swarm -manager
可以从如下的Terminal输出中看到,该节点现在为active,表示新的任务可以在其上进行执行:
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
mzglz35ns4vaxz7i999q1lhvk swarm -manager Ready Drain 18.09.5
f2pezx3uyntagav2fe3amiy34 * swarm -worker01 Ready Active Leader 18.09.5
ek2yyqg1bs6vwlp8lbgy58nhb swarm -worker02 Ready Active 18.09.5
$ docker node update --availability active swarm -manager
swarm -manager
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
mzglz35ns4vaxz7i999q1lhvk swarm -manager Ready Active 18.09.5
f2pezx3uyntagav2fe3amiy34 * swarm -worker01 Ready Active Leader 18.09.5
ek2yyqg1bs6vwlp8lbgy58nhb swarm -worker02 Ready Active 18.09.5
我们已经学习了如何创建和管理Docker Swarm集群,下面就来看如何运行创建和扩展服务等任务。
Docker Swarm服务和栈
至此,我们学习了如下命令:
$ docker swarm < command >
$ docker node < command >
这两条命令让我们可以通过一群已存在的Docker主机来启动和管理我们的Docker Swarm集群。接下来我们将学习下面这两条命令:
$ docker service < command >
$ docker stack < command >
service和stack命令让我们可以执行的任务有:在Swarm集群内启动、扩展和管理容器。
服务
service命令是一种利用Swarm集群启动容器的方式。我们来学习在Swarm集群上启动一个非常简单的单容器服务。运行如下命令来进行实现:
$ docker service create \
--name cluster \
--constraint "node.role == worker" \
-p : 80 : 80 /tcp \
russmckendrick /cluster
这会创建一个名为cluster的服务,包含一个以容器80端口映射到宿主机的单容器,它仅会在角色为worker的节点上进行运行。
在对这一服务进行其它操作之前,先检查在浏览器是否可以正常打开。为此,我们需要知道两个worker节点的 IP 地址。首先通过运行如下命令来查看哪些是worker节点:
知道了各个节点的角色之后,你可以通过运行这条命令来查看节点的 IP 地址:
查看如下的 Terminal 输出:
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
biwvrfmxo6ftofnodt5es2q5i swarm -manager Ready Active 18.09.5
f2pezx3uyntagav2fe3amiy34 * swarm -worker01 Ready Active Leader 18.09.5
ighichgqs4aqc380vo3pg1ye4 swarm -worker02 Ready Active 18.09.5
$ docker -machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
swarm -manager - virtualbox Running tcp : //192.168.99.102:2376 v18.09.5
swarm -worker01 * virtualbox Running tcp : //192.168.99.100:2376 v18.09.5
swarm -worker02 - virtualbox Running tcp : //192.168.99.101:2376 v18.09.5
译者注: 重新启动后 worker 节点出现故障重新加入,因而此处 ID 和 IP 地址发生了变化
我的worker节点为swarm-manager和swarm-worker02,IP 地址分别为192.168.99.100和192.168.99.102。
访问worker节点的任一 IP 地址,如http://192.168.99.101/或http://192.168.99.102/,浏览器中会显示russmckendrick/cluster应用的输出,内容为Docker Swarm的图像及展示该页面的容器名:
此时我们的服务已在集群上运行,可以查找更多的信息了。首先,我们可以通过运行这条命令来再次列出服务:
本例中,应该会返回我们所启动的单个服务,名为cluster。
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
083hzsrmvjrn cluster replicated 1 /1 russmckendrick /cluster : latest * : 80 -> 80 /tcp
可以看到这是一个复制的服务,并且1/1的容器为active。下面你可以通过运行inspect命令来深入挖掘该服务更多的信息:
$ docker service inspect cluster --pretty
这会返回有关服务的详细信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ docker service inspect cluster --pretty
ID : 083hzsrmvjrnjocpcaun2cxta
Name : cluster
Service Mode : Replicated
Replicas : 1
Placement :
Constraints : [ node . role == worker ]
UpdateConfig :
Parallelism : 1
On failure : pause
Monitoring Period : 5s
Max failure ratio : 0
Update order : stop -first
RollbackConfig :
Parallelism : 1
On failure : pause
Monitoring Period : 5s
Max failure ratio : 0
Rollback order : stop -first
ContainerSpec :
Image : russmckendrick /cluster : latest @ sha256 : 702d320906c5dd4429dfc488e4402e3f12737eea30f1d11ed2cf02d75b74294c
Init : false
Resources :
Endpoint Mode : vip
Ports :
PublishedPort = 80
Protocol = tcp
TargetPort = 80
PublishMode = ingress
你也许注意到了截至目前,我们都无需关心服务运行在两个worker节点的哪一个上。这是Docker Swarm的一个重要功能,因为它完全消除了对于单个容器位置的排忧。
在我们对服务进行扩展之前,先通过运行这些命令来快速看下容器运行在哪个主机上:
$ docker node ps
$ docker node ps swarm -manager
$ docker node ps swarm -worker02
这会列出每个主机上所运行的容器。默认情况下,它会列出命令所对应运行的主机,我这里是swarm-worker01:
$ docker node ps
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
$ docker node ps swarm -manager
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
$ docker node ps swarm -worker02
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
qjp6uwrllyd5 cluster . 1 russmckendrick /cluster : latest swarm -worker02 Running Running 3 hours ago
我们来看将我们的服务扩展到6个应用容器实例。运行如下命令来扩展并查看我们的服务:
$ docker service scale cluster =6
$ docker service ls
$ docker node ps swarm -manager
$ docker node ps swarm -worker02
我们只查看两个节点,因为这是最早我们用于启动服务的worker节点。可以从如下的Terminal输出中看到,现在每个worker节点上有三个在运行的容器:
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
083hzsrmvjrn cluster replicated 6 /6 russmckendrick /cluster : latest * : 80 -> 80 /tcp
$ docker node ps swarm -manager
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
rdgdotowxui3 cluster . 2 russmckendrick /cluster : latest swarm -manager Running Running 56 seconds ago
lu4hc33krmmc cluster . 3 russmckendrick /cluster : latest swarm -manager Running Running 56 seconds ago
bxywy17tiruq cluster . 5 russmckendrick /cluster : latest swarm -manager Running Running 56 seconds ago
$ docker node ps swarm -worker02
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
qjp6uwrllyd5 cluster . 1 russmckendrick /cluster : latest swarm -worker02 Running Running 3 hours ago
r2aou8zqqcas cluster . 4 russmckendrick /cluster : latest swarm -worker02 Running Running about a minute ago
6nahokzatjxb cluster . 6 russmckendrick /cluster : latest swarm -worker02 Running Running about a minute ago
在我们学习栈(stack)之前,先删除掉服务。通过运行如下命令来进行删除:
$ docker service rm cluster
这会删除所有的容器,但在主机上保留所下载的镜像。
栈
完全有可能使用Swarm和服务创建一个相当复杂、高可用多容器应用。在一个非Swarm集群中,手动为应用启动每个容器工作量会有些大,并很难分享。至此Docker创建了功能,允许你在Docker Compose文件中定义服务。
以下的Docker Compose文件,名称应为 docker-compose.yml,会启动与前面部分相同的服务:
version : "3"
services :
cluster :
image : russmckendrick /cluster
ports :
- "80:80"
deploy :
replicas : 6
restart_policy :
condition : on -failure
placement :
constraints :
- node . role == worker
可以看到,栈可以由多个服务组成,在Docker Compose文件中定义在不同的service代码块下。
除了常规的Docker Compose命令外,你可以添加deploy代码块,这是定义与栈的Swarm元素相关的地方。
上例中,我们说过要有6份拷贝,分布在两个worker节点上。同时,我们更新了默认的重启策略,在前一部分查看服务时显示为paused,这样在每次容器不响应时,都会进行重启。
$ docker stack deploy --compose -file =docker -compose . yml cluster
在使用Docker Compose启动容器时,Docker会创建一个新的网络,然后在上面启动服务。
你可以运行这条命令来查看栈的状态:
这会显示创建了单个服务。可以通过运行如下命令来获取栈所创建服务的明细信息:
$ docker stack services cluster
最后,运行如下命令会显示栈内容器所运行的位置:
$ docker stack ps cluster
参见如下Terminal输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ docker stack deploy --compose -file =docker -compose . yml cluster
Creating network cluster_default
Creating service cluster_cluster
$ docker stack ls
NAME SERVICES ORCHESTRATOR
cluster 1 Swarm
$ docker stack services cluster
ID NAME MODE REPLICAS IMAGE PORTS
81pyhx4ke56f cluster_cluster replicated 6 /6 russmckendrick /cluster : latest * : 80 -> 80 /tcp
$ docker stack ps cluster
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
wrgq4hlosp8g cluster_cluster . 1 russmckendrick /cluster : latest swarm -manager Running Running about a minute ago
ugeym0kh91u8 cluster_cluster . 2 russmckendrick /cluster : latest swarm -worker02 Running Running about a minute ago
ft68tw0dwxe1 cluster_cluster . 3 russmckendrick /cluster : latest swarm -manager Running Running about a minute ago
pvaor1my50rb cluster_cluster . 4 russmckendrick /cluster : latest swarm -worker02 Running Running about a minute ago
zc2u458ygt8d cluster_cluster . 5 russmckendrick /cluster : latest swarm -manager Running Running about a minute ago
opyqdvmg4pog cluster_cluster . 6 russmckendrick /cluster : latest swarm -worker02 Running Running about a minute ago
同样地,你可以使用节点的 IP 地址来访问栈,并会被路由到其中一个运行的容器上。要删除栈,只需运行如下命令:
$ docker stack rm cluster
这会删除栈启动时创建的所有服务和网络。
删除Swarm集群
在继续学习之前,因为我们在下一部分中不再需要该集群,你可以通过运行如下命令来删除我们的Swarm集群:
$ docker -machine rm swarm -manager swarm -worker01 swarm -worker02
如果出于某种原因你需要再次启动这一Swarm集群,只需按照本章开始处所介绍的方法再次创建集群即可。
负载均衡、叠加和调度
在以上的几个部分中,我们学习了启动服务和栈。要访问我们启动的应用,可以使用集群中的任一主机 IP 地址,这是如何做到的呢?
Ingress负载均衡
Docker Swarm有一个内置的ingress负载均衡器,使其易于将流量分配到面向外部的容器中。
这表示你可以在Swarm集群中的应用暴露给服务,例如,像Amazon弹性负载均衡器这样的外部负载均衡器,知道将请求路由到正确的容器上而不用管当前托管它的主机,如下图所示:
这表示我们的应用可以增容或缩减、出错或更新,都无需对外部负载均衡器进行重新配置。
覆盖网络
在示例中,我们启动了一个运行单应用的简单服务。假设我们需要为应用添加数据库层,通常是网络中固定的点,如何来实现呢?
Docker Swarm的覆盖网络(network overlay)层对跨主机启动容器的网络进行扩展,也就是说每个服务或栈可以启用其自己的网络。那么我们运行MongoDB的数据库容器可以通过在覆盖网络的27017端口上运行以在所有其它容器中进行访问,而不用理会运行容器的主机。
你可能在想,稍安勿躁。那是否表示我需要在应用的配置中硬编码入 IP 地址呢?这显然不适合解决Docker Swarm所要解决的问题,所以你不需要这么做。
每个覆盖网络有其自身内建的DNS服务,也即网络中启动的每一个容器都可以将相同网络内的另一个容器的主机名解析到当前所分配的 IP 地址上。这意味着当我们配置应用连接数据库实例时,只需告诉它连接到mongodb:27017这样的地址,它就会连接到MongoDB容器上。
这样我们的图示如下:
在采用这一方案时需要考虑到其它的因素,但我们要到第十四章 Docker进阶 中讲解了。
调度
在写本单时,Docker Swarm只一个单调度(schedule)策略,称为Spread。该策略会在达到启动服务或栈时所定义的约束条件时在最小化载入节点上运行计划任务。大部分情况下,你不需要为服务添加太多的约束。
Docker Swarm当前暂不支持的功能是亲和性(affinity)和反亲和性(anti-affinity)规则。虽然通过使用约束可以很容易绕过,我会建议不要把事件复杂化,如果在定义服务时加入过多约束很容易会导致主机过载或创建故障的单节点。
总结
本章中,我们探讨了Docker Swarm。学习了如何安装 Docker Swarm以及组成Docker Swarm的组件。我们讲解了如何使用Docker Swarm:加入、列出和管理Swarm manager和worker节点。我们回顾了service和stack命令以及如何使用它们,还讲到了Swarm内置的ingress负载均衡器、覆盖网络和调试器。
下一章中,我们将会学习Docker Swarm一种替代,称为Kubernetes。它由Docker及其它供应商所支持。
课后问题
是非题:你应该使用单机 Docker Swarm而非内置的Docker Swarm模式来运行Docker Swarm?
在初始化Docker Swarm manager之后需要做哪两件事来向Docker Swarm集群添加worker节点?
你会使用哪条命令来在Docker Swarm集群内查看各节点的状态?
为docker node inspect swarm-manager添加哪个标记来让其可读性更强?
如何升级一个节点为manager?
使用什么命令来对服务进行扩容?
扩展阅读
有关Raft共识算法(consensus algorithm)更详细的说明,我推荐题为The Secret Lives of Data的 PPT,可通过http://thesecretlivesofdata.com/raft/进行查看。它以易于掌握的动画讲解了manager节点背后所发生的整个过程。