九、滚动更新
Docker 群模式提供由在群中的节点上运行的副本组成的服务。服务定义是在首次创建/定义服务时创建的。使用 docker service create 命令创建服务定义。该命令提供了几个选项,包括用于添加放置约束、容器标签、服务标签、DNS 选项、环境变量、资源保留和限制、日志记录驱动程序、装载、副本数量、重启条件和延迟、更新延迟、故障操作、最大故障率和并行性的选项,其中大部分在第 4 章 中讨论。
问题
创建服务定义后,可能需要更新一些服务选项,例如增加/减少副本数量、添加/删除放置约束、更新资源保留和限制、添加/删除装载、添加/删除环境变量、添加/删除容器和服务标签、添加/删除 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 群,如第 3 章所述。从 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 上设置的,但副本不是在全局服务上设置的。
摘要
本章讨论了服务的滚动更新。服务的滚动更新包括关闭以前的服务任务,并更新服务定义以启动新任务。在下一章,我们将讨论在群组模式下配置网络。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论