返回介绍

九、滚动更新

发布于 2025-11-02 16:55:29 字数 48306 浏览 0 评论 0 收藏

Docker 群模式提供由在群中的节点上运行的副本组成的服务。服务定义是在首次创建/定义服务时创建的。使用 docker service create 命令创建服务定义。该命令提供了几个选项,包括用于添加放置约束、容器标签、服务标签、DNS 选项、环境变量、资源保留和限制、日志记录驱动程序、装载、副本数量、重启条件和延迟、更新延迟、故障操作、最大故障率和并行性的选项,其中大部分在第 4 章 中讨论。

问题

创建服务定义后,可能需要更新一些服务选项,例如增加/减少副本数量、添加/删除放置约束、更新资源保留和限制、添加/删除装载、添加/删除环境变量、添加/删除容器和服务标签、添加/删除 DNS 选项,以及修改重启和更新参数。如果需要整体关闭服务来更新服务定义选项,则会导致服务中断。

解决方案

Docker Swarm 模式包括滚动更新。在滚动更新中,服务不会关闭,但是服务中的单个副本/任务会一次关闭一个,并且基于新服务定义的新服务副本/任务会一次启动一个,如图 9-1 所示。因此,该服务在滚动更新期间继续可用。在滚动更新期间,提供给客户端的服务任务可以来自旧的和新的服务定义。例如,如果滚动更新对更近的映像标签执行更新,则在滚动更新期间提供给外部客户端的一些任务可能来自旧映像标签和新映像标签的混合。

A454123_1_En_9_Fig1_HTML.gif

图 9-1。

Rolling update

滚动更新为服务创建新的服务定义和新的期望状态。滚动更新包括关闭所有服务副本和启动所有新的服务副本,但不适用于尚未计划的服务副本,例如由于缺乏资源。在滚动更新中,即使只更新副本的数量,也会关闭所有旧副本或使其失败,并启动所有新副本。

在滚动更新期间,调度程序使用以下顺序。

  1. 第一个任务停止。
  2. 已计划对停止的任务进行更新。
  3. 启动更新任务的 Docker 容器。
  4. 如果任务更新返回 RUNNING ,等待 --update-delay 中指定的持续时间,并开始下一个任务的更新。
  5. 如果在更新过程中,任务返回 FAILED ,执行 --update-failure-action ,默认暂停更新。
  6. docker service update <SERVICE-ID> 重启暂停的更新。
  7. 如果更新失败重复出现,请找到失败的原因,并通过向 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:latestUpdate 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_DATABASEMYSQL 用户的 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 。更新状态的 Stateupdating ,消息为 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"
        }
    }
]

StartedAtCompletedAt 时间戳所示,滚动更新大约需要两分钟。只列出期望状态为 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 LimitsReservations 被配置开始。

∼ $ 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 所示。

A454123_1_En_9_Fig2_HTML.jpg

图 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                            

服务名称保持不变,副本名称也包含前缀 mysqlmysql 服务定义 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 createdocker 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)"   

mysqlpostgres 的滚动更新被回滚。回滚完成后,所有副本都是基于 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>Imagemysql:latestPreviousSpec>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 上设置的,但副本不是在全局服务上设置的。

摘要

本章讨论了服务的滚动更新。服务的滚动更新包括关闭以前的服务任务,并更新服务定义以启动新任务。在下一章,我们将讨论在群组模式下配置网络。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。