Docker 管理设计模式(二)
五、扩展服务
Docker Engine 适合开发运行在彼此隔离的 Docker 容器中的轻量级应用。Docker 容器能够提供自己的网络和文件系统。
问题
Docker 引擎(在原生群模式之前)被设计用来运行必须单独启动的 Docker 容器。考虑需要创建服务的多个副本或实例的用例。随着 Docker 容器中运行的应用的客户端负载增加,该应用可能需要在多个节点上运行。Docker 引擎的一个限制是,每次为 Docker 映像启动 Docker 容器时,必须运行docker run命令。如果 Docker 应用必须在三个节点上运行,那么docker run <img>命令也必须在每个节点上运行,如图 5-1 所示。Docker 引擎中不提供扩展应用或运行多个副本的功能(在 Docker 1.12 原生群模式支持之前)。
图 5-1。
Docker engine without provision for scaling
解决方案
Docker 群模式提供了扩展 Docker 服务的功能。服务抽象与零个或多个副本(任务)相关联,每个任务为服务启动一个 Docker 容器。该服务可以根据需要扩大或缩小规模,以运行更多/更少的副本。只需一个docker service scale <svc>=<replicas>命令,服务就可以运行所需数量的副本,如图 5-2 所示。如果要跨分布式集群启动 10 个服务副本,那么一个命令就能够提供扩展。
图 5-2。
Docker Swarm mode with provision for scaling
只有复制的服务才支持伸缩。全局服务在集群中的每个节点上运行一个服务任务。第三章介绍了扩展服务,在本章中,我们将讨论第三章中未讨论的扩展服务的其他一些方面。本章涵盖以下主题:
- 设置环境
- 创建复制的服务
- 扩大服务规模
- 缩小服务规模
- 移除服务
- 全球服务无法扩展
- 在同一命令中扩展多个服务
- 离开集群的节点上的服务副本替换
设置环境
在 Docker 上为 Swarm 创建一个三节点 Swarm,这在第三章讨论。你在另一章中为 AWS Swarm 创建的 Docker 可能会在本章中使用。为 Swarm manager 获取 EC2 实例的公共 IP 地址。
使用用户“docker”通过 SSH 登录到 Swarm manager EC2 实例。
[root@localhost ∼]# ssh -i "docker.pem" docker@34.200.225.39
Welcome to Docker!
命令列出了集群中的节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
ilru4f0i280w2tlsrg9hglwsj ip-172-31-10-132.ec2.internal Ready Active
w5to186ipblpcq390625wyq2e ip-172-31-37-135.ec2.internal Ready Active
zkxle7kafwcmt1sd93kh5cy5e * ip-172-31-13-155.ec2.internal Ready Active Leader
创建复制的服务
正如第四章所讨论的,Docker Swarm 模式支持两种类型的服务——全局服务和复制服务。默认为复制模式。只有复制的服务可以扩展。接下来,使用docker service create命令为 MySQL 数据库创建一个复制服务,最初由一个副本组成,如--replicas选项中所指定的。如果没有指定--replicas选项,默认的副本数量也是一个。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> mysql
ndu4kwqk9ol7e7wxvv5bremr4
使用docker service ls列出服务。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ndu4kwqk9ol7 mysql replicated 1/1 mysql:latest
由于服务副本需要一段时间(尽管只有几秒钟)才能启动,最初 0/1 个副本可能会列在REPLICAS列中,这意味着运行一个服务副本的理想状态还没有达到。几秒钟后运行相同的命令,并且1/1 REPLICAS应该被列为正在运行。
可选地,也可以通过设置--mode选项来运行docker service create命令。如果先前创建了mysql服务,则删除该服务,并按如下方式使用--mode选项。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --mode replicated \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> mysql
rl2s2ptgbs9z2t7fy5e63wf2j
创建的mysql服务没有--mode复制选项。用docker service ps mysql列出服务副本或任务。将列出一个副本。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
yrikmh7mciv7 mysql.1 mysql: ip-172-31-13- Running Running 21 latest 155.ec2.internal seconds ago
如果省略了--replicas选项,默认情况下会创建一个服务副本。应该提到的是,运行 MySQL 数据库的多个副本并不自动意味着它们共享数据,因此访问一个副本不会给你与另一个副本相同的数据。使用挂载共享数据将在第六章中讨论。
扩大服务规模
具有以下语法的docker service scale命令可用于扩大/缩小服务,这改变了服务的期望状态。
docker service scale SERVICE=REPLICAS [SERVICE=REPLICAS...]
首先,将服务扩展到三个副本。
∼ $ docker service scale mysql=3
mysql scaled to 3
随后,三个任务被按计划列在群中的三个节点上。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
yrikmh7mciv7 mysql.1 mysql: ip-172-31-13- Running Running 37 latest 155.ec2.internal seconds ago
3zxmotmy6n2t mysql.2 mysql: ip-172-31-37- Running Running 7 latest 135.ec2.internal seconds ago
rdfsowttd3b9 mysql.3 mysql: ip-172-31-10- Running Running 7 latest 132.ec2.internal seconds ago
除了 manager 节点上的一个副本之外,两个 worker 节点上还各启动了一个副本。如果在管理器节点上运行docker ps命令,那么只列出一个用于mysql Docker 映像的 Docker 容器。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6d2161a3b282 mysql: "docker- 50 seconds ago Up 49 3306/tcp mysql.1.yrikmh7mci latest entrypoint..." seconds v7dsmql1nhdi62l
还可以使用带有--replicas选项的docker service update命令来扩展服务。例如,将其扩展到 50 个副本。
∼ $ docker service update --replicas=50 mysql
mysql
该服务扩展到 50 个副本,随后列出了 50 个服务任务。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
t026kjbsgzmq mysql.1 mysql:latest ip-172-31-37-135.ec2.internal Running Running 11 seconds ago
f3tx2kbe55dh mysql.2 mysql:latest ip-172-31-10-132.ec2.internal Running Running 20 seconds ago
5mzej75us115 mysql.3 mysql:latest ip-172-31-10-132.ec2.internal Running Running 13 seconds ago
wluix1b3z863 mysql.4 mysql:latest ip-172-31-13-155.ec2.internal Running Preparing 13 seconds ago
9ld8smvahk9g mysql.5 mysql:latest ip-172-31-13-155.ec2.internal Running Running 47 seconds ago
3tgw8ni5mfi1 mysql.6 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
1gm8e7pxkg0o mysql.7 mysql:latest ip-172-31-13-155.ec2.internal Running Running 46 seconds ago
iq5p2g48oagq mysql.8 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
i4yh072h1gs6 mysql.9 mysql:latest ip-172-31-13-155.ec2.internal Running Running 46 seconds ago
r1z5tgu0dg13 mysql.10 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
mekfjvxi9pds mysql.11 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
nd8f2pr4oivc mysql.12 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
xou9hztlj637 mysql.13 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
t95flokvca2y mysql.14 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
rda5shwwfmsc mysql.15 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
ibb2fk2llm3w mysql.16 mysql:latest ip-172-31-13-155.ec2.internal Running Running 47 seconds ago
st4ofpvrfaip mysql.17 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
iw4daunt6s63 mysql.18 mysql:latest ip-172-31-37-135.ec2.internal Running Running 47 seconds ago
vk4nzq7utyl2 mysql.19 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
oj59qjcy51qw mysql.20 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
wiou769z8xeh mysql.21 mysql:latest ip-172-31-10-132.ec2.internal Running Running 47 seconds ago
5exwimn64w94 mysql.22 mysql:latest ip-172-31-10-132.ec2.internal Running Running 48 seconds ago
agqongnh9uu3 mysql.23 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
ynkvjwgqqqlx mysql.24 mysql:latest ip-172-31-37-135.ec2.internal Running Running 47 seconds ago
yf87kbsn1cga mysql.25 mysql:latest ip-172-31-13-155.ec2.internal Running Running 10 seconds ago
xxqj62007cxd mysql.26 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
50ym9i8tjwd5 mysql.27 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
7btl2pga1l5o mysql.28 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
62dqj60q1ol8 mysql.29 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
psn7zl4th2zb mysql.30 mysql:latest ip-172-31-37-135.ec2.internal Running Preparing 16 seconds ago
khsj2an2f5gk mysql.31 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
rzpndzjpmuj7 mysql.32 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
9zrcga93u5fi mysql.33 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
x565ry5ugj8m mysql.34 mysql:latest ip-172-31-10-132.ec2.internal Running Running 48 seconds ago
o1os5dievj37 mysql.35 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
dritgxq0zrua mysql.36 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
n8hs01m8picr mysql.37 mysql:latest ip-172-31-37-135.ec2.internal Running Running 47 seconds ago
dk5w0qnkfb63 mysql.38 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
joii103na4ao mysql.39 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
db5hz7m2vac1 mysql.40 mysql:latest ip-172-31-13-155.ec2.internal Running Running 46 seconds ago
ghk6s12eeo48 mysql.41 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
jbi8aksksozs mysql.42 mysql:latest ip-172-31-13-155.ec2.internal Running Running 47 seconds ago
rx3rded30oa4 mysql.43 mysql:latest ip-172-31-37-135.ec2.internal Running Running 47 seconds ago
c3zaacke440s mysql.44 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
l6ppiurx4306 mysql.46 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
of06zibtlsum mysql.47 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
kgjjwlc9zmp8 mysql.48 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
rw1icgkyw61u mysql.49 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
j5jpl9a5jgbj mysql.50 mysql:latest ip-172-31-10-132.ec2.internal Running Running 47 seconds ago
小规模的 MySQL 数据库服务可能不会从扩展到 50 个副本中受益,但是企业规模的应用可以使用 50 个甚至更多的副本。
缩小服务规模
一项服务可以在扩大的同时缩小。一个服务甚至可以缩减到没有复制品。通过使用docker service update或docker service scale命令将副本数量设置为0,可以将mysql服务缩小到没有副本。
∼ $ docker service scale mysql=0
mysql scaled to 0
服务缩减到没有副本。没有列出正在运行的服务副本。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
实际的服务任务可能需要一段时间才能关闭,但是所有任务的期望状态都被设置为Shutdown。
将服务扩展为无任务不会运行任何任务,但服务不会被删除。例如,mysql服务可以再次从无任务扩大到三个任务。
∼ $ docker service scale mysql=3
mysql scaled to 3
三个服务任务开始运行。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
py7aqwy2reku mysql.1 mysql:latest ip-172-31-37-135.ec2.internal Running Running 9 seconds ago
re1l3q3iwmvo mysql.2 mysql:latest ip-172-31-37-135.ec2.internal Running Running 9 seconds ago
h7my2ucpfz3u mysql.3 mysql:latest ip-172-31-37-135.ec2.internal Running Running 9 seconds ago
移除服务
可以使用docker service rm命令删除服务。
∼ $ docker service rm mysql
mysql
mysql服务被删除后不再列出。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
使用docker service rm命令可以删除多个服务。为了进行演示,您可以创建两个服务,hello-world和nginx。
∼ $ docker service create \
> --name hello-world \
> --publish 8080:80 \
> --replicas 2 \
> tutum/hello-world
t3msb25rc8b6xcm30k0zoh4ws
∼ $ docker service create --name nginx nginx
ncn4aqkgzrcjc8w1uorjo5jrd
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ncn4aqkgzrcj nginx replicated 1/1 nginx:latest
t3msb25rc8b6 hello-world replicated 2/2 tutum/hello-world:latest *:8080->80/tcp
随后,用一个docker service rm命令删除这两个服务。如果命令成功,则输出删除的服务。
∼ $ docker service rm nginx hello-world
nginx
hello-world
全球服务无法扩展
全局服务在群中的每个节点上创建一个服务任务,并且不能扩展。使用docker service create命令为 MySQL 数据库创建一个全局服务。该命令的显著区别是--mode被设置为global,并且不包括--replicas选项。
∼ $ docker service create \
> --mode global \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --name mysql-global \
> mysql
nxhnrsiulymd9n4171cie9a8j
创建了全局服务,并且列出服务应该指示设置为global的Mode。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
nxhnrsiulymd mysql-global global 3/3 mysql:latest
在群中的每个节点上创建一个服务任务。
∼ $ docker service ps mysql-global
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
nfbmkqdh46k0 mysql-global.zkxle7kafwcmt1sd93kh5cy5e mysql:latest ip-172-31-13-155.ec2.internal Running Running 22 seconds ago
t55ba3bobwzf mysql-global.w5to186ipblpcq390625wyq2e mysql:latest ip-172-31-37-135.ec2.internal Running Running 22 seconds ago
kqg656m30lj3 mysql-global.ilru4f0i280w2tlsrg9hglwsj mysql:latest ip-172-31-10-132.ec2.internal Running Running 22 seconds ago
如果将另一个节点添加到群中,服务任务会在新节点上自动开始。
如果对全局服务运行docker service scale命令,则服务不会扩展。相反,会输出以下消息。
∼ $ docker service scale mysql-global=5
mysql-global: scale can only be used with replicated mode
使用docker service rm命令,可以像删除复制服务一样删除全局服务。
∼ $ docker service rm mysql-global
mysql-global
使用同一命令扩展多个服务
使用单个docker service scale命令可以扩展多个服务。为了演示,创建两个服务:nginx和mysql。
∼ $ docker service create \
> --replicas 1 \
> --name nginx \
> nginx
u6i4e8eg720dwzz425inhxqrp
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --name mysql \
> mysql
1umb7e2gr68s54utujr6khjgd
请列出这两种服务。每个服务应该运行一个副本。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
1umb7e2gr68s mysql replicated 1/1 mysql:latest
u6i4e8eg720d nginx replicated 1/1 nginx:latest
用一个命令扩展nginx服务和mysql服务。不同的服务可以扩展到不同数量的副本。
∼ $ docker service scale mysql=5 nginx=10
mysql scaled to 5
nginx scaled to 10
mysql服务扩展到 5 个任务,而nginx服务扩展到 10 个副本。最初,服务的一些新任务可能还没有开始,比如nginx服务,它只列出了 10 个任务中的 8 个正在运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
1umb7e2gr68s mysql replicated 5/5 mysql:latest
u6i4e8eg720d nginx replicated 8/10 nginx:latest
过一会儿,所有服务任务都应该被列为正在运行,如nginx服务的10/10所示。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
1umb7e2gr68s mysql replicated 5/5 mysql:latest
u6i4e8eg720d nginx replicated 10/10 nginx:latest
可以使用单个docker service ps命令列出两个服务的服务任务。
∼ $ docker service ps nginx mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
f9g1tw88nppk mysql.1 mysql:latest ip-172-31-26-234.ec2.internal Running Running about a minute ago
zcl1qfdiqrvu nginx.1 nginx:latest ip-172-31-10-132.ec2.internal Running Running about a minute ago
vu4xo99xr0y4 nginx.2 nginx:latest ip-172-31-13-155.ec2.internal Running Running 40 seconds ago
xvxgfoacxjos mysql.2 mysql:latest ip-172-31-37-135.ec2.internal Running Running 41 seconds ago
yw0opq5y0x20 nginx.3 nginx:latest ip-172-31-13-155.ec2.internal Running Running 41 seconds ago
vb92hkua6eyo mysql.3 mysql:latest ip-172-31-13-155.ec2.internal Running Running 40 seconds ago
1cnqwtb24zvy nginx.4 nginx:latest ip-172-31-13-155.ec2.internal Running Running 41 seconds ago
hclu53xkosva mysql.4 mysql:latest ip-172-31-26-234.ec2.internal Running Running 40 seconds ago
2xjcw4i9xw89 nginx.5 nginx:latest ip-172-31-10-132.ec2.internal Running Running 41 seconds ago
ocvb2qctuids mysql.5 mysql:latest ip-172-31-10-132.ec2.internal Running Running 41 seconds ago
l8mlu3jpp9cx nginx.6 nginx:latest ip-172-31-10-132.ec2.internal Running Running 41 seconds ago
p84m8yh5if5t nginx.7 nginx:latest ip-172-31-37-135.ec2.internal Running Running 41 seconds ago
7yp8m7ytt7z4 nginx.8 nginx:latest ip-172-31-26-234.ec2.internal Running Running 24 seconds ago
zegs90r015nn nginx.9 nginx:latest ip-172-31-37-135.ec2.internal Running Running 41 seconds ago
qfkpvy28g1g6 nginx.10 nginx:latest ip-172-31-26-234.ec2.internal Running Running 24 seconds ago
离开群体的节点上的服务任务替换
Docker 群模式中的期望状态协调确保了如果资源可用,期望数量的副本正在运行。如果一个节点离开群,在该节点上运行的副本被调度到另一个节点上。从运行在三节点集群中每个节点上的mysql服务副本开始,您可以让一个工作节点离开集群。
∼ docker swarm 1eave
Node left the swarm.
运行在shutdown节点上的服务任务的替换服务任务被调度到另一个节点上。
∼ s docker service ps mysql
NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
6zu7a59ejdxip3y9oeu548hv5 mysql.l mysql ip-10-0-0-46.ec2.internal Running Running 3 minutes ago
441cuufa7sa9möeatqbiq7vi3 mysql.2 mysql ip-10-0-0-28.ec2.internal Running Running about a minute ago
blcdm8Bh6v86gl..pwp6zx3janv mysql.3 mysql ip-10-0-0-28.ec2.internal Running Running 4 seconds ago
Or3oki4acf3d6ils5iazmg425 \_ mysql.3 mysql ip-10-0-0-106.ec2.internal Shutdown Running about a minute ago
使其他工人节点也离开蜂群。另一个工作节点上的服务副本也被关闭,并被调度到群中唯一剩余的节点上。
∼ s docker service ps mysql
NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
6zu7a59ejdxip3y9oeu548hv5 mysql.1 mysql ip-10-0-0-46.ec2\. internal Running Running 5 minutes ago
dbdaxvl6lohlxrsxh5aobjxi8 mysq.2 mysql ip-10-0-0-46.ec2.internal Running Running 7 seconds ago
44tcuufa7sa9m6eatqbiq7vi3 \_ mysql.2 mysql ip-10-0-0-28.ec2.internal Shutdown Running 2 minutes ago
216iu28xh5hztm3bgtvy7ttk8 mysql.3 mysql ip-10-0-0-46.ec2.internal Running Running 7 seconds ago
blcdm88h6v86gLpwp6zx3janv \_ mysql.3 mysql ip-10-0-0-28.ec2.internal Shutdown Running about a minute ago
Or3oki4acf3d6ils5iazmg425 \_ mysql.3 mysql ip-10-0-0-106.ec2.internal Shutdown Running 2 minutes ago
如果仅列出所需状态为正在运行的复制副本,则所有复制副本都会在管理器节点上显示为正在运行。
∼s docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
6zu7a59ejdxip3y9oeu548hv5 mysql.1 mysql ip-10-0-0-46.ec2.internal Running Running 7 minutes ago
dbdaxvl6lohlxrsxh5aobjxi8 mysql.2 mysql ip-10-0-0-46.ec2.internal Running Running 2 minutes ago
216iu28xh5hztm3bgtvy7ttk8 mysql.3 mysql ip-10-0-0-46.ec2.internal Running Running 2 minutes ago
摘要
本章讨论了群模式下的服务扩展。只能扩展复制的服务,而不能扩展全局服务。一个服务可以扩展到资源可以支持的尽可能多的副本,也可以缩减到没有副本。使用同一命令可以扩展多个服务。期望状态协调确保期望数量的服务副本正在运行。下一章将介绍 Docker 服务挂载。
六、使用挂载
Swarm 中的服务任务容器可以访问从其 Docker 映像继承的文件系统。数据通过其 Docker 映像集成到 Docker 容器中。有时,Docker 容器可能需要在持久文件系统上存储或访问数据。虽然容器有一个文件系统,但是一旦容器退出,它就会被删除。为了在容器重启时存储数据,数据必须保存在容器之外的某个地方。
问题
仅存储在容器中的数据可能会导致以下问题:
- 数据不是持久的。当 Docker 容器停止时,数据将被删除。
- 数据不能与其他 Docker 容器或主机文件系统共享。
解决方案
基于单一责任原则(SRP)的模块化设计建议将数据从 Docker 容器中分离出来。Docker Swarm 模式提供了共享数据的挂载,并使数据在容器启动和关闭时保持持久。Docker Swarm 模式为服务提供了两种类型的挂载:
- 卷装载
- 绑定安装
默认值是卷装载。使用docker service create命令的--mount选项创建服务的挂载。
卷装载
卷装载是主机上装载到服务任务容器中的命名卷。即使在停止和删除容器后,主机上的命名卷仍然存在。可以在创建使用该卷的服务之前创建命名卷,或者可以在服务部署时创建该卷。在部署时创建的命名卷是在启动服务任务容器之前创建的。如果在服务部署时创建,并且未指定卷名,则命名卷会自动生成一个名称。图 6-1 显示了一个卷挂载的例子,其中一个在创建服务之前就存在的命名卷mysql-scripts被挂载到目录路径/etc/mysql/scripts下的服务任务容器中。
图 6-1。
Volume mount
服务中的每个容器都可以访问运行该容器的主机上的相同命名卷,但是该主机命名卷可以存储相同或不同的数据。
使用卷装载时,内容不会跨群集复制。例如,如果您将某些内容放入正在使用的mysql-scripts目录,这些新文件将只能被运行在同一节点上的其他任务访问。在其他节点上运行的副本将无法访问这些文件。
绑定安装
绑定装载是要在其上计划服务任务的主机上的文件系统路径。主机文件系统路径被装载到服务任务容器的指定目录路径中。主机文件系统路径必须存在于群中的每个主机上,在创建服务之前可以在其上调度任务。如果使用节点约束将某些节点排除在服务部署之外,则绑定装载主机文件系统不必存在于这些节点上。使用绑定挂载时,请记住使用绑定挂载的服务本身是不可移植的。如果要在生产中部署服务,主机目录路径必须存在于生产集群中的每个主机上。
主机文件系统路径不必与任务容器中的目标目录路径相同。例如,在图 6-2 中,主机路径/db/mysql/data作为绑定挂载被挂载到目录路径/etc/mysql/data的服务容器中。默认情况下,绑定装载是读写的,但是在服务部署时可以设置为只读。服务中的每个容器都可以访问运行容器的主机上的相同目录路径,但是主机目录路径可以存储不同或相同的数据。
图 6-2。
Bind mount
群模式装载在主机上提供可共享的命名卷和文件系统路径,这些路径在服务任务启动和关闭期间保持不变。Docker 映像的文件系统仍然位于文件系统层次结构的根位置,并且只能在根文件系统内的目录路径上进行挂载。
本章涵盖以下主题:
- 设置环境
- 安装类型
- 创建命名卷
- 使用卷装载来获取有关卷的详细信息
- 删除卷
- 创建和使用绑定装载
设置环境
为基于 AWS 的群创建一个 Docker,由一个管理节点和两个工作节点组成,如第三章所述。AWS Swarm 的 Docker 将用于一种类型的挂载,即卷挂载。对于绑定挂载,在 CoreOS 实例上创建一个由一个管理器和两个工作者节点组成的三节点集群。在第二章中讨论了在 CoreOS 实例上创建蜂群。使用基于 CoreOS 的 Swarm 是因为 Docker for AWS Swarm 不支持开箱即用的绑定挂载。从 EC2 控制台获取 AWS Swarm Docker 的 manager 实例的公共 IP 地址,如图 6-3 所示。
图 6-3。
EC2 instances for Docker for AWS Swarm nodes
SSH 登录到管理器实例。
[root@localhost ∼]# ssh -i "docker.pem" docker@52.91.115.180
Welcome to Docker!
列出群体中的节点。列出了一个管理节点和两个工作节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
8ynq7exfo5v74ymoe7hrsghxh ip-172-31-33-230.ec2.internal Ready Active
o0h7o09a61ico7n1t8ooe281g * ip-172-31-16-11.ec2.internal Ready Active Leader
yzlv7c3qwcwozhxz439dbknj4 ip-172-31-25-163.ec2.internal Ready Active
创建命名卷
在服务中用作卷类型装载的命名卷可以在创建服务之前创建,也可以在部署时创建。使用以下命令语法创建新的命名卷。
docker volume create [OPTIONS] [VOLUME]
支持表 6-1 中讨论的选项。
表 6-1。
Options for the docker volume create Command for a Named Volume
| [计]选项 | 描述 | 类型 | 缺省值 | | --- | --- | --- | --- | | `--driver, -d` | 指定卷驱动程序名称 | `string` | `local` | | `--label` | 设置卷的元数据 | `value` | `[]` | | `--name` | 指定卷名 | `string` | | | `--opt, -o` | 设置驱动程序特定选项 | `value` | `map[]` |使用docker volume create命令创建一个名为hello的命名卷。
∼ $ docker volume create --name hello
hello
随后,使用docker volume ls命令列出卷。除了可能存在的其他命名卷之外,还列出了hello卷。
∼ $ docker volume ls
DRIVER VOLUME NAME
local hello
您可以使用以下命令找到有关该卷的详细信息。
docker volume inspect hello
除了卷名和驱动程序之外,还会列出卷的挂载点。
∼ $ docker volume inspect hello
[ { "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/hello/_data", "Name": "hello", "Options": {}, "Scope": "local" }]
本地驱动程序卷的范围是local。另一个支持的范围是global。在单个 Docker 主机上创建一个local卷,在集群中的每个 Docker 主机上创建一个global卷。
使用卷装载
使用带有--mount选项的docker service create命令中的hello音量。表 6-2 中讨论的选项可用于绑定挂载和卷挂载。
表 6-2。
Options for Volume and Bind Mounts
| [计]选项 | 需要 | 描述 | 默认 | | `type` | 不 | 指定装载的类型。可以指定三个值之一:`volume-Mounts`是容器中的命名卷。`bind-Bind-mounts`是将一个目录或文件从主机放入一个容器中。`tmpfs-Mounts`是一只`tmpfs`变成的容器。 | `volume` | | `src`或`source` | 仅适用于`type=bind`。`type=volume`的否 | 源目录或卷。该选项对于不同类型的挂载有不同的含义。`type=volume: src`指定卷的名称。如果指定的卷不存在,则创建该卷。如果省略了`src`,则使用自动生成的名称创建命名卷,该名称在主机上是唯一的,但在群集范围内可能不是唯一的。当使用卷的容器被删除时,自动生成的命名卷也被删除。`docker service update`命令关闭任务容器并启动新的任务容器,扩展服务也是如此。`volume source`不能是绝对路径。`type=bind: src`指定要绑定挂载的目录或文件的绝对路径。目录路径必须是绝对路径,而不是相对路径。类型为`bind`的挂载需要`src`选项,如果没有指定,就会产生错误。不支持`type=tmpfs:`。 | | | `dst`或`destination`或`target` | 是 | 指定容器内的挂载路径。如果容器的文件系统中不存在该路径,Docker 引擎会在挂载绑定或卷挂载之前创建挂载路径。卷目标必须是相对路径。 | | | `readonly`或`ro` | 不 | 一个布尔值(真/假)或(1/0)来指示 Docker 引擎应该装入卷并绑定读写还是只读。如果未指定选项,引擎将装载绑定或卷读写。如果将该选项指定为 true 值、1 值或无值,则引擎会以只读方式装载卷或绑定。如果将该选项指定为值 false 或 0,则引擎会装载卷或绑定读写。 | |一些装载选项仅支持卷装载,并在表 6-3 中讨论。
表 6-3。
Options for Volume Mounts
| [计]选项 | 需要 | 描述 | 缺省值 | | --- | --- | --- | --- | | `volume-driver` | 不 | 指定用于卷的卷驱动程序插件的名称。如果在`src`中没有指定命名卷,则`volume-driver`用于创建一个命名卷。 | `local` | | `volume-` `label` | 不 | 指定要应用于卷的一个或多个逗号分隔的元数据标签。例子:`volume-label=label-1=hello-world,label-2=hello`。 | | | `volume-nocopy` | 不 | 适用于在文件和目录已经存在的装载路径的容器中装载的空卷。指定是否将挂载路径(`dst`)下容器的文件系统文件和目录复制到卷中。主机能够访问从容器复制到指定卷的文件和目录。true 或 1 值禁止将文件从容器的文件系统复制到主机卷。false 或 0 值表示允许复制。 | 真或 1 | | `volume-` `opt` | 不 | 指定在创建命名卷(如果不存在)时提供给`volume-driver`的选项。`volume-opt`选项被指定为逗号分隔的键/值对列表。例如:`volume-opt-1=option-1=value1,option-2=value2`。要装载卷类型装载的每个主机上都必须有一个命名卷。在 Swarm manager 上创建命名卷并不会在 worker 节点上创建命名卷。`volume-driver`和`volume-opt`选项用于在工作节点上创建命名卷。 | |表 6-4 中讨论的选项仅支持tmpfs类型的安装。
表 6-4。
Options for the tmpfs Mount
| [计]选项 | 需要 | 描述 | 缺省值 | | --- | --- | --- | --- | | `tmpfs-size` | 不 | 以字节为单位的`tmpfs`挂载的大小 | Linux 的无限价值 | | `tmpfs-` `mode` | 不 | 以八进制指定`tmpfs`的文件模式 | `1777`在 Linux 中 |接下来,我们将在用 Docker 映像tutum/hello-world创建的服务中使用命名卷hello。在下面的docker service create命令中,--mount选项将src指定为hello,并包含一些卷的volume-label标签。
∼ $ docker service create \
--name hello-world \
--mount src=hello,dst=/hello,volume-label="msg=hello",volume-label="msg2=world" \
--publish 8080:80 \
--replicas 2 \
tutum/hello-world
创建服务并输出服务 ID。
∼ $ docker service create \
> --name hello-world \
> --mount src=hello,dst=/hello,volume-label="msg=hello",volume-label="msg2=world" \
> --publish 8080:80 \
> --replicas 2 \
> tutum/hello-world
8ily37o72wyxkyw2jt60kdqoz
创建了两个服务副本。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
8ily37o72wyx hello-world replicated 2/2 tutum/hello-world:latest *:8080->80/tcp
∼ $ docker service ps hello-world
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
uw6coztxwqhf hello-world.1 tutum/hello-world:latest ip-172-31-25-163.ec2.internalRunning Running 20 seconds ago
cfkwefwadkki hello-world.2 tutum/hello-world:latest ip-172-31-16-11.ec2.internalRunning Running 21 seconds ago
命名的卷安装在服务的每个任务容器中。
服务定义列出了装载,包括装载标签。
∼ $ docker service inspect hello-world
[ ... "Spec": { "ContainerSpec": { "Image": "tutum/hello-world:latest@sha256:0d57def8055178aafb4c7669cbc25ec17f0acdab97cc587f30150802da8f8d85", "Mounts": [ { "Type": "volume", "Source": "hello", "Target": "/hello", "VolumeOptions": { "Labels": { "msg": "hello", "msg2": "world" },...]
在前面的示例中,在卷装入中使用卷之前,创建了一个命名卷。又如,在部署时创建一个命名卷。在下面的docker service create命令中,--mount选项被设置为type=volume,源被设置为nginx-root。创建服务之前,命名卷nginx-root不存在。
∼ $ docker service create \
> --name nginx-service \
> --replicas 3 \
> --mount type=volume,source="nginx-root",destination="/var/lib/nginx",volume-
label="type=nginx root dir" \
> nginx:alpine
rtz1ldok405mr03uhdk1htlnk
运行该命令时,会创建一个服务。服务描述包括mounts中的卷挂载。
∼ $ docker service inspect nginx-service
[... "Spec": { "Name": "nginx-service",... "Mounts": [ { "Type": "volume", "Source": "nginx-root", "Target": "/var/lib/nginx", "VolumeOptions": { "Labels": { "type": "nginx root dir" },...]
命名卷nginx-root不是在创建服务之前创建的,因此是在为服务任务启动容器之前创建的。命名卷nginx-root仅在计划任务的节点上创建。三个节点中的每一个都计划了一个服务任务。
∼ $ docker service ps nginx-service
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
pfqinizqmgur nginx-service.1 nginx:alpine ip-172-31-33-230.ec2.internalRunning Running 19 seconds ago
mn8h3p40chgs nginx-service.2 nginx:alpine ip-172-31-25-163.ec2.internalRunning Running 19 seconds ago
k8n5zzlnn46s nginx-service.3 nginx:alpine ip-172-31-16-11.ec2.internalRunning Running 18 seconds ago
当在管理器节点上调度任务时,在管理器节点上创建一个名为nginx-root的命名卷,如docker volume ls命令的输出中所列。
∼ $ docker volume ls
DRIVER VOLUME NAME
local hello
local nginx-root
服务任务和任务容器在两个工作节点的每一个上启动。在每个工作节点上创建一个nginx-root命名卷。列出工作节点上的卷会列出nginx-root卷。
[root@localhost ∼]# ssh -i "docker.pem" docker@34.229.86.64
Welcome to Docker!
∼ $ docker volume ls
DRIVER VOLUME NAME
local hello
local nginx-root
[root@localhost ∼]# ssh -i "docker.pem" docker@52.91.200.241
Welcome to Docker!
∼ $ docker volume ls
DRIVER VOLUME NAME
local hello
local nginx-root
在前面的示例中,在src中指定了一个命名卷。在下面的服务定义中,可以省略指定的卷。
∼ $ docker service create \
> --name nginx-service-2 \
> --replicas 3 \
> --mount type=volume,destination=/var/lib/nginx \
> nginx:alpine
q8ordkmkwqrwiwhmaemvcypc3
服务是用一个副本创建的,并在每个群节点上进行调度。
∼ $ docker service ps nginx-service-2
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
kz8d8k6bxp7u nginx-service-2.1 nginx:alpine ip-172-31-25-163.ec2.internalRunning Running 27 seconds ago
wd65qsmqixpg nginx-service-2.2 nginx:alpine ip-172-31-16-11.ec2.internalRunning Running 27 seconds ago
mbnmzldtaaed nginx-service-2.3 nginx:alpine ip-172-31-33-230.ec2.internalRunning Running 26 seconds ago
服务定义没有列出命名卷。
∼ $ docker service inspect nginx-service-2
[ "Spec": { "Name": "nginx-service-2", "ContainerSpec": { "Mounts": [ { "Type": "volume", "Target": "/var/lib/nginx" } ],
...
]
如果没有明确指定卷名,则会创建具有自动生成名称的命名卷。在运行服务任务的每个节点上,都会创建一个具有自动生成名称的自动生成的命名卷。manager 节点上列出的一个命名卷是自动生成的命名卷,具有自动生成的名称。
∼ $ docker volume ls
DRIVER VOLUME NAME
local 305f1fa3673e811b3b320fad0e2dd5786567bcec49b3e66480eab2309101e233
local hello
local nginx-root
作为在服务中使用命名卷作为挂载的另一个例子,为 MySQL 数据库服务创建一个名为mysql-scripts的命名卷。
∼ $ docker volume create --name mysql-scripts
mysql-scripts
命名的卷已创建并列出。
∼ $ docker volume ls
DRIVER VOLUME NAME
local 305f1fa3673e811b3b320fad0e2dd5786567bcec49b3e66480eab2309101e233
local hello
local mysql-scripts
local nginx-root
卷描述将范围列为local,并列出挂载点。
∼ $ docker volume inspect mysql-scripts
[ { "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/mysql-scripts/_data", "Name": "mysql-scripts", "Options": {}, "Scope": "local" }]
接下来,创建一个在卷装载中使用命名卷的服务。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --mount type=volume,src="mysql-scripts",dst="/etc/mysql/scripts",
el="msg=mysql",volume-label="msg2=scripts" \
> --publish 3306:3306\
> --replicas 2 \
> --name mysql \
> mysql
cghaz4zoxurpyqil5iknqf4c1
服务被创建并列出。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
8ily37o72wyx hello-world replicated 2/2 tutum/hello-world:latest *:8080->80/tcp
cghaz4zoxurp ysql replicated 1/2 mysql:latest *:3306->3306/tcp
列出服务任务表明任务被安排在管理器节点和一个工作者节点上。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
y59yhzwch2fj mysql.1 mysql:latest ip-172-31-33-230.ec2.internalRunning Preparing 12 seconds ago
zg7wrludkr84 mysql.2 mysql:latest ip-172-31-16-11.ec2.internalRunning Running less than a second ago
命名卷的目标目录在 Docker 容器中创建。管理节点上的 Docker 容器可以用docker ps列出,容器上的 bash shell 可以用docker exec -it <containerid> bash命令启动。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a855826cdc75 mysql:latest "docker-entrypoint..." 22 seconds ago Up 21 seconds 3306/tcp mysql.2.zg7wrludkr84zf8vhdkf8wnlh
∼ $ docker exec -it a855826cdc75 bash
root@a855826cd75:/#
将目录更改为容器中的/etc/mysql/scripts。最初,目录是空的。
root@a855826cdc75:/# cd /etc/mysql/scripts
root@a855826cdc75:/etc/mysql/scripts# ls -l
total 0
root@a855826cdc75:/etc/mysql/scripts# exit
exit
在工作者节点之一上创建服务的任务容器,并且可以在工作者节点上列出该任务容器。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eb8d59cc2dff mysql:latest "docker-entrypoint..." 8 minutes ago Up 8 minutes 3306/tcp mysql.1.xjmx7qviihyq2so7n0oxi1muq
在 worker 节点上为 Docker 容器启动一个 bash shell。在 Docker 容器中创建了挂载指定卷的/etc/mysql/scripts目录。
∼ $ docker exec -it eb8d59cc2dff bash
root@eb8d59cc2dff:/# cd /etc/mysql/scripts
root@eb8d59cc2dff:/etc/mysql/scripts# exit
exit
如果使用自动生成的命名卷的服务被扩展为在先前未运行任务的节点上运行任务,则命名卷也会在这些节点上自动生成。作为在服务中使用自动生成的命名卷作为挂载时发现扩展服务的效果的示例,创建一个带有卷挂载的 MySQL 数据库服务。在创建服务之前,卷mysql-scripts不存在;移除mysql-scripts卷(如果存在)。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --mount type=volume,src="mysql-scripts",dst="/etc/mysql/scripts"\
> --name mysql \
> mysql
088ddf5pt4yb3yvr5s7elyhpn
服务任务被安排在一个节点上。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xlix91njbaq0 mysql.1 mysql:latest ip-172-31-13-122.ec2.internalRunning Preparing 12 seconds ago
列出节点;安排服务任务的节点是管理器节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
o5hyue3hzuds8vtyughswbosl ip-172-31-11-41.ec2.internal Ready Active
p6uuzp8pmoahlcwexr3wdulxv ip-172-31-23-247.ec2.internal Ready Active
qnk35m0141lx8jljp87ggnsnq * ip-172-31-13-122.ec2.internal Ready Active Leader
在调度任务的 manager 节点上创建一个命名卷mysql-scripts和一个具有自动生成名称的辅助命名卷。
∼ $ docker volume ls
DRIVER VOLUME NAME
local a2bc631f1b1da354d30aaea37935c65f9d99c5f084d92341c6506f1e2aab1d55
local mysql-scripts
工作节点没有列出mysql-scripts命名卷,因为工作节点上没有调度任务。
∼ $ docker volume ls
DRIVER VOLUME NAME
将服务扩展到三个副本。在三个节点中的每一个节点上都安排了一个复制副本。
∼ $ docker service scale mysql=3
mysql scaled to 3
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xlix91njbaq0 mysql.1 mysql:latest ip-172-31-13-122.ec2.internal Running Running about a minute ago
ifk7xuvfp9p2 mysql.2 mysql:latest ip-172-31-23-247.ec2.internal Running Running less than a second ago
3c53fxgcjqyt mysql.3 mysql:latest ip-172-31-11-41.ec2.internal Running Running less than a second ago
由于计划了一个副本,因此在工作节点上创建了一个命名卷mysql-scripts和一个具有自动生成名称的辅助命名卷。
[root@localhost ∼]# ssh -i "docker.pem" docker@54.165.69.9
Welcome to Docker!
∼ $ docker volume ls
DRIVER VOLUME NAME
local 431a792646d0b04b5ace49a32e6c0631ec5e92f3dda57008b1987e4fe2a1b561
local mysql-scripts
[root@localhost ∼]# ssh -i "docker.pem" docker@34.232.95.243
Welcome to Docker!
∼ $ docker volume ls
DRIVER VOLUME NAME
local afb2401a9a916a365304b8aa0cc96b1be0c161462d375745c9829f2b6f180873
local mysql-scripts
自动生成的命名卷是永久性的,不会在服务复制副本关闭时被删除。具有自动生成名称的命名卷不是永久卷。例如,将服务扩展回一个副本。两个复制副本关闭,包括管理器节点上的复制副本。
∼ $ docker service scale mysql=1
mysql scaled to 1
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
3c53fxgcjqyt mysql.3 mysql:latest ip-172-31-11-41.ec2.internal Running Running 2 minutes ago
但是管理器节点上的命名卷mysql-scripts不会被删除,即使没有使用该卷的 Docker 容器正在运行。
∼ $ docker volume ls
DRIVER VOLUME NAME
local mysql-scripts
类似地,关闭服务副本的工作节点上的命名卷也不会被删除,即使没有使用该命名卷的 Docker 容器正在运行。当没有容器在使用时,具有自动生成名称的命名卷会被删除,但mysql-scripts命名卷不会被删除。
移除卷mysql-scripts仍未移除。
∼ $ docker service rm mysql
mysql
∼ $ docker volume ls
DRIVER VOLUME NAME
local mysql-scripts
删除卷
可以使用以下命令删除命名卷。
docker volume rm <VOL>
例如,删除命名卷mysql-scripts。
∼ $ docker volume rm mysql-scripts
mysql-scripts
如果您尝试删除的卷在 Docker 容器中使用,则会生成一个错误,并且该卷不会被删除。如果正在容器中使用,即使是具有自动生成名称的命名卷也不能删除。
创建和使用绑定装载
在本节中,我们将创建一个 bind 类型的装载。如果需要从 Docker 容器中访问主机上已经存在的目录中的数据,则适合使用绑定装载。当创建具有绑定类型装载的服务时,必须使用--mount选项指定type=bind。主机源目录和卷目标必须都是绝对路径。创建服务之前,主机源目录必须存在。服务的每个 Docker 容器中的目标目录是自动创建的。在管理器节点上创建一个目录,然后向该目录添加一个名为createtable.sql的文件。
core@ip-10-0-0-143 ∼ $ sudo mkdir -p /etc/mysql/scripts
core@ip-10-0-0-143 ∼ $ cd /etc/mysql/scripts
core@ip-10-0-0-143 /etc/mysql/scripts $ sudo vi createtable.sql
在示例 SQL 文件中保存一个 SQL 脚本,如图 6-4 所示。
图 6-4。
Adding a SQL script to the host directory
类似地,创建一个目录并向 worker 节点添加一个 SQL 脚本。
使用使用主机目录的绑定装载创建服务。目标目录被指定为/scripts。
core@ip-10-0-0-143 ∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql' \
> --replicas 3 \
> --mount type=bind,src="/etc/mysql/scripts",dst="/scripts" \
> --name mysql \
> mysql
0kvk2hk2qigqyeem8x1r8qkvk
从调度任务的节点启动服务容器的 bash shell。列出目标目录/scripts。
core@ip-10-0-0-143 ∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e71275e6c65c mysql:latest "docker-entrypoint.sh" 5 seconds ago Up 4 seconds 3306/tcp mysql.1.btqfrx7uffym2xvc441pubaza
core@ip-10-0-0-143 ∼ $ docker exec -it e71275e6c65c bash
root@e71275e6c65c:/# ls -l
drwxr-xr-x. 2 root root 4096 Jul 24 20:44 scripts
将目录(cd)更改为目标挂载路径/scripts。在绑定挂载的目标挂载路径中列出了createtable.sql脚本。
root@e71275e6c65c:/# cd /scripts
root@e71275e6c65c:/scripts# ls -l
-rw-r--r--. 1 root root 1478 Jul 24 20:44 createtable.sql
每个服务任务 Docker 容器在主机上都有自己的文件副本。因为默认情况下,挂载是read-write,所以挂载路径中的文件可能会被修改或删除。例如,从容器中删除createtable.sql脚本。
core@ip-10-0-0-137 ∼ $ docker exec -it 995b9455aff2 bash
root@995b9455aff2:/# cd /scripts
root@995b9455aff2:/scripts# ls -l
total 8
-rw-r--r--. 1 root root 1478 Jul 24 20:45 createtable.sql
root@995b9455aff2:/scripts# rm createtable.sql
root@995b9455aff2:/scripts# ls -l
total 0
root@995b9455aff2:/scripts#
如前所述,通过在--mount arg 中包含一个额外的选项,可以将挂载设置为只读。为了演示a readonly挂载,首先删除已经运行的mysql服务。创建一个服务并使用与之前相同的命令挂载一个readonly绑定,除了包含一个额外的readonly选项。
core@ip-10-0-0-143 ∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql' \
> --replicas 3 \
> --mount type=bind,src="/etc/mysql/scripts",dst="/scripts",readonly \
> --name mysql \
> mysql
c27se8vfygk2z57rtswentrix
安装了类型为readonly的mount活页夹。
访问调度任务的节点上的容器,并列出主机目录中的示例脚本。
core@ip-10-0-0-143 ∼ $ docker exec -it 3bf9cf777d25 bash
root@3bf9cf777d25:/# cd /scripts
root@3bf9cf777d25:/scripts# ls -l
-rw-r--r--. 1 root root 1478 Jul 24 20:44 createtable.sql
删除或尝试删除示例脚本。会产生一个错误。
root@3bf9cf777d25:/scripts# rm createtable.sql
rm: cannot remove 'createtable.sql': Read-only file system
摘要
本章介绍了蜂群模式下的坐骑。支持两种类型的装载—绑定装载和卷装载。绑定装载将预先存在的目录或文件从主机装载到服务的每个容器中。卷挂载将命名卷挂载到服务中的每个容器中,该命名卷在创建服务之前可能存在,也可能不存在。下一章讨论配置资源。
七、配置资源
Docker 容器在底层操作系统内核上独立运行,运行时需要资源。Docker Swarm 模式支持两种类型的资源——CPU 和内存——如图 7-1 所示。
图 7-1。
Types of resources supported by Docker Swarm mode
问题
默认情况下,Docker Swarm 模式对一个服务任务可以消耗多少资源(CPU 周期或内存)没有任何限制。Swarm 模式也不能保证最少的资源。如果在 Docker Swarm 模式下没有指定资源配置,会导致两个问题。
一些服务任务可能消耗不成比例的资源量,而其他服务任务由于缺乏资源而不能被调度。例如,考虑一个具有 3GB 资源容量和 3 个 CPU 的节点。在没有任何资源保证和限制的情况下,一个服务任务容器可以消耗大部分资源(2.8GB 和 2.8 CPUs),而另外两个服务任务容器每个只剩下 0.1GB 和 0.1 CPU 的资源可以使用,并且没有得到调度,如图 7-2 所示。没有足够资源进行调度的 Docker 服务任务被置于Pending状态。
图 7-2。
Unequal allocation of resources
可能导致的第二个问题是,节点的资源容量可能被完全用尽,而没有任何供应来调度任何更多的服务任务。例如,一个资源容量为 9GB 和 9 个 CPU 的节点运行着三个服务任务容器,每个容器使用 3GB 和 3 个 CPU,如图 7-3 所示。如果为同一个或另一个服务创建了新的服务任务,它在节点上没有任何可用的资源。
图 7-3。
Fully resource-utilized node
解决方案
Docker Swarm 模式具有设置资源保证(或储备)和资源限制的规定,如图 7-4 所示。资源储备是为服务任务保证或保留的最小资源量。资源限制是服务任务可以使用的最大资源量,而不管有多少资源可用。
图 7-4。
Managing Swarm resources with resource reserves and limits
有了资源储备,在前面讨论的问题中,每个服务任务容器可以保证 1 个 CPU 和 1GB,如图 7-5 所示。
图 7-5。
Resource allocation with resource reserves set
并且,如果对服务任务容器实施资源限制,则多余的资源将可用于启动新的服务任务容器。在前面讨论的示例中,每个服务任务 2GB 和 2 个 CPU 的限制将使 3GB 和 3 个 CPU 的额外资源可用于新的服务任务容器,如图 7-6 所示。
图 7-6。
Resource allocation with resource limits set
本章涵盖以下主题:
- 设置环境
- 创建没有资源规范的服务
- 保留资源
- 设置资源限制
- 使用资源规范创建服务
- 扩展和资源
- 保留资源必须小于资源限制
- 设置资源限制和储备的滚动更新
- 资源使用和节点容量
设置环境
在 Docker 上为 AWS 创建一个三节点 Swarm,其中有一个管理节点和两个工作节点。在 Docker 上为 AWS 创建一个 Swarm 将在第三章中讨论。我们在本章中也使用了在第六章中创建的三节点蜂群。获取 Swarm manager 实例的公共 IP 地址,如图 7-7 所示。
图 7-7。
EC2 instances for Swarm nodes
以用户“docker”的身份通过 SSH 登录 manager 实例。
[root@localhost ∼]# ssh -i "docker.pem" docker@52.91.115.180
Welcome to Docker!
列出群节点;列出了一个管理节点和两个工作节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
8ynq7exfo5v74ymoe7hrsghxh ip-172-31-33-230.ec2.internal Ready Active
o0h7o09a61ico7n1t8ooe281g * ip-172-31-16-11.ec2.internal Ready Active Leader
yzlv7c3qwcwozhxz439dbknj4 ip-172-31-25-163.ec2.internal Ready Active
创建没有资源规范的服务
我们首先创建一个没有任何资源规范的服务。创建一个 MySQL 数据库服务,不设置任何资源储备或限制。
docker service create \
--env MYSQL_ROOT_PASSWORD='mysql'\
--replicas 1 \
--name mysql \
mysql
将创建一个服务副本。该命令的输出是服务 ID(以斜体显示)。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> mysql
2kcq6cf72t4wu94o00k3sax41
列出服务;列出了mysql服务。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
2kcq6cf72t4w mysql replicated 1/1 mysql:latest
列出服务任务。唯一的服务任务正在工作节点上运行。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
sccqv4k9r22h mysql.1 mysql:latest ip-172-31-33-230.ec2.internal Running Running 10 seconds ago
在检查服务时,容器规范不包括任何资源、限制或保留。单个服务任务可能会使用调度它的节点上的所有可用资源。
∼ $ docker service inspect mysql
[ "Resources": { "Limits": {}, "Reservations": {} },]
保留资源
Swarm 模式在docker service create和docker service update命令中提供了两个资源预留选项,如表 7-1 所示。
表 7-1。
Options for Resource Reserves
| [计]选项 | 描述 | 缺省值 | | --- | --- | --- | | `--reserve-cpu` | 储备 CPU。值`0.000`意味着没有设置储备。 | `0.000` | | `--reserve-memory` | 保留记忆。值`0`意味着没有设置储备。 | `0` |设置资源限制
群模式在docker service create和docker service update命令中提供了两个资源限制选项,如表 7-2 中所述。
表 7-2。
Options for Resource Limits
| [计]选项 | 描述 | 缺省值 | | --- | --- | --- | | `--limit-cpu` | 限制 CPU | `0.000` | | `--limit-memory` | 限制内存 | `0` |使用资源规范创建服务
接下来,使用资源规范创建一个服务。设置 0.25 个 CPU 和 128MB 的资源预留,以及 1 个 CPU 和 256MB 的资源限制。在创建定义了资源的新服务之前,删除先前创建的mysql服务。该命令的输出是服务 ID(以斜体显示)。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> --reserve-cpu .25 --limit-cpu 1 --reserve-memory 128mb --limit-memory 256mb \
> mysql
abwq9budo7joyd00u32z2b047
在检查服务时,会列出资源限制和保留,这与创建服务时没有资源定义的空资源设置形成对比。
∼ $ docker service inspect mysql
[ "Resources": { "Limits": { "NanoCPUs": 1000000000, "MemoryBytes": 268435456 }, "Reservations": { "NanoCPUs": 250000000, "MemoryBytes": 134217728 } },]
扩展和资源
在纵向扩展服务之前,根据 CPU 和内存资源确定节点容量可能是合适的。因为群中的所有三个节点都是相同的,所以一个节点上的节点容量与其他节点上的相同。节点容量是 1 个 CPU 和 1GB,如docker node inspect命令的输出中所列。
∼ $ docker node inspect ip-172-31-16-11.ec2.internal
[
"Resources": {
"NanoCPUs": 1000000000,
"MemoryBytes": 1039040512
},
]
上一节中创建的每个服务任务的 CPU 限制也是 1 个 CPU。扩展时,节点上所有服务任务的资源限制总数可能会超过节点的容量。但是,资源预留总量不得超过节点容量。
例如,扩展到五个副本。
∼ $ docker service scale mysql=5
mysql scaled to 5
扩展到五个时,管理节点上有两个副本,一个工作节点上有两个副本,另一个工作节点上有一个副本。超过了工作节点上的资源限制的总和,但是资源保留的总和在节点的容量之内。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
npc5r7xf98fg mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 2 minutes ago
xokdhowntp0w mysql.2 mysql:latest ip-172-31-25-163.ec2.internal Running Running 13 seconds ago
b6h4bsf7xzdc mysql.3 mysql:latest ip-172-31-16-11.ec2.internal Running Running 12 seconds ago
j1d7ti7nb80u mysql.4 mysql:latest ip-172-31-33-230.ec2.internal Running Running 13 seconds ago
w6to9pxcdbm5 mysql.5 mysql:latest ip-172-31-25-163.ec2.internal Running Running 13 seconds ago
保留资源不得超过资源限制
在计划服务任务时,不考虑资源限制,只考虑资源储备。如果运行任务所需的资源在节点容量范围内,则不设置预留(无论是否设置限制以及限制是否超过节点容量)会调度服务任务。资源储备不得超过资源限制,否则服务任务可能无法调度,或者可能会在一段时间后失败。例如,删除mysql服务,并在资源储备超过资源限制的情况下创建一个新服务。该命令的输出是服务 ID(以斜体显示)。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> --reserve-cpu .75 --limit-cpu .5 --reserve-memory 256mb --limit-memory 128mb \
> mysql
srot5vr8x7v7iml2awc3fxb1u
服务被创建,甚至被调度。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
pmcjrj6p3wfp mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 20 seconds ago
服务配置的资源储备超过了资源限制。
∼ $ docker service inspect mysql
[ }, "Resources": { "Limits": { "NanoCPUs": 500000000, "MemoryBytes": 134217728 }, "Reservations": { "NanoCPUs": 750000000, "MemoryBytes": 268435456 } },]
资源储备在节点容量之内,但是因为资源限制小于资源储备,所以新启动的服务任务失败并被关闭。服务任务不断重启和关闭。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
vjcnjkwfdfkb mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 16 seconds ago
pxdku8pxviyn \_ mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Shutdown Failed 21 seconds ago "task: non-zero exit (1)"
pmcjrj6p3wfp \_ mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Shutdown Failed about a minute ago "task: non-zero exit (1)"
服务任务资源限制可以与资源储备相同。删除mysql服务,并使用与资源保留相同的资源限制重新创建它。该命令的输出是服务 ID(以斜体显示)。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> --reserve-cpu .5 --limit-cpu .5 --reserve-memory 256mb --limit-memory 256mb \
> mysql
81bu63v97p9rm81xfyxv9k11e
创建服务并调度单个任务。当资源储备超过资源限制时,服务任务不会失败。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
4i1fpha53abs mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 33 seconds ago
并启动 Docker 容器。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
14d5553f0393 mysql:latest "docker-entrypoint..." 34 seconds ago Up 33 seconds 3306/tcp mysql.1.4i1fpha53absl4qky9dgafo8t
滚动更新以修改资源限额和储量
本节演示了设置新的 CPU 和内存限制和预留的滚动更新。在上一节中创建的服务用于本节中的更新。使用docker service update命令,更新 CPU 和内存预留和限制。该命令的输出是服务名mysql(以斜体显示)。
∼ $ docker service update --reserve-cpu 1 --limit-cpu 2 --reserve-memory 256mb
--limit-memory 512mb mysql
mysql
资源被更新。更新服务的资源规范会关闭服务副本,并使用新的资源规范启动新的副本。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
81bu63v97p9r mysql replicated 1/1 mysql:latest
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xkis4mirgbtv mysql.1 mysql:latest ip-172-31-33-230.ec2.internal Running Running 14 seconds ago
4i1fpha53abs \_ mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Shutdown Shutdown 15 seconds ago
服务资源配置已更新。
∼ $ docker service inspect mysql
[ }, "Resources": { "Limits": { "NanoCPUs": 2000000000, "MemoryBytes": 536870912 }, "Reservations": { "NanoCPUs": 1000000000, "MemoryBytes": 268435456 } },]
资源使用和节点容量
资源使用不能超过节点容量。在三节点 Swarm 上(一个管理器和两个工作者节点),回想一下节点容量是 1GB 和 1 个 CPU。
删除已经运行的mysql服务,并创建一个mysql服务,它有三个请求 4GB 内存的副本。服务已创建。该命令的输出是服务 ID(以斜体显示)。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 3 \
> --name mysql \
> --reserve-memory=4GB\
> mysql
cgrihwij2znn4jkfe6hswxgr7
如0/3的Replicas列值所示,没有服务副本被调度,因为请求的容量大于单个节点的节点容量。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
cgrihwij2znn mysql replicated 0/3 mysql:latest
副本的Current State列为Pending。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
vm7z20krx3j6 mysql.1 mysql:latest Running Pending 19 seconds ago
exmsheo144ef mysql.2 mysql:latest Running Pending 19 seconds ago
kiset9poqz2s mysql.3 mysql:latest Running Pending 19 seconds ago
如果之前运行所有副本的服务被纵向扩展,一些或所有副本可能会被取消调度。如果运行新复制副本所需的资源超过了可用的节点容量,就会发生这种情况。例如,删除mysql服务并创建一个新的mysql服务,其资源设置在一个节点的供应中。该命令的输出是服务 ID(以斜体显示)。
∼ $ docker service rm mysql
mysql
∼ $
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> --reserve-cpu .5 --reserve-memory 512mb \
> mysql
ysef8n02mhuwa7sxerc9jwjqx
服务被创建,单个副本正在运行,如1/1的Replicas列值所示。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 1/1 mysql:latest
逐步扩展服务,以确定是否所有服务副本都已安排。首先,扩展到三个副本。
∼ $ docker service scale mysql=3
mysql scaled to 3
服务描述将3/3 Replicas列为正在运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 3/3 mysql:latest
使用分散调度策略来调度服务副本,群中的每个节点上有一个副本,这将在第八章中详细讨论。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
8kkkdns0l690 mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 51 seconds ago
k209uge36bih mysql.2 mysql:latest ip-172-31-25-163.ec2.internal Running Running 16 seconds ago
oiublpclz9eu mysql.3 mysql:latest ip-172-31-33-230.ec2.internal Running Running 16 seconds ago
将 mysql 服务进一步扩展到副本。
∼ $ docker service scale mysql=10
mysql scaled to 10
只有 3/10 的副本被列为正在运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 3/10 mysql:latest
有些副本是Allocated的,但由于资源不足,没有计划在任何节点上运行。没有运行的服务副本被列出,其中Current State被设置为Pending。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
8kkkdns0l690 mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running about a minute ago
k209uge36bih mysql.2 mysql:latest ip-172-31-25-163.ec2.internal Running Running 35 seconds ago
oiublpclz9eu mysql.3 mysql:latest ip-172-31-33-230.ec2.internal Running Running 35 seconds ago
u807b7h0qvqc mysql.4 mysql:latest Running Pending 7 seconds ago
jh2ep10sonxy mysql.5 mysql:latest Running Pending 7 seconds ago
8d19osxa4fwf mysql.6 mysql:latest Running Pending 7 seconds ago
k8hba8j5o9vi mysql.7 mysql:latest Running Pending 7 seconds ago
ettk65bpin3b mysql.8 mysql:latest Running Pending 7 seconds ago
i3otbqfsfvr7 mysql.9 mysql:latest Running Pending 7 seconds ago
sxdi970o6d3b mysql.10 mysql:latest Running Pending 7 seconds ago
添加一个或多个新的工作节点可能会使服务协调其所需的状态,并导致所有副本运行。为了进行下一步演示,我们扩展了 CloudFormation 栈,以增加工作节点的数量。
向上扩展栈
要纵向扩展 CloudFormation 栈,请在 CloudFormation ➤栈表中选择 Docker 栈,然后选择操作➤更新栈,如图 7-8 所示。
图 7-8。
Choosing Actions ➤ Update Stack
“更新 Docker 栈向导”启动。它类似于创建栈向导。在选择模板中,单击下一步,不修改任何设置。具体来说,增加集群工作节点的数量?至 10,如图 7-9 所示。点击下一步。
图 7-9。
Increasing the number of worker nodes to 10
在预览你的修改时,点击更新,如图 7-10 所示。
图 7-10。
Click Update to preview your changes
更新完成后,栈状态变为UPDATE_COMPLETE,如图 7-11 所示。
图 7-11。
Stack update is complete
蜂群获得 8 个新的工作者节点,总共 10 个工作者节点。定期(间隔几秒钟后)列出服务描述,随着新工作节点的创建,新的副本开始将当前状态与所需状态相协调。Replicas列中的副本数量在几秒钟内逐渐增加。如服务列表中的10/10所示,mysql服务的所有副本开始运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 3/10 mysql:latest
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 6/10 mysql:latest
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 9/10 mysql:latest
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 10/10 mysql:latest
列出服务副本会将所有副本列为Running。先前的Pending副本被安排在新节点上。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
8kkkdns0l690 mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 7 minutes ago
k209uge36bih mysql.2 mysql:latest ip-172-31-25-163.ec2.internal Running Running 6 minutes ago
oiublpclz9eu mysql.3 mysql:latest ip-172-31-33-230.ec2.internal Running Running 6 minutes ago
u807b7h0qvqc mysql.4 mysql:latest ip-172-31-11-105.ec2.internal Running Running 45 seconds ago
jh2ep10sonxy mysql.5 mysql:latest ip-172-31-13-141.ec2.internal Running Running about a minute ago
8d19osxa4fwf mysql.6 mysql:latest ip-172-31-24-10.ec2.internal Running Running about a minute ago
k8hba8j5o9vi mysql.7 mysql:latest ip-172-31-0-114.ec2.internal Running Running 55 seconds ago
ettk65bpin3b mysql.8 mysql:latest ip-172-31-5-127.ec2.internal Running Running about a minute ago
i3otbqfsfvr7 mysql.9 mysql:latest ip-172-31-35-209.ec2.internal Running Running 24 seconds ago
sxdi970o6d3b mysql.10 mysql:latest ip-172-31-21-57.ec2.internal Running Running 49 seconds ago
如果再次更新栈以减少工作节点的数量,一些副本将关闭并取消调度。在减少工作节点的数量后,Replicas列只列出了正在运行的5/10个副本。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 5/10 mysql:latest
一些服务任务被列为Shutdown,因为一些工作者节点已经从群中移除。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
8kkkdns0l690 mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 10 minutes ago
ulknt3e5zxy1 mysql.2 mysql:latest Ready Pending 3 seconds ago
k209uge36bih \_ mysql.2 mysql:latest ip-172-31-25-163.ec2.internal Shutdown Running 14 seconds ago
oiublpclz9eu mysql.3 mysql:latest ip-172-31-33-230.ec2.internal Running Running 9 minutes ago
mh2fpioi441k mysql.4 mysql:latest Running Pending 3 seconds ago
u807b7h0qvqc \_ mysql.4 mysql:latest v53huw84hskqsb3e8o0a2pmun Shutdown Running about a minute ago
jzghd72nk0zc mysql.5 mysql:latest Ready Pending 3 seconds ago
jh2ep10sonxy \_ mysql.5 mysql:latest ip-172-31-13-141.ec2.internal Shutdown Running 14 seconds ago
8d19osxa4fwf mysql.6 mysql:latest ip-172-31-24-10.ec2.internal Running Running 4 minutes ago
dlcgstxxkd9t mysql.7 mysql:latest Running Pending 3 seconds ago
ziqslz7u9d9l \_ mysql.7 mysql:latest ip-172-31-43-179.ec2.internal Shutdown Assigned 57 seconds ago
k8hba8j5o9vi \_ mysql.7 mysql:latest op1dzvmt5eyc74l6pcl5ut64p Shutdown Running about a minute ago
ettk65bpin3b mysql.8 mysql:latest ip-172-31-5-127.ec2.internal Running Running 4 minutes ago
i3otbqfsfvr7 mysql.9 mysql:latest ip-172-31-35-209.ec2.internal Running Running 3 minutes ago
sxdi970o6d3b mysql.10 mysql:latest ip-172-31-21-57.ec2.internal Running Running 12 seconds ago
摘要
本章讨论了基于资源储备和资源限制的 Docker Swarm 模式的资源模型。预留资源不能超过资源限制,分配给服务任务的资源受节点容量的限制。下一章讨论 Docker 群模式下的调度。
八、调度
在第二章,介绍了 Docker Swarm。在第四章中,介绍了 Docker 群服务。一个服务由零个或多个服务任务(副本)组成,它在集群中的节点上调度这些任务。服务的期望状态包括必须运行的任务数量。调度被定义为将需要运行的服务任务放置在集群中的一个节点上以保持服务的期望状态的过程,如图 8-1 所示。只能在工作节点上计划服务任务。默认情况下,管理器节点也是工作者节点。
图 8-1。
Scheduling
问题
如果没有调度策略,服务任务可能会被调度到群中的一个节点子集上。例如,一个服务中的所有三个任务都可以在一个群中的同一个节点上被调度,如图 8-2 所示。
图 8-2。
Avoid scheduling all tasks on one node
不使用调度策略可能会导致以下问题:
- 群中资源利用不足—如果所有任务都在单个节点或节点子集上调度,则其他节点的资源容量没有得到利用。
- 资源利用不均衡—如果所有任务都安排在单个节点或节点子集上,则安排任务的节点上的资源会被过度利用,任务甚至会用尽所有资源容量,而没有扩展副本的余地。
- 缺乏局部性—客户端基于节点位置访问服务的任务。如果所有服务任务都被安排在单个节点上,则在其他节点上访问服务的外部客户端不能本地访问服务,从而在访问相对远程的任务时导致网络开销。
- 单点故障—如果所有服务都在一个节点上运行,而该节点出现问题,就会导致停机。增加节点间的冗余可以避免这个问题。
解决方案
为了克服前面讨论的问题,Docker 群中的服务任务调度是基于一个内置的调度策略。Docker Swarm 模式使用分散调度策略对节点进行排序,以放置服务任务(副本)。为每个任务的调度计算节点排序,并且在具有最高计算排序的节点上调度任务。分布调度策略根据节点的可用 CPU、RAM 和已经在节点上运行的容器数量来计算节点等级。传播策略针对容器数量最少的节点进行优化。负载共享是分布策略的目标,它导致任务(容器)稀疏而均匀地分布在集群中的几台机器上。传播策略的预期结果是,如果单个节点或节点的一个小子集宕机或变得可用,则只有少数任务丢失,而群中的大多数任务继续可用。
Note
因为容器在所有状态下都会消耗资源,包括退出时,所以扩展策略不会考虑容器的状态。建议用户删除已停止的容器,因为如果某个节点有几个已停止的容器,那么该节点将变得不适合调度新任务。
扩展调度策略不考虑为哪个服务调度任务。只有可用的和请求的资源被用于调度新的任务。图 8-3 显示了使用扩展调度策略的调度。
图 8-3。
Using the spread scheduling policy
举个假设的例子:
- 从三个节点开始,每个节点有 3GB 的容量和 3 个 CPU,并且没有容器运行。
- 创建一个带有一个副本的
mysql服务,它需要 1GB 和 1 个 CPU 的资源。第一个副本被随机安排在群中三个节点中的一个上,因为所有节点都具有相同的排名。如果所有的节点都有相同的等级,一个新的任务会被随机安排在其中一个节点上。 - 将
mysql服务扩展为三个任务。由于其中一个节点已经加载,这两个新任务被安排在另外两个节点上,将一个任务分配给每个节点。 - 将
mysql服务扩展到五个任务。必须启动两个新任务,并且所有节点都具有相同的等级,因为它们具有相同的可用资源容量和相同数量的正在运行的容器。这两个新任务随机安排在两个节点上。因此,两个节点各有两个任务,一个节点有一个任务。 - 为
nginx服务器创建另一个服务,需要两个任务,每个任务需要 0.5GB 和 0.5 CPU。这两个任务都被安排在只有mysql服务任务的节点上,因为它负载最小。因此,两个节点有两个mysql服务任务和 1GB 和 1 个 CPU 的可用容量,一个节点有两个nginx服务任务和一个mysql服务任务以及 1GB 和 1 个 CPU 的可用资源容量。 - 将
nginx服务扩展到三个。即使所有节点都有相同的可用 CPU 和 RAM,新任务也不会随机安排在三个节点中的一个上,而是安排在容器数量最少的节点上。结果,新的nginx任务被随机安排在其中一个节点上,每个节点有两个mysql任务。如果节点具有相同的可用 CPU 和 RAM,则选择具有较少容器(运行或停止)的节点来调度新任务。
本章涵盖以下主题:
- 设置环境
- 创建和计划服务—扩展计划
- 期望的国家和解
- 受节点资源容量限制的任务调度
- 添加服务调度约束
- 在特定节点上调度
- 添加多个日程排定限制
- 为调度添加节点标签
- 添加、更新和删除服务调度约束
- 扩展调度和全球服务
设置环境
使用 Docker for AWS 创建一个由一个管理节点和两个工作节点组成的 CloudFormation 栈。AWS 的 Docker 在第三章中介绍。栈如图 8-4 所示。
图 8-4。
CloudFormation stack
栈中的三个 EC2 实例如图 8-5 所示。
图 8-5。
EC2 instances for the Docker swarm
使用公共 IP 地址 SSH 登录到 Swarm manager,该地址可以从 EC2 控制台获得,如图 8-5 所示。
[root@localhost ∼]# ssh -i "docker.pem" docker@54.84.133.157
Welcome to Docker!
列出群体中的节点;应该列出三个节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
0waa5g3b6j641xtwsygvjvwc1 ip-172-31-0-147.ec2.internal Ready Active
e7vigin0luuo1kynjnl33v9pa ip-172-31-29-67.ec2.internal Ready Active
ptm7e0p346zwypos7wnpcm72d * ip-172-31-25-121.ec2.internal Ready Active Leader
创建和计划服务:扩展计划
首先,我们以 MySQL 数据库服务为例来讨论默认的扩展调度。从 Swarm manager 节点,运行以下命令为 MySQL 创建一个五副本服务。输出是服务 ID(以斜体显示)。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 5 \
> --name mysql \
> mysql
1onpemnoz4x1lh3sv5umab8uo
随后,使用docker service ls列出服务。最初,REPLICAS列可能是0/5,表示没有副本被调度和运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
1onpemnoz4x1 mysql replicated 0/5 mysql:latest
过一会儿再次运行该命令;所有副本应该都在运行,如REPLICAS列中的5/5所示。使用docker service ps mysql命令列出服务副本。任务应该正在运行或准备运行。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
fwjbu3gt2zn0 mysql.1 mysql:latest ip-172-31-0-147.ec2.internal Running Preparing 8 seconds ago
w0521ik1awjf mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Preparing 8 seconds ago
z9wn2nrzfzt8 mysql.3 mysql:latest ip-172-31-0-147.ec2.internal Running Preparing 8 seconds ago
tm8jbque3xbb mysql.4 mysql:latest ip-172-31-25-121.ec2.internal Running Preparing 8 seconds ago
7drxfy3vbmp5 mysql.5 mysql:latest ip-172-31-29-67.ec2.internal Running Preparing 8 seconds ago
按照分布调度策略,其中两个副本在一个工作节点上按计划列出,两个在另一个工作节点上,一个在管理器节点上。由于副本的数量为奇数,所以位置不能完全均匀分布,但是单个节点不会有两个以上的副本。
要查看分布调度策略如何在整个群中均匀地分布副本,请将服务扩展到六个副本。docker service scale命令的输出以斜体显示。
∼ $ docker service scale mysql=6
mysql scaled to 6
随后,列出副本。每个节点上都安排了两个复制副本,因为扩展计划策略旨在计划。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
fwjbu3gt2zn0 mysql.1 mysql:latest ip-172-31-0-147.ec2.internal Running Running 13 seconds ago
w0521ik1awjf mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 12 seconds ago
z9wn2nrzfzt8 mysql.3 mysql:latest ip-172-31-0-147.ec2.internal Running Running 13 seconds ago
tm8jbque3xbb mysql.4 mysql:latest ip-172-31-25-121.ec2.internal Running Running 8 seconds ago
7drxfy3vbmp5 mysql.5 mysql:latest ip-172-31-29-67.ec2.internal Running Running 12 seconds ago
utjo8lwbtzf7 mysql.6 mysql:latest ip-172-31-25-121.ec2.internal Running Running 5 seconds ago
因为服务副本或任务只不过是运行容器的一个槽,所以每个节点为mysql服务运行两个容器。
为了进一步演示扩展调度,将服务缩减为三个任务。命令输出以斜体显示。
∼ $ docker service scale mysql=3
mysql scaled to 3
列出服务任务。每个节点上运行一个任务,这也是一个均匀分布的任务调度。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
w0521ik1awjf mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 40 seconds ago
z9wn2nrzfzt8 mysql.3 mysql:latest ip-172-31-0-147.ec2.internal Running Running 41 seconds ago
utjo8lwbtzf7 mysql.6 mysql:latest ip-172-31-25-121.ec2.internal Running Running 33 seconds ago
期望的国家和解
当一个服务被创建或者被放大或缩小时,该服务最初在当前状态和期望状态之间存在差异。期望状态的不同值是ready、running、shutdown和accepted。Docker 服务是为期望的状态协调而设计的,这意味着群管理器持续监视集群状态,以协调服务的期望状态和当前状态之间的任何差异。任务的当前状态可以是assigned、preparing、ready、running、shutdown或pending。已经分配给节点但当前未运行的任务处于assigned状态。期望状态为running并准备运行的任务处于preparing当前状态。如果群中没有节点可以运行任务,则该任务处于pending状态。
在下面的任务列表中,一些任务具有期望状态和当前状态running。这些任务已经协调了它们期望的状态。一个任务的期望状态被设置为running,但是当前状态是pending。另一个任务的期望状态设置为shutdown,当前状态设置为assigned。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
opxf4ne7iyy6 mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 9 minutes ago
x30y3jlea047 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 8 minutes ago
w4ivsbvwqqzq mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 4 minutes ago
j9lp08ojofj7 mysql.4 mysql:latest Running Pending 28 seconds ago
ph1zpsjsvp69 \_ mysql.4 mysql:latest ip-172-31-7-137.ec2.internal Shutdown Assigned 33 seconds ago
d3oxy6hxfjh3 \_ mysql.4 mysql:latest ip-172-31-40-70.ec2.internal Shutdown Running 43 seconds ago
ic331aasjpdm mysql.5 mysql:latest ip-172-31-44-104.ec2.internal Running Running 8 minutes ago
在早期的任务列表中,所有任务都处于当前状态preparing和期望状态running。
群模式旨在尽可能地协调所需的状态,这意味着如果节点资源可用,将运行所需数量的副本。为了进行演示,通过选择操作➤更新栈来更新 AWS CloudFormation 栈的 Docker,如图 8-6 所示。
图 8-6。
Updating a stack
将工作节点的数量从两个减少到一个,如图 8-7 所示。
图 8-7。
Decreasing the number of worker nodes to one
随后,列出来自 Swarm manager 节点的服务副本。
docker service ps mysql
在离开群的群工作者节点上运行的服务副本被列为shutdown。在群中剩余的两个节点上开始新的副本以协调期望的状态。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
p14bbk7ij1mt mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 5 minutes ago
w0521ik1awjf mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 7 minutes ago
uatsaay7axlc mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running about a minute ago
z9wn2nrzfzt8 \_ mysql.3 mysql:latest 0waa5g3b6j641xtwsygvjvwc1 Shutdown Running 2 minutes ago
w1tlw0fom42q mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running about a minute ago
qc75buhzzct3 \_ mysql.4 mysql:latest 0waa5g3b6j641xtwsygvjvwc1 Shutdown Running 2 minutes ago
s09ts9s8np3d mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running 5 minutes ago
utjo8lwbtzf7 mysql.6 mysql:latest ip-172-31-25-121.ec2.internal Running Running 7 minutes ago
仅列出所需状态为running的副本,六个副本在两个节点之间按计划平均列出—三个副本在管理节点上,三个副本在工作节点上。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
p14bbk7ij1mt mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 6 minutes ago
w0521ik1awjf mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 8 minutes ago
uatsaay7axlc mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 2 minutes ago
w1tlw0fom42q mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 2 minutes ago
s09ts9s8np3d mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running 6 minutes ago
utjo8lwbtzf7 mysql.6 mysql:latest ip-172-31-25-121.ec2.internal Running Running 8 minutes ago
如果新的节点被添加到群中,传播调度策略不重新调度已经运行的副本来实现群中的均匀传播。为了演示这一点,我们将工作节点的数量增加回两个,如图 8-8 所示。
图 8-8。
Re-adding a worker node to Swarm
将一个节点添加到群中不会关闭其他节点上的副本并在新节点上启动副本。列出正在运行的副本并不表示服务副本的替换。在添加新节点之前,服务副本继续在运行它们的节点上运行,其中三个在管理节点上,三个在工作节点上。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
p14bbk7ij1mt mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 15 minutes ago
w0521ik1awjf mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 17 minutes ago
uatsaay7axlc mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 12 minutes ago
z9wn2nrzfzt8 \_ mysql.3 mysql:latest 0waa5g3b6j641xtwsygvjvwc1 Shutdown Running 13 minutes ago
w1tlw0fom42q mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 12 minutes ago
qc75buhzzct3 \_ mysql.4 mysql:latest 0waa5g3b6j641xtwsygvjvwc1 Shutdown Running 13 minutes ago
s09ts9s8np3d mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running 15 minutes ago
utjo8lwbtzf7 mysql.6 mysql:latest ip-172-31-25-121.ec2.internal Running Running 17 minutes ago
受节点资源容量限制的任务调度
调度策略受到可用节点资源的限制,这意味着如果没有足够的节点资源(CPU 和内存)可用,就无法运行服务副本。资源使用不能超过节点容量。副本仍被分配给服务以定义所需的状态,但由于资源不足,可能不会运行。为了演示这一点,我们删除服务mysql并使用指定的资源请求和限制再次创建服务。命令输出以斜体显示。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> --reserve-cpu 1 --limit-cpu 2 --reserve-memory 256mb --limit-memory 512mb mysql
0qe2thy0dlviroli6k8thist1
列出服务表明创建了服务的一个副本。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
0qe2thy0dlvi mysql replicated 1/1 mysql:latest
单个副本被调度在管理节点上,如果群中的所有节点具有相同的节点排名,则管理节点被随机选择。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
opxf4ne7iyy6 mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 8 seconds ago
接下来,为了潜在地使服务副本消耗比可用资源更多的资源,将服务扩展到五个副本。
∼ $ docker service scale mysql=5
mysql scaled to 5
列出服务表明3/5 Replicas正在运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
0qe2thy0dlvi mysql replicated 3/5 mysql:latest
列出服务副本表明一些副本是pending而不是running。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
opxf4ne7iyy6 mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 4 minutes ago
x30y3jlea047 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 3 minutes ago
w4ivsbvwqqzq mysql.3 mysql:latest Running Pending 3 minutes ago
d3oxy6hxfjh3 mysql.4 mysql:latest Running Pending 3 minutes ago
ic331aasjpdm mysql.5 mysql:latest ip-172-31-44-104.ec2.internal Running Running 3 minutes ago
pending状态意味着副本被分配给服务,但是还没有在任何节点上被调度。根据请求的资源和可用的节点资源,只能运行三个副本,每个节点一个。
因为由于缺乏资源而没有调度副本,所以我们添加一个或多个新的工作节点来潜在地调度副本以协调期望的状态。将工作节点的数量增加到 5 个,如图 8-9 所示。
图 8-9。
Increasing the number of worker nodes to five
在添加一个新节点后,Swarm 应该列出六个节点。随着资源变得可用于pending任务,任务得到调度并开始运行。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
opxf4ne7iyy6 mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 5 minutes ago
x30y3jlea047 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 4 minutes ago
w4ivsbvwqqzq mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 21 seconds ago
d3oxy6hxfjh3 mysql.4 mysql:latest ip-172-31-40-70.ec2.internal Running Preparing 30 seconds ago
ic331aasjpdm mysql.5 mysql:latest ip-172-31-44-104.ec2.internal Running Running 4 minutes ago
如果工作者节点的数量减少,一些任务被重新调度,如shutdown期望状态所示。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
opxf4ne7iyy6 mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 9 minutes ago
x30y3jlea047 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 8 minutes ago
w4ivsbvwqqzq mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 4 minutes ago
j9lp08ojofj7 mysql.4 mysql:latest Running Pending 28 seconds ago
ph1zpsjsvp69 \_ mysql.4 mysql:latest ip-172-31-7-137.ec2.internal Shutdown Assigned 33 seconds ago
d3oxy6hxfjh3 \_ mysql.4 mysql:latest ip-172-31-40-70.ec2.internal Shutdown Running 43 seconds ago
ic331aasjpdm mysql.5 mysql:latest ip-172-31-44-104.ec2.internal Running Running 8 minutes ago
更新服务以降低保留的 CPU 和内存资源使用率只会更新服务的UpdateConfig。这不会降低已经运行的任务的资源使用率,也不会使pending或shutdown任务运行。例如,当一些任务由于缺乏资源而处于pending或shutdown时,降低mysql服务的资源储备和限制。
∼ $ docker service update --reserve-cpu .1 --limit-cpu .5 --reserve-memory 64mb
--limit-memory 128mb mysql
mysql
UpdateConfig被修改,但仅适用于在该点之后创建的新副本。
∼ $ docker service inspect mysql
[ }, "Resources": { "Limits": { "NanoCPUs": 500000000, "MemoryBytes": 134217728 }, "Reservations": { "NanoCPUs": 100000000, "MemoryBytes": 67108864 } },]
mysql服务中只有三个副本在实际运行。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
opxf4ne7iyy6 mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 10 minutes ago
x30y3jlea047 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 10 minutes ago
w4ivsbvwqqzq mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 5 minutes ago
rm9uj4qevt5b mysql.5 mysql:latest Running Pending 33 seconds ago
要强制服务任务使用新的资源设置,请将服务缩减为一个任务,然后再缩减为五个任务。
∼ $ docker service scale mysql=1
mysql scaled to 1
∼ $ docker service scale mysql=5
mysql scaled to 5
所有五个任务现在都在运行。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
anai3mptbnkp mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Running Running 17 seconds ago
opxf4ne7iyy6 \_ mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Shutdown Shutdown 18 seconds ago
lmkn8l50t334 mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running 10 seconds ago
7uz7q86wnzn4 mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 11 seconds ago
ubh4m39aw8m9 mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 11 seconds ago
56pnrzajogvs mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running 10 seconds ago
添加服务调度约束
Docker Swarm 支持放置或调度约束来调度新任务。服务放置约束是放置服务任务的附加标准,可以基于节点属性、元数据和引擎元数据。群调度器使用以下序列来调度服务任务。
- 节点是否满足所有放置约束?
- 一个节点是否满足均匀分布的调度策略要求?
- 节点是否有足够的资源来调度任务?
可通过docker service create命令使用--constraint选项添加一个布局约束。对于一个已经运行的服务,可以分别使用--constraint-add和--constraint-rm选项和docker service update命令添加和删除约束。表 8-1 中讨论的节点属性可用于指定约束。
表 8-1。
Node Attributes for Constraints
| 节点属性 | 描述 | 例子 | | --- | --- | --- | | `node.id` | 指定节点 ID。使用`docker node ls`命令列出节点 id。 | `node.id==a3r56hj7y` | | `node.hostname` | 指定节点的主机名。使用`docker node ls`命令列出了节点的主机名。 | `node.hostname!=ip-10-0-0-ec2.internal` | | `node.role` | 指定节点角色,它是`worker`或`manager`之一。 | `node.role==manager` | | `node.labels` | 指定用户添加的节点标签。标签是一个键值对。添加节点标签时,`node.labels.`前缀将被省略,并自动添加。添加和使用节点标签将在后续章节中讨论。 | `node.labels.db==mysql` | | `engine.labels` | Docker 引擎标签,如驱动程序、操作系统、版本。 | `engine.labels.os==coreos` |接下来,我们讨论一些使用调度约束的例子。
在特定节点上调度
在本节中,我们将在集群中的特定节点上调度服务副本。用docker node ls命令列出节点 id。Swarm 有以下三个可用于调度的节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
81h6uvu8uq0emnovzkg6v7mzg ip-172-31-2-177.ec2.internal Ready Active
e7vigin0luuo1kynjnl33v9pa ip-172-31-29-67.ec2.internal Ready Active
ptm7e0p346zwypos7wnpcm72d * ip-172-31-25-121.ec2.internal Ready Active Leader
我们可以按节点角色安排服务。创建一个mysql服务,其位置约束是服务任务只能在工作节点上调度。首先,删除已经运行的mysql服务
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 3 \
> --constraint node.role==worker \
> --name mysql \
> mysql
nzgte4zac1x8itx6t98y5gi42
创建了服务,并且仅在两个 worker 节点上调度了三个任务,如正在运行的服务任务中所列。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
f5t15mnrft0h mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 19 seconds ago
oxvq4ljuq6yz mysql.2 mysql:latest ip-172-31-2-177.ec2.internal Running Running 19 seconds ago
k5jo862lvsxf mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 19 seconds ago
接下来,我们使用节点 ID 来调度服务的任务。复制管理节点的节点 ID,它也是群中的领导者,是唯一的管理节点。在下面的命令中替换节点 ID,为 MySQL 数据库创建一个服务,并只在 manager 节点上调度副本。
docker service create \
--env MYSQL_ROOT_PASSWORD='mysql'\
--replicas 3 \
--constraint node.id ==<nodeid>
--name mysql \
mysql
创建服务有三个任务。命令输出以斜体显示。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 3 \
> --constraint node.id==ptm7e0p346zwypos7wnpcm72d\
> --name mysql \
> mysql
u1qi6zqnch9hn7x6k516axg7h
该服务的所有三个副本仅在管理器节点上进行调度。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
lbttu95qdjvy mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 21 seconds ago
89x0z94on0fb mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running 21 seconds ago
3s6508aimdaj mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 22 seconds ago
添加多个日程排定限制
还可以指定多个节点约束,并且必须使用AND来满足每个约束表达式,以便调度程序在节点上调度副本。例如,我们创建一个具有两个角色的服务,一个将节点角色约束为 worker,另一个将节点主机名约束为不是特定的主机名ip-172-31-2-177.ec2.internal。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 3 \
> --constraint node.role==worker \
> --constraint node.hostname!=ip-172-31-2-177.ec2.internal\
> --name mysql \
> mysql
87g0c8kauhz8yb4wv2ryc2vqr
服务被创建。列出服务会将3/3 replicas列为正在运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
87g0c8kauhz8 mysql replicated 3/3 mysql:latest
列出服务任务表明所有任务都被安排在单个工作节点上。满足两个约束:该节点是一个工作节点,而不是主机名为ip-172-31-2-177.ec2.internal的工作节点。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
jlfk79mb6m6a mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 13 seconds ago
if5y39ky884q mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 13 seconds ago
zctm6mzbl4du mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Running Running 13 seconds ago
如果mysql服务被更新以移除约束,则扩展调度策略基于节点排序来重新调度任务。例如,更新服务以移除添加的两个放置约束。使用docker service update命令的–constraint-rm选项删除一个约束。
∼ $ docker service update \
> --constraint-rm node.role==worker \
> --constraint-rm node.hostname!=ip-172-31-2-177.ec2.internal\
> mysql
mysql
当服务被更新以移除约束时,所有服务任务被关闭,并且新的服务任务被启动。开始新的服务任务,在群中的三个节点上各一个。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
d22bkgteivot mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Ready Ready less than a second ago
jlfk79mb6m6a \_ mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Shutdown Running 1 second ago
mp757499j3io mysql.2 mysql:latest ip-172-31-2-177.ec2.internal Running Running 1 second ago
if5y39ky884q \_ mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Shutdown Shutdown 2 seconds ago
jtdxucteb0fl mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 4 seconds ago
zctm6mzbl4du \_ mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Shutdown Shutdown 5 seconds ago
仅列出正在运行的任务。列出了每个节点上运行的一个任务。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
d22bkgteivot mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 46 seconds ago
mp757499j3io mysql.2 mysql:latest ip-172-31-2-177.ec2.internal Running Running 49 seconds ago
jtdxucteb0fl mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 53 seconds ago
类似地,多节点约束可用于仅在管理器节点上运行副本。接下来,我们更新mysql服务以在特定的管理器节点上运行。首先,将一个 worker 节点提升为 manager。
∼ $ docker node promote ip-172-31-2-177.ec2.internal
Node ip-172-31-2-177.ec2.internal promoted to a manager in the swarm.
随后,列出了两个管理器节点,如其中两个节点的Manager Status所示。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
81h6uvu8uq0emnovzkg6v7mzg ip-172-31-2-177.ec2.internal Ready Active Reachable
e7vigin0luuo1kynjnl33v9pa ip-172-31-29-67.ec2.internal Ready Active
ptm7e0p346zwypos7wnpcm72d * ip-172-31-25-121.ec2.internal Ready Active Leader
更新mysql服务以添加多个节点约束,从而仅在特定的管理器节点上运行副本。使用docker service update命令的--constraint-add选项添加约束。
∼ $ docker service update \
> --constraint-add node.role==manager \
> --constraint-add node.hostname==ip-172-31-2-177.ec2.internal\
> mysql
mysql
同样,所有服务任务被关闭,新任务被启动,所有这些都在从 worker 节点升级的指定 manager 节点上进行。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
eghm1or6yg5g mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Running Running 28 seconds ago
bhfngac5ssm7 mysql.2 mysql:latest ip-172-31-2-177.ec2.internal Running Running 22 seconds ago
ts3fgvq900os mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 25 seconds ago
为调度添加节点标签
接下来,我们讨论如何使用节点标签来指定服务放置约束。可以使用以下命令语法向节点添加标签,其中变量为<LABELKEY>、<LABELVALUE>和<NODE>。<NODE>是节点 ID 或主机名。
docker node update --label-add <LABELKEY>=<LABELVALUE> <NODE>
例如,将标签db=mysql添加到主机名设置为ip-172-31-25-121.ec2.internal的节点,该节点是领导者节点。
∼ $ docker node update --label-add db=mysql ip-172-31-25-121.ec2.internal
ip-172-31-25-121.ec2.internal
添加一个节点标签。检查节点时,标签列在Labels字段中。
∼ $ docker node inspect ip-172-31-25-121.ec2.internal
[
"Spec": {
"Labels": {
"db": "mysql"
},
"Role": "manager",
"Availability": "active"
},
]
接下来,创建一个使用节点标签添加放置约束的服务。标签的- constraint 选项必须包括前缀 node.labels。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 3 \
> --constraint node.labels.db==mysql \
> --name mysql \
> mysql
2hhccmj9senseazbet11dekoa
服务已创建。列出任务会列出领导者管理器节点上的所有任务,这是节点标签约束所指定的。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
g5jz9im3fufv mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 18 seconds ago
bupr27bs57h1 mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running 18 seconds ago
5bb2yf8aehqn mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 18 seconds ago
添加的标签可通过docker node update命令的--label-rm选项移除,其中仅指定了标签键。
docker node update --label-rm db ip-172-31-25-121.ec2.internal
添加、更新和删除服务调度约束
在前面的部分中,我们讨论了在使用docker service create创建服务时添加位置约束。可使用--constraint-add和--constraint-rm选项通过docker service update命令添加/删除布局约束。为了讨论一个更新布局约束的例子,我们创建了一个mysql服务,它有三个副本,开始时没有布局约束。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 3 \
> --name mysql \
> mysql
az3cq6sxwrrk4mxkksdu21i25
一个mysql服务被创建,在集群中的三个节点上调度三个副本,使用传播策略。
接下来,用docker service update命令更新服务,为服务副本添加一个仅在管理器节点上运行的约束。
∼ $ docker service update \
> --constraint-add node.role==manager \
> mysql
mysql
在具有两个管理节点的群中,所有服务任务都被关闭,新任务只在管理节点上启动。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
pjwseruvy4rj mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Running Running 4 seconds ago
s66g9stz9af5 \_ mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Shutdown Shutdown 4 seconds ago
yqco9zd0vq79 mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running 9 seconds ago
8muu6gbghhnd \_ mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Shutdown Shutdown 10 seconds ago
8x7xlavcxdau mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 7 seconds ago
qx95vwi2h547 \_ mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Shutdown Shutdown 7 seconds ago
可以在同一个docker service update命令中添加和删除调度约束。例如,删除节点成为管理者的约束,并添加节点成为工作者的约束。
∼ $ docker service update \
> --constraint-rm node.role==manager \
> --constraint-add node.role==worker \
> mysql
mysql
又来了。所有服务任务都将关闭,新任务仅在工作节点上启动。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
6ppgmvw9lv75 mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 9 seconds ago
qm0loki65v9s mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 17 seconds ago
ypl0tc1ft92o mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Running Running
如果将节点角色指定为工作者的唯一调度约束被移除,则分布调度策略开始在整个群中均匀分布新任务。要进行演示,请删除节点角色成为工作者的约束。
∼ $ docker service update --constraint-rm node.role==worker mysql
mysql
随后,新的任务在群体中的节点间传播。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
jpx4jjw6l9d5 mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 5 seconds ago
ngajiik1hugb mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running 12 seconds ago
40eaujzlux88 mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 8 seconds ago
扩展调度和全球服务
全局服务在集群中的每个节点上运行一个任务。全局服务不能被扩展以创建更多/更少的任务。因此,分布调度策略概念不适用于全局服务。然而,节点约束可以应用于全局服务。例如,我们为mysql数据库创建了一个全局服务。应用一个放置约束,即服务应该只在工作节点上可用。
∼ $ docker service create \
> --mode global \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --constraint node.role==worker \
> --name mysql \
> mysql
jtzcwatp001q9r26n1uubd8me
创建了全局服务。将具有期望状态的任务的服务任务列为running仅列出工作节点上的任务。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
o5nskzpv27j9 mysql.e7vigin0luuo1kynjnl33v9pa mysql:latest ip-172-31-29-67.ec2.internal Running Running 17 seconds ago
如果创建时没有仅在工作节点上调度的约束,全局服务将在每个节点上调度一个任务,如下例所示。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --mode global \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --name mysql \
> mysql
mv9yzyyntdhzz41zssbutcsvw
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
mc87btddhmpl mysql.e7vigin0luuo1kynjnl33v9pa mysql:latest ip-172-31-29-67.ec2.internal Running Running 19 seconds ago
o0wfdq9sd8yt mysql.ptm7e0p346zwypos7wnpcm72d mysql:latest ip-172-31-25-121.ec2.internal Running Running 19 seconds ago
wt2q5k2dhqjt mysql.81h6uvu8uq0emnovzkg6v7mzg mysql:latest ip-172-31-2-177.ec2.internal Running Running 19 seconds ago
摘要
本章讨论了 Docker Swarm 模式中使用的分布调度策略,通过这种策略,服务副本基于节点排序均匀地分布在集群中的节点上;较高的节点等级获得服务副本放置优先级。我们还讨论了有限节点资源容量的影响,以及如何通过向群集添加新节点来缓解这种影响。我们讨论了调度新副本的放置约束。分布调度策略与全局服务无关,因为默认情况下,全局服务在每个节点上创建一个服务任务。然而,调度约束可以与全局服务一起使用。在下一章,我们将讨论 Docker 服务的滚动更新。