Docker-管理设计模式-三-

151 阅读32分钟

Docker 管理设计模式(三)

原文:Docker Management Design Patterns

协议:CC BY-NC-SA 4.0

九、滚动更新

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

问题

创建服务定义后,可能需要更新一些服务选项,例如增加/减少副本数量、添加/删除放置约束、更新资源保留和限制、添加/删除装载、添加/删除环境变量、添加/删除容器和服务标签、添加/删除 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 群,如第三章所述。从 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上设置的,但副本不是在全局服务上设置的。

摘要

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

十、网络连接

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 docker0 bridge does not provide any external client access outside the network.

    A454123_1_En_10_Fig1_HTML.gif

    图 10-1。

    The default docker0 bridge network

解决方案

群模式(Docker 引擎> =1.12)为群中的节点创建一个名为ingressoverlay网络。ingress覆盖网络是一个多主机网络,用于将ingress流量路由到群;外部客户端使用它来访问群服务。如果服务发布了一个端口,它们就会被添加到ingress网络中。ingress覆盖网络具有默认网关和子网,并且ingress网络中的所有服务都暴露在群中的所有节点上,无论服务是否在每个节点上有任务调度。除了ingress网络,可使用覆盖驱动程序创建自定义覆盖网络。自定义覆盖网络在群中的 Docker 守护进程之间提供网络连接,并用于服务对服务的通信。Ingress是一种特殊类型的覆盖网络,不用于服务或任务之间的网络流量。群模式网络如图 10-2 所示。

A454123_1_En_10_Fig2_HTML.gif

图 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 所示,一个自动气象站云形成栈被用来创建一个蜂群。

A454123_1_En_10_Fig3_HTML.jpg

图 10-3。

AWS CloudFormation stack

获取群管理器节点的公共 IP 地址,如图 10-4 所示。

A454123_1_En_10_Fig4_HTML.jpg

图 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/1610.255.0.1false的内部设置显示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 所示。

A454123_1_En_10_Fig5_HTML.jpg

图 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 所示。

A454123_1_En_10_Fig6_HTML.jpg

图 10-6。

Adding a load balancer listener

<Load Balancer DNS>:<30000> URL 调用服务,如图 10-7 所示。

A454123_1_En_10_Fig7_HTML.jpg

图 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-networkmysql-network-2在管理节点上可用,但是网络没有扩展到两个工作节点。SSH 登录到一个工作节点。

[root@localhost ∼]# ssh -i "docker.pem" docker@54.209.159.170
Welcome to Docker!

工作节点上没有列出mysql-networkmysql-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 所示。

A454123_1_En_11_Fig1_HTML.gif

图 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 进行日志记录和监控的过程如下。

  1. https://apps.sematext.com/ui/registration 开户。
  2. https://apps.sematext.com/ui/login 登录用户账号。
  3. https://apps.sematext.com/ui/integrations?newUser 中选择集成(Logsene app 和 SPM Docker app),如步骤 4 和 5 所列。
  4. 创建一个 SPM(一个性能监控应用)。应用就像是数据的命名空间。生成 SPM 令牌,用于在每个群节点上安装 Sematext 代理。
  5. 创建一个 Logsene 应用。生成一个 Logsene 令牌,该令牌也用于在每个群节点上安装 Sematext 代理。
  6. 在每个群节点上安装一个 Sematext 代理。在 SPM 仪表板和 Logsene 仪表板中开始收集 Docker 群体指标、日志和事件。

创建 SPM 应用

https://apps.sematext.com/ui/integrations?newUser 登录 Sematext 账户,显示整合页面。对于 SPM Docker 应用,请从基础架构和应用性能监控中选择 Docker。在添加 SPM Docker App 对话框中,指定一个应用名称(DockerSwarmSPM),如图 11-2 所示。点击创建应用。

A454123_1_En_11_Fig2_HTML.jpg

图 11-2。

Adding a SPM Docker app

创建一个 SPM App,如图 11-3 所示。列出了几种客户端配置。

A454123_1_En_11_Fig3_HTML.jpg

图 11-3。

SPM app is created

点击 Docker Swarm 的客户端配置选项卡,如图 11-4 所示。Docker Swarm 选项卡显示docker service create命令,为 Sematext Docker 代理创建服务;复制命令。该命令包含一个SPM_TOKEN,它对于每个 SPM 应用都是唯一的。

A454123_1_En_11_Fig4_HTML.jpg

图 11-4。

Docker Swarm configuration

SPM app 被添加到仪表盘中,如图 11-5 所示。单击应用链接导航至应用报告,其中显示了 SPM 应用收集的监控数据、指标和事件,以及根据这些数据生成的图表。

A454123_1_En_11_Fig5_HTML.jpg

图 11-5。

DockerSwarmSPM app on the dashboard

如图 11-6 所示,app 还没有收到任何数据。所有度量图形最初都是空的,但是当开始接收数据时,它们会显示图形。

A454123_1_En_11_Fig6_HTML.jpg

图 11-6。

The DockerSwarmSPM app has not received any data

创建 Logsene 应用

要创建 Logsene app,在 https://apps.sematext.com/ui/integrations?newUser 的集成页面中选择 Logs App,如图 11-7 所示。

A454123_1_En_11_Fig7_HTML.jpg

图 11-7。

Selecting the Logs app

在添加登录应用对话框中,指定应用名称(DockerSwarmLogsene)并点击创建应用,如图 11-8 所示。

A454123_1_En_11_Fig8_HTML.jpg

图 11-8。

Adding the Logsene app

一个名为DockerSwarmLogsene的新 Logsene 应用被创建,如图 11-9 所示。复制生成的LOGSENE_TOKEN,我们将使用它在 Docker 群中创建一个 Sematext Docker 代理服务。

A454123_1_En_11_Fig9_HTML.jpg

图 11-9。

The Logsene app is added and LOGSENE_TOKEN is generated

一个名为DockerSwarmLogsene的新 Logsene 应用被添加到仪表板中,如图 11-10 所示。

A454123_1_En_11_Fig10_HTML.jpg

图 11-10。

The DockerSwarmLogsene app

点击DockerSwarmLogsene app 链接,显示 app 采集的日志数据。最初,应用没有接收到任何数据,如图 11-11 中的消息所示,因为我们还没有在 Docker Swarm 上配置 Sematext Docker 代理服务。Logsene UI 与 Kibana 仪表板集成在一起。

A454123_1_En_11_Fig11_HTML.jpg

图 11-11。

The app does not receive any data at first

连接 SPM 和 Logsene 应用

接下来,连接 SPM 和 Logsene 应用,以便 SPM 收集的指标和事件与 Logsene 应用集成。选择集成➤互联应用,如图 11-12 所示。

A454123_1_En_11_Fig12_HTML.jpg

图 11-12。

Choosing Integrations ➤ Connected Apps

选择 DockerSwarmSPM 作为第一个 app,DockerSwarmLogsene 作为第二个 app,如图 11-13 所示。然后点击连接应用。

A454123_1_En_11_Fig13_HTML.jpg

图 11-13。

DockerSwarmLogsene

已连接的应用被列出,如图 11-14 所示。

A454123_1_En_11_Fig14_HTML.jpg

图 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 所示。

A454123_1_En_11_Fig15_HTML.jpg

图 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 。

A454123_1_En_11_Fig16_HTML.jpg

图 11-16。

DockerSwarmSPM overview

监控 Docker 群体指标

在 Swarm 上启动mysql服务后,服务的指标开始加载到 SPM——性能监控仪表板中。一旦安装了 Sematext Docker 代理,并且部署中的新指标可用,就会出现这种情况。显示不同指标的图表,包括主机 CPU、容器 CPU、容器内存、容器计数、容器内存故障计数器、容器交换、容器 I/O 吞吐量、容器网络流量和容器网络错误,如图 11-17 所示。

A454123_1_En_11_Fig17_HTML.jpg

图 11-17。

Docker Swarm SPM overview

通过在导航中选择 Docker,可以显示 Docker 容器指标,包括容器计数、容器 CPU、容器磁盘、容器内存和容器网络。Docker 容器计数指标如图 11-18 所示。

A454123_1_En_11_Fig18_HTML.jpg

图 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"。单击刷新按钮刷新日志。

A454123_1_En_11_Fig19_HTML.jpg

图 11-19。

Logs generated by the mysql Docker Service

Logsene 收集所有的 Docker 事件,例如mysql:latest映像的 Docker pull 事件,如图 11-20 所示。

A454123_1_En_11_Fig20_HTML.jpg

图 11-20。

Logs for Docker event for mysql image pull

另一个 Docker 事件(卷装载)的日志如图 11-21 所示。

A454123_1_En_11_Fig21a_HTML.jpg A454123_1_En_11_Fig21b_HTML.jpg

图 11-21。

Logs for Docker event volume mount

摘要

本章讨论了使用 Sematext SPM 性能监控和 Logsene 日志管理对 Docker 群进行连续日志记录和监控。首先,您学习了如何创建 SPM 应用和 Logsene 应用。然后,您在每个群节点上安装了 Sematext 代理服务,并在 SPM 仪表板中监控指标和事件。您还了解了如何在 Logsene UI 或 Kibana 4 仪表板中监控日志。下一章将讨论 Docker 集群中的负载均衡。