Docker 管理设计模式(三)
九、滚动更新
Docker 群模式提供由在群中的节点上运行的副本组成的服务。服务定义是在首次创建/定义服务时创建的。使用docker service create命令创建服务定义。该命令提供了几个选项,包括用于添加放置约束、容器标签、服务标签、DNS 选项、环境变量、资源保留和限制、日志记录驱动程序、装载、副本数量、重启条件和延迟、更新延迟、故障操作、最大故障率和并行性的选项,其中大部分在第四章中讨论。
问题
创建服务定义后,可能需要更新一些服务选项,例如增加/减少副本数量、添加/删除放置约束、更新资源保留和限制、添加/删除装载、添加/删除环境变量、添加/删除容器和服务标签、添加/删除 DNS 选项,以及修改重启和更新参数。如果需要整体关闭服务来更新服务定义选项,则会导致服务中断。
解决方案
Docker Swarm 模式包括滚动更新。在滚动更新中,服务不会关闭,但是服务中的单个副本/任务会一次关闭一个,并且基于新服务定义的新服务副本/任务会一次启动一个,如图 9-1 所示。因此,该服务在滚动更新期间继续可用。在滚动更新期间,提供给客户端的服务任务可以来自旧的和新的服务定义。例如,如果滚动更新对更近的映像标签执行更新,则在滚动更新期间提供给外部客户端的一些任务可能来自旧映像标签和新映像标签的混合。
图 9-1。
Rolling update
滚动更新为服务创建新的服务定义和新的期望状态。滚动更新包括关闭所有服务副本和启动所有新的服务副本,但不适用于尚未计划的服务副本,例如由于缺乏资源。在滚动更新中,即使只更新副本的数量,也会关闭所有旧副本或使其失败,并启动所有新副本。
在滚动更新期间,调度程序使用以下顺序。
- 第一个任务停止。
- 已计划对停止的任务进行更新。
- 启动更新任务的 Docker 容器。
- 如果任务更新返回
RUNNING,等待--update-delay中指定的持续时间,并开始下一个任务的更新。 - 如果在更新过程中,任务返回
FAILED,执行--update-failure-action,默认暂停更新。 - 用
docker service update <SERVICE-ID>重启暂停的更新。 - 如果更新失败重复出现,请找到失败的原因,并通过向 docker 服务更新提供其他选项来重新配置服务。
设置环境
使用 Docker for AWS 创建一个由一个管理节点和两个工作节点组成的 Docker 群,如第三章所述。从 EC2 控制台获取 manager 实例的公共 IP 地址,然后 SSH 登录到该实例。
[root@localhost ∼]# ssh -i "docker.pem" docker@54.84.133.157
Welcome to Docker!
列出群体节点。
∼ $ 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
使用滚动更新策略创建服务
滚动更新策略或更新配置由表 9-1 中讨论的服务定义选项组成。
表 9-1。
Rolling Update Options
| [计]选项 | 描述 | 缺省值 | | --- | --- | --- | | `--update-delay` | 更新之间的延迟(ns|us|ms|s|m|h)。 | 0 秒 | | `--update-failure-action` | 更新失败时的操作。值可以是`pause`或`continue`。 | 中止 | | `--update-max-failure-ratio` | | | | `--update-monitor` | 每次任务更新后监视失败的持续时间(ns|us|ms|s|m|h)。 | 0 秒 | | `--update-` `parallelism` | 同时更新的最大任务数。值为 0 会一次更新所有内容。 | one |要在服务部署时配置滚动更新策略,必须在创建服务时提供要配置的选项。例如,为 MySQL 数据库创建一个服务,并指定更新策略选项--update-delay和--update-parallelism。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> --update-delay 10s \
> --update-parallelism 1 \
> mysql:5.6
wr0z48v1uguk1c40pa42ywrpn
服务已创建。列出服务可能不会列出最初运行的所有副本,如REPLICAS列中的0/1所示。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
wr0z48v1uguk mysql replicated 0/1 mysql:5.6
过一会儿运行相同的命令应该会将所有副本列为正在运行,如REPLICAS列中的1/1所示。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
wr0z48v1uguk mysql replicated 1/1 mysql:5.6
单个服务副本被调度在管理器节点本身上,并且副本的 Docker 容器被启动。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
38dm9gm6cmvk mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 13 seconds ago
使用滚动更新选项创建服务本身并不演示滚动更新。它仅定义服务的UpdateConfig设置。在下一节中,我们将执行滚动更新。
滚动更新以增加副本数量
滚动更新可用于通过对docker service update命令使用--replicas选项来更新副本的数量。滚动更新更新首次部署服务时应用的UpdateConfig策略。接下来,我们从上一节中创建的一个副本更新基于mysql:5.6映像的服务的副本数量。运行以下命令,将服务定义从一个副本更新到五个副本。--update-delay和--update-parallelism选项修改服务定义的UpdateConfig。如果更新成功,则docker service update命令输出服务名。
∼ $ docker service update \
> --replicas 5 \
> --update-delay 20s \
> --update-parallelism 1 \
> mysql
mysql
随后,服务列表可能会在docker service ls命令的输出中列出一些尚未启动的副本。但是,过一会儿再次运行该命令应该会将所有复制副本列为正在运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
wr0z48v1uguk mysql replicated 5/5 mysql:5.6
在滚动更新期间,所有正在运行的任务都将关闭,并启动新的任务。mysql.1任务的期望状态被更新为shutdown,当前状态被设置为failed。新任务mysql.1开始。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
ydqj6vf9rsgw mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 26 seconds ago
38dm9gm6cmvk \_ mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed 31 seconds ago "task: non-zero exit (137)"
7bns96iu8ygz mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 32 seconds ago
62wfdbcv3cr4 mysql.3 mysql:5.6 ip-172-31-2-177.ec2.internal Running Running 33 seconds ago
ql66z5x0a2lf mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 14 seconds ago
3n3b1j7ey732 \_ mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed 19 seconds ago "task: non-zero exit (137)"
bl1365y60vuu mysql.5 mysql:5.6 ip-172-31-2-177.ec2.internal Running Running 33 seconds ago
当从一个复制副本扩展到五个复制副本时,首先启动几个新任务,然后关闭最初运行的任务,以便在滚动更新期间服务继续可用。如果服务中的唯一任务在开始任何新任务之前被首先关闭,那么服务在短时间内不会有任何正在运行的任务。
在滚动更新期间,运行五个副本的理想状态不会立即协调。滚动更新正在进行时,运行的任务可能少于五个。列出正在运行的服务任务只列出三个任务作为running。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
ydqj6vf9rsgw mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 35 seconds ago
7bns96iu8ygz mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 40 seconds ago
ql66z5x0a2lf mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 22 seconds ago
当滚动更新完成时,五个任务正在运行。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
u8falo7q95cq mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 20 seconds ago
luabknwzwqoj mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 13 seconds ago
ce4l2qvtcanv mysql.3 mysql:5.6 ip-172-31-2-177.ec2.internal Running Running 25 seconds ago
iw8vwsxq3tjz mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 6 seconds ago
qfi5fionjt2v mysql.5 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 25 seconds ago
检查服务应该会列出更新后的副本数量。UpdateConfig也与docker service inspect命令一起列出。
∼ $ docker service inspect mysql
[
...
"Spec": {
"Name": "mysql",
...
},
"Mode": {
"Replicated": {
"Replicas": 5
}
},
"UpdateConfig": {
"Parallelism": 1,
"Delay": 20000000000,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"RollbackConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
...
]
滚动更新到不同的映像标签
滚动更新的一个用例是更新到较新的映像标签。例如,为mysql服务执行滚动更新,从mysql:5.6更新到 Docker 映像mysql:latest。Update parallelism设置为 2,一次更新两个副本。
∼ $ docker service update --image mysql:latest --update-parallelism 2 mysql
mysql
服务滚动更新开始。列出服务复制副本时,会将基于映像的复制副本列为正在关闭,如shutdown期望状态所示,并将基于映像的复制副本列为正在启动,如running期望状态所示。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
vqc6rhzw5uxz mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Ready Ready 7 seconds ago
80kswuu4d5gc \_ mysql.1 mysql:5.6 ip-172-31-2-177.ec2.internal Shutdown Running 7 seconds ago
u8falo7q95cq \_ mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed 12 seconds ago "task: non-zero exit (1)"
ydqj6vf9rsgw \_ mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed 56 seconds ago "task: non-zero exit (1)"
38dm9gm6cmvk \_ mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed about a minute ago "task: non-zero exit (137)"
tvxjmahy08uh mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 2 seconds ago
luabknwzwqoj \_ mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Shutdown Failed 8 seconds ago "task: non-zero exit (137)"
7bns96iu8ygz \_ mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Shutdown Failed 50 seconds ago "task: non-zero exit (137)"
u2ea4xq4yx6t mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 4 seconds ago
ce4l2qvtcanv \_ mysql.3 mysql:5.6 ip-172-31-2-177.ec2.internal Shutdown Shutdown 4 seconds ago
62wfdbcv3cr4 \_ mysql.3 mysql:5.6 ip-172-31-2-177.ec2.internal Shutdown Failed about a minute ago "task: non-zero exit (1)"
iw8vwsxq3tjz mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 37 seconds ago
ql66z5x0a2lf \_ mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed 43 seconds ago "task: non-zero exit (137)"
3n3b1j7ey732 \_ mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed about a minute ago "task: non-zero exit (137)"
f5vcf9mgluqe mysql.5 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 14 seconds ago
qfi5fionjt2v \_ mysql.5 mysql:5.6 ip-172-31-29-67.ec2.internal Shutdown Failed 19 seconds ago "task: non-zero exit (1)"
bl1365y60vuu \_ mysql.5 mysql:5.6 ip-172-31-2-177.ec2.internal Shutdown Failed about a minute ago "task: non-zero exit (1)"
当滚动更新正在进行时,一些正在运行的任务可以基于先前的服务规范(mysql:5.6),而其他的基于新的服务规范(mysql:latest)。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
vqc6rhzw5uxz mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Running Running 4 seconds ago
tvxjmahy08uh mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 11 seconds ago
u2ea4xq4yx6t mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 13 seconds ago
iw8vwsxq3tjz mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 46 seconds ago
f5vcf9mgluqe mysql.5 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 23 seconds ago
当滚动更新完成时,所有正在运行的任务都基于新的服务规范。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
vqc6rhzw5uxz mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Running Running 45 seconds ago
53choz0dd967 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running less than a second ago
u2ea4xq4yx6t mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 53 seconds ago
tyo6v0yen7ev mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 21 seconds ago
upt212osx7au mysql.5 mysql:latest ip-172-31-29-67.ec2.internal Running Running 25 seconds ago
添加和删除环境变量的滚动更新
Docker 镜像mysql需要一个强制的环境变量MYSQL_ROOT_PASSWORD作为根密码,并支持其他一些可能被指定的环境变量。其他环境变量是 MySQL 数据库的MYSQL_DATABASE、MYSQL用户的MYSQL_USER、MySQL 密码的MYSQL_PASSWORD以及是否允许 root 密码为空的MYSQL_ALLOW_EMPTY_PASSWORD。创建mysql服务时已经设置了MYSQL_ROOT_PASSWORD。使用docker service update命令的--env-add选项,我们可以添加其他环境变量。
∼ $ docker service update --env-add MYSQL_DATABASE='mysqldb' --env-add MYSQL_USER='mysql' --env-add MYSQL_PASSWORD='mysql' --env-add MYSQL_ALLOW_EMPTY_PASSWORD='no' --update-parallelism 1 mysql
mysql
mysql的输出意味着命令成功运行。
滚动更新状态是通过docker service inspect命令找到的,该命令除了列出添加到Env JSON 对象中的env变量之外,还列出了UpdateStatus。更新状态的State为updating,消息为“update in progress”。
∼ $ docker service inspect mysql
[ {... "Spec": { "Name": "mysql", "ContainerSpec": {... "Env": [ "MYSQL_ROOT_PASSWORD=mysql", "MYSQL_DATABASE=mysqldb", "MYSQL_USER=mysql", "MYSQL_PASSWORD=mysql", "MYSQL_ALLOW_EMPTY_PASSWORD=no" ],
... },
"UpdateStatus": {
"State": "updating",
"StartedAt": "2017-07-25T19:18:11.44139778Z",
"Message": "update in progress"
}
}
]
当更新完成后,UpdateStatus状态变为"completed",Message变为"update completed"。
∼ $ docker service inspect mysql
[... }, "UpdateStatus": { "State": "completed", "StartedAt": "2017-07-25T19:18:11.44139778Z", "CompletedAt": "2017-07-25T19:20:37.912993431Z", "Message": "update completed" } }]
如StartedAt和CompletedAt时间戳所示,滚动更新大约需要两分钟。只列出期望状态为running的任务表明一个任务已经running了 21 秒,另一个任务已经运行了两分钟。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
3zhf94kklu6r mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 21 seconds ago
ta16ch5kjlr9 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 2 minutes ago
fc7uxvwvcmk3 mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running about a minute ago
jir97p344kol mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running about a minute ago
5rly53mcc8yq mysql.5 mysql:latest ip-172-31-2-177.ec2.internal Running Running 45 seconds ago
添加的环境变量可以用另一个docker service update命令和每个要删除的环境变量的--env-rm选项删除。在--env-rm中只指定了env变量名,而不是env值。
∼ $ docker service update --env-rm MYSQL_DATABASE --env-rm MYSQL_USER --env-rm
MYSQL_PASSWORD --env-rm MYSQL_ALLOW_EMPTY_PASSWORD mysql
mysql
执行另一个滚动更新。关闭所有服务任务,并启动基于新服务规范的新服务任务。服务定义只列出了强制的环境变量MYSQL_ROOT_PASSWORD。
∼ $ docker service inspect mysql
[... "Env": [ "MYSQL_ROOT_PASSWORD=mysql" ],
},
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T19:20:57.968668604Z",
"CompletedAt": "2017-07-25T19:22:59.18517919Z",
"Message": "update completed"
}
}
]
滚动更新以设置 CPU 和内存限制和预留
滚动更新可用于设置新的资源限制和储备。
∼ $ docker service update --reserve-cpu 1 --limit-cpu 2 --reserve-memory 256mb -
-limit-memory 512mb mysql
mysql
配置新的资源限制和预留,如服务规范中所列。PreviousSpec表示没有Resources Limits和Reservations被配置开始。
∼ $ docker service inspect mysql
[
...
"Spec": {
"Name": "mysql",
...
"ContainerSpec": {
... },
"Resources": {
"Limits": {
"NanoCPUs": 2000000000,
"MemoryBytes": 536870912
},
"Reservations": {
"NanoCPUs": 1000000000,
"MemoryBytes": 268435456
}
},
... },
"PreviousSpec": {
...
"Name": "mysql",
"Resources": {
"Limits": {},
"Reservations": {}
},
"UpdateStatus": {
"State": "updating",
"StartedAt": "2017-07-25T19:23:44.004458295Z",
"Message": "update in progress"
}
}
]
设置新的资源限制和保留受节点容量限制的约束。如果请求的资源超过了节点容量,滚动更新可能会继续运行而无法完成,一些任务处于pending当前状态。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
5u7zifw15n7t mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running about an hour ago
2kgsb16c8m8u mysql.2 mysql:latest Running Pending about an hour ago
mu08iu9qzqlh mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Running Running about an hour ago
aakxr8dw5s15 mysql.4 mysql:latest ip-172-31-2-177.ec2.internal Running Running about an hour ago
z6045639f20p mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running about an hour ago
如果一些任务是pending,向集群添加资源可以使pending任务运行。我们可以更新 CloudFormation 栈,将工作节点的数量从 2 个增加到 3 个,如图 9-2 所示。
图 9-2。
Increasing the number of worker nodes in the Swarm
随后,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
t4d0aq9w2a6avjx94zgkwc557 ip-172-31-42-198.ec2.internal Ready Active
随着群中资源的增加,pending任务也开始运行。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
5u7zifw15n7t mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running about an hour ago
2kgsb16c8m8u mysql.2 mysql:latest ip-172-31-2-177.ec2.internal Running Running 7 minutes ago
mu08iu9qzqlh mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Running Running about an hour ago
i5j2drlcm75f mysql.4 mysql:latest ip-172-31-42-198.ec2.internal Running Running 4 seconds ago
z6045639f20p mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running about an hour ago
滚动更新到不同的映像
滚动更新也可用于更新到完全不同的 Docker 映像。例如,对mysql服务执行滚动更新,以使用 Docker 映像postgres而不是它正在使用的mysql映像。也可以设置其他选项,如--update-parallelism。
∼ $ docker service update --image postgres --update-parallelism 1 mysql
mysql
mysql:latest基于映像的任务开始关闭,postgres基于映像的替换任务开始一次启动一个任务。滚动更新不会立即完成,将具有所需状态的服务任务列为running会列出一些基于postgres:latest映像的任务,而其他任务仍然使用mysql:latest映像。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
9tzm5pa6pcyx mysql.1 postgres:latest ip-172-31-2-177.ec2.internal Running Running 39 seconds ago
xj23fu5svv9d mysql.2 postgres:latest ip-172-31-42-198.ec2.internal Running Running about a minute ago
mu08iu9qzqlh mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Running Running about an hour ago
skzxi33c606o mysql.4 postgres:latest ip-172-31-2-177.ec2.internal Running Running 13 seconds ago
z6045639f20p mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running about an hour ago
一次一个复制副本,关闭mysql基于映像的复制副本,启动postgres基于映像的复制副本。大约两分钟后,所有任务都更新到了postgres:latest的映像上。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
9tzm5pa6pcyx mysql.1 postgres:latest ip-172-31-2-177.ec2.internal Running Running about a minute ago
xj23fu5svv9d mysql.2 postgres:latest ip-172-31-42-198.ec2.internal Running Running about a minute ago
kd9pk31vpof2 mysql.3 postgres:latest ip-172-31-42-198.ec2.internal Running Running 35 seconds ago
skzxi33c606o mysql.4 postgres:latest ip-172-31-2-177.ec2.internal Running Running 59 seconds ago
umtitiuvt5gg mysql.5 postgres:latest ip-172-31-25-121.ec2.internal Running Running 8 seconds ago
服务名称保持不变,副本名称也包含前缀mysql。mysql服务定义ContainerSpec将映像列为postgres。将映像更新为postgres并不意味着为新映像更新所有其他服务定义设置。postgres映像不使用MYSQL_ROOT_PASSWORD,但是环境变量仍然在服务规范中。
∼ $ docker service inspect mysql
[
"Spec": {
"Name": "mysql",
"ContainerSpec": {
"Image": "postgres:latest@sha256:e92fe21f695d27be7050284229a1c8c63ac10d88cba58d779c243566e125aa34",
"Env": [
"MYSQL_ROOT_PASSWORD=mysql"
],
"PreviousSpec": {
"Name": "mysql",
"ContainerSpec": {
"Image": "mysql:latest@sha256:75c563c474f1adc149978011fedfe2e6670483d133b22b07ee32789b626f8de3",
"Env": [
"MYSQL_ROOT_PASSWORD=mysql"
... },
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T20:39:45.230997671Z",
"CompletedAt": "2017-07-25T20:42:04.186537673Z",
"Message": "update completed"
}
}
]
可以用另一个update命令删除MYSQL_ROOT_PASSWORD环境变量。
∼ $ docker service update --env-rm MYSQL_ROOT_PASSWORD mysql
mysql
随后,ContainerSpec不包括MYSQL_ROOT_PASSWORD环境变量。
∼ $ docker service inspect mysql
[
...
"Spec": {
"Name": "mysql",
...
"ContainerSpec": {
"Image": "postgres:latest@sha256:e92fe21f695d27be7050284229a1c8c63ac10d88cba58d779c243566e125aa34",
"StopGracePeriod": 10000000000,
"DNSConfig": {}
},
... },
"PreviousSpec": {
"ContainerSpec": {
"Image": "postgres:latest@sha256:e92fe21f695d27be7050284229a1c8c63ac10d88cba58d779c243566e125aa34",
"Env": [
"MYSQL_ROOT_PASSWORD=mysql"
],
... },
"UpdateStatus": {
"State": "updating",
"StartedAt": "2017-07-25T20:42:56.651025816Z",
"Message": "update in progress"
}
}
]
删除环境变量的滚动更新涉及关闭所有服务任务并启动所有新任务。更新大约需要两分钟完成。
∼ $ docker service inspect mysql
[ }, "UpdateStatus": { "State": "completed", "StartedAt": "2017-07-25T20:42:56.651025816Z", "CompletedAt": "2017-07-25T20:44:55.078906359Z", "Message": "update completed" } }]
列出正在运行的任务表明任务最多只运行了两分钟。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
menpo2zgit5u mysql.1 postgres:latest ip-172-31-2-177.ec2.internal Running Running about a minute ago
adnid3t69sue mysql.2 postgres:latest ip-172-31-25-121.ec2.internal Running Running about a minute ago
we92apfuivil mysql.3 postgres:latest ip-172-31-42-198.ec2.internal Running Running 46 seconds ago
ed7vh4ozefm5 mysql.4 postgres:latest ip-172-31-29-67.ec2.internal Running Running 2 minutes ago
i2x2377ad7u0 mysql.5 postgres:latest ip-172-31-25-121.ec2.internal Running Running about a minute ago
通过移除env变量MYSQL_ROOT_PASSWORD,mysql服务被更新以使用 Docker 映像postgres。服务名称本身无法更新。该服务可以被更新回mysql映像,并且强制环境变量MYSQL_ROOT_PASSWORD被添加另一个滚动更新。
∼ $ docker service update --image mysql --env-add MYSQL_ROOT_PASSWORD='mysql' mysql
mysql
同样,将具有所需状态的副本列为running会列出由mysql基于映像的副本替换的postgres基于映像的副本。一次一个副本,postgres基于映像的副本被mysql基于映像的副本替换。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
menpo2zgit5u mysql.1 postgres:latest ip-172-31-2-177.ec2.internal Running Running 2 minutes ago
adnid3t69sue mysql.2 postgres:latest ip-172-31-25-121.ec2.internal Running Running 2 minutes ago
we92apfuivil mysql.3 postgres:latest ip-172-31-42-198.ec2.internal Running Running about a minute ago
pjvj50j822xr mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 12 seconds ago
i2x2377ad7u0 mysql.5 postgres:latest ip-172-31-25-121.ec2.internal Running Running 2 minutes ago
在一两分钟内,所有的postgres映像副本都被基于mysql映像的副本所取代。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
sobd90v7gbmz mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running about a minute ago
st5t7y8rdgg1 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 57 seconds ago
upekevrlbmgo mysql.3 mysql:latest ip-172-31-42-198.ec2.internal Running Running about a minute ago
pjvj50j822xr mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 2 minutes ago
nmrmdug87cy0 mysql.5 mysql:latest ip-172-31-2-177.ec2.internal Running Running 2 minutes ago
服务规范被更新为mysql映像,并添加了强制环境变量MYSQL_ROOT_PASSWORD。更新完成后,UpdateStatus State变为completed。
∼ $ docker service inspect mysql
[
"Spec": {
"Name": "mysql",
...
"Image": "mysql:latest@sha256:75c563c474f1adc149978011fedfe2e6670483d133b22b07ee32789b626f8de3",
"Env": [
"MYSQL_ROOT_PASSWORD=mysql"
],
... },
"PreviousSpec": {
"Name": "mysql",
"ContainerSpec": {
"Image": "postgres:latest@sha256:e92fe21f695d27be7050284229a1c8c63ac10d88cba58d779c243566e125aa34",
... },
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T20:45:54.104241339Z",
"CompletedAt": "2017-07-25T20:47:47.996420791Z",
"Message": "update completed"
}
}
]
滚动重启
Docker 1.13 增加了一个新选项,即使根据更新选项不需要更新,也可以执行滚动重启。例如,从更新配置为--update-parallelism 1 和--update-delay 20s 的mysql服务开始,下面的update命令不会执行任何滚动更新,因为没有对服务进行任何更改。
∼ $ docker service update --update-parallelism 1 --update-delay 20s mysql
mysql
要强制滚动重启,包括--force选项。
∼ $ docker service update --force --update-parallelism 1 --update-delay 20s mysql
mysql
服务任务开始被关闭,并且新的服务任务开始,即使没有对服务规范进行更新。一些任务被列为几秒钟前已经开始。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
sobd90v7gbmz mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 3 minutes ago
trye9chir91l mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running 23 seconds ago
uu7sfp147xnu mysql.3 mysql:latest ip-172-31-42-198.ec2.internal Running Running less than a second ago
pjvj50j822xr mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 4 minutes ago
nmrmdug87cy0 mysql.5 mysql:latest ip-172-31-2-177.ec2.internal Running Running 3 minutes ago
滚动重启可能需要 1-2 分钟才能完成。
∼ $ docker service inspect mysql
[ ... }, "UpdateStatus": { "State": "completed", "StartedAt": "2017-07-25T20:49:34.716535081Z", "CompletedAt": "2017-07-25T20:51:36.880045931Z", "Message": "update completed" } }]
滚动重启完成后,该服务具有所有新的服务任务,如下所示。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
z2n2qcgfsbke mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 6 seconds ago
trye9chir91l mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running about a minute ago
uu7sfp147xnu mysql.3 mysql:latest ip-172-31-42-198.ec2.internal Running Running about a minute ago
1aovurxkteq1 mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 29 seconds ago
r0lslq6jibvp mysql.5 mysql:latest ip-172-31-2-177.ec2.internal Running Running 52 seconds ago
添加和删除装载的滚动更新
滚动更新还可以用于添加和删除挂载。例如,我们添加一个类型为volume的挂载,其中源卷由src指定,目标目录由dst指定。
∼ $ docker service update \
> --mount-add type=volume,src=mysql-scripts,dst=/etc/mysql/scripts \
> mysql
mysql
装载将添加到服务中,并在服务定义中列出。添加装载涉及关闭所有服务任务并启动新任务。滚动更新可能需要 1-2 分钟。
∼ $ docker service inspect mysql
[ "Spec": { "ContainerSpec": {... "Mounts": [ { "Type": "volume", "Source": "mysql-scripts", "Target": "/etc/mysql/scripts" } ],
...
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T20:51:55.205456644Z",
"CompletedAt": "2017-07-25T20:53:56.451313826Z",
"Message": "update completed"
}
}
]
添加的挂载可以用docker service update命令的--mount-rm选项删除,只需提供挂载目标目录作为参数。
∼ $ docker service update \
> --mount-rm /etc/mysql/scripts \
> mysql
mysql
执行另一次滚动更新,并删除挂载。它不会在服务定义中列出。PreviousSpec列出了挂载。UpdateStatus表示滚动更新的状态。
∼ $ docker service inspect mysql
[ "Spec": { "Name": "mysql", "ContainerSpec": {... "PreviousSpec": { "Name": "mysql",... "Mounts": [ { "Type": "volume", "Source": "mysql-scripts", "Target": "/etc/mysql/scripts" } "UpdateStatus": { "State": "completed", "StartedAt": "2017-07-25T20:55:56.30844324Z", "CompletedAt": "2017-07-25T20:57:58.489349432Z", "Message": "update completed" } }]
滚动更新失败操作
docker service create和docker service update命令的--update-failure-action选项指定如果任务更新失败并返回FAILED时要采取的后续动作。我们将mysql服务的UpdateConfig设置为包含一个pause的--update-failure-action(默认)。另一个选项设置是continue,它不会暂停滚动更新,而是继续下一个任务的更新。为了演示一个更新失败动作,指定一个不存在的 Docker 映像,比如mysql:5.9。
∼ $ docker service update \
> --replicas 10 \
> --image mysql:5.9 \
> --update-delay 10s \
> --update-failure-action pause \
> mysql
image mysql:5.9 could not be accessed on a registry to record
its digest. Each node will access mysql:5.9 independently,
possibly leading to different nodes running different
versions of the image.
mysql
滚动更新仍在开始,更新状态显示更新为paused。更新状态消息指示“由于失败或任务提前终止,更新暂停”。
∼ $ docker service inspect mysql
[ "Spec": { "Name": "mysql", }, "UpdateConfig": { "Parallelism": 1, "Delay": 10000000000, "FailureAction": "pause", "Monitor": 5000000000, "MaxFailureRatio": 0, "Order": "stop-first" }, "RollbackConfig": { "Parallelism": 1, "FailureAction": "pause", "Monitor": 5000000000, "MaxFailureRatio": 0, "Order": "stop-first" },... }, "UpdateStatus": { "State": "paused", "StartedAt": "2017-07-25T20:58:51.695333064Z", "Message": "update paused due to failure or early termination of task s1p1n0x3k67uwpoj7qxg13747" } }]
如果滚动更新因任务更新失败而暂停,则有两个选项可用。
- 使用
docker service update <SERVICE-ID>重启暂停的更新。 - 如果更新失败重复出现,找到失败的原因,并通过向
docker service update <SERVICE-ID>命令提供其他选项来重新配置服务。
回滚到以前的规格
Docker 1.13 Swarm mode 增加了回滚到之前服务定义的特性。例如,执行滚动更新,将mysql服务的映像更新为postgres。基于mysql的副本开始关闭,基于postgres的副本开始启动。在从mysql映像到postgres映像的滚动更新过程中的任何时候,或者在对postgres映像的更新完成之后,如果确定滚动更新不应该开始或执行,可以使用以下命令回滚滚动更新。为了演示回滚,我们首先启动一个mysql服务。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 5 \
> --name mysql \
> --update-delay 10s \
> --update-parallelism 1 \
> mysql:5.6
xkmrhnk0a444zambp9yh1mk9h
我们从mysql映像开始对postgres映像进行滚动更新。
∼ $ docker service update --image postgres mysql
mysql
随后,一些任务基于postgres映像,一些基于mysql映像。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
mnm5pg9ha61u mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 58 seconds ago
9y0fzn4sgiv0 mysql.2 postgres:latest ip-172-31-2-177.ec2.internal Ready Ready 2 seconds ago
ewl7zxwi07gc \_ mysql.2 mysql:5.6 ip-172-31-2-177.ec2.internal Shutdown Running 2 seconds ago
l3ock28cmtzx mysql.3 mysql:5.6 ip-172-31-42-198.ec2.internal Running Running 22 seconds ago
1vqs3lcqvbt5 mysql.4 postgres:latest ip-172-31-29-67.ec2.internal Running Running 12 seconds ago
wu11jjbszesy \_ mysql.4 mysql:5.6 ip-172-31-29-67.ec2.internal Shutdown Shutdown 13 seconds ago
g3tr6z9l5vzx mysql.5 mysql:5.6 ip-172-31-42-198.ec2.internal Running Running 22 seconds ago
开始回滚以恢复到mysql映像。
∼ $ docker service update --rollback mysql
mysql
postgres基于映像的任务开始关闭,而mysql基于映像的任务开始。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
mnm5pg9ha61u mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running about a minute ago
gyqgtoc4ix3y mysql.2 mysql:5.6 ip-172-31-2-177.ec2.internal Running Running 14 seconds ago
9y0fzn4sgiv0 \_ mysql.2 postgres:latest ip-172-31-2-177.ec2.internal Shutdown Shutdown 15 seconds ago
ewl7zxwi07gc \_ mysql.2 mysql:5.6 ip-172-31-2-177.ec2.internal Shutdown Shutdown 23 seconds ago
l3ock28cmtzx mysql.3 mysql:5.6 ip-172-31-42-198.ec2.internal Running Running 46 seconds ago
ecvh8fd5308k mysql.4 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 16 seconds ago
1vqs3lcqvbt5 \_ mysql.4 postgres:latest ip-172-31-29-67.ec2.internal Shutdown Shutdown 16 seconds ago
wu11jjbszesy \_ mysql.4 mysql:5.6 ip-172-31-29-67.ec2.internal Shutdown Shutdown 37 seconds ago
m27d3gz4g6dy mysql.5 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 1 second ago
g3tr6z9l5vzx \_ mysql.5 mysql:5.6 ip-172-31-42-198.ec2.internal Shutdown Failed 6 seconds ago "task: non-zero exit (1)"
从mysql到postgres的滚动更新被回滚。回滚完成后,所有副本都是基于mysql映像的,这是服务开始时的理想状态。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xamxi29okj74 mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 30 seconds ago
gyqgtoc4ix3y mysql.2 mysql:5.6 ip-172-31-2-177.ec2.internal Running Running 56 seconds ago
l3ock28cmtzx mysql.3 mysql:5.6 ip-172-31-42-198.ec2.internal Running Running about a minute ago
ecvh8fd5308k mysql.4 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 58 seconds ago
全球服务的滚动更新
也可以在全局服务上执行滚动更新。为了演示,我们为mysql:latest映像创建了一个全局服务。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --mode global \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --name mysql \
> mysql
7nokncnti3izud08gfdovwxwa
Start a rolling update to Docker image mysql:5.6\. ∼ $ docker service update \
> --image mysql:5.6 \
> --update-delay 10s \
> mysql
mysql
服务已更新。Spec>ContainerSpec>Image从mysql:latest的PreviousSpec>ContainerSpec>Image更新为mysql:5.6。
∼ $ docker service inspect mysql
[
"Spec": {
"Name": "mysql",
"ContainerSpec": {
"Image": "mysql:5.6@sha256:6ad5bd392c9190fa92e65fd21f6debc8b2a76fc54f13949f9b5bc6a0096a5285",
},
"PreviousSpec": {
"Name": "mysql",
"ContainerSpec": {
"Image": "mysql:latest@sha256:75c563c474f1adc149978011fedfe2e6670483d133b22b07ee32789b626f8de3",
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T21:06:46.973666693Z",
"CompletedAt": "2017-07-25T21:07:46.656023733Z",
"Message": "update completed"
}
}
]
一分钟之内,所有基于mysql:5.6的新服务任务开始。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
ybf4xpofte8l mysql.81h6uvu8uq0emnovzkg6v7mzg mysql:5.6 ip-172-31-2-177.ec2.internal Running Running 46 seconds ago
7nq99jeil9n0 mysql.t4d0aq9w2a6avjx94zgkwc557 mysql:5.6 ip-172-31-42-198.ec2.internal Running Running about a minute ago
wcng24mq7e8m mysql.e7vigin0luuo1kynjnl33v9pa mysql:5.6 ip-172-31-29-67.ec2.internal Running Running about a minute ago
q14t2pyhra3w mysql.ptm7e0p346zwypos7wnpcm72d mysql:5.6 ip-172-31-25-121.ec2.internal Running Running about a minute ago
不能在全局服务上执行滚动更新以使用--replicas选项设置副本,如下面的docker service update命令中的消息所示。
∼ $ docker service update \
> --image mysql \
> --replicas 1 \
> mysql
replicas can only be used with replicated mode
如输出所示,虽然副本是在复制的服务mysql上设置的,但副本不是在全局服务上设置的。
摘要
本章讨论了服务的滚动更新。服务的滚动更新包括关闭以前的服务任务,并更新服务定义以启动新任务。在下一章,我们将讨论在群组模式下配置网络。
十、网络连接
Docker 引擎上的网络是由桥接网络提供的,即docker0桥。docker0桥在 Docker 主机的范围内是本地的,并且在安装 Docker 时默认安装。所有 Docker 容器都运行在 Docker 主机上,并连接到docker0桥网络。他们通过网络互相交流。
问题
默认的docker0桥接网络有以下限制:
-
桥接网络的范围限于本地 Docker 主机,以提供容器到容器的联网,而不是多主机联网。
-
The bridge network isolates the Docker containers on the host from external access. A Docker container may expose a port or multiple ports and the ports may be published on the host for an external client host access, as illustrated in Figure 10-1, but by default the
docker0bridge does not provide any external client access outside the network.图 10-1。
The default docker0 bridge network
解决方案
群模式(Docker 引擎> =1.12)为群中的节点创建一个名为ingress的overlay网络。ingress覆盖网络是一个多主机网络,用于将ingress流量路由到群;外部客户端使用它来访问群服务。如果服务发布了一个端口,它们就会被添加到ingress网络中。ingress覆盖网络具有默认网关和子网,并且ingress网络中的所有服务都暴露在群中的所有节点上,无论服务是否在每个节点上有任务调度。除了ingress网络,可使用覆盖驱动程序创建自定义覆盖网络。自定义覆盖网络在群中的 Docker 守护进程之间提供网络连接,并用于服务对服务的通信。Ingress是一种特殊类型的覆盖网络,不用于服务或任务之间的网络流量。群模式网络如图 10-2 所示。
图 10-2。
The Swarm overlay networks
以下 Docker 网络用于或可用于群组模式。
入口网络
初始化群组模式时,会自动创建ingress网络。在 Docker for AWS 上,ingress网络是现成可用的,因为托管服务默认启用了群组模式。名为ingress的默认覆盖网络扩展到群中的所有节点,无论该节点是否有预定的服务任务。ingress提供服务任务之间的负载均衡。所有发布端口的服务都被添加到ingress网络中。如果服务发布一个端口,甚至在内部网络中创建的服务也被添加到ingress。如果服务没有发布端口,它不会被添加到ingress网络中。服务使用下面的docker service create命令语法发布带有--publish或–p选项的端口。
docker service create \
--name <SERVICE-NAME> \
--publish <PUBLISHED-PORT>:<TARGET-PORT> \
<IMAGE>
如果省略了<PUBLISHED-PORT>,群组管理器选择 30000-32767 范围内的一个端口来发布服务。
要使用ingress网络,群组节点之间必须开放以下端口。
- 端口 7946 TCP/UDP 用于容器网络发现
- 端口 4789 UDP 用于容器
ingress网络
自定义覆盖网络
使用覆盖驱动器创建定制覆盖网络,并且可以在覆盖网络中创建服务。使用docker service create命令的--network选项在覆盖网络中创建服务。覆盖网络提供服务对服务的通信。覆盖网络中的一个对接容器可以直接与网络中的另一个对接容器通信,无论该容器是在同一节点上还是在不同的节点上。只有用于群服务任务的 Docker 容器可以使用覆盖网络相互连接,而不仅仅是群中主机上运行的任何 Docker 容器。例如,用docker run <img>命令启动的 Docker 容器不能连接到 Swarm 覆盖网络,例如使用docker network connect <overlay network> <container>。不在群中的对接主机上的对接容器也不能与群中的对接容器直接连接和通信。不同群覆盖网络中的 Docker 容器不能彼此直接通信,因为每个群覆盖网络与其他网络隔离。
尽管群中的默认覆盖网络ingress扩展到群中的所有节点,而不管服务任务是否在其上运行,但是其范围也是群的定制覆盖网络默认不扩展到群中的所有节点。定制的群覆盖网络仅扩展到群中运行由定制的群覆盖网络创建的服务任务的那些节点。
“覆盖”网络覆盖主机的底层网络,覆盖网络的范围是群。覆盖网络中的服务容器具有不同的 IP 地址,并且每个覆盖网络分配有不同范围的 IP 地址。在现代内核中,覆盖网络可以与底层网络重叠,因此,多个网络可以拥有相同的 IP 地址。
docker_gwbridge 网络
初始化群组模式时自动创建的另一个网络(除了ingress网络)是docker_gwbridge网络。docker_gwbr idge 网络是一个桥接网络,它将包括ingress网络在内的所有覆盖网络连接到 Docker 守护进程的主机网络。每个服务容器都连接到本地 Docker 守护进程主机的docker_gwbridge网络。
桥接网络
网桥网络是由 Docker 管理的主机上的网络。主机上的 Docker 容器通过桥接网络相互通信。不公布端口的群模式服务也在桥接网络中创建。以docker run命令开始的 Docker 容器也是如此。这意味着不发布端口的群模式 Docker 服务与用docker run命令启动的 Docker 容器在同一个网络中。
本章涵盖以下主题:
- 设置环境
- Swarm 模式下的联网
- 使用默认覆盖网络
ingress创建服务 - 创建自定义叠加网络
- 使用自定义覆盖网络创建服务
- 连接到同一覆盖网络中的另一个 Docker 容器
- 创建内部网络
- 删除网络
设置环境
在 Docker 上为 AWS 创建一个三节点 Docker 群,如第三章所述。如图 10-3 所示,一个自动气象站云形成栈被用来创建一个蜂群。
图 10-3。
AWS CloudFormation stack
获取群管理器节点的公共 IP 地址,如图 10-4 所示。
图 10-4。
Obtaining the public IP address of a Swarm manager node instance
SSH 登录到 Swarm manager 实例。
[root@localhost ∼]# ssh -i "docker.pem" docker@174.129.48.148
Welcome to Docker!
列出群节点——一个管理者和两个工作者节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
npz2akark8etv4ib9biob5yyk ip-172-31-47-123.ec2.internal Ready Active
p6wat4lxq6a1o3h4fp2ikgw6r ip-172-31-3-168.ec2.internal Ready Active
tb5agvzbi0rupq7b83tk00cx3 * ip-172-31-47-15.ec2.internal Ready Active Leader
Swarm 模式下的联网
群组模式提供了一些默认网络,可以用docker network ls命令列出。这些网络不仅可以在 Docker for AWS 上使用,还可以在任何平台(如 CoreOS)上以 Swarm 模式使用。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
34a5f77de8cf bridge bridge local
0e06b811a613 docker_gwbridge bridge local
6763ebad69cf host host local
e41an60iwval ingress overlay swarm
eb7399d3ffdd none null local
我们在上一节中讨论了这些网络中的大多数。“host”网络是主机的网络栈。“none”网络不提供 Docker 容器和主机网络栈之间的网络,并且创建没有网络访问的容器。
即使在调度任何服务任务之前,默认网络在群管理器节点和群工作者节点上也是可用的。
可使用设置为overlay的驱动过滤器过滤列出的网络。
docker network ls --filter driver=overlay
仅列出了ingress网络。默认情况下,不会提供其他覆盖网络。
∼ $ docker network ls --filter driver=overlay
NETWORK ID NAME DRIVER SCOPE
e41an60iwval ingress overlay swarm
感兴趣的网络是称为ingress的覆盖网络,但是除了在章节介绍中讨论之外,所有默认网络都在表 10-1 中讨论。
表 10-1。
Docker Networks
| 网络 | 描述 | | `bridge` | `bridge`网络是在所有 Docker 主机上创建的`docker0`网络。默认情况下,Docker 守护进程将容器连接到`docker0`网络。任何以`docker run command`开头的 Docker 容器,甚至在一个群节点上,都连接到`docker0`桥网络。 | | `docker_` `gwbridge` | 用于不同主机上的群节点之间的通信。该网络用于向缺少用于连接外部网络和其他群节点的替代网络的容器提供外部连接。当一个容器连接到多个网络时,它的外部连接是通过第一个非内部网络按词汇顺序提供的。 | | `host` | 将容器添加到主机的网络栈中。容器内部的网络配置与主机的配置相同。 | | `ingress` | Swarm 用于`ingress`的覆盖网络,是外部接入。`ingress`网络仅用于路由网状网/ `ingress`流量。 | | `none` | 将容器添加到特定于容器的网络栈中,该容器缺少网络接口。 |默认网络不能删除,除了ingress网络,用户不需要直接连接或使用其他网络。要查找关于ingress网络的详细信息,请运行以下命令。
docker network inspect ingress
ingress网络的范围是 Swarm,使用的驱动程序是overlay。子网和网关分别是10.255.0.0/16和10.255.0.1。false的内部设置显示ingress网络不是内部网络,这意味着该网络连接到外部网络。ingress网络有一个 IPv4 地址,但该网络不支持 IPv6。
∼ $ docker network inspect ingress
[
{
"Name": "ingress",
"Id": "e41an60iwvalbeq5y3stdfem9",
"Created": "2017-07-26T18:38:29.753424199Z",
"Scope": "swarm",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "10.255.0.0/16",
"Gateway": "10.255.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": true,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"ingress-sbox": {
"Name": "ingress-endpoint",
"EndpointID": "f646b5cc4316994b8f9e5041ae7c82550bc7ce733db70df3f66b8d771d0f53c4",
"MacAddress": "02:42:0a:ff:00:02",
"IPv4Address": "10.255.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.driver.overlay.vxlanid_list": "4096"
},
"Labels": {},
"Peers": [
{
"Name": "ip-172-31-47-15.ec2.internal-17c7f752fb1a",
"IP": "172.31.47.15"
},
{
"Name": "ip-172-31-47-123.ec2.internal-d6ebe8111adf",
"IP": "172.31.47.123"
},
{
"Name": "ip-172-31-3-168.ec2.internal-99510f4855ce",
"IP": "172.31.3.168"
}
]
}
]
使用默认桥接网络创建服务
要使用默认桥接网络在群组模式下创建服务,不需要指定任何特殊选项。不得指定--publish或–p选项。为mysql数据库创建一个服务。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> mysql
likujs72e46ti5go1xjtksnky
创建服务,并在其中一个节点上安排服务任务。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
likujs72e46t mysql replicated 1/1 mysql:latest
该服务可以扩展到在整个群体中运行任务。
∼ $ docker service scale mysql=3
mysql scaled to 3
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
v4bn24seygc6 mysql.1 mysql:latest ip-172-31-47-15.ec2.internal Running Running 2 minutes ago
29702ebj52gs mysql.2 mysql:latest ip-172-31-47-123.ec2.internal Running Running 3 seconds ago
c7b8v16msudl mysql.3 mysql:latest ip-172-31-3-168.ec2.internal Running Running 3 seconds ago
创建的mysql服务没有添加到ingress网络,因为它没有发布端口。
在入口网络中创建服务
在本节中,我们将在ingress网络中创建一个 Docker 服务。不使用docker service create的--network选项指定ingress网络。服务必须发布一个要在ingress网络中创建的端口。创建一个在端口8080上发布的 Hello World 服务。
∼ $ docker service rm hello-world
hello-world
∼ $ docker service create \
> --name hello-world \
> -p 8080:80\
> --replicas 3 \
> tutum/hello-world
l76ukzrctq22mn97dmg0oatup
该服务创建三个任务,群中的每个节点一个。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
l76ukzrctq22 hello-world replicated 3/3 tutum/hello-world:latest *:8080->80/tcp
∼ $ docker service ps hello-world
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
5ownzdjdt1yu hello-world.1 tutum/hello-world: latest ip-172-31-14-234.ec2.internal Running Running 33 seconds ago
csgofrbrznhq hello-world.2 tutum/hello-world:latest ip-172-31-47-203.ec2.internal Running Running 33 seconds ago
sctlt9rvn571 hello-world.3 tutum/hello-world:latest ip-172-31-35-44.ec2.internal Running Running 32 seconds ago
可以使用<Public DNS>:<8080> URL 在端口 8080 上的 Swarm 中的任何节点实例上访问该服务。如果创建了弹性负载均衡器,对于 AWS 的 Docker,可以在<LoadBalancer DNS>:<8080>访问服务,如图 10-5 所示。
图 10-5。
Invoking a Docker service in the ingress network using EC2 elastic load balancer public DNS
在docker service create命令中可以省略<PublishedPort> 8080。
∼ $ docker service create \
> --name hello-world \
> -p 80\
> --replicas 3 \
> tutum/hello-world
pbjcjhx163wm37d5cc5au2fog
在整个蜂群中启动三个服务任务。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
pbjcjhx163wm hello-world replicated 3/3 tutum/hello-world:latest *:0->80/tcp
∼ $ docker service ps hello-world
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xotbpvl0508n hello-world.1 tutum/hello-world:latest ip-172-31-37-130.ec2.internal Running Running 13 seconds ago
nvdn3j5pzuqi hello-world.2 tutum/hello-world:latest ip-172-31-44-205.ec2.internal Running Running 13 seconds ago
uuveltc5izpl hello-world.3 tutum/hello-world:latest ip-172-31-15-233.ec2.internal Running Running 14 seconds ago
群组管理器自动分配一个发布端口(30000),如docker service inspect命令中所列。
∼ $ docker service inspect hello-world
[ "Spec": { "Name": "hello-world",... "EndpointSpec": { "Mode": "vip", "Ports": [ { "Protocol": "tcp", "TargetPort": 80, "PublishMode": "ingress" } ]
}
},
"Endpoint": {
"Spec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishMode": "ingress"
}
]
},
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 30000,
"PublishMode": "ingress"
}
],
"VirtualIPs": [
{
"NetworkID": "bllwwocjw5xejffmy6n8nhgm8",
"Addr": "10.255.0.5/16"
}
]
}
}
]
即使服务发布了一个端口(30000 或 30000-32767 范围内的其他可用端口),AWS 群组 Docker 的 AWS 弹性负载均衡器也不会为发布的端口(30000 或 30000-32767 范围内的其他可用端口)添加侦听器。我们添加一个<Load Balancer Port:Instance Port>映射为 30000:30000 的监听器,如图 10-6 所示。
图 10-6。
Adding a load balancer listener
在<Load Balancer DNS>:<30000> URL 调用服务,如图 10-7 所示。
图 10-7。
Invoking a Hello World service on port 30000
创建自定义叠加网络
我们使用默认的覆盖网络ingress在 Swarm 模式下提供。ingress网络仅用于包含所有节点的群模式路由网格。提供群路由网格,使得群中的每个节点可以接受群中服务的公布端口上的连接,即使服务没有在节点上运行任务。ingress网络不是用于服务对服务的通信。
定制的覆盖网络可以在群组模式中用于服务到服务的通信。接下来,使用一些高级选项创建一个覆盖网络,包括使用--subnet选项设置子网,使用--gateway选项设置默认网关,以及使用--ip-range选项设置 IP 范围。--driver选项必须设置为overlay,网络必须以群组模式创建。指定 IP 范围的匹配子网必须可用。子网是 IP 网络的逻辑细分。网关是将主机的子网连接到其他网络的路由器。以下命令必须从管理器节点运行。
∼ $ docker network create \
> --subnet=192.168.0.0/16 \
> --subnet=192.170.0.0/16 \
> --gateway=192.168.0.100 \
> --gateway=192.170.0.100 \
> --ip-range=192.168.1.0/24 \
> --driver overlay \
> mysql-network
mkileuo6ve329jx5xbd1m6r1o
自定义覆盖网络被创建并在网络中作为具有群组范围的覆盖网络列出。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
34a5f77de8cf bridge bridge local
0e06b811a613 docker_gwbridge bridge local
6763ebad69cf host host local
e41an60iwval ingress overlay swarm
mkileuo6ve32 mysql-network overlay swarm
eb7399d3ffdd none null local
仅列出覆盖网络应列出ingress网络和自定义mysql-network。
∼ $ docker network ls --filter driver=overlay
NETWORK ID NAME DRIVER SCOPE
e41an60iwval ingress overlay swarm
mkileuo6ve32 mysql-network overlay swarm
关于自定义覆盖网络的详细信息mysql-network列出了子网和网关。
∼ $ docker network inspect mysql-network
[
{
"Name": "mysql-network",
"Id": "mkileuo6ve329jx5xbd1m6r1o",
"Created": "0001-01-01T00:00:00Z",
"Scope": "swarm",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "192.168.0.0/16",
"IPRange": "192.168.1.0/24",
"Gateway": "192.168.0.100"
},
{
"Subnet": "192.170.0.0/16",
"Gateway": "192.170.0.100"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": null,
"Options": {
"com.docker.network.driver.overlay.vxlanid_list": "4097,4098"
},
"Labels": null
}
]
只能为特定的子网、网关和 IP 范围创建一个覆盖网络。使用不同的子网、网关或 IP 范围,可以创建不同的覆盖网络。
∼ $ docker network create \
> --subnet=10.0.0.0/16 \
> --gateway=10.0.0.100 \
> --ip-range=10.0.1.0/24 \
> --driver overlay \
> mysql-network-2
qwgb1lwycgvogoq9t62ea4ny1
创建mysql-network-2并将其添加到网络列表中。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
34a5f77de8cf bridge bridge local
0e06b811a613 docker_gwbridge bridge local
6763ebad69cf host host local
e41an60iwval ingress overlay swarm
mkileuo6ve32 mysql-network overlay swarm
qwgb1lwycgvo mysql-network-2 overlay swarm
eb7399d3ffdd none null local
新的覆盖网络仅对具有使用覆盖的容器的工作者节点可用。虽然新的覆盖网络mysql-network和mysql-network-2在管理节点上可用,但是网络没有扩展到两个工作节点。SSH 登录到一个工作节点。
[root@localhost ∼]# ssh -i "docker.pem" docker@54.209.159.170
Welcome to Docker!
工作节点上没有列出mysql-network和mysql-network-2网络。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
255542d86c1b bridge bridge local
3a4436c0fb00 docker_gwbridge bridge local
bdd0be4885e9 host host local
e41an60iwval ingress overlay swarm
5c5f44ec3933 none null local
要将自定义覆盖网络扩展到工作节点,请在网络中创建一个在工作节点上运行任务的服务,这将在下一节中讨论。
默认情况下,群组模式覆盖网络是安全的。gossip协议用于在群节点之间交换覆盖网络信息。节点在 GCM 模式下使用 AES 算法对交换的信息进行加密和认证。默认情况下,管理节点每 12 小时轮换一次八卦数据的加密密钥。覆盖网络上不同节点上的容器之间交换的数据也可以使用--opt encrypted选项来加密,这在调度任务的所有节点之间创建 IPSEC 隧道。IPSEC 隧道还在 GCM 模式下使用 AES 算法,并每 12 小时轮换一次 gossip 数据的加密密钥。以下命令创建一个加密网络。
∼ $ docker network create \
> --driver overlay \
> --opt encrypted \
> overlay-network-2
aqppoe3qpy6mzln46g5tunecr
创建加密的群范围网络。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
34a5f77de8cf bridge bridge local
0e06b811a613 docker_gwbridge bridge local
6763ebad69cf host host local
e41an60iwval ingress overlay swarm
mkileuo6ve32 mysql-network overlay swarm
qwgb1lwycgvo mysql-network-2 overlay swarm
eb7399d3ffdd none null local
aqppoe3qpy6m overlay-network-2 overlay swarm
使用自定义覆盖网络创建服务
如果使用自定义覆盖网络来创建服务,则必须指定--network。下面的命令使用自定义 Swarm 作用域覆盖网络mysql-network在 Swarm 模式下创建一个 MySQL 数据库服务。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --network mysql-network \
> --name mysql-2\
> mysql
ocd9sz8qqp2becf0ww2rj5p5n
创建了mysql-2服务。将mysql-2服务扩展到三个副本,并列出该服务的服务任务。
∼ $ docker service scale mysql-2=3
mysql-2 scaled to 3
两种服务的两个不同网络中的 Docker 容器— mysql(桥接网络)和mysql-2(MySQL-网络覆盖网络)—同时运行在同一个节点上。
定制覆盖网络不扩展到群中的所有节点,直到节点具有使用定制网络的服务任务。直到在节点上为mysql-2安排了服务任务之后,mysql-network才被扩展到工作节点并在其上列出。
由默认对接引擎桥接网络docker0管理的对接容器不能与群范围覆盖网络中的对接容器连接。不支持在docker run命令中使用群组覆盖网络,使用docker network connect命令连接群组覆盖网络,或者使用docker network connect命令的--link选项链接 Docker 容器和群组覆盖网络。群范围内的覆盖网络只能由群中的 Docker 服务使用。
对于服务容器之间的连接:
- 同一群范围覆盖网络中的相同或不同服务的对接容器能够彼此连接。
- 不同群范围覆盖网络中相同或不同服务的对接容器不能相互连接。
在下一节中,我们将讨论内部网络,但在此之前,应该先介绍一下外部网络。我们已经创建的 Docker 容器是外部网络容器。ingress网络和自定义覆盖网络mysql- network是外部网络。外部网络提供通往网关的默认路由。主机和更广泛的互联网网络可以连接到ingress或定制覆盖网络中的 Docker 容器。例如,运行以下命令从 Docker 容器的 bash shell pinggoogle.com;Docker 容器应该在ingress覆盖网络或定制的 Swarm 覆盖网络中。
docker exec –it <containerid> ping –c 1 google.com
建立连接并交换数据。命令输出以斜体显示。
∼ $ docker exec -it 3762d7c4ea68 ping -c 1 google.com
PING google.com (172.217.7.142): 56 data bytes
64 bytes from 172.217.7.142: icmp_seq=0 ttl=47 time=0.703 ms
--- google.com ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.703/0.703/0.703/0.000 ms
创建内部覆盖网络
在本节中,我们将讨论如何创建和使用内部覆盖网络。内部网络不提供外部连接。使网络内部化的原因是,没有为来自主机或更广泛的互联网的外部连接提供通往网关的默认路由。
首先,使用docker network create命令的--internal选项创建一个内部覆盖网络。添加一些其他选项,如--label,,这些选项与内部网络无关。它是用docker network create命令的--internal选项配置的。
∼ $ docker network create \
> --subnet=10.0.0.0/16 \
> --gateway=10.0.0.100 \
> --internal \
> --label HelloWorldService \
> --ip-range=10.0.1.0/24 \
> --driver overlay \
> hello-world-network
pfwsrjeakomplo5zm6t4p19a9
内部网络的创建和列出方式与外部网络完全相同。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
194d51d460e6 bridge bridge local
a0674c5f1a4d docker_gwbridge bridge local
pfwsrjeakomp hello-world-network overlay swarm
03a68475552f host host local
tozyadp06rxr ingress overlay swarm
3dbd3c3ef439 none null local
在网络描述中,internal被设置为true。
core@ip-172-30-2-7 ∼ $ docker network inspect hello-world-network
[
{
"Name": "hello-world-network",
"Id": "58fzvj4arudk2053q6k2t8rrk",
"Scope": "swarm",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "10.0.0.0/16",
"IPRange": "10.0.1.0/24",
"Gateway": "10.0.0.100"
}
]
},
"Internal": true,
"Containers": null,
"Options": {
"com.docker.network.driver.overlay.vxlanid_list": "257"
},
"Labels": {
"HelloWorldService": ""
}
}
]
使用--network选项创建一个使用内部网络的服务。
∼ $ docker service create \
> --name hello-world \
> --network hello-world-network \
> --replicas 3 \
> tutum/hello-world
hm5pf6ftcvphdrd2zm3pp4lpj
创建服务并安排副本。
获取服务任务之一的容器 ID,d365d4a5ff4c。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d365d4a5ff4c tutum/hello-world:latest "/bin/sh -c 'php-f..." About a minute ago Up About a minute hello-world.3.r759ddnl1de11spo0zdi7xj4z
和以前一样,从 Docker 容器中 ping google.com。
docker exec –it <containerid> ping –c 1 google.com
未建立连接,这是因为容器位于内部覆盖网络中。
∼ $ docker exec -it d365d4a5ff4c ping -c 1 google.com
ping: bad address 'google.com'
在同一内部网络中的容器之间建立连接,因为限制仅在外部连接性上。为了进行演示,获取同一内部网络中另一个容器的容器 ID。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b7b505f5eb8d tutum/hello-world:latest "/bin/sh -c 'php-f..." 3 seconds ago Up 2 seconds hello-world.6.i60ezt6da2t1odwdjvecb75fx
57e612f35a38 tutum/hello-world:latest "/bin/sh -c 'php-f..." 3 seconds ago Up 2 seconds hello-world.7.6ltqnybn8twhtblpqjtvulkup
d365d4a5ff4c tutum/hello-world:latest "/bin/sh -c 'php-f..." 7 minutes ago Up 7 minutes hello-world.3.r759ddnl1de11spo0zdi7xj4z
连接同一个内部网络中的两个容器。建立了连接。
∼ $ docker exec -it d365d4a5ff4c ping -c 1 57e612f35a38
PING 57e612f35a38 (10.0.1.7): 56 data bytes
64 bytes from 10.0.1.7: seq=0 ttl=64 time=0.288 ms
--- 57e612f35a38 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.288/0.288/0.288 ms
如果在内部网络中创建的服务发布(公开)了一个端口,该服务将被添加到ingress网络中,即使该服务位于内部网络中,也会提供外部连接。例如,我们添加了docker service create命令的--publish选项,以在端口8080上发布服务。
∼ $ docker service create \
> --name hello-world \
> --network hello-world-network \
> --publish 8080:80 \
> --replicas 3 \
> tutum/hello-world
mqgek4umisgycagy4qa206f9c
查找服务任务的 Docker 容器 ID。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c52804dc256 tutum/hello-world:latest "/bin/sh -c 'php-f..." 28 seconds ago Up 27 seconds 80/tcp hello-world.1.20152n01ng3t6uaiahpex9n4f
例如,在google.com从内部网络中的容器连接到更大的外部网络。建立了连接。命令输出以斜体显示。
∼ $ docker exec -it 1c52804dc256 ping -c 1 google.com
PING google.com (172.217.7.238): 56 data bytes
64 bytes from 172.217.7.238: seq=0 ttl=47 time=1.076 ms
--- google.com ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 1.076/1.076/1.076 ms
删除网络
可以使用 docker network rm <networkid>命令删除未使用的网络。可以在同一个命令中删除多个网络。例如,我们可以列出和删除多个网络。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
34a5f77de8cf bridge bridge local
0e06b811a613 docker_gwbridge bridge local
wozpfgo8vbmh hello-world-network swarm
6763ebad69cf host host local
e41an60iwval ingress overlay swarm
mkileuo6ve32 mysql-network overlay swarm
qwgb1lwycgvo mysql-network-2 overlay swarm
eb7399d3ffdd none null local
aqppoe3qpy6m overlay-network-2 overlay swarm
服务正在使用的网络不会被删除。命令输出以斜体显示。
∼ $ docker network rm hello-world-network mkileuo6ve32 qwgb1lwycgvo overlay-network-2
hello-world-network
Error response from daemon: rpc error: code = 9 desc = network mkileuo6ve329jx5xbd1m6r1o is in use by service ocd9sz8qqp2becf0ww2rj5p5nqwgb1lwycgvo
overlay-network-2
摘要
本章讨论了 Docker 群组模式使用的网络。Swarm 模式中使用的默认网络是覆盖网络ingress,这是一个跨同一 Swarm 中所有 Docker 节点的多主机网络,为每个节点提供路由网格,以便能够接受发布端口上服务的ingress连接。自定义覆盖网络可用于创建 Docker 服务,不同之处在于,自定义覆盖网络提供服务对服务的通信,而不是ingress通信,并且仅当使用网络的服务任务被调度在节点上时,才扩展到群工作器节点。本章还讨论了内部网络和外部网络的区别。在下一章中,我们将讨论 Docker 群组模式下的日志记录和监控。
十一、日志和监控
Docker 包括几个内置的容器日志驱动,比如json-file、syslog、journald、gelf、fluentd 和 awslogs。Docker 还提供了docker logs命令来获取容器的日志。Docker 1.13 包括一个实验性的特性,使用docker service logs命令获取 Docker 服务日志。
问题
Docker Swarm 模式不包括针对 Docker 服务和容器的本地监控服务。此外,获取服务日志的实验性特性是一个命令行特性,并且每个服务都需要运行该特性。缺少一个日志记录服务,通过它可以收集所有服务的日志和指标,并在仪表板中查看。
解决方案
Sematext 是一个集成的数据分析平台,为指标和事件收集提供 SPM 性能监控,为日志收集提供 Logsene,包括性能指标、日志和事件之间的关联。Logsene 是一个托管的 ELK (Elasticsearch,Logtash,Kibana)栈。需要在群中的每个群节点上安装 Sematext Docker 代理,用于持续收集日志、指标和事件,如图 11-1 所示。
图 11-1。
Sematext Docker agent on each Swarm node
本章涵盖以下主题:
- 设置环境
- 创建 SPM 应用
- 创建 Logsene 应用
- 将 Sematext Docker 代理部署为服务
- 在 Docker Swarm 上创建 MySQL 数据库部署
- 监控 Docker 群体指标
- 获取 Logsene 中的 Docker 群集日志
设置环境
使用 Docker for AWS 启动一个由一个管理器和两个工作者节点组成的三节点集群。(这将在第三章中讨论。)从 EC2 控制台获取 manager 节点实例的公共 IP 地址,并通过 SSH 登录到该实例。
[root@localhost ∼]# ssh -i "docker.pem" docker@54.227.123.67
Welcome to Docker!
使用 Sematext SPM 和 Logsene 通过 Docker Swarm 进行日志记录和监控的过程如下。
- 在
https://apps.sematext.com/ui/registration开户。 - 在
https://apps.sematext.com/ui/login登录用户账号。 - 从
https://apps.sematext.com/ui/integrations?newUser中选择集成(Logsene app 和 SPM Docker app),如步骤 4 和 5 所列。 - 创建一个 SPM(一个性能监控应用)。应用就像是数据的命名空间。生成 SPM 令牌,用于在每个群节点上安装 Sematext 代理。
- 创建一个 Logsene 应用。生成一个 Logsene 令牌,该令牌也用于在每个群节点上安装 Sematext 代理。
- 在每个群节点上安装一个 Sematext 代理。在 SPM 仪表板和 Logsene 仪表板中开始收集 Docker 群体指标、日志和事件。
创建 SPM 应用
在 https://apps.sematext.com/ui/integrations?newUser 登录 Sematext 账户,显示整合页面。对于 SPM Docker 应用,请从基础架构和应用性能监控中选择 Docker。在添加 SPM Docker App 对话框中,指定一个应用名称(DockerSwarmSPM),如图 11-2 所示。点击创建应用。
图 11-2。
Adding a SPM Docker app
创建一个 SPM App,如图 11-3 所示。列出了几种客户端配置。
图 11-3。
SPM app is created
点击 Docker Swarm 的客户端配置选项卡,如图 11-4 所示。Docker Swarm 选项卡显示docker service create命令,为 Sematext Docker 代理创建服务;复制命令。该命令包含一个SPM_TOKEN,它对于每个 SPM 应用都是唯一的。
图 11-4。
Docker Swarm configuration
SPM app 被添加到仪表盘中,如图 11-5 所示。单击应用链接导航至应用报告,其中显示了 SPM 应用收集的监控数据、指标和事件,以及根据这些数据生成的图表。
图 11-5。
DockerSwarmSPM app on the dashboard
如图 11-6 所示,app 还没有收到任何数据。所有度量图形最初都是空的,但是当开始接收数据时,它们会显示图形。
图 11-6。
The DockerSwarmSPM app has not received any data
创建 Logsene 应用
要创建 Logsene app,在 https://apps.sematext.com/ui/integrations?newUser 的集成页面中选择 Logs App,如图 11-7 所示。
图 11-7。
Selecting the Logs app
在添加登录应用对话框中,指定应用名称(DockerSwarmLogsene)并点击创建应用,如图 11-8 所示。
图 11-8。
Adding the Logsene app
一个名为DockerSwarmLogsene的新 Logsene 应用被创建,如图 11-9 所示。复制生成的LOGSENE_TOKEN,我们将使用它在 Docker 群中创建一个 Sematext Docker 代理服务。
图 11-9。
The Logsene app is added and LOGSENE_TOKEN is generated
一个名为DockerSwarmLogsene的新 Logsene 应用被添加到仪表板中,如图 11-10 所示。
图 11-10。
The DockerSwarmLogsene app
点击DockerSwarmLogsene app 链接,显示 app 采集的日志数据。最初,应用没有接收到任何数据,如图 11-11 中的消息所示,因为我们还没有在 Docker Swarm 上配置 Sematext Docker 代理服务。Logsene UI 与 Kibana 仪表板集成在一起。
图 11-11。
The app does not receive any data at first
连接 SPM 和 Logsene 应用
接下来,连接 SPM 和 Logsene 应用,以便 SPM 收集的指标和事件与 Logsene 应用集成。选择集成➤互联应用,如图 11-12 所示。
图 11-12。
Choosing Integrations ➤ Connected Apps
选择 DockerSwarmSPM 作为第一个 app,DockerSwarmLogsene 作为第二个 app,如图 11-13 所示。然后点击连接应用。
图 11-13。
DockerSwarmLogsene
已连接的应用被列出,如图 11-14 所示。
图 11-14。
The connected apps
将 Sematext Docker 代理部署为服务
之前复制的docker service create命令只包含了SPM_TOKEN令牌。添加从 Logsene 应用获得的–e LOGSENE_TOKEN。在群管理器节点上运行docker service create命令。
∼ $ docker service create --mode global \
> --restart-condition any \
> --name sematext-agent-docker \
> --mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
> --mount type=bind,src=/,dst=/rootfs,readonly=true \
> -e SPM_TOKEN=9b5552fd-001d-44f0-9452-76046d4a3413 \
> -e LOGSENE_TOKEN=81ac5395-fe8f-47d9-93b2-dc00c649116a \
> sematext/sematext-agent-docker
oubjk53mpdnjgak5dgfdxs4ft
为语义对接代理创建服务;使用docker service ls列出。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
oubjk53mpdnj sematext-agent-docker global 3/3 sematext/sematext-agent-docker:latest
列出服务任务。因为这是一个全局服务,所以在每个节点上启动一个任务。
∼ $ docker service ps sematext-agent-docker
ID NAME IMAGENODE DESIRED STATE CURRENT STATE ERROR PORTS
5jvl7gnvl0te sematext-agent-docker.8d0qv1epqu8xop4o2f94i8j40 sematext/sematext-agent-docker:latest
ip-172-31-8-4.ec2.internal Running Running 2 minutes ago
y53f20d3kknh sematext-agent-docker.xks3sw6qgwbcuacyypemfbxyj sematext/sematext-agent-docker:latest
ip-172-31-31-117.ec2.internal Running Running 2 minutes ago
t5w2pxy4fc9l sematext-agent-docker.r02ftwtp3n4m0cl7v2llw4gi8 sematext/sematext-agent-docker:latest
ip-172-31-44-8.ec2.internal Running Running 2 minutes ago
如果额外的节点被添加到群中,则 Sematext Docker 代理在新节点上启动服务任务。例如,更新 CloudFormation 栈,将管理节点的数量增加到 3 个,将工作节点的数量增加到 5 个,如图 11-15 所示。
图 11-15。
Increasing the number of worker nodes
当栈更新完成时,群节点增加到三个管理节点和五个工作节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
8d0qv1epqu8xop4o2f94i8j40 ip-172-31-8-4.ec2.internal Ready Active
9rvieyqnndgecagbuf73r9gs5 ip-172-31-35-125.ec2.internal Ready Active Reachable
j4mg3fyzjtsdcnmr7rkiytltj ip-172-31-18-156.ec2.internal Ready Active
mhbbunhl358chah1dmr0y6i71 ip-172-31-7-78.ec2.internal Ready Active Reachable
r02ftwtp3n4m0cl7v2llw4gi8 ip-172-31-44-8.ec2.internal Ready Active
vdamjjjrz7a3ri3prv9fjngvy ip-172-31-6-92.ec2.internal Ready Active
xks3sw6qgwbcuacyypemfbxyj * ip-172-31-31-117.ec2.internal Ready Active Leader
xxyy4ys4oo30bb4l5daoicsr2 ip-172-31-21-138.ec2.internal Ready Active
向群组添加节点会在添加的节点上启动一个语义代理。
∼ $ docker service ps sematext-agent-docker
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
cgaturw05p59 sematext-agent-docker.xxyy4ys4oo30bb4l5daoicsr2 sematext/sematext-agent-docker:latest ip-172-31-21-138.ec2.internal RunningRunning 2 minutes ago
lj4f46q3ydv1 sematext-agent-docker.j4mg3fyzjtsdcnmr7rkiytltj sematext/sematext-agent-docker:latest ip-172-31-18-156.ec2.internal RunningRunning 2 minutes ago
v54bjs3c8u5r sematext-agent-docker.vdamjjjrz7a3ri3prv9fjngvy sematext/sematext-agent-docker:latest ip-172-31-6-92.ec2.internal Running Running 2 minutes ago
s7arohbeoake sematext-agent-docker.9rvieyqnndgecagbuf73r9gs5 sematext/sematext-agent-docker:latest ip-172-31-35-125.ec2.internal Running Running 3 minutes ago
ixpri65xwpds sematext-agent-docker.mhbbunhl358chah1dmr0y6i71 sematext/sematext-agent-docker:latest ip-172-31-7-78.ec2.internal Running Running 4 minutes ago
5jvl7gnvl0te sematext-agent-docker.8d0qv1epqu8xop4o2f94i8j40 sematext/sematext-agent-docker:latest ip-172-31-8-4.ec2.internal Running Running 15 minutes ago
y53f20d3kknh sematext-agent-docker.xks3sw6qgwbcuacyypemfbxyj sematext/sematext-agent-docker:latest ip-172-31-31-117.ec2.internal Running Running 15 minutes ago
t5w2pxy4fc9l sematext-agent-docker.r02ftwtp3n4m0cl7v2llw4gi8 sematext/sematext-agent-docker:latest ip-172-31-44-8.ec2.internal Running Running 15 minutes ago
在 Docker Swarm 上创建 MySQL 数据库服务
在本节中,我们将创建一个 MySQL 数据库服务,使用我们安装的 Sematext Docker 代理,通过 Sematext SCM 和 Logsene 从该服务中收集指标、日志和事件。首先,运行下面的命令来创建一个有 10 个副本的mysql服务。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 10 \
> --name mysql \
> mysql
rmy45fpa31twkyb3dowzpc74a
除了 Sematext Docker 代理服务之外,还会创建并列出该服务。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
oubjk53mpdnj sematext-agent-docker global 8/8 sematext/sematext-agent-docker:latest
rmy45fpa31tw mysql replicated 10/10 mysql:latest
还列出了mysql服务的服务任务。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATECURRENT STATE ERROR PORTS
x8j221ws4kx2 mysql.1 mysql:latest ip-172-31-21-138.ec2.internal Running Running 13 seconds ago
98rbd6nwspqz mysql.2 mysql:latest ip-172-31-44-8.ec2.internal Running Running 11 seconds ago
vmq0lylni8or mysql.3 mysql:latest ip-172-31-8-4.ec2.internal Running Running 24 seconds ago
0vb6oda3yh3d mysql.4 mysql:latest ip-172-31-7-78.ec2.internal Running Running 23 seconds ago
vdpplkyxy1uy mysql.5 mysql:latest ip-172-31-6-92.ec2.internal Running Running 23 seconds ago
9ser7fwz6998 mysql.6 mysql:latest ip-172-31-18-156.ec2.internal Running Running 17 seconds ago
vfsfvanghns0 mysql.7 mysql:latest ip-172-31-18-156.ec2.internal Running Running 17 seconds ago
v71qwpvjhhzn mysql.8 mysql:latest ip-172-31-6-92.ec2.internal Running Running 23 seconds ago
j7172i5ml43d mysql.9 mysql:latest ip-172-31-31-117.ec2.internal Running Running 24 seconds ago
5p5mg2wnbb0o mysql.10 mysql:latest ip-172-31-35-125.ec2.internal Running Running 20 seconds ago
在 Swarm 上启动 Sematext Docker 代理服务和 MySQL 数据库服务后,SPM 和 Logsene 应用都开始接收数据,如仪表板中的Data Received列所示。参见图 11-16 。
图 11-16。
DockerSwarmSPM overview
监控 Docker 群体指标
在 Swarm 上启动mysql服务后,服务的指标开始加载到 SPM——性能监控仪表板中。一旦安装了 Sematext Docker 代理,并且部署中的新指标可用,就会出现这种情况。显示不同指标的图表,包括主机 CPU、容器 CPU、容器内存、容器计数、容器内存故障计数器、容器交换、容器 I/O 吞吐量、容器网络流量和容器网络错误,如图 11-17 所示。
图 11-17。
Docker Swarm SPM overview
通过在导航中选择 Docker,可以显示 Docker 容器指标,包括容器计数、容器 CPU、容器磁盘、容器内存和容器网络。Docker 容器计数指标如图 11-18 所示。
图 11-18。
Docker metrics
Docker ➤容器网络选项显示接收和传输的网络流量、接收速率和传输速率。通过选择操作系统➤磁盘,可以显示已使用的操作系统磁盘空间。指标收集粒度可以设置为自动粒度(默认)、按月、按周、按天、按小时、按 5 分钟或按 1 分钟。可以使用日志按钮显示日志概述。
如果图表未设置为默认的自动刷新,请单击刷新图表按钮刷新图表。
使用 Logsene UI 或 Kibana 4 显示详细的日志,我们将在下一节中讨论。
获取 Logsene 中的 Docker 群集日志
在利润导航中选择日志 dockerswarmLogsene 以显示 logsene 收集的日志。显示日志计数、日志事件和过滤字段,如图 11-19 所示。要搜索由mysql服务生成的日志,请将“mysql”添加到搜索字段,然后单击搜索按钮。显示由mysql Docker 服务生成的日志,包括状态消息,如"mysqld ready for connections"。单击刷新按钮刷新日志。
图 11-19。
Logs generated by the mysql Docker Service
Logsene 收集所有的 Docker 事件,例如mysql:latest映像的 Docker pull 事件,如图 11-20 所示。
图 11-20。
Logs for Docker event for mysql image pull
另一个 Docker 事件(卷装载)的日志如图 11-21 所示。
图 11-21。
Logs for Docker event volume mount
摘要
本章讨论了使用 Sematext SPM 性能监控和 Logsene 日志管理对 Docker 群进行连续日志记录和监控。首先,您学习了如何创建 SPM 应用和 Logsene 应用。然后,您在每个群节点上安装了 Sematext 代理服务,并在 SPM 仪表板中监控指标和事件。您还了解了如何在 Logsene UI 或 Kibana 4 仪表板中监控日志。下一章将讨论 Docker 集群中的负载均衡。