Docker-管理设计模式-全-
Docker 管理设计模式(全)
一、Docker 入门
Docker 已经成为事实上的容器化平台。Docker 相对于虚拟机的主要吸引力在于它是轻量级的。虚拟机除了打包应用二进制文件外,还打包了完整的操作系统,而 Docker 容器是应用层的轻量级抽象,只打包运行应用所需的代码和依赖项。多个 Docker 容器在同一个底层操作系统内核上作为独立的进程运行。大多数常用的操作系统都支持 Docker,包括几个 Linux 发行版、Windows 和 MacOS。在这些平台上安装 Docker 需要运行几个命令并设置几个参数。CoreOS Linux 已经安装了 Docker。在本章中,我们将开始在 CoreOS 上使用 Docker 引擎。这一章为后面的章节设置了上下文,这些章节讨论了使用 Swarm 模式管理 Docker 引擎的设计模式。本章不使用 Swarm 模式,并提供了与使用 Swarm 模式的对比。本章包括以下几节:
- 设置环境
- 运行 Docker 应用
设置环境
我们将在亚马逊网络服务(AWS) EC2 上使用 CoreOS,您可以在 https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#
访问。单击“启动实例”以启动 EC2 实例。接下来,为 CoreOS 选择一个 Amazon 机器映像(AMI)。点击 AWS Marketplace 查找 CoreOS AMI。在搜索栏中键入 CoreOS 以查找 CoreOS AMI。选择容器 Linux by CoreOS (Stable),如图 1-1 中 EC2 向导所示,启动一个实例。
图 1-1。
Selecting an AMI for CoreOS Linux
从选择实例类型中,选择 t2.micro 类型并单击下一步。在“配置实例详细信息”中,将实例数量指定为 1。选择一个网络或单击“创建新 VPC”来创建新的 VPC。选择一个子网或单击“创建新子网”来创建新子网。选择启用自动分配公共 IP。点击下一步。
从添加存储中,选择默认设置,然后单击下一步。在添加标签中,不需要添加任何标签。点击下一步。从配置安全组中,添加一个安全组,以允许来自任何来源(0.0.0.0/0)的所有端口范围内的任何协议的所有流量。点击查看和启动,然后点击启动。
选择一个密钥对,并在选择现有密钥对或创建新密钥对对话框中点击启动实例,如图 1-2 所示。
图 1-2。
Launch instances
带有 CoreOS 的 EC2 实例被启动。从 EC2 控制台获取 EC2 实例的公共 DNS 或 IPv4 公共 IP 地址,如图 1-3 所示,SSH 登录实例。
图 1-3。
Public DNS and public IPv4
SSH 以用户“core”的身份登录到 EC2 实例。
ssh -i "coreos.pem" core@<public ip>
运行 Docker 应用
如前所述,Docker 预装在 CoreOS 上。运行docker
命令来列出它的用法,如下面的 bash shell 所示:
core@ip-172-30-4-75 ∼ $ docker
Usage: docker [OPTIONS] COMMAND [arg...]
docker [ --help | -v | --version ]
A self-sufficient runtime for containers.
Options:
--config=∼/.docker Location of client config files
-D, --debug Enable debug mode
-H, --host=[] Daemon socket(s) to connect to
-h, --help Print usage
-l, --log-level=info Set the logging level
--tls Use TLS; implied by --tlsverify
--tlscacert=∼/.docker/ca.pem Trust certs signed only by this CA
--tlscert=∼/.docker/cert.pem Path to TLS certificate file
--tlskey=∼/.docker/key.pem Path to TLS key file
--tlsverify Use TLS and verify the remote
-v, --version Print version information and quit
Commands:
attach Attach to a running container
build Build an image from a Dockerfile
commit Create a new image from a container's changes
cp Copy files/folders between a container and the local filesystem
create Create a new container
diff Inspect changes on a container's filesystem
使用docker version
命令输出 Docker 版本。对于本机 Docker Swarm 支持,Docker 版本必须是 bash shell 输出中列出的 1.12 或更高版本。
core@ip-172-30-4-75 ∼ $ docker version
Client:
Version: 1.12.6
API version: 1.24
Go version: go1.7.5
Git commit: a82d35e
Built: Mon Jun 19 23:04:34 2017
OS/Arch: linux/amd64
Server:
Version: 1.12.6
API version: 1.24
Go version: go1.7.5
Git commit: a82d35e
Built: Mon Jun 19 23:04:34 2017
OS/Arch: linux/amd64
使用tutum/hello-world
Docker 映像运行 Hello World 应用。
docker run -d -p 8080:80 --name helloapp tutum/hello-world
提取 Docker 映像并创建 Docker 容器,如下面的清单所示。
core@ip-172-30-4-75 ∼ $ docker run -d -p 8080:80 --name helloapp tutum/hello-world
Unable to find image 'tutum/hello-world:latest' locally
latest: Pulling from tutum/hello-world
658bc4dc7069: Pull complete
a3ed95caeb02: Pull complete
af3cc4b92fa1: Pull complete
d0034177ece9: Pull complete
983d35417974: Pull complete
Digest: sha256:0d57def8055178aafb4c7669cbc25ec17f0acdab97cc587f30150802da8f8d85
Status: Downloaded newer image for tutum/hello-world:latest
1b7a85df6006b41ea1260b5ab957113c9505521cc8732010d663a5e236097502
使用docker ps
命令列出 Docker 容器。
core@ip-172-30-4-75 ∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1b7a85df6006 tutum/hello-world "/bin/sh -c 'php-fpm " 19 minutes ago Up 19 minutes0.0.0.0:8080->80/tcp helloapp
Docker 容器的端口映射也使用docker ps
命令列出,但是也可以使用docker port <container>
命令获得。
core@ip-172-30-4-75 ∼ $ docker port helloapp
80/tcp -> 0.0.0.0:8080
使用 8080 端口和localhost
,通过curl
调用 Hello World 应用。
curl localhost:8080
Hello World 应用的 HTML 标记输出如下所示。
core@ip-172-30-4-75 ∼ $ curl localhost:8080
<html>
<head>
<title>Hello world!</title>
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>
<style>
body {
background-color: white;
text-align: center;
padding: 50px;
font-family: "Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
}
#logo {
margin-bottom: 40px;
}
</style>
</head>
<body>
<img id="logo" src="logo.png" />
<h1>Hello world!</h1>
<h3>My hostname is 1b7a85df6006</h3>
</body>
</html>
使用 EC2 实例的公共 DNS,Hello World 应用也可以在浏览器中调用。这显示在图 1-4 的网络浏览器中。
图 1-4。
Invoking the Hello World application in a web browser
命令停止一个 Docker 容器。docker rm <container>
命令删除一个 Docker 容器。您可以使用docker images
命令列出 Docker 图片。可以使用docker rmi <image>
命令删除 Docker 映像。
core@ip-172-30-4-75 ∼ $ docker stop helloapp
helloapp
core@ip-172-30-4-75 ∼ $ docker rm helloapp
helloapp
core@ip-172-30-4-75 ∼ $ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tutum/hello-world latest 31e17b0746e4 19 months ago 17.79 MB
core@ip-172-30-4-75 ∼ $ docker rmi tutum/hello-world
Untagged: tutum/hello-world:latest
Untagged: tutum/hello-world@sha256:0d57def8055178aafb4c7669cbc25ec17f0acdab97cc587f30150802da8f8d85
Deleted: sha256:31e17b0746e48958b27f1d3dd4fe179fbba7e8efe14ad7a51e964181a92847a6
Deleted: sha256:e1bc9d364d30cd2530cb673004dbcdf1eae0286e41a0fb217dd14397bf9debc8
Deleted: sha256:a1f3077d3071bd3eed5bbe5c9c036f15ce3f6b4b36bdd77601f8b8f03c6f874f
Deleted: sha256:ff7802c271f507dd79ad5661ef0e8c7321947c145f1e3cd434621fa869fa648d
Deleted: sha256:e38b71a2478cad712590a0eace1e08f100a293ee19a181d5f5d5a3cdb0663646
Deleted: sha256:5f27c27ccc6daedbc6ee05562f96f719d7f0bb38d8e95b1c1f23bb9696d39916
Deleted: sha256:fab20b60d8503ff0bc94ac3d25910d4a10f366d6da1f69ea53a05bdef469426b
Deleted: sha256:a58990fe25749e088fd9a9d2999c9a17b51921eb3f7df925a00205207a172b08
core@ip-172-30-4-75 ∼ $
摘要
本章通过在 CoreOS 上使用单个 Docker 引擎为后续章节奠定了基础。随后的章节探讨了在集群中管理分布式 Docker 应用的不同设计模式。下一章介绍 Docker Swarm 模式。
二、在 Swarm 模式下使用 Docker
Docker 引擎是一个用于运行 Docker 容器的容器化平台。多个 Docker 容器在同一个底层操作系统内核上独立运行,每个容器都有自己的网络和文件系统。每个 Docker 容器都是应用所需的软件和依赖项的封装,不会产生打包整个操作系统的开销,这可能需要几个 GB。Docker 应用从 Docker 容器中的 Docker 映像运行,每个 Docker 映像特定于特定的应用或软件。Docker 映像是从 Docker 文件构建的,Docker 文件定义了用于下载和安装软件、设置环境变量和运行命令的指令集。
问题
虽然 Docker Engine 1.12 之前的版本(没有本机 Swarm 模式)是为在轻量级容器中运行应用而设计的,但它缺少一些功能,以下是主要功能。
-
无分布式计算—不提供分布式计算,因为 Docker 引擎安装并运行在单个节点或操作系统实例上。
-
No fault tolerance—As shown in the diagram in Figure 2-1, if the single node on which a Docker Engine is running fails, the Docker applications running on the Docker Engine fail as well.
图 2-1。
Single node Docker cluster
解决方案
从 Docker 引擎版本 1.12 开始,Docker 容器编排以群组模式内置于 Docker 引擎中,并且是 Docker 引擎的原生功能。使用群模式,分布在多个机器(OS 实例)上的节点群(或集群)可以以主/工/模式运行。默认情况下,Docker 引擎中不启用 Docker 群组模式,必须使用docker
命令进行初始化。接下来,作为对 Docker Swarm 模式的介绍,我们介绍一些术语。
DockerSwarm 模式
Docker Swarm 是一个由覆盖网络连接的 Docker 主机集群,用于服务发现。Docker 群包括一个或多个管理节点和一个或多个工作节点,如图 2-2 所示。在 Swarm 模式中,Docker 服务是 Docker 容器化的单元。从管理节点创建的服务的 Docker 容器在集群中部署或调度,并且 Swarm 包括用于扩展服务的内置负载均衡。服务的预期状态在管理器上声明,然后管理器调度任务在节点上运行。但是,worker 节点本身仍然提取映像并启动容器。
图 2-2。
Docker Swarm mode cluster
节点
Docker 主机(Docker 引擎)的一个实例称为节点。提供了两种类型的节点角色:管理者节点和工作者节点。
服务
服务是分布在整个群体中的一组任务(也称为副本或复制任务)的抽象。例如,一个服务可以运行 Nginx 服务器的三个副本。默认调度在第七章中讨论,它使用“分散”调度策略,根据计算的节点等级将任务分散到集群的节点上。服务由一个或多个彼此独立运行的任务组成,这意味着停止一个任务或启动一个新任务不会影响其他任务的运行。运行在三个节点上的 Nginx 服务可以包含三个复制任务。每个任务为服务运行一个 Docker 容器。一个节点可以为一项服务运行多个任务。任务是调度的原子单位的抽象,是调度程序运行 Docker 容器的“槽”。
服务的期望状态
服务的“期望状态”指的是创建服务时在服务定义中定义的服务状态。例如,服务定义可以将服务定义为由 Nginx 服务器的三个副本组成。
管理节点和 Raft 共识
当第一次创建群时,当前节点成为第一个管理节点。默认情况下,所有管理器节点也是工作节点。管理节点执行集群协调并管理群,包括服务任务的初始调度和服务的期望状态和实际状态之间的后续协调(如果有的话)。例如,对于由 Nginx 服务器的三个副本组成的服务定义,管理节点将创建三个任务,并在群中的群工作节点上调度这些任务。随后,如果运行任务的节点失败,群管理器将在仍在群中的工作节点上启动新的替换任务。当服务被创建时,群管理器接受服务定义,并在一个或多个工作者节点上将服务调度为服务任务。群组管理器节点还通过添加/移除服务任务来管理服务的扩展。群组管理器为每个服务分配一个唯一的 DNS 名称,并通过服务复制任务启动 Docker 容器。管理器节点监视集群状态。默认情况下,Swarm manager 也是一个 worker 节点,这将在下一节讨论。
提到“管理节点”实际上是群管理器的简化,因为群可以由一个或多个管理节点组成。每个管理节点保存完整的集群状态数据,包括哪个服务副本任务在哪个节点上运行以及节点角色,并参与 Raft 一致性的群管理。Raft 共识仅仅是一种算法,用于以分布式方式在一个组内创建决策/协议(共识)。Swarm 使用它来做出决定,如领导者选举、集群成员资格、服务更改等。在群模式中,Raft 共识是管理节点之间对于全局集群状态参数的协定,例如关于存储在数据库中的数据值的状态。群体管理者使用 Raft 共享数据。Raft 一致性协议是一种在集群中所有可达管理节点之间实现分布式一致性的协议。Raft 一致性算法有几种实现方式,其在 Swarm 模式下的实现方式具有分布式系统中常见的特性,如下所示:
- 容错值的一致性
- 集群成员管理
- 使用互斥的领袖选举
只有一个管理器节点(称为领导者)执行所有的集群协调和管理。只有领导节点执行服务任务的服务调度、缩放和重启。其他管理器节点用于群管理器的容错,这意味着如果领导者节点失败,其他管理器节点中的一个将被选为新的领导者并接管集群管理。领导者的选举是由来自大多数管理节点的共识来执行的。
工作节点
工作节点实际上运行服务副本任务和相关的 Docker 容器。作为管理者节点和工作者节点的节点角色之间的区别不是在服务部署时处理的,而是在运行时处理的,因为节点角色可以被提升/降级。升级/降级节点将在后面的章节中讨论。工作者节点不影响管理者 Raft 共识。工作节点只是增加了群集运行服务副本任务的能力。工作者节点本身对 raft 中保存的投票和状态没有贡献,但是它们是工作者节点的事实保存在 raft 中。由于运行一个服务任务需要资源(CPU 和内存),而一个节点有一定的固定可分配资源,所以群的容量受到群中工作节点数量的限制。
法定人数
法定人数是指大多数群管理器节点或管理器之间的一致。如果一个群体失去了法定人数,它就不能执行任何管理或编排功能。已经计划的服务任务不受影响,并继续运行。没有安排新的服务任务,也没有执行其他需要一致同意的管理决策,例如添加或删除节点。所有群管理器都被认为是决定容错的多数共识。对于领导者选举,只有可到达的管理器节点被包括在 Raft 共识中。任何群体更新,如节点的添加或删除或新领导者的选举,都需要法定人数。筏共识和法定人数是一样的。为了实现高可用性,建议在生产中使用三到五个群组管理器。一般情况下,建议使用奇数数量的群管理器。容错性指的是群管理器节点的故障容限,或者在不使群不可用的情况下可能发生故障的群管理器的数量。数学上,“多数”是指超过一半,但是对于群模式 Raft 共识算法,Raft 容许(N-1)/2 次失败,并且 Raft 共识的多数由(N/2)+1 确定。n 指的是群的大小或群中管理节点的数量。
Swarm Size = Majority + Fault Tolerance
作为一个例子,群大小为 1 和 2 的每一个都有一个容错值 0,因为如果任何一个群管理器失败,就不能为群大小达成 Raft 共识。更多的管理器节点提高了容错能力。对于奇数 N,群大小 N 和 N+1 的容错是相同的。
举个例子,一个有三个管理者的蜂群,容错度为 1,如图 2-3 所示。容错和 Raft 一致性不适用于工作节点,因为群容量仅基于工作节点。即使三个工作者节点中的两个发生故障,一个工作者节点,即使管理器节点是仅管理器节点,也将保持群可用,尽管群容量减少,并且可以将一些运行任务转换到非运行状态。
图 2-3。
Fault tolerance for a Swarm
本节涵盖以下主题:
- 设置环境
- 初始化 Docker 群组模式
- 将节点加入群集群
- 测试群集群集
- 将工作节点提升为管理器
- 将管理员节点降级为工作节点
- 使工作节点离开集群
- 使工作节点重新加入集群
- 使管理节点离开集群
- 重新初始化蜂群
- 修改节点可用性
- 删除节点
设置环境
本章向您展示了如何创建一个由一个管理节点和两个工作节点组成的三节点集群。使用 CoreOS Stable AMI 创建三个 Amazon EC2 实例,如图 2-4 中的 EC2 控制台所示。为 EC2 实例配置安全组时,启用 EC2 实例之间的所有流量。获取为群组管理器启动的 EC2 实例的 IP 地址。
图 2-4。
EC2 instances
初始化 Docker 群组模式
Docker Swarm 模式默认不启用,需要启用。使用公共 IP 地址为 Swarm manager 启动了到 EC2 实例的 SSH 登录。
ssh -i "coreos.pem" core@34.204.168.217
Docker Swarm 模式从 Docker 版本 1.12 开始提供。使用docker --version
命令验证 Docker 版本至少为 1.12。
[root@localhost ∼]# ssh -i "coreos.pem" core@34.204.168.217
Container Linux by CoreOS stable (1409.7.0)
core@ip-172-30-5-70 ∼ $ docker --version
Docker version 1.12.6, build a82d35e
要初始化 Swarm,使用 docker swarm init
options 命令。该命令支持的一些选项在表 2-1 中列出。
表 2-1。
Command Swarm init Options
| [计]选项 | 描述 | 缺省值 | | --- | --- | --- | | `--advertise-addr` | 格式为`除了没有提供默认值的--advertise-addr
,所有选项都使用默认值。使用私有地址作为广告地址,可以从 EC2 控制台获得,如图 2-5 所示。如果 AWS 上的 EC2 实例位于不同的区域,则应该使用外部公共 IP 地址来访问 manager 节点,该地址也可以从 EC2 控制台获得。
图 2-5。
Private IP
运行以下命令来初始化 Docker Swarm 模式。
docker swarm init --advertise-addr 172.30.5.70
如下面清单中的输出所示,Swarm 被初始化,当前节点是一个管理节点。添加工作节点的命令也包含在输出中。还输出获得添加管理器节点的命令的命令。复制docker swarm join
命令,将一个工作者节点添加到群中。
core@ip-172-30-5-70 ∼ $ docker swarm init --advertise-addr 172.30.5.70
Swarm initialized: current node (bgzqx2cfsf05qdradxytmdcp3) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-3o3zi1rxgkzy5gq5itr580yp9pbagxnkelinzh42ovrb7znt6f-dmgeg3veppor942vsavma3s47 \
172.30.5.70:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
运行docker info
命令来获取关于 Docker 引擎的系统范围的信息。该命令输出正在运行、暂停或停止的 Docker 容器的总数。列出了部分输出。
core@ip-172-30-5-70 ∼ $ docker info
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 1.12.6
Storage Driver: overlay
Backing Filesystem: extfs
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: null host bridge overlay
Swarm: active
NodeID: bgzqx2cfsf05qdradxytmdcp3
Is Manager: true
ClusterID: 056zm05kk6em6u7vlki8pbhc9
Managers: 1
Nodes: 1
CPUs: 1
Total Memory: 994.6 MiB
Name: ip-172-30-5-70.ec2.internal
Docker Root Dir: /var/lib/docker
存储驱动程序是覆盖的,后备文件系统是extfs
。测井驱动程序是json-file
,这在关于测井的第十一章节中有所涉及。这个蜂群被显示为active
。还列出了关于节点的信息,例如 NodeID、节点是否是管理器、群中管理器的数量以及群中节点的数量。
还列出了节点的资源容量(CPU 和内存)。第七章讨论了更多关于资源使用的内容。节点名是初始化群组的 EC2 实例的私有 DNS。
使用以下命令列出群中的节点:
docker node ls
列出了单个节点,包括节点 ID,这是节点的唯一参数。如果一个节点没有离开集群并重新加入,主机名也是唯一的。
core@ip-172-30-5-70 ∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
bgzqx2cfsf05qdradxytmdcp3 * ip-172-30-5-70.ec2.internal Ready Active Leader
节点 ID 后面的*
表示这是当前节点。群中的节点也有一个STATUS
、AVAILABILITY
和MANAGER STATUS
列。STATUS
可以是表 2-2 中列出的值之一。
表 2-2。
Node Status
| 状态 | 描述 | | --- | --- | | `Ready` | 随时可以使用 | | `Down` | 不准备使用 | | `Unknown` | 不知道 |AVAILABILITY
可以是表 2-3 中列出的值之一。
表 2-3。
AVAILABILITY Column
| 有效 | 描述 | | --- | --- | | `Active` | 调度器可以向节点分配任务。 | | `Pause` | Scheduler 不会向节点分配新任务,但现有任务会继续运行。 | | `Drain` | Scheduler 不会向节点分配新任务,现有任务会关闭。替换任务在其他节点上启动。 |MANAGER STATUS
可以是表 2-4 中列出的值之一。如果MANAGER STATUS
列没有值,则表示一个 worker 节点。
表 2-4。
Manager Status
| 经理状态 | 描述 | | --- | --- | | `Reachable` | 该节点加入 Raft 共识仲裁,并且如果领导者变得不可用,该节点有资格成为领导者节点。 | | `Unreachable` | 该节点是一个可到达的管理器节点,但已经变得不可到达,并且无法与群中的其他管理器节点通信。通过执行以下操作之一,可以使无法访问的管理器节点变得可以访问,但不能保证可以恢复:-重新启动计算机-重新启动守护程序如果前面的操作都无法恢复无法访问的管理器节点,则应该执行以下操作。降级并删除故障节点。`docker node demote将节点加入群
额外的节点,管理者或工作者,可以根据需要被添加或加入到群中。默认情况下,管理器节点也是工作者节点,但反之亦然。添加管理节点的原因与添加工作节点的原因不同。添加管理节点以使群更加容错,添加工作者节点以增加群的容量。添加管理器和工作器节点的命令也不同。当初始化群时,输出添加工作者节点的命令。使用以下命令也可以找到添加工作节点的命令。
docker swarm join-token worker
使用以下命令可以找到添加管理器节点的命令。
docker swarm join-token manager
添加工作者节点的原因是在一些节点上调度的服务任务没有运行,并且处于Allocated
状态。添加管理器节点的原因是另一个管理器节点变得不可访问。
要加入的节点(管理器或工作器)必须至少安装 Docker 引擎版本 1.12。接下来,添加两个工作节点。获取为工作节点启动的 EC2 实例的公共 IP 地址。工作者实例的 SSH 登录。
ssh -i "coreos.pem" core@34.204.199.
运行具有以下语法的docker swarm join
命令,将该节点作为工作者节点加入群。
docker swarm join [OPTIONS] HOST:PORT
表 2-5 中列出了docker swarm join
命令支持的选项。
表 2-5。
Options for docker swarm join Command
| [计]选项 | 描述 | 缺省值 | | --- | --- | --- | | `--advertise-addr` | 格式为`在 Swarm 模式初始化期间运行docker swarm join
命令输出,将 worker 实例加入 Swarm。正如输出消息所示,“节点作为一个工人加入了群体。”
[root@localhost ∼]# ssh -i "coreos.pem" core@34.204.199.45
Container Linux by CoreOS stable (1409.7.0)
core@ip-172-30-5-31 ∼ $ docker swarm join \
> --token SWMTKN-1-3o3zi1rxgkzy5gq5itr580yp9pbagxnkelinzh42ovrb7znt6f-dmgeg3veppor942vsavma3s47 \
> 172.30.5.70:2377
This node joined a swarm as a worker.
类似地,SSH 登录到另一个 worker 实例。
ssh -i "coreos.pem" core@34.231.70.10
运行相同的docker swarm join
命令,第二个节点作为工作者节点加入群。
[root@localhost ∼]# ssh -i "coreos.pem" core@34.231.70.10
Container Linux by CoreOS stable (1409.7.0)
core@ip-172-30-5-108 ∼ $ docker swarm join \
> --token SWMTKN-1-3o3zi1rxgkzy5gq5itr580yp9pbagxnkelinzh42ovrb7znt6f-dmgeg3veppor942vsavma3s47 \
> 172.30.5.70:2377
This node joined a swarm as a worker.
当运行docker swarm join
命令将一个工作者节点加入群时,发生以下事件序列。
- 启用节点上 Docker 引擎的群组模式。
- 对 TLS 证书的请求被发送给管理者。
- 该节点以机器主机名命名。
- 当前节点在管理器监听地址加入群。基于该令牌,该节点作为工作者节点或管理者节点加入。
- 将当前节点设置为
Active
可用性。 - 入口覆盖网络扩展到当前节点。
当节点使用管理器令牌加入群时,该节点作为管理器节点加入。新的管理器节点应该是Reachable
,并且只有第一个管理器节点是领导者。仅当初始引导者节点失败或被降级时,才会发生不同管理器节点的引导者选举。
工作者节点在另一方面不同于管理者节点。工作节点不能用于查看或修改群集状态。只有管理节点可用于查看集群状态,如群中的节点。只有管理器节点可用于修改集群状态,如删除节点。如果在工作节点上运行docker node ls
命令,将生成以下错误消息。
core@ip-172-30-5-31 ∼ $ docker node ls
Error response from daemon: This node is not a swarm manager. Worker nodes can't be used to view or modify cluster state. Please run this command on a manager node or promote the current node to a manager.
测试蜂群
接下来,您向 Swarm 部署一个简单的 Hello World 服务来测试集群。使用下面的命令从 manager 节点列出 Swarm 中的节点。
docker node ls
应该会列出这三个节点。
core@ip-172-30-5-70 ∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
9n5qmj4pp91f0n3s0n2jwjdv8 ip-172-30-5-108.ec2.internal Ready Active
bgzqx2cfsf05qdradxytmdcp3 * ip-172-30-5-70.ec2.internal Ready Active Leader
bqq4bryuobylu0glm4p19tko4 ip-172-30-5-31.ec2.internal Ready Active
如何判断一个节点是管理节点还是工作节点?从“经理状态”列。如果管理者状态为空,则该节点为工作者节点,如果管理者状态具有值,则该节点为管理者节点,该值为表 2-4 中讨论的值之一。列出了两个工作节点和一个管理节点。
我们已经讨论过工作节点不能用于查看或修改集群状态。接下来,使用docker service create
命令创建一个 Docker 服务,该命令只有在启用了 Swarm 模式时才可用。使用 Docker image alpine
,这是一个 Linux 发行版,创建两个副本并从服务容器 pingdocker.com
域。
docker service create --replicas 2 --name helloworld alpine ping docker.com
如果前面的命令运行没有错误,Docker Swarm 安装得很好。该命令返回服务 ID。
core@ip-172-30-5-70 ∼ $ docker service create --replicas 2 --name helloworld alpine ping docker.com
bkwskfzqa173dp55j54erg5cg
可以使用以下命令列出服务。
docker service ls
服务helloworld
被列出,副本的数量被列为 2/2,这意味着两个副本存在并且满足两个副本的期望状态。REPLICAS
列输出被命令为“实际/期望”。Docker 映像是alpine
,运行服务的命令是ping docker.com
。
core@ip-172-30-5-70 ∼ $ docker service ls
ID NAME REPLICAS IMAGE COMMAND
bkwskfzqa173 helloworld 2/2 alpine ping docker.com
docker service inspect
命令用于查找关于服务的更多信息。
docker service inspect helloworld
列出了关于helloworld
服务的详细信息——包括容器规格、资源、重启策略、位置、模式、更新配置和更新状态。
core@ip-172-30-5-70 ∼ $ docker service inspect helloworld
[
{
"ID": "bkwskfzqa173dp55j54erg5cg",
"Version": {
"Index": 22
},
"CreatedAt": "2017-07-22T19:11:50.345823466Z",
"UpdatedAt": "2017-07-22T19:11:50.345823466Z",
"Spec": {
"Name": "helloworld",
"TaskTemplate": {
"ContainerSpec": {
"Image": "alpine",
"Args": [
"ping",
"docker.com"
]
},
"Resources": {
"Limits": {},
"Reservations": {}
},
"RestartPolicy": {
"Condition": "any",
"MaxAttempts": 0
},
"Placement": {}
},
"Mode": {
"Replicated": {
"Replicas": 2
}
},
"UpdateConfig": {
"Parallelism": 1,
"FailureAction": "pause"
},
"EndpointSpec": {
"Mode": "vip"
}
},
"Endpoint": {
"Spec": {}
},
"UpdateStatus": {
"StartedAt": "0001-01-01T00:00:00Z",
"CompletedAt": "0001-01-01T00:00:00Z"
}
}
]
可以使用以下命令语法列出副本和放置副本的节点。
docker service ps <SERVICE
<SERVICE>
占位符或者是服务名(比如helloworld
)或者是实际的服务 ID(比如本例中的bkwskfzqa173
)。对于helloworld
服务,命令变成:
docker service ps helloworld
前面的命令还列出了运行复制副本的节点。为服务启动的 Docker 容器与之前的命令一样,即docker ps
命令。
core@ip-172-30-5-70 ∼ $ docker service ps helloworld
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
2x8gqd2qbylpkug1kg0pxi1c2 helloworld.1 alpine ip-172-30-5-70.ec2.internal Running Running 34 seconds ago
6twq1v0lr2gflnb6ae19hrpx9 helloworld.2 alpine ip-172-30-5-108.ec2.internal Running Running 34 seconds ago
core@ip-172-30-5-70 ∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
acbdaccad6ea alpine:latest "ping docker.com" 47 seconds ago Up 46 seconds helloworld.1.2x8gqd2qbylpkug1kg0pxi1c2
docker ps
命令不是群模式命令,但是可以在工作者节点上运行,以找到在工作者节点上运行的服务容器。docker ps
命令给出了在一个节点上运行的所有容器,即使它们不是服务容器。
core@ip-172-30-5-108 ∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
74ea31054fb4 alpine:latest "ping docker.com" About a minute ago Up About a minute helloworld.2.6twq1v0lr2gflnb6ae19hrpx9
docker service ps helloworld
命令只列出了两个调度副本的节点,一个是管理节点,另一个是工作节点。另一个 worker 节点上的docker ps
命令没有列出任何 Docker 容器。
core@ip-172-30-5-31 ∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
docker node inspect <node>
命令用于获取节点的详细信息,例如节点角色、可用性、主机名、资源容量、插件和状态。
core@ip-172-30-5-70 ∼ $ docker node inspect ip-172-30-5-70.ec2.internal
[
{
"ID": "bgzqx2cfsf05qdradxytmdcp3",
"Version": {
"Index": 10
},
"CreatedAt": "2017-07-22T19:09:45.647701768Z",
"UpdatedAt": "2017-07-22T19:09:45.68030039Z",
"Spec": {
"Role": "manager",
"Availability": "active"
},
"Description": {
"Hostname": "ip-172-30-5-70.ec2.internal",
"Platform": {
"Architecture": "x86_64",
"OS": "linux"
},
"Resources": {
"NanoCPUs": 1000000000,
"MemoryBytes": 1042935808
},
"Engine": {
"EngineVersion": "1.12.6",
"Plugins": [
{
"Type": "Network",
"Name": "bridge"
},
{
"Type": "Network",
"Name": "host"
},
{
"Type": "Network",
"Name": "null"
},
{
"Type": "Network",
"Name": "overlay"
},
{
"Type": "Volume",
"Name": "local"
}
]
}
},
"Status": {
"State": "ready"
},
"ManagerStatus": {
"Leader": true,
"Reachability": "reachable",
"Addr": "172.30.5.70:2377"
}
}
]
可以使用docker service rm <service>
命令删除服务。随后,docker service inspect <service>
命令不应该列出任何副本,运行中的docker ps
将不再显示运行中的 Docker 容器。
core@ip-172-30-5-70 ∼ $ docker service rm helloworld
helloworld
core@ip-172-30-5-70 ∼ $ docker service inspect helloworld
[]
Error: no such service: helloworld
第四章讨论了更多关于服务的内容。
将工作节点提升为管理器
如前所述,默认情况下,管理器节点也是工作者节点,但是工作者节点只是工作者节点。但是工作者节点可以被提升为管理者节点。将一个或多个工作节点提升为管理节点的 Docker 命令具有以下语法。
docker node promote NODE [NODE...]
该命令必须从 leader 节点运行。举个例子,提升节点ip-172-30-5-108.ec2.internal
。如输出所示,该节点被提升为管理器节点。随后列出群中的节点,被提升的节点应该具有管理者状态Reachable
。
工作者节点应该优选地使用节点 ID 来升级;其原因将在后面讨论。使用节点 ID 提升另一个工作节点。随后,两个工作节点在管理器状态列中被列为Reachable
。
core@ip-172-30-5-70 ∼ $ docker node promote ip-172-30-5-108.ec2.internal
Node ip-172-30-5-108.ec2.internal promoted to a manager in the swarm.
core@ip-172-30-5-70 ∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
9n5qmj4pp91f0n3s0n2jwjdv8 ip-172-30-5-108.ec2.internal Ready Active Reachable
bgzqx2cfsf05qdradxytmdcp3 * ip-172-30-5-70.ec2.internal Ready Active Leader
bqq4bryuobylu0glm4p19tko4 ip-172-30-5-31.ec2.internal Ready Active
将管理员节点降级为工作节点
可以使用以下 Docker 命令将管理节点降级为工作节点。
docker node demote NODE [NODE...]
包括领导者节点在内的任何管理者节点都可能被降级。例如,降级管理器节点ip-172-30-5-108.ec2.internal
。
core@ip-172-30-5-70 ∼ $ docker node demote ip-172-30-5-108.ec2.internal
Manager ip-172-30-5-108.ec2.internal demoted in the swarm.
一旦降级,只能从管理器节点运行的命令(如docker node ls
)将无法在该节点上运行。docker node ls
命令将降级的节点列为工作节点;没有为工人节点列出MANAGER STATUS
。
core@ip-172-30-5-70 ∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
9n5qmj4pp91f0n3s0n2jwjdv8 ip-172-30-5-108.ec2.internal Ready Active
bgzqx2cfsf05qdradxytmdcp3 * ip-172-30-5-70.ec2.internal Ready Active Leader
bqq4bryuobylu0glm4p19tko4 ip-172-30-5-31.ec2.internal Ready Active
节点应该优选地被升级/降级,或者在使用节点 ID 指向该节点的任何命令中被引用,该节点 ID 对于节点是唯一的。原因是,降级的节点如果升级回来,可能会添加不同的节点 ID,并且docker node ls
命令可能会为同一个主机名列出两个节点 ID。如果主机名用于引用一个节点,可能会导致node is ambiguous
错误消息。
使一个工作者节点离开群体
之前,您将一个节点作为工作者节点加入到群中。工作者节点也可以离开群。例如,使用下面的命令让一个工作节点离开,该命令必须从您想要从集群中删除的节点上运行。
docker swarm leave
如消息输出所示,节点已经离开了群。
core@ip-172-30-5-31 ∼ $ docker swarm leave
Node left the swarm.
类似地,让另一个工作者节点离开群体。
core@ip-172-30-5-108 ∼ $ docker swarm leave
Node left the swarm.
在一个工作者节点离开群之后,该节点本身并没有被移除,而是继续被列在具有Down
状态的docker node ls
命令中。
core@ip-172-30-5-70 ∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
9n5qmj4pp91f0n3s0n2jwjdv8 ip-172-30-5-108.ec2.internal Down Active
bgzqx2cfsf05qdradxytmdcp3 * ip-172-30-5-70.ec2.internal Ready Active Leader
bqq4bryuobylu0glm4p19tko4 ip-172-30-5-31.ec2.internal Down Active
使管理节点离开群体
虽然使工作者节点离开群更容易,但是当管理者节点必须离开群时就不同了。使工作者节点离开群只会降低群中可调度的服务任务方面的群容量。但是让一个管理节点离开群会使群变得不可用。如果容错不允许管理节点失败或从群中移除,则使工作节点离开群的相同的docker swarm leave
命令不能用于使管理节点离开群。如果一个群只有一个管理节点,那么docker swarm leave
命令会生成下面的错误消息。
core@ip-172-30-5-70 ∼ $ docker swarm leave
Error response from daemon: You are attempting to leave the swarm on a node that is participating as a manager. Removing the last manager erases all current state of the swarm. Use `--force` to ignore this message.
将--force
选项添加到管理节点上的docker swarm leave
命令,以使管理节点离开群。
core@ip-172-30-5-70 ∼ $ docker swarm leave --force
Node left the swarm.
如果唯一的管理节点被移除,那么群体将不复存在。如果要使用群组模式,必须再次初始化群组。
core@ip-172-30-5-70 ∼ $ docker swarm init --advertise-addr 172.30.5.70
Swarm initialized: current node (cnyc2w3n8q8zuxjujcd2s729k) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-4lxmisvlszjgck4ly0swsxubejfx0phlne1xegho2fiq99amqf-11mpscd8gs6bsayzren8fa2ki \
172.30.5.70:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
创建一个只有管理节点的新群,该群最初只有一个节点。
core@ip-172-30-5-70 ∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
cnyc2w3n8q8zuxjujcd2s729k * ip-172-30-5-70.ec2.internal Ready Active Leader
如果一个群有两个管理节点,让其中一个管理节点离开群会有不同的效果。如前所述,对于两个管理器,容错能力为 0。要创建一个有两个管理节点的群,从一个有一个管理节点和两个工作节点的群开始。
core@ip-172-30-5-70 ∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
4z03hudbo3fz17q94leo24pvh ip-172-30-5-108.ec2.internal Ready Active
cnyc2w3n8q8zuxjujcd2s729k * ip-172-30-5-70.ec2.internal Ready Active Leader
efsxwt43iskasa6poh2stkjeb ip-172-30-5-31.ec2.internal Ready Active
将其中一个工作节点提升为管理节点。
core@ip-172-30-5-70 ∼ $ docker node promote ip-172-30-5-108.ec2.internal
Node ip-172-30-5-108.ec2.internal promoted to a manager in the swarm.
该群将有两个管理节点。
core@ip-172-30-5-70 ∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
4z03hudbo3fz17q94leo24pvh ip-172-30-5-108.ec2.internal Ready Active Reachable
cnyc2w3n8q8zuxjujcd2s729k * ip-172-30-5-70.ec2.internal Ready Active Leader
efsxwt43iskasa6poh2stkjeb ip-172-30-5-31.ec2.internal Ready Active
从不是领导者节点的管理器节点运行docker swarm leave
命令。将生成以下消息。
core@ip-172-30-5-108 ∼ $ docker swarm leave
守护程序的错误响应如下:
You are attempting to leave the swarm on a node that is participating as a manager.
删除此节点会使两个管理器中只剩下一个。没有 Raft quorum,你的虫群将无法接近。恢复失去共识的蜂群的唯一方法是用--force-new-cluster
重新初始化它。使用--force
抑制此消息。
要使管理器节点离开,必须在命令中添加--force
选项。
core@ip-172-30-5-108 ∼ $ docker swarm leave --force
Node left the swarm.
当两个管理者中的一个离开群体时,Raft 的法定人数就会丢失,群体变得不可访问。如前所述,必须使用--force-new-cluster
选项重新初始化蜂群。
重新初始化群集
无法使用用于初始化群组的命令来重新初始化失去仲裁的群组。如果同一个命令在一个失去仲裁的群上运行,会有一条消息指出该节点已经在该群中,必须首先离开该群:
core@ip-172-30-5-70 ∼ $ docker swarm init --advertise-addr 172.30.5.70
Error response from daemon: This node is already part of a swarm. Use "docker swarm leave" to leave this swarm and join another one.
To reinitialize the Swarm the --force-new-cluster option must be added to the docker swarm init command. core@ip-172-30-5-70 ∼ $ docker swarm init --advertise-addr 172.30.5.70 --force-new-cluster
Swarm initialized: current node (cnyc2w3n8q8zuxjujcd2s729k) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-4lxmisvlszjgck4ly0swsxubejfx0phlne1xegho2fiq99amqf-11mpscd8gs6bsayzren8fa2ki \
172.30.5.70:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
群被重新初始化,并输出添加工作者节点的docker swarm join
命令。
修改节点可用性
可以使用带有--availability
选项的 D 命令修改节点的可用性。可以设置表 2-6 中的--availability
选项之一。
表 2-6。
Availability Options
| 可用性选项 | 描述 | | --- | --- | | `active` | 将暂停或耗尽的节点恢复为活动状态。 | | `pause` | 暂停节点,使其无法接收新任务。 | | `drain` | 对于工作节点,该节点会停机,无法调度新任务。管理节点也变得不可用于调度新任务,但是继续执行群管理。 |例如,您可以如下所示清空一个工作节点。
core@ip-172-30-5-70 ∼ $ docker node update --availability drain ip-172-30-5-108.ec2.internal
ip-172-30-5-108.ec2.internal
工作节点已耗尽。耗尽节点上的所有服务任务都将关闭,并在其他可用节点上启动。docker node ls
命令的输出列出了状态设置为Drain
的节点。
core@ip-172-30-5-70 ∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
bhuzgyqvb83dx0zvms54o0a58 ip-172-30-5-108.ec2.internal Ready Drain
cnyc2w3n8q8zuxjujcd2s729k * ip-172-30-5-70.ec2.internal Ready Active Leader
efsxwt43iskasa6poh2stkjeb ip-172-30-5-31.ec2.internal Ready Active
The node detail (partial output is listed) for the drained worker node lists the node availability as "drain".core@ip-172-30-5-70 ∼ $ docker node inspect ip-172-30-5-108.ec2.internal
[
{
"ID": "bhuzgyqvb83dx0zvms54o0a58",
"Version": {
"Index": 49
},
"CreatedAt": "2017-07-22T19:30:31.544403951Z",
"UpdatedAt": "2017-07-22T19:33:37.45659544Z",
"Spec": {
"Role": "worker",
"Availability": "drain"
},
"Description": {
"Hostname": "ip-172-30-5-108.ec2.internal",
耗尽节点上的所有服务任务都将关闭,并在其他可用节点上启动。带有docker node ls
的节点可用性被列为Drain
。
在--availability
设置为Active
的情况下,可以使用docker node update
命令再次激活被清空的节点。
core@ip-172-30-5-70 ∼ $ docker node update --availability active ip-172-30-5-108.ec2.internal
ip-172-30-5-108.ec2.internal
被清空的节点变为活动状态,并以设置为Active
的状态列出。
core@ip-172-30-5-70 ∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
bhuzgyqvb83dx0zvms54o0a58 ip-172-30-5-108.ec2.internal Ready Active
cnyc2w3n8q8zuxjujcd2s729k * ip-172-30-5-70.ec2.internal Ready Active Leader
efsxwt43iskasa6poh2stkjeb ip-172-30-5-31.ec2.internal Ready Active
删除节点
可以使用从任何管理节点运行的docker node rm
命令从群中删除一个或多个节点。
docker node rm [OPTIONS] NODE [NODE...]
docker swarm leave
和docker node rm
的区别在于docker node rm
只能从管理节点运行。降级的节点只能用docker node rm
命令从集群中移除。不使用--force
选项删除管理器节点的顺序如下。
- 降级 manager 节点,使其成为 worker 节点。
- 清空工作节点。
- 让工作者节点离开群体。
- 移除节点。
摘要
本章讨论了在群组模式下使用 Docker。首先,用docker swarm init
命令初始化 Swarm 模式,使当前节点成为 Swarm 中的管理节点。随后,您使用docker swarm join
命令将工作节点加入到集群中。本章还讨论了将工作者节点提升为管理者节点/将管理者节点降级为工作者节点、使工作者节点离开群组然后重新加入群组、使管理者节点离开群组、重新初始化群组以及修改节点可用性和删除节点。下一章介绍 Docker for AWS,这是 Docker Swarm 模式的托管服务。
三、使用 Docker for AWS 创建多区域 Swarm
Docker 群组是通过首先启动群组来创建管理器节点,然后将工作者节点加入到该管理器节点来提供的。Docker Swarm 为 Docker 应用提供分布式服务部署。
问题
默认情况下,Docker 群组被配置在 AWS 上的单个区域中,如图 3-1 所示。如果管理节点和所有工作节点都在同一个 AWS 区域中,则该区域的故障将导致该区域不可用。单区域群不是高可用性群,并且没有容错能力。
图 3-1。
A single-zone Swarm
解决方案
Docker 和 AWS 合作创建了 Docker for AWS 部署平台,在 AWS 的多个区域提供 Docker 群。Docker for AWS 不需要用户在命令行上运行任何命令,并且是基于图形用户界面(GUI)的。对于多个区域中的管理者和工作者节点,单个 AWS 区域的故障不会使群不可用,如图 3-2 所示。Docker for AWS 为蜂群提供了容错能力。
图 3-2。
A Multi-zone Swarm
Docker for AWS 是 Docker Swarm 在 AWS 云平台上的托管服务。除了多个区域之外,Docker for AWS 还有其他一些优势:
- 所有必需的基础架构都是自动配置的。
- 自动升级到新的软件版本,无需中断服务。
- 为 Docker 优化的定制 Linux 发行版。定制 Linux 发行版在 AWS 上不单独提供,它使用重叠 2 存储驱动程序。
- 未使用的 Docker 资源会被自动删除。
- 用于管理节点的自动扩展组。
- 主机本机日志循环,以避免冗长的日志消耗所有磁盘空间。
- 使用 AWS CloudWatch 进行集中日志记录。
- 基于 docker-diagnose 脚本的错误报告工具。
有两个版本的 Docker for Swarm 可用:
- 适用于 AWS 的 Docker 企业版(EE)
- 面向 AWS 的 Docker 社区版(CE)
我们在本章中使用 Docker Community Edition(CE)for AWS 来创建一个多区域 Swarm。本章包括以下主题:
- 设置环境
- 为 Docker Swarm 创建 AWS CloudFormation 栈
- 连接群管理器
- 利用蜂群
- 删除蜂群
设置环境
Docker for AWS 提供了两个部署选项。
- 使用预先存在的 VPC
- 使用 Docker 创建的新 VPC
让 Docker 创建 VPC、子网和网关是更简单的选择,也是本章使用的方法。
如果您在 https://aws.amazon.com/resources/create-account/
还没有 AWS 帐户,请创建一个。AWS 帐户必须支持 EC2-VPC。尽管 AWS 服务(如 VPC)是自动创建的,但该帐户必须拥有创建 EC2 实例的权限,包括自动伸缩组、IAM 配置文件、DynamoDB 表、SQS 队列、VPC(包括子网、网关和安全组)、弹性负载均衡器和 CloudWatch 日志组。除了创建具有所需权限的帐户之外,唯一的用户输入是在 Docker Swarm 的 AWS 区域中创建一个 SSH 密钥对。
选择 EC2 AWS 服务,并单击 EC2 仪表板中的密钥对链接。单击创建密钥对以创建和下载密钥对。在创建密钥对对话框中指定一个密钥对名称(例如docker
),然后点击创建。密钥对被创建,如图 3-3 所示。将密钥对文件(docker.pem
)复制到本地 Linux 机器上。
图 3-3。
A key pair
将docker.pem
上的权限设置为400
,这将只提供读取权限,并删除所有其他权限。
chmod 400 docker.pem
为 Docker Swarm 创建 AWS CloudFormation 栈
在网页浏览器中导航到 https://docs.docker.com/docker-for-aws/
,点击【AWS 部署 Docker】选项,如图 3-4 所示。标签可以不同,例如为 AWS [stable]部署 Docker 社区版[CE]。
图 3-4。
Deploy Docker for AWS
Create Stack 向导开始提供设计新模板或为 AWS 上的 Docker 选择默认 CloudFormation 模板。选择指定亚马逊 S3 模板 URL 选项,为其预先指定一个 URL,如图 3-5 所示。点击下一步。
图 3-5。
Selecting a template
在指定详细信息中,指定栈名称(DockerSwarm
)。群组参数部分包含表 3-1 中列出的字段。
表 3-1。
Swarm Parameters
| 参数 | 描述 | | --- | --- | | 群体管理者的数量? | 群管理器节点的数量。有效值为 1、3 和 5。 | | 群体工作者节点的数量? | 群中工作节点的数量(0-1000)。 |群管理器数量保持默认设置 3,群工作器节点数量保持默认设置 5,如图 3-6 所示。
图 3-6。
Specifying a stack name
接下来,指定群体属性,如表 3-2 中所讨论的。
表 3-2。
Swarm Properties
| 群体属性 | 描述 | 值集 | | --- | --- | --- | | 使用哪个 SSH 密钥? | 允许对实例进行 SSH 访问的现有 EC2 密钥对的名称。 | `docker` | | 启用每日资源清理? | 清理未使用的映像、容器、网络和卷。 | `no` | | 使用 CloudWatch 进行容器日志记录? | 将所有容器日志发送到 CloudWatch。 | `yes` |在使用哪个 SSH 密钥?属性,选择docker
SSH 键。蜂群属性如图 3-7 所示。
图 3-7。
Swarm properties
指定群组管理器属性,如表 3-3 中所述。
表 3-3。
Swarm Manager Properties
| 群体属性 | 描述 | 值集 | | --- | --- | --- | | Swarm manager 实例类型? | EC2 HVM 实例类型(t2.micro、m3.medium 等。) | `t2.micro` | | 管理临时存储卷大小? | 管理中心的临时存储卷的大小(GB) | `20` | | 管理临时存储卷类型? | 管理器卷类型 | `standard` |群组管理器属性如图 3-8 所示。指定蜂群工作属性,如表 3-4 所述。
表 3-4。
Swarm Worker Properties
| 群体工人财产 | 描述 | 值集 | | --- | --- | --- | | 代理工作实例类型? | EC2 HVM 实例类型(t2.micro、m3.medium 等。) | `t2.micro` | | 工作人员临时存储卷大小? | worker 的临时存储卷的大小(GB) | `20` | | Worker 临时存储卷类型? | 工人数量类型 | `standard` |图 3-8。
Swarm worker properties
群体工作者属性如图 3-8 所示。点击下一步。
接下来,指定栈的选项。可以为栈中的资源指定标签(键值对)。对于权限,可以选择云形成的 IAM 角色。这些选项都不需要设置,如图 3-9 所示。
图 3-9。
Optional settings
对于高级选项,通知选项设置为无通知。将故障回退设置为是,如图 3-10 所示。点击下一步。
图 3-10。
Setting rollback on failure
查看栈设置,如图 3-11 所示。
图 3-11。
Reviewing the stack settings
选择确认复选框,然后点击创建,如图 3-12 所示。
图 3-12。
Creating the stack
开始创建新的栈。点击刷新按钮刷新列表中的栈,如图 3-13 所示。
图 3-13。
Refresh
如图 3-14 所示的状态CREATE_IN_PROGRESS
所示,基于 Docker Swarm 的云形成模板的新栈开始创建。
图 3-14。
CloudFormation stack status
为不同的栈详细信息提供了不同的选项卡。资源页签显示了 CloudFormation 模板创建的 AWS 资源,如图 3-15 所示。
图 3-15。
CloudFormation stack resources
事件选项卡显示了创建云形成栈时发生的事件,如图 3-16 所示。
图 3-16。
CloudFormation stack events
栈创建完成后,状态显示为CREATE_COMPLETE
,如图 3-17 所示。
图 3-17。
Stack status is CREATE_COMPLETE
所有需要的资源——包括自动扩展组、EC2 互联网网关、EC2 安全组、弹性负载均衡器、IAM 策略、日志组和 VPC 网关——都已创建,如图 3-18 所示。
图 3-18。
Resources are created
输出选项卡列出了默认 DNS 目标、关于可用区域数量的区域可用性注释以及管理器节点,如图 3-19 所示。
图 3-19。
Outputs
要列出群管理器的 EC2 实例,点击管理器中的链接,如图 3-20 所示。
图 3-20。
The Managers link
这三个管理器实例都位于不同的可用性区域。每个 EC2 实例的公共/私有 IP 地址和公共 DNS 名称可以从 EC2 控制台获得,如图 3-21 所示。
图 3-21。
Manager instances on EC2
使用 AMI ID 可以找到用于 EC2 实例的 AMI,如图 3-22 所示。一个莫比 Linux AMI 用于这个群,但是 AMI 对于不同的用户和在不同的 AWS 区域可能是不同的。
图 3-22。
Moby Linux AMI
通过将实例状态设置为 Running,可以列出所有 EC2 实例。列出了 Docker Swarm manager 节点(三个)和 worker 节点(五个),如图 3-23 所示。管理节点和工作节点位于三个不同的可用性区域。
图 3-23。
Swarm managers and workers in three different availability zones
在 EC2 仪表板中选择负载均衡器,将会列出提供的弹性负载均衡器,如图 3-24 所示。单击“实例”选项卡列出实例。所有实例的状态都应该设置为正在使用,如图 3-24 所示。
图 3-24。
Elastic Load Balancer
从 EC2 仪表板中选择启动配置。将列出两个启动配置—一个用于管理器节点,一个用于工作者节点,如图 3-25 所示。
图 3-25。
Launch configurations
在 EC2 仪表板中选择自动缩放组。将列出两个自动缩放组—一个用于管理器节点,一个用于工作者节点,如图 3-26 所示。
图 3-26。
Auto-scaling groups
连接群管理器
接下来,从复制了密钥对docker.pem
的本地机器连接到 Swarm manager 节点。使用 manager EC2 实例的公共 IP 地址,SSH 以用户“docker”的身份登录实例。
ssh -i "docker.pem" docker@54.89.68.201
将显示管理器节点的命令提示符。
[root@localhost ∼]# ssh -i "docker.pem" docker@54.89.68.201
Welcome to Docker!
群组节点的 Docker 版本可以使用docker --version
列出。版本将是 17.06 或更高。Docker 1.12 或更高版本支持群组模式。
∼ $ docker --version
Docker version 17.06.0-ce, build 02c1d87
利用蜂群
用docker node ls
列出群节点,将列出三个管理节点和五个工作节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
255llm8729rns82bmloaxs6usl ip-172-31-8-37.ec2.internal Ready Active
ikyskl4ysocymoe4pbrj3qnh3 ip-172-31-4-154.ec2.înternal Ready Active Reachable
p2ky6meej8tnph5wyuw59xtmr ip-172-31-21-30.ec2.internal Ready Active Leader
r56kkltfgc4zzzfbslinrun2d1 ip-172-31-24-185.ec2.internal Ready Active
soggz5qplcihk8y2y58uj9md4 ip-172-31-1-33.ec2.internal Ready Active
xbdeo8qp9jhi398h478wl2zrv * ip-172-31-33-35.ec2.internal Ready Active Reachable
ykk4odpjps6t6eqc9mriqvo4a ip-172-31-47-162.ec2.internal Ready Active
zrlrmijyj5vklxl3ag7gayb3w ip-172-31-39-210.ec2.internal Ready Active
列出了由管理者状态Leader
和Reachable
指示的领导者节点和另外两个管理者节点。工作节点都是可用的,如可用性列中的Active
所示。
Docker 服务将在下一章介绍,但是您可以运行下面的docker service create
命令为 MySQL 数据库创建一个 Docker 服务示例。
docker service create \
--env MYSQL_ROOT_PASSWORD='mysql'\
--replicas 1 \
--name mysql \
--update-delay 10s \
--update-parallelism 1 \
mysql
服务被创建:
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> --update-delay 10s \
> --update-parallelism 1 \
> mysql
12hg71a3vy793quv14uems5gk
使用docker service ls
命令列出服务,这也将在下一章中讨论,并且列出了服务 ID、模式、副本和映像。
∼S docker service ls
ID NAME MODE REPLICAS IMAGE
n2tomumtl9sbniysql replicated 1/1 mysql:latest
使用docker service scale
命令将服务扩展到三个副本。三个副本被调度—一个在 leader manager 节点上,两个在 worker 节点上。列出服务副本的docker service ps
命令也将在下一章详细讨论。
∼ S docker service scale mysql=3
mysql scaled to 3
∼ S docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
slqtuf9l4hxo mysq1.1 mysql:latest ip-172-31-35-3.us-east-2.compute.internal
Running Running about a minute ago
exqsthrgszzc mysql.2 mysql:latest ip-172-31-27-83.us-east-2.compute.internal
Running Preparing 8 seconds ago
vtuhsl6mya85 mysql.3 mysql:1atest ip-172-31-29-199.us-east-2.compute.internal Running Preparing 8 seconds ago
删除蜂群
要删除一个蜂群,从云编队控制台选择操作➤删除栈,如图 3-27 所示。
图 3-27。
Choosing Actions ➤ Delete Stack
在删除栈确认对话框中,点击是,删除,如图 3-28 所示。
图 3-28。
Delete stack confirmation dialog
栈状态变为DELETE_IN_PROGRESS
,如图 3-29 所示。
图 3-29。
Delete in progress
随着每个栈的资源被删除,其状态变为DELETE_COMPLETE
,如图 3-30 中事件选项卡上的一些资源所示。
图 3-30。
Events list some of the resources with a status of DELETE_COMPLETE
当 EC2 实例被删除后,EC2 控制台将其状态列为terminated
,如图 3-31 所示。
图 3-31。
EC2 instances with status set to terminated
摘要
本章讨论了使用 Docker for AWS 服务创建由 CloudFormation 模板提供的多区域 Docker 群。您学习了如何连接到群管理器来运行docker service
命令。下一章介绍 Docker 服务。
四、Docker 服务
Docker 容器包含运行应用所需的所有二进制文件和依赖项。用户只需要运行 Docker 容器来启动和访问应用。CoreOS Linux 操作系统安装了 Docker,甚至可以在不安装 Docker 的情况下运行 Docker 命令。
问题
默认情况下,Docker 容器仅在单个节点上启动。然而,对于正常运行时间和冗余很重要的生产环境,您需要在多台主机上运行您的应用。
当使用docker run
命令启动 Docker 容器时,该容器仅在单个主机上启动,如图 4-1 所示。软件通常不被设计成仅在单个主机上运行。例如,生产环境中的 MySQL 数据库可能需要跨主机集群运行,以实现冗余和高可用性。为单个主机设计的应用应该能够根据需要扩展到多个主机。但是分布式 Docker 应用不能在单个 Docker 引擎上运行。
图 4-1。
Docker container on a single host
解决方案
Docker Swarm 模式使 Docker 应用能够跨由覆盖网络连接的分布式 Docker 引擎集群运行,如图 4-2 所示。Docker 服务可以用特定数量的副本来创建,每个副本可能在集群中的不同主机上运行。群由一个或多个管理节点组成,由一个领导者负责群管理和协调。工作节点运行实际的服务任务,默认情况下,管理节点是工作节点。停靠服务只能从领导者节点启动。因此,在工作节点上调度的服务副本运行分布式应用。分布式应用提供了一些好处,例如容错、故障转移、增加容量和负载均衡等。
图 4-2。
Docker service tasks and containers spread across the nodes
本章涵盖以下主题:
- 设置环境
- Docker 服务命令
- 服务类型
- 创建服务
- 列出服务的任务
- 在命令行上调用 Hello World 服务任务
- 获取关于服务的详细信息
- 在浏览器中调用 Hello World 服务
- 为 MySQL 数据库创建服务
- 扩展服务
- 列出服务任务
- 在 Docker 容器中访问 MySQL 数据库
- 更新服务
- 更新副本
- 更新 Docker 映像标签
- 更新放置约束
- 更新环境变量
- 更新 Docker 映像
- 更新容器标签
- 更新资源设置
- 移除服务
设置环境
使用第三章中讨论的过程,创建一个由一个管理者和两个工作者节点组成的 Docker 群。首先,启动三个 CoreOS 实例——一个用于群管理器,两个用于群工作者。获取群管理器的公共 IP 地址,如图 4-3 中 EC2 控制台所示。
图 4-3。
EC2 instances for Swarm
以“docker”用户身份 SSH 登录 Swarm manager 实例。
[root@localhost ∼]# ssh -i "docker.pem" docker@34.200.225.39
Welcome to Docker!
使用docker node ls
命令应该会在群中列出三个节点——一个管理节点和两个工作节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
ilru4f0i280w2tlsrg9hglwsj ip-172-31-10-132.ec2.internal Ready Active
w5to186ipblpcq390625wyq2e ip-172-31-37-135.ec2.internal Ready Active
zkxle7kafwcmt1sd93kh5cy5e * ip-172-31-13-155.ec2.internal Ready Active Leader
可以使用docker node promote <node ip>
命令将工作节点提升为管理节点。
∼ $ docker node promote ilru4f0i280w2tlsrg9hglwsj
Node ilru4f0i280w2tlsrg9hglwsj promoted to a manager in the swarm.
如果再次列出节点,应该会列出两个管理器节点。管理器节点由“管理器状态”列中的值标识。一个节点的管理器状态为Reachable
,另一个节点的状态为Leader
。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
ilru4f0i280w2tlsrg9hglwsj ip-172-31-10-132.ec2.internal Ready Active Reachable
w5to186ipblpcq390625wyq2e ip-172-31-37-135.ec2.internal Ready Active
zkxle7kafwcmt1sd93kh5cy5e * ip-172-31-13-155.ec2.internal Ready Active Leader
作为领导者的管理节点执行所有的群管理和协调。可到达的管理者节点加入 raft 共识仲裁,并且如果当前领导者节点变得不可用,则有资格被选举为新的领导者。
拥有多个管理器节点增加了群的容错性,但是一个或两个群管理器提供了相同的容错性。如果需要,还可以将一个或多个工作节点提升为管理节点,以提高容错能力。
对于到 Swarm 实例的连接,修改与 Swarm manager 和 worker 实例相关联的安全组的入站规则,以允许所有流量。与群组节点相关联的安全组的入站规则如图 4-4 所示。
图 4-4。
Setting inbound rules on a security group to allow all traffic
与群组管理器相关联的安全组的出站规则如图 4-5 所示。
图 4-5。
Setting outbound rules on a security group to allow all traffic
docker 服务命令
docker service
命令用于管理 Docker 服务。docker service
命令提供了表 4-1 中列出的子命令。
表 4-1。
The docker service Sub-Commands
| 命令 | 描述 | | --- | --- | | `docker service create` | 创建新服务。 | | `docker service inspect` | 显示一项或多项服务的详细信息。 | | `docker service logs` | 获取服务的日志。Docker 17.0.6 中添加了该命令。 | | `docker service ls` | 列出服务。 | | `docker service ps` | 列出一个或多个服务的任务。 | | `docker service rm` | 删除一个或多个服务。 | | `docker service scale` | 扩展一个或多个复制服务。 | | `docker service update` | 更新服务。 |要运行docker service
命令,必须满足以下要求。
- 必须启用 Docker 群组模式
- 命令必须从作为领导者的群管理器节点运行
docker service
命令仅在群组模式下可用,不能在群组模式外运行。
不能从工作节点运行docker service
命令。工作节点不能用于查看或修改群集群状态。
服务类型
Docker Swarm 模式支持两种类型的服务,也称为服务模式——复制服务和全局服务。全局服务只在 Docker 集群中的每个节点上运行一个任务。复制服务作为配置数量的任务运行,这些任务也称为副本,默认为一个。副本的数量可以在创建新服务时指定,并且可以在以后更新。默认服务类型是复制服务。全局服务要求将--mode
选项设置为global
。只有复制的服务可以扩展;全球服务无法扩展。
我们从创建一个复制服务开始。在本章的后面,我们还将讨论创建一个全局服务。
创建服务
创建 Docker 服务的命令语法如下。
docker service create [OPTIONS] IMAGE [COMMAND] [ARG...]
表 4-2 中列出了一些支持的选项。
表 4-2。
Supported Options for Creating a Service
| [计]选项 | 描述 | | --- | --- | | `--constraint` | 位置约束。 | | `--container-label` | 容器标签。 | | `--env, -e` | 设置环境变量。 | | `--env-file` | 读入环境变量文件。直到 Docker 1.13 才添加选项。 | | `--host` | 设置一个或多个自定义主机到 IP 的映射。直到 Docker 1.13 才添加选项。格式为`host:ip`。 | | `--hostname` | 容器主机名。直到 Docker 1.13 才添加选项。 | | `--label, -l` | 服务标签。 | | `--limit-cpu` | 限制 CPU。默认值为 0.000。 | | `--limit-memory` | 限制记忆。默认值为 0。 | | `--log-driver` | 服务的日志驱动程序。 | | `--log-opt` | 日志驱动程序选项。 | | `--mode` | 服务模式。值可以是复制的或全局的。默认为`replicated`。 | | `--mount` | 将文件系统挂载附加到服务。 | | `--name` | 服务名称。 | | `--network` | 网络附件。默认情况下,使用“入口”覆盖网络。 | | `--publish, -p` | 将端口发布为节点端口。 | | `--read-only` | 以只读方式挂载容器的根文件系统。直到文件 17.03 才添加选项。默认为`false`。 | | `--` `replicas` | 任务数量。 | | `--reserve-cpu` | 保留 CPU。默认值为 0.000。 | | `--reserve-memory` | 保留记忆。默认值为 0。 | | `--restart-condition` | 满足条件时重新启动。值可以是无、失败时或任何。 | | `--restart-delay` | 重新启动尝试之间的延迟(ns|us|ms|s|m|h)。 | | `--restart-max-attempts` | 放弃前重新启动的最大次数。 | | `--tty, -t` | 是否分配一个伪 TTY。直到 Docker 1.13 才添加选项。默认为`false`。 | | `--update-delay` | 更新之间的延迟(ns|us|ms|s|m|h)。默认值为 0s。 | | `--update-failure-action` | 更新失败时的操作。值可以是`pause`或`continue`。默认值是`pause`。 | | `--update-monitor` | 每次任务更新后监视失败的持续时间(ns|us|ms|s|m|h)。默认值为 0s。 | | `--update-parallelism` | 同时更新的最大任务数。值为 0 表示一次更新所有内容。默认值为`1`。 | | `--user, -u` | 用户名或 UID 格式:`例如,创建一个名为hello-world
的服务,其 Docker 映像tutum/hello-world
由两个副本组成。公开主机端口 8080 上的服务。如果成功,docker service create
命令输出一个服务 ID。
∼ $ docker service create \
> --name hello-world \
> --publish 8080:80 \
> --replicas 2 \
> tutum/hello-world
vyxnpstt351124h12niqm7s64
服务被创建。
列出服务的任务
您可以使用以下命令列出服务任务,在复制服务的上下文中也称为副本。
docker service ps hello-world
列出了两项服务任务。
∼ $ docker service ps hello-world
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
zjm03bjsqyhp hello-world.1 tutum/hello-world:latest ip-172-31-10-132.ec2.internal Running Running 41 seconds ago
kezidi82ol5c hello-world.2 tutum/hello-world:latest ip-172-31-13-155.ec2.internal Running Running 41 seconds ago
ID 列列出了任务 ID。任务名称的格式为servicename.n
;hello-world.1
和hello-world.2
为两个复制品。Docker 映像也会列出。NODE
列列出了调度任务的节点的私有 dn。DESIRED STATE
是服务定义中定义的期望状态。CURRENT STATE
是任务的实际状态。有时,由于缺乏 CPU 和内存方面的资源容量,任务可能处于挂起状态。
服务任务是运行 Docker 容器的插槽。在运行任务的每个节点上,Docker 容器也应该在运行。Docker 容器可以用docker ps
命令列出。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0ccdcde64e7d tutum/hello-world:latest "/bin/sh -c 'php-f..." 2 minutes agoUp 2 minutes 80/tcp hello-world.2.kezidi82ol5ct81u59jpgfhs1
在命令行上调用 Hello World 服务任务
在<hostname>:8080
使用curl
调用hello-world
服务。curl
命令输出是服务的 HTML 标记。
∼ $ curl ec2-34-200-225-39.compute-1.amazonaws.com:8080
<html>
<head>
<title>Hello world!</title>
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>
<style>
body {
background-color: white;
text-align: center;
padding: 50px;
font-family: "Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
}
#logo {
margin-bottom: 40px;
}
</style>
</head>
<body>
<img id="logo" src="logo.png" />
<h1>Hello world!</h1>
<h3>My hostname is 20b121986df6</h3>
</body>
</html>
获取关于服务的详细信息
要获得关于hello-world
服务的详细信息,请运行docker service inspect
命令。
docker service inspect hello-world
详细信息包括容器规格、资源、重启策略、位置、模式、更新配置、端口(目标端口和发布端口)、虚拟 IPs 和更新状态。
∼ $ docker service inspect hello-world
[
{
"ID": "vyxnpstt351124h12niqm7s64",
"Version": {
"Index": 30
},
"CreatedAt": "2017-07-23T19:00:09.98992017Z",
"UpdatedAt": "2017-07-23T19:00:09.993001487Z",
"Spec": {
"Name": "hello-world",
"Labels": {},
"TaskTemplate": {
"ContainerSpec": {
"Image": "tutum/hello-world:latest@sha256:0d57def8055178aafb4c7669cbc25ec17f0acdab97cc587f30150802da8f8d85",
"StopGracePeriod": 10000000000,
"DNSConfig": {}
},
"Resources": {
"Limits": {},
"Reservations": {}
},
"RestartPolicy": {
"Condition": "any",
"Delay": 5000000000,
"MaxAttempts": 0
},
"Placement": {
"Platforms": [
{
"Architecture": "amd64",
"OS": "linux"
}
]
},
"ForceUpdate": 0,
"Runtime": "container"
},
"Mode": {
"Replicated": {
"Replicas": 2
}
},
"UpdateConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"RollbackConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"EndpointSpec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
}
},
"Endpoint": {
"Spec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
},
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
],
"VirtualIPs": [
{
"NetworkID": "y3k655bdlp3x102a2bslh4swh",
"Addr": "10.255.0.5/16"
}
]
}
}
]
在浏览器中调用 Hello World 服务
Hello World 服务可以在浏览器中使用托管群节点的 EC2 实例的公共 DNS 来调用。服务副本不一定要在节点上运行才能从该节点调用服务。你从 EC2 控制台获取一个 manager 节点的公共 DNS,如图 4-3 所示。使用<Public DNS>:<Published Port>
URL 调用 Hello World 服务。随着 Hello World 服务在端口 8080 上公开或发布,浏览器中要调用的 URL 变成了<Public DNS>:8080
。服务被调用,服务输出显示在浏览器中,如图 4-6 所示。
图 4-6。
Invoking a service in a browser
类似地,您可以获得一个托管有 Swarm worker 节点的 EC2 实例的公共 DNS,如图 4-7 所示。
图 4-7。
Obtaining the public DNS for a EC2 instance on which a Swarm worker node is hosted
在浏览器中使用PublicDNS:8080
URL 调用服务,如图 4-8 所示。
图 4-8。
Invoking a service in a browser using public DNS for a EC2 instance on which a Swarm worker node is hosted
默认情况下,manager 节点也是 worker 节点,服务任务也在 manager 节点上运行。
为 MySQL 数据库创建服务
接下来,我们为 MySQL 数据库创建一个服务。使用mysql
Docker 映像与使用tutum/hello-world
Docker 映像在两个方面有所不同。
mysql
Docker 映像有一个名为MYSQL_ROOT_PASSWORD
的强制环境变量。mysql
Docker 镜像基于一个 Debian Linux,启动 Docker 容器中的 MySQL 数据库服务器,而tutum/hello-world
镜像基于 Alpine Linux,启动 Apache 服务器运行 PHP 应用。
运行下面的docker service create
命令来创建 MySQL 数据库服务的一个副本。使用环境变量MYSQL_ROOT_PASSWORD
提供一个 root 密码。包括重启条件、重启最大尝试次数、更新延迟和更新失败操作的一些其他选项。用docker service rm mysql
命令删除任何以前运行的名为mysql
的 Docker 服务。
∼ $ docker service create \
--env MYSQL_ROOT_PASSWORD='mysql'\
--replicas 1 \
--restart-condition none \
--restart-max-attempts 5 \
--update-failure-action continue \
--name mysql \
--update-delay 10s \
mysql
为 MySQL 数据库创建一个服务,并输出服务 ID。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --restart-condition none \
> --restart-max-attempts 5 \
> --update-failure-action continue \
> --name mysql \
> --update-delay 10s \
> mysql
gzl8k1wy8kf3ms1nu5zwlfxm6
用docker service ls
命令列出服务;应该会列出mysql
服务。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
gzl8k1wy8kf3 mysql replicated 1/1 mysql:latest
vyxnpstt3511 hello-world replicated 2/2 tutum/hello-world:latest *:8080->80/tcp
用docker service ps mysql
命令列出服务任务/副本。一个任务正在管理器工作节点上运行。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
mfw76m4rxbhp mysql.1 mysql:latest ip-172-31-37-135.ec2.internalRunning Running 16 seconds ago
如何调度服务任务,包括基于节点排序的节点选择,将在第八章中讨论,该章涵盖了调度。
扩展服务
接下来,我们扩展mysql
服务。只有复制的服务可以扩展,扩展一个或多个服务的命令语法如下。
docker service scale SERVICE=REPLICAS [SERVICE=REPLICAS...]
要将mysql
服务扩展到三个任务,运行以下命令。
docker service scale mysql=3
如命令输出所示,mysql
服务扩展到三个。
∼ $ docker service scale mysql=3
mysql scaled to 3
列出服务任务
列出服务任务的docker service ps
命令语法如下。
docker service ps [OPTIONS] SERVICE [SERVICE...]
该命令支持表 4-3 中列出的选项。
表 4-3。
Options for the docker service ps Command
| [计]选项 | 描述 | | --- | --- | | `--filter, -f` | 根据提供的条件过滤输出。支持以下滤镜:`id=例如,您可以仅列出正在运行的服务任务。
docker service ps –f desired-state=running mysql
仅列出正在运行的任务。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
mfw76m4rxbhp mysql.1 mysql:latest ip-172-31-37-135.ec2.internal Running Running 46 seconds ago
s4flvtode8od mysql.2 mysql:latest ip-172-31-13-155.ec2.internal Running Running 8 seconds ago
j0jd92p5dmd8 mysql.3 mysql:latest ip-172-31-10-132.ec2.internal Running Running 9 seconds ago
所有任务都在运行;所以使用滤镜的效果不是很明显。但是,在随后的示例中,当一些任务没有运行时,您将列出正在运行的服务任务。
如果节点的数量大于任务的数量,则不是所有的工作节点都用于运行服务任务,例如当hello-world
和mysql
服务运行的任务少于三个时。如果副本的数量大于群中节点的数量,则一个节点可以运行多个服务任务。扩展到五个复制副本会在两个节点上启动多个复制副本。
∼ $ docker service scale mysql=5
mysql scaled to 5
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
mfw76m4rxbhp mysql.1 mysql:latest ip-172-31-37-135.ec2.internal Running Running about a minute ago
s4flvtode8od mysql.2 mysql:latest ip-172-31-13-155.ec2.internal Running Running 44 seconds ago
j0jd92p5dmd8 mysql.3 mysql:latest ip-172-31-10-132.ec2.internal Running Running 45 seconds ago
vh9qxhm452pt mysql.4 mysql:latest ip-172-31-37-135.ec2.internal Running Running 26 seconds ago
6jtkvstssnkf mysql.5 mysql:latest ip-172-31-10-132.ec2.internal Running Running 26 seconds ago
管理器节点上仅运行一个mysql
服务副本;因此,只有一个用于mysql
服务的 Docker 容器在 manager 节点上运行。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6bbe40000874 mysql:latest "docker-entrypoint..."About a minute ago Up About a minute 3306/tcp mysql.2.s4flvtode8odjjere2zsi9gdx
扩展到 10 个任务会在每个群节点上启动多个任务。
∼ $ docker service scale mysql=10
mysql scaled to 10
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
s4flvtode8od mysql.2 mysql:latest ip-172-31-13-155.ec2.internal Running Running about a minute ago
j0jd92p5dmd8 mysql.3 mysql:latest ip-172-31-10-132.ec2.internalRunning Running 2 minutes ago
6jtkvstssnkf mysql.5 mysql:latest ip-172-31-10-132.ec2.internalRunning Running about a minute ago
jxunbdec3fnj mysql.6 mysql:latest ip-172-31-37-135.ec2.internalRunning Running 14 seconds ago
t1nz59dyoi2s mysql.7 mysql:latest ip-172-31-10-132.ec2.internalRunning Running 14 seconds ago
lousvchdirn9 mysql.8 mysql:latest ip-172-31-13-155.ec2.internalRunning Running 14 seconds ago
94ml0f52344d mysql.9 mysql:latest ip-172-31-37-135.ec2.internalRunning Running 14 seconds ago
pd40sd7qlk3j mysql.10 mysql:latest ip-172-31-13-155.ec2.internalRunning Running 14 seconds ago
对于在管理器节点上运行的三个任务,管理器节点上的mysql
服务的 Docker 容器的数量增加到三个。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
15e3253f69f1 mysql:latest "docker-entrypoint..." 50 seconds ago Up 49 seconds 3306/tcp mysql.8.lousvchdirn9fv8wot5vivk6d
cca7ab20c914 mysql:latest "docker-entrypoint..." 50 seconds ago Up 49 seconds 3306/tcp mysql.10.pd40sd7qlk3jc0i73huop8e4r
6bbe40000874 mysql:latest "docker-entrypoint..." 2 minutes ago Up 2 minutes 3306/tcp mysql.2.s4flvtode8odjjere2zsi9gdx
因为您将在后面的部分通过 MySQL 数据库服务示例了解更多关于 Docker 服务的内容,并且为了完整性,接下来我们将讨论使用 MySQL 数据库的 Docker 容器来创建数据库表。
在 Docker 容器中访问 MySQL 数据库
接下来,我们访问 Docker 容器中的 MySQL 数据库。在每个实例上运行时,docker ps
命令会列出实例上mysql
服务的 Docker 容器。用docker exec –it <containerid> bash
命令启动 Docker 容器的 bash shell。为 Docker 容器显示root
提示符。
∼ $ docker exec -it 15e3253f69f1 bash
root@15e3253f69f1:/#
以用户root
的身份使用mysql
命令启动 MySQL CLI。出现提示时指定密码;使用环境变量MYSQL_ROOT_PASSWORD
在docker service create
命令的--env
选项中指定了用于创建服务的密码。显示mysql>
CLI 命令提示符。
root@15e3253f69f1:/# mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.7.19 MySQL Community Server (GPL)
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
使用use mysql
命令将数据库设置为mysql
。
mysql> use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
用下面的 SQL 脚本创建一个数据库表。
CREATE TABLE wlslog(time_stamp VARCHAR(45) PRIMARY KEY,category VARCHAR(25),type VARCHAR(25),servername VARCHAR(25),code VARCHAR(25),msg VARCHAR(45));
创建了wlslog
表。
mysql> CREATE TABLE wlslog(time_stamp VARCHAR(45) PRIMARY KEY,category VARCHAR(25),type VARCHAR(25),servername VARCHAR(25),code VARCHAR(25),msg VARCHAR(45));
Query OK, 0 rows affected (0.06 sec)
从 MySQL CLI 运行以下 SQL 命令,向wlslog
表添加一些数据。
mysql> INSERT INTO wlslog VALUES('Apr-8-2014-7:06:16-PM-PDT','Notice','WebLogicServer','AdminServer','BEA-000365','Server state changed to STANDBY');
Query OK, 1 row affected (0.02 sec)
mysql> INSERT INTO wlslog VALUES('Apr-8-2014-7:06:17-PM-PDT','Notice','WebLogicServer','AdminServer','BEA-000365','Server state changed to STARTING');
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO wlslog VALUES('Apr-8-2014-7:06:18-PM-PDT','Notice','WebLogicServer','AdminServer','BEA-000365','Server state changed to ADMIN');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO wlslog VALUES('Apr-8-2014-7:06:19-PM-PDT','Notice','WebLogicServer','AdminServer','BEA-000365','Server state changed to RESUMING');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO wlslog VALUES('Apr-8-2014-7:06:20-PM-PDT','Notice','WebLogicServer','AdminServer','BEA-000331','Started WebLogic AdminServer');
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO wlslog VALUES('Apr-8-2014-7:06:21-PM-PDT','Notice','WebLogicServer','AdminServer','BEA-000365','Server state changed to RUNNING');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO wlslog VALUES('Apr-8-2014-7:06:22-PM-PDT','Notice','WebLogicServer','AdminServer','BEA-000360','Server started in RUNNING mode');
Query OK, 1 row affected (0.00 sec)
运行 SQL 查询以列出数据库表数据。
mysql> SELECT * FROM wlslog;
+---------------------------+----------+----------------+-------------+------------+---------------------------------+
| time_stamp | category | type | servername | code | msg |
+---------------------------+----------+----------------+-------------+------------+---------------------------------+
| Apr-8-2014-7:06:16-PM-PDT | Notice | WebLogicServer | AdminServer | BEA-000365 | Server state changed to STANDBY |
| Apr-8-2014-7:06:17-PM-PDT | Notice | WebLogicServer | AdminServer | BEA-000365 | Server state changed to STARTING|
| Apr-8-2014-7:06:18-PM-PDT | Notice | WebLogicServer | AdminServer | BEA-000365 | Server state changed to ADMIN |
| Apr-8-2014-7:06:19-PM-PDT | Notice | WebLogicServer | AdminServer | BEA-000365 | Server state changed to RESUMING|
| Apr-8-2014-7:06:20-PM-PDT | Notice | WebLogicServer | AdminServer | BEA-000331 | Started WebLogic AdminServer |
| Apr-8-2014-7:06:21-PM-PDT | Notice | WebLogicServer | AdminServer | BEA-000365 | Server state changed to RUNNING |
| Apr-8-2014-7:06:22-PM-PDT | Notice | WebLogicServer | AdminServer | BEA-000360 | Server started in RUNNING mode |
+---------------------------+----------+----------------+-------------+------------+---------------------------------+
7 rows in set (0.00 sec)
使用exit
命令退出 MySQL CLI 和 bash shell。
mysql> exit
Bye
root@15e3253f69f1:/# exit
exit
更新服务
服务可以在使用docker service update
命令创建后更新,其语法如下:
docker service update [OPTIONS] SERVICE
表 4-4 中列出了一些支持的选项。
表 4-4。
Options for the docker service update Command
| [计]选项 | 描述 | | --- | --- | | `--args` | 命令的参数。 | | `--constraint-add` | 添加或更新放置约束。 | | `--constraint-rm` | 删除放置约束。 | | `--container-label-add` | 添加或更新 Docker 容器标签。 | | `--container-label-rm` | 通过关键字移除容器标签。 | | `--env-add` | 添加或更新环境变量。 | | `--env-rm` | 删除环境变量。 | | `--` `force` | 是否强制更新,即使没有需要更新的更改。Docker 1.13 中增加了选项。默认为`false`。 | | `--group-add` | 向容器中添加额外的补充用户组。Docker 1.13 中增加了选项。 | | `--group-rm` | 从容器中删除以前添加的补充用户组。Docker 1.13 中增加了选项。 | | `--host-add` | 添加或更新自定义主机到 IP 的映射(`host:ip`)。Docker 1.13 中增加了选项。 | | `--host-rm` | 删除自定义主机到 IP 的映射(`host:ip`)。Docker 1.13 中增加了选项。 | | `--hostname` | 更新容器主机名。Docker 1.13 中增加了选项。 | | `--image` | 更新服务映像标签。 | | `--label-add` | 添加或更新服务标签。 | | `--label-rm` | 通过标签的键移除标签。 | | `--limit-cpu` | 更新极限 CPU。默认值为 0.000。 | | `--limit-memory` | 更新极限内存。默认值为 0。 | | `--log-driver` | 更新服务的日志记录驱动程序。 | | `--log-opt` | 更新日志记录驱动程序选项。 | | `--mount-add` | 在服务上添加或更新装载。 | | `--mount-rm` | 按装载的目标路径删除装载。 | | `--publish-add` | 添加或更新发布的端口。 | | `--publish-rm` | 通过目标端口删除发布的端口。 | | `--read-only` | 以只读方式挂载容器的根文件系统。在 Docker 17.06 中添加了选项。默认为`false`。 | | `--replicas` | 更新任务数量。 | | `--reserve-cpu` | 更新备用 CPU。默认值为 0.000。 | | `--reserve-memory` | 更新保留内存。默认值为 0。 | | `--restart-condition` | 满足条件时更新重启(无、失败时或任何)。 | | `--restart-delay` | 更新重新启动尝试之间的延迟(ns|us|ms|s|m|h)。 | | `--restart-max-attempts` | 放弃前更新最大重启次数。 | | `--` `rollback` | 是否回滚到以前的规范。Docker 1.13 中增加了选项。默认为`false`。 | | `--tty, -t` | 是否分配一个伪 TTY。Docker 1.13 中增加了选项。默认为`false`。 | | `--update-delay` | 更新之间的更新延迟(ns|us|ms|s|m|h)。默认值为 0s。 | | `--update-failure-action` | 更新失败时的更新操作(暂停|继续)。默认为`pause`。 | | `--update-monitor` | 每次任务更新后监视失败的持续时间(ns|us|ms|s|m|h)。Docker 1.13 中增加了选项。默认 0s。 | | `--update-parallelism` | 更新同时更新的最大任务数(0 表示一次更新所有任务)。默认为`1`。 | | `--user, -u` | 添加用户名或 UID(格式:`接下来,我们更新已部署服务的一些参数。
更新副本
首先,创建一个要更新的mysql
服务。
docker service create \
--env MYSQL_ROOT_PASSWORD='mysql'\
--replicas 1 \
--restart-condition on-failure \
--restart-max-attempts 5 \
--update-failure-action continue \
--name mysql \
--update-delay 10s \
mysql:5.6
来自 Docker 映像mysql:5.6
的服务被创建,并且服务 ID 被输出。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --restart-condition on-failure \
> --restart-max-attempts 5 \
> --update-failure-action continue \
> --name mysql \
> --update-delay 10s \
> mysql:5.6
mecdt3zluvlvxqc3hdpw8edg1
使用docker service update
命令将副本数量更新为五个。如果命令成功,将从命令中输出服务名。
∼ $ docker service update --replicas 5 mysql
mysql
将副本设置为五个并不只是启动四个新任务,从而使任务总数达到五个。当更新服务以更改副本数量时,所有服务任务都将关闭,并启动新任务。随后列出的服务任务中,第一个任务被关闭,五个新任务被启动。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
jen0fmkjj13k mysql.1 mysql:5.6 ip-172-31-37-135.ec2.internalRunning Starting less than a second ago
r616gx588opd \_ mysql.1 mysql:5.6 ip-172-31-37-135.ec2.internalShutdown Failed 5 seconds ago "task: non-zero exit (137)"
y350n4e8furo mysql.2 mysql:5.6 ip-172-31-13-155.ec2.internalRunning Running 7 seconds ago
ktrwxnn13fug mysql.3 mysql:5.6 ip-172-31-37-135.ec2.internalRunning Running 14 seconds ago
2t8j1zd8uts1 mysql.4 mysql:5.6 ip-172-31-10-132.ec2.internalRunning Running 10 seconds ago
8tf0uuwb8i31 mysql.5 mysql:5.6 ip-172-31-10-132.ec2.internalRunning Running 10 seconds ago
更新 Docker 映像标签
从针对 Docker image mysql:5.6
的名为mysql
的 MySQL 数据库服务开始,接下来我们将该服务更新为一个不同的 Docker image 标签—mysql:latest
Docker image。运行以下命令来更新 Docker 映像;输出服务名以表明更新成功。
∼ $ docker service update --image mysql:latest mysql
mysql
您可以使用docker service inspect
命令列出服务的详细信息。ContainerSpec
中列出的映像是mysql:latest
。PreviousSpec
也在列。
∼ $ docker service inspect mysql
[
{
"Spec": {
"Name": "mysql",
"Labels": {},
"TaskTemplate": {
"ContainerSpec": {
"Image":
"mysql:latest@sha256:75c563c474f1adc149978011fedfe2e6670483d133b22b07ee32789b626f8de3",
"Env": [
"MYSQL_ROOT_PASSWORD=mysql"
],
"PreviousSpec": {
"Name": "mysql",
"Labels": {},
"TaskTemplate": {
"ContainerSpec": {
"Image": "mysql:5.6@sha256:6ad5bd392c9190fa92e65fd21f6debc8b2a76fc54f13949f9b5bc6a0096a5285",
]
即使docker service update
命令完成了,更新也不会立即完成。当服务正在更新时,服务的UpdateStatus
被列出,State
被设置为"updating"
,"update in progress"
的Message
。
"UpdateStatus": {
"State": "updating",
"StartedAt": "2017-07-23T19:24:15.539042747Z",
"Message": "update in progress"
}
更新完成后,UpdateStatus State
变为"completed"
,Message
变为"update completed"
。
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-23T19:24:15.539042747Z",
"CompletedAt": "2017-07-23T19:25:25.660907984Z",
"Message": "update completed"
}
当服务正在更新时,服务任务正在关闭,新的服务任务正在启动。当更新开始时,一些正在运行的任务可能基于先前的映像mysql:5.6
,而其他的可能基于新的映像mysql:latest
。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
jen0fmkjj13k mysql.1 mysql:5.6 ip-172-31-37-135.ec2.internalRunning Running 38 seconds ago
r616gx588opd \_ mysql.1 mysql:5.6 ip-172-31-37-135.ec2.internalShutdown Failed 43 seconds ago "task: non-zero exit (137)"
y350n4e8furo mysql.2 mysql:5.6 ip-172-31-13-155.ec2.internalRunning Running 45 seconds ago
bswz4sm8e3vj mysql.3 mysql:5.6 ip-172-31-37-135.ec2.internalRunning Running 6 seconds ago
ktrwxnn13fug \_ mysql.3 mysql:5.6 ip-172-31-37-135.ec2.internalShutdown Failed 12 seconds ago "task: non-zero exit (1)"
wj1x26wvp0pt mysql.4 mysql:latest ip-172-31-13-155.ec2.internalRunning Running 7 seconds ago
2t8j1zd8uts1 \_ mysql.4 mysql:5.6 ip-172-31-10-132.ec2.internalShutdown Shutdown 7 seconds ago
hppq840ekrh7 mysql.5 mysql:latest ip-172-31-10-132.ec2.internalRunning Running 2 seconds ago
8tf0uuwb8i31 \_ mysql.5 mysql:5.6 ip-172-31-10-132.ec2.internalShutdown Failed 8 seconds ago "task: non-zero exit (1)"
映像为mysql:5.6
的任务的期望状态被设置为Shutdown
。渐渐地,所有基于新形象mysql:latest
的新服务任务都开始了。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
2uafxtcbj9qj mysql.1 mysql:latest ip-172-31-37-135.ec2.internalRunning Running 30 seconds ago
jen0fmkjj13k \_ mysql.1 mysql:5.6 ip-172-31-37-135.ec2.internalShutdown Failed 36 seconds ago "task: non-zero exit (137)"
r616gx588opd \_ mysql.1 mysql:5.6 ip-172-31-37-135.ec2.internalShutdown Failed about a minute ago "task: non-zero exit (137)"
mkv95bvx3sl1 mysql.2 mysql:latest ip-172-31-13-155.ec2.internalReady Ready 3 seconds ago
y350n4e8furo \_ mysql.2 mysql:5.6 ip-172-31-13-155.ec2.internalShutdown Failed 4 seconds ago "task: non-zero exit (137)"
yevunzer12vm mysql.3 mysql:latest ip-172-31-37-135.ec2.internalRunning Running 12 seconds ago
bswz4sm8e3vj \_ mysql.3 mysql:5.6 ip-172-31-37-135.ec2.internalShutdown Shutdown 12 seconds ago
ktrwxnn13fug \_ mysql.3 mysql:5.6 ip-172-31-37-135.ec2.internalShutdown Failed 48 seconds ago "task: non-zero exit (1)"
wj1x26wvp0pt mysql.4 mysql:latest ip-172-31-13-155.ec2.internalRunning Running 44 seconds ago
2t8j1zd8uts1 \_ mysql.4 mysql:5.6 ip-172-31-10-132.ec2.internalShutdown Shutdown 44 seconds ago
hppq840ekrh7 mysql.5 mysql:latest ip-172-31-10-132.ec2.internalRunning Running 39 seconds ago
8tf0uuwb8i31 \_ mysql.5 mysql:5.6 ip-172-31-10-132.ec2.internalShutdown Failed 44 seconds ago "task: non-zero exit (1)"
前面介绍了使用–f
选项过滤服务任务。要查找在特定节点上调度了哪些任务(如果有的话),可以运行docker service ps
命令,并将过滤器设置为该节点。然后列出过滤后的任务,包括Running
和Shutdown
。
∼ $ docker service ps -f node=ip-172-31-13-155.ec2.internal mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
mkv95bvx3sl1 mysql.2 mysql:latest ip-172-31-13-155.ec2.internalRunning Running about a minute ago
y350n4e8furo \_ mysql.2 mysql:5.6 ip-172-31-13-155.ec2.internalShutdown Failed about a minute ago "task: non-zero exit (137)"
oksssg7gsh79 mysql.4 mysql:latest ip-172-31-13-155.ec2.internalRunning Running 50 seconds ago
wj1x26wvp0pt \_ mysql.4 mysql:latest ip-172-31-13-155.ec2.internalShutdown Failed 55 seconds ago "task: non-zero exit (1)"
服务任务也可以通过期望的状态来过滤。要仅列出正在运行的任务,请将desired-state
过滤器设置为running
。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE
DESIRED STATE CURRENT STATE ERROR PORTS
2uafxtcbj9qj mysql.1 mysql:latest ip-172-31-37-135.ec2.internalRunning Running 3 minutes ago
mkv95bvx3sl1 mysql.2 mysql:latest ip-172-31-13-155.ec2.internalRunning Running 2 minutes ago
yevunzer12vm mysql.3 mysql:latest ip-172-31-37-135.ec2.internalRunning Running 2 minutes ago
oksssg7gsh79 mysql.4 mysql:latest ip-172-31-13-155.ec2.internalRunning Running 2 minutes ago
hppq840ekrh7 mysql.5 mysql:latest ip-172-31-10-132.ec2.internalRunning Running 3 minutes ago
同样,通过将desired-state
过滤器设置为shutdown
,仅列出关闭任务。
∼ $ docker service ps -f desired-state=shutdown mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
jen0fmkjj13k mysql.1 mysql:5.6 ip-172-31-37-135.ec2.internalShutdown Failed 3 minutes ago "task: non-zero exit (137)"
r616gx588opd \_ mysql.1 mysql:5.6 ip-172-31-37-135.ec2.internalShutdown Failed 3 minutes ago "task: non-zero exit (137)"
y350n4e8furo mysql.2 mysql:5.6 ip-172-31-13-155.ec2.internalShutdown Failed 2 minutes ago "task: non-zero exit (137)"
bswz4sm8e3vj mysql.3 mysql:5.6 ip-172-31-37-135.ec2.internalShutdown Shutdown 2 minutes ago
ktrwxnn13fug \_ mysql.3 mysql:5.6 ip-172-31-37-135.ec2.internalShutdown Failed 3 minutes ago "task: non-zero exit (1)"
wj1x26wvp0pt mysql.4 mysql:latest ip-172-31-13-155.ec2.internalShutdown Failed 2 minutes ago "task: non-zero exit (1)"
2t8j1zd8uts1 \_ mysql.4 mysql:5.6 ip-172-31-10-132.ec2.internalShutdown Shutdown 3 minutes ago
8tf0uuwb8i31 mysql.5 mysql:5.6 ip-172-31-10-132.ec2.internalShutdown Failed 3 minutes ago "task: non-zero exit (1)"
更新放置约束
可通过--constraint-add
和--constraint-rm
选项添加/删除布局约束。我们从一个由三个节点组成的集群开始——一个管理节点和两个工作节点。然后,我们将一个工作节点提升为管理节点,从而形成一个包含两个管理节点和一个工作节点的集群。。
从跨群节点运行的服务副本开始,可以使用以下命令将副本限制为仅在工作节点上运行。如果成功,docker service update
命令输出服务名。
∼ $ docker service update --constraint-add "node.role==worker" mysql
mysql
可能需要一段时间(几秒或几分钟)来协调服务的期望状态,在此期间,即使node.role
被设置为worker
或者运行的任务少于所需的数量,任务也可以在管理器节点上运行。当更新完成时(更新状态可以从docker service inspect
命令中找到),列出mysql
服务正在运行的任务表明这些任务只在工作节点上运行。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
smk5q4nhu1rw mysql.1 mysql:latest ip-172-31-37-135.ec2.internalRunning Running about a minute ago
wzmou8f6r2tg mysql.2 mysql:latest ip-172-31-37-135.ec2.internalRunning Running 23 seconds ago
byavev89hukv mysql.3 mysql:latest ip-172-31-37-135.ec2.internalRunning Running 23 seconds ago
erx409p0sgcc mysql.4 mysql:latest ip-172-31-37-135.ec2.internalRunning Running 53 seconds ago
q7eqw8jlqig8 mysql.5 mysql:latest ip-172-31-37-135.ec2.internalRunning Running 46 seconds ago
作为另一个例子,mysql
服务的服务任务可以被限制为仅在管理器节点上运行。从运行在 manager 和 worker 节点上的服务任务开始,并且没有添加其他约束,运行以下命令将所有任务放置在 manager 节点上。
∼ $ docker service update --constraint-add 'node.role==manager' mysql
mysql
任务不会在工作者节点上关闭,而是立即在管理器节点上启动,并且最初可能会继续在工作者节点上运行。
过一会儿再列出服务副本。您将看到所有的任务都在管理器节点上列为running
。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
7tj8bck4jr5n mysql.1 mysql:latest ip-172-31-13-155.ec2.internalRunning Running 14 seconds ago
uyeu3y67v2rt mysql.2 mysql:latest ip-172-31-10-132.ec2.internalRunning Running about a minute ago
lt9p7479lkta mysql.3 mysql:latest ip-172-31-10-132.ec2.internalRunning Running 1 second ago
t7d9c4viuo5y mysql.4 mysql:latest ip-172-31-13-155.ec2.internalRunning Running 40 seconds ago
8xufz871yx1x mysql.5 mysql:latest ip-172-31-13-155.ec2.internalRunning Running 27 seconds ago
更新环境变量
--env-add
和--env-rm
选项用于在服务中添加/删除环境变量。我们创建的mysql
服务只包含一个环境变量——强制的MYSQL_ROOT_PASSWORD
变量。您可以使用docker service update
命令添加环境变量MYSQL_DATABASE
、MYSQL_PASSWORD
和MYSQL_ALLOW_EMPTY_PASSWORD
,并在同一个命令中将MYSQL_ROOT_PASSWORD
更新为空密码。如果成功,该命令将输出服务名称。
∼ $ docker service update --env-add 'MYSQL _DATABASE=mysql' --env-add 'MYSQL_PASSWORD=mysql' --env-add 'MYSQL_ALLOW_EMPTY_PASSWORD=yes' --env-add 'MYSQL_ROOT_PASSWORD=yes' mysql
mysql
当更新完成后,docker service inspect
命令列出添加的环境变量。
∼ $ docker service inspect mysql
[...
"Spec": {
"Name": "mysql",
...
"Env": [
"MYSQL_ROOT_PASSWORD=yes",
"MYSQL _DATABASE=mysql",
"MYSQL_PASSWORD=mysql",
"MYSQL_ALLOW_EMPTY_PASSWORD=yes"
],
...
]
更新环境变量会导致容器重新启动。因此,简单地添加环境变量不会导致在同一个容器中创建新的数据库。使用更新后的环境变量启动一个新容器。
更新 Docker 映像
Docker 映像也可以被更新,而不仅仅是映像标签。例如,更新 MySQL 数据库服务的 Docker 映像,以使用用于 PostgreSQL 数据库的postgres
Docker 映像。如果更新成功,该命令将输出服务名称。
∼ $ docker service update --image postgres mysql
mysql
更新完成后,显示正在运行的服务任务会列出postgres
映像的新任务。服务名保持不变,Docker 映像更新为postgres
。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
hmk7128ls19a mysql.1 postgres:latest ip-172-31-13-155.ec2.internalRunning Running 18 seconds ago
5ofbkc82gp0i mysql.2 postgres:latest ip-172-31-10-132.ec2.internalRunning Running about a minute ago
v0gfc65lhw62 mysql.3 postgres:latest ip-172-31-13-155.ec2.internalRunning Running 31 seconds ago
miscjf9n66qq mysql.4 postgres:latest ip-172-31-13-155.ec2.internalRunning Running 45 seconds ago
g5viy8jyzpi1 mysql.5 postgres:latest ip-172-31-10-132.ec2.internalRunning Running about a minute ago
更新 Docker 映像并不会删除与mysql
Docker 映像相关联的环境变量,这些变量仍然列在服务细节中。
∼ $ docker service inspect mysql
[
...
"Spec": {
"Name": "mysql",
...
"ContainerSpec": {
"Env": [
"MYSQL_ROOT_PASSWORD=yes",
"MYSQL _DATABASE=mysql",
"MYSQL_PASSWORD=mysql",
"MYSQL_ALLOW_EMPTY_PASSWORD=yes"
],
...
]
需要删除为 MySQL 数据库添加的环境变量,因为 PostgreSQL 数据库 Docker 映像postgres
不使用相同的环境变量。使用docker service update
命令的--env-rm
选项删除mysql
服务中的所有环境变量。要仅删除env
变量,需要指定名称,而不是env
值。
docker service update --env-rm 'MYSQL_DATABASE' --env-rm 'MYSQL_PASSWORD' --env-rm 'MYSQL_ALLOW_EMPTY_PASSWORD' --env-rm 'MYSQL_ROOT_PASSWORD' mysql
更新容器标签
--container-label
- add
和--container-label-rm
选项用于更新服务的 Docker 容器标签。要将容器标签添加到mysql
服务中,运行一个docker service update
命令,如果成功,它将输出服务名。
∼ $ docker service update --container-label-add 'com.docker.swarm.service.version=latest' mysql
mysql
在列出服务的详细信息时,添加的标签会在ContainerSpec
标签中列出。
∼ $ docker service inspect mysql
[
...
"ContainerSpec": {
"Labels": {
"com.docker.swarm.service.version": "latest"
},
...
]
添加的标签可通过--container-label-rm
选项移除。要仅删除标签,需要指定键,而不是标签值。
∼ $ docker service update --container-label-rm 'com.docker.swarm.service.version' mysql
mysql
更新资源设置
docker service update
命令的--limit-cpu
、--limit-memory
、--reserve-cpu
和--reserve-memory
选项用于更新服务的资源设置。例如,更新资源限制和储量。如果成功,该命令将输出服务名称。
∼ $ docker service update --limit-cpu 0.5 --limit-memory 1GB --reserve-cpu "0.5" --reserve-memory "1GB" mysql
mysql
资源设置已更新。服务细节列出了Resources
JSON 对象中更新的资源设置。
∼ $ docker service inspect mysql
[
...
"ContainerSpec": {
"Resources": {
"Limits": {
"NanoCPUs": 500000000,
"MemoryBytes": 1073741824
},
"Reservations": {
"NanoCPUs": 500000000,
"MemoryBytes": 1073741824
}
},
...
]
移除服务
docker service rm
命令删除一个服务。如果该命令的输出是服务名,则该服务已被删除。所有相关的服务任务和 Docker 容器也被删除。
∼ $ docker service rm mysql
mysql
创建全球服务
如前所述,服务有两种模式——复制模式或全局模式。默认模式是复制。该模式也可通过docker service create
命令的--mode
选项明确设置为复制。创建服务后,不能更新服务模式,例如使用docker service update
命令。使用--mode
选项为nginx
创建一个复制服务。
∼ $ docker service create --mode replicated --name nginx nginx
no177eh3gxsyemb1gfzc99mmd
复制模式服务是使用默认的副本数量 1 创建的。用docker service ls
命令列出服务。nginx
服务与一个副本一起列出。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
no177eh3gxsy nginx replicated 1/1 nginx:latest
默认情况下,全局服务在集群中的每个节点上运行一个任务。有时可能需要全局服务,例如需要在每个节点上运行的代理(日志/监控)。全局服务用于第十一章中的登录。接下来,我们创建一个全球性的nginx
Docker 基于映像的服务。用docker service rm nginx
命令删除复制的服务nginx
。即使不同的服务具有不同的模式,服务名也必须是唯一的。接下来,使用与复制服务相同的命令创建一个全局模式nginx
服务,除了将--mode
选项设置为global
而不是replicated
。
∼ $ docker service create --mode global --name nginx nginx
5prj6c4v4be6ga0odnb22qa4n
创建全局模式服务。docker service ls
命令列出了服务。全局服务的REPLICAS
列没有列出副本的数量,因为没有创建副本。相反,global
被列在REPLICAS
栏中。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
5prj6c4v4be6 nginx global 3/3 nginx:latest
在任务可以运行的群中的每个节点上为全局服务创建服务任务。调度约束可以与全局服务一起使用,以防止在每个节点上运行任务。第章和第章讨论了时间安排。全球服务无法扩展。
摘要
本章介绍了在 Docker 集群上运行的 Docker 服务。服务由服务任务或副本组成。Docker Swarm 支持两种类型的服务——复制服务和全局服务。复制的服务具有指定数量的副本,并且是可伸缩的。全局服务在群中的每个节点上都有一个任务。术语“副本”在复制服务的上下文中用来指在群中的节点上运行的服务任务。复制的服务可以为一个服务运行指定数量的任务,这可能意味着在特定节点上不运行任何任务或运行多个任务。术语“副本”通常不在全局服务的上下文中使用,全局服务在集群中的每个节点上只运行一个任务。每个任务(副本)与一个 Docker 容器相关联。我们从 Hello World 服务开始,并在命令行和浏览器中使用curl
调用该服务。随后,我们讨论了 MySQL 数据库的服务。我们为 MySQL 服务容器启动了一个 bash shell,并创建了一个数据库表。扩展、更新和删除服务是本章介绍的其他一些服务功能。本章最后创建了一个全球服务。下一章将详细介绍 Docker Swarm scaling 服务。
五、扩展服务
Docker Engine 适合开发运行在彼此隔离的 Docker 容器中的轻量级应用。Docker 容器能够提供自己的网络和文件系统。
问题
Docker 引擎(在原生群模式之前)被设计用来运行必须单独启动的 Docker 容器。考虑需要创建服务的多个副本或实例的用例。随着 Docker 容器中运行的应用的客户端负载增加,该应用可能需要在多个节点上运行。Docker 引擎的一个限制是,每次为 Docker 映像启动 Docker 容器时,必须运行docker run
命令。如果 Docker 应用必须在三个节点上运行,那么docker run <img>
命令也必须在每个节点上运行,如图 5-1 所示。Docker 引擎中不提供扩展应用或运行多个副本的功能(在 Docker 1.12 原生群模式支持之前)。
图 5-1。
Docker engine without provision for scaling
解决方案
Docker 群模式提供了扩展 Docker 服务的功能。服务抽象与零个或多个副本(任务)相关联,每个任务为服务启动一个 Docker 容器。该服务可以根据需要扩大或缩小规模,以运行更多/更少的副本。只需一个docker service scale <svc>=<replicas>
命令,服务就可以运行所需数量的副本,如图 5-2 所示。如果要跨分布式集群启动 10 个服务副本,那么一个命令就能够提供扩展。
图 5-2。
Docker Swarm mode with provision for scaling
只有复制的服务才支持伸缩。全局服务在集群中的每个节点上运行一个服务任务。第三章介绍了扩展服务,在本章中,我们将讨论第三章中未讨论的扩展服务的其他一些方面。本章涵盖以下主题:
- 设置环境
- 创建复制的服务
- 扩大服务规模
- 缩小服务规模
- 移除服务
- 全球服务无法扩展
- 在同一命令中扩展多个服务
- 离开集群的节点上的服务副本替换
设置环境
在 Docker 上为 Swarm 创建一个三节点 Swarm,这在第三章讨论。你在另一章中为 AWS Swarm 创建的 Docker 可能会在本章中使用。为 Swarm manager 获取 EC2 实例的公共 IP 地址。
使用用户“docker”通过 SSH 登录到 Swarm manager EC2 实例。
[root@localhost ∼]# ssh -i "docker.pem" docker@34.200.225.39
Welcome to Docker!
命令列出了集群中的节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
ilru4f0i280w2tlsrg9hglwsj ip-172-31-10-132.ec2.internal Ready Active
w5to186ipblpcq390625wyq2e ip-172-31-37-135.ec2.internal Ready Active
zkxle7kafwcmt1sd93kh5cy5e * ip-172-31-13-155.ec2.internal Ready Active Leader
创建复制的服务
正如第四章所讨论的,Docker Swarm 模式支持两种类型的服务——全局服务和复制服务。默认为复制模式。只有复制的服务可以扩展。接下来,使用docker service create
命令为 MySQL 数据库创建一个复制服务,最初由一个副本组成,如--replicas
选项中所指定的。如果没有指定--replicas
选项,默认的副本数量也是一个。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> mysql
ndu4kwqk9ol7e7wxvv5bremr4
使用docker service ls
列出服务。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ndu4kwqk9ol7 mysql replicated 1/1 mysql:latest
由于服务副本需要一段时间(尽管只有几秒钟)才能启动,最初 0/1 个副本可能会列在REPLICAS
列中,这意味着运行一个服务副本的理想状态还没有达到。几秒钟后运行相同的命令,并且1/1 REPLICAS
应该被列为正在运行。
可选地,也可以通过设置--mode
选项来运行docker service create
命令。如果先前创建了mysql
服务,则删除该服务,并按如下方式使用--mode
选项。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --mode replicated \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> mysql
rl2s2ptgbs9z2t7fy5e63wf2j
创建的mysql
服务没有--mode
复制选项。用docker service ps mysql
列出服务副本或任务。将列出一个副本。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
yrikmh7mciv7 mysql.1 mysql: ip-172-31-13- Running Running 21 latest 155.ec2.internal seconds ago
如果省略了--replicas
选项,默认情况下会创建一个服务副本。应该提到的是,运行 MySQL 数据库的多个副本并不自动意味着它们共享数据,因此访问一个副本不会给你与另一个副本相同的数据。使用挂载共享数据将在第六章中讨论。
扩大服务规模
具有以下语法的docker service scale
命令可用于扩大/缩小服务,这改变了服务的期望状态。
docker service scale SERVICE=REPLICAS [SERVICE=REPLICAS...]
首先,将服务扩展到三个副本。
∼ $ docker service scale mysql=3
mysql scaled to 3
随后,三个任务被按计划列在群中的三个节点上。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
yrikmh7mciv7 mysql.1 mysql: ip-172-31-13- Running Running 37 latest 155.ec2.internal seconds ago
3zxmotmy6n2t mysql.2 mysql: ip-172-31-37- Running Running 7 latest 135.ec2.internal seconds ago
rdfsowttd3b9 mysql.3 mysql: ip-172-31-10- Running Running 7 latest 132.ec2.internal seconds ago
除了 manager 节点上的一个副本之外,两个 worker 节点上还各启动了一个副本。如果在管理器节点上运行docker ps
命令,那么只列出一个用于mysql
Docker 映像的 Docker 容器。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6d2161a3b282 mysql: "docker- 50 seconds ago Up 49 3306/tcp mysql.1.yrikmh7mci latest entrypoint..." seconds v7dsmql1nhdi62l
还可以使用带有--replicas
选项的docker service update
命令来扩展服务。例如,将其扩展到 50 个副本。
∼ $ docker service update --replicas=50 mysql
mysql
该服务扩展到 50 个副本,随后列出了 50 个服务任务。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
t026kjbsgzmq mysql.1 mysql:latest ip-172-31-37-135.ec2.internal Running Running 11 seconds ago
f3tx2kbe55dh mysql.2 mysql:latest ip-172-31-10-132.ec2.internal Running Running 20 seconds ago
5mzej75us115 mysql.3 mysql:latest ip-172-31-10-132.ec2.internal Running Running 13 seconds ago
wluix1b3z863 mysql.4 mysql:latest ip-172-31-13-155.ec2.internal Running Preparing 13 seconds ago
9ld8smvahk9g mysql.5 mysql:latest ip-172-31-13-155.ec2.internal Running Running 47 seconds ago
3tgw8ni5mfi1 mysql.6 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
1gm8e7pxkg0o mysql.7 mysql:latest ip-172-31-13-155.ec2.internal Running Running 46 seconds ago
iq5p2g48oagq mysql.8 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
i4yh072h1gs6 mysql.9 mysql:latest ip-172-31-13-155.ec2.internal Running Running 46 seconds ago
r1z5tgu0dg13 mysql.10 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
mekfjvxi9pds mysql.11 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
nd8f2pr4oivc mysql.12 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
xou9hztlj637 mysql.13 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
t95flokvca2y mysql.14 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
rda5shwwfmsc mysql.15 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
ibb2fk2llm3w mysql.16 mysql:latest ip-172-31-13-155.ec2.internal Running Running 47 seconds ago
st4ofpvrfaip mysql.17 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
iw4daunt6s63 mysql.18 mysql:latest ip-172-31-37-135.ec2.internal Running Running 47 seconds ago
vk4nzq7utyl2 mysql.19 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
oj59qjcy51qw mysql.20 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
wiou769z8xeh mysql.21 mysql:latest ip-172-31-10-132.ec2.internal Running Running 47 seconds ago
5exwimn64w94 mysql.22 mysql:latest ip-172-31-10-132.ec2.internal Running Running 48 seconds ago
agqongnh9uu3 mysql.23 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
ynkvjwgqqqlx mysql.24 mysql:latest ip-172-31-37-135.ec2.internal Running Running 47 seconds ago
yf87kbsn1cga mysql.25 mysql:latest ip-172-31-13-155.ec2.internal Running Running 10 seconds ago
xxqj62007cxd mysql.26 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
50ym9i8tjwd5 mysql.27 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
7btl2pga1l5o mysql.28 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
62dqj60q1ol8 mysql.29 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
psn7zl4th2zb mysql.30 mysql:latest ip-172-31-37-135.ec2.internal Running Preparing 16 seconds ago
khsj2an2f5gk mysql.31 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
rzpndzjpmuj7 mysql.32 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
9zrcga93u5fi mysql.33 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
x565ry5ugj8m mysql.34 mysql:latest ip-172-31-10-132.ec2.internal Running Running 48 seconds ago
o1os5dievj37 mysql.35 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
dritgxq0zrua mysql.36 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
n8hs01m8picr mysql.37 mysql:latest ip-172-31-37-135.ec2.internal Running Running 47 seconds ago
dk5w0qnkfb63 mysql.38 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
joii103na4ao mysql.39 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
db5hz7m2vac1 mysql.40 mysql:latest ip-172-31-13-155.ec2.internal Running Running 46 seconds ago
ghk6s12eeo48 mysql.41 mysql:latest ip-172-31-37-135.ec2.internal Running Running 45 seconds ago
jbi8aksksozs mysql.42 mysql:latest ip-172-31-13-155.ec2.internal Running Running 47 seconds ago
rx3rded30oa4 mysql.43 mysql:latest ip-172-31-37-135.ec2.internal Running Running 47 seconds ago
c3zaacke440s mysql.44 mysql:latest ip-172-31-13-155.ec2.internal Running Running 45 seconds ago
l6ppiurx4306 mysql.46 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
of06zibtlsum mysql.47 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
kgjjwlc9zmp8 mysql.48 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
rw1icgkyw61u mysql.49 mysql:latest ip-172-31-10-132.ec2.internal Running Running 46 seconds ago
j5jpl9a5jgbj mysql.50 mysql:latest ip-172-31-10-132.ec2.internal Running Running 47 seconds ago
小规模的 MySQL 数据库服务可能不会从扩展到 50 个副本中受益,但是企业规模的应用可以使用 50 个甚至更多的副本。
缩小服务规模
一项服务可以在扩大的同时缩小。一个服务甚至可以缩减到没有复制品。通过使用docker service update
或docker service scale
命令将副本数量设置为0
,可以将mysql
服务缩小到没有副本。
∼ $ docker service scale mysql=0
mysql scaled to 0
服务缩减到没有副本。没有列出正在运行的服务副本。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
实际的服务任务可能需要一段时间才能关闭,但是所有任务的期望状态都被设置为Shutdown
。
将服务扩展为无任务不会运行任何任务,但服务不会被删除。例如,mysql
服务可以再次从无任务扩大到三个任务。
∼ $ docker service scale mysql=3
mysql scaled to 3
三个服务任务开始运行。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
py7aqwy2reku mysql.1 mysql:latest ip-172-31-37-135.ec2.internal Running Running 9 seconds ago
re1l3q3iwmvo mysql.2 mysql:latest ip-172-31-37-135.ec2.internal Running Running 9 seconds ago
h7my2ucpfz3u mysql.3 mysql:latest ip-172-31-37-135.ec2.internal Running Running 9 seconds ago
移除服务
可以使用docker service rm
命令删除服务。
∼ $ docker service rm mysql
mysql
mysql
服务被删除后不再列出。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
使用docker service rm
命令可以删除多个服务。为了进行演示,您可以创建两个服务,hello-world
和nginx
。
∼ $ docker service create \
> --name hello-world \
> --publish 8080:80 \
> --replicas 2 \
> tutum/hello-world
t3msb25rc8b6xcm30k0zoh4ws
∼ $ docker service create --name nginx nginx
ncn4aqkgzrcjc8w1uorjo5jrd
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ncn4aqkgzrcj nginx replicated 1/1 nginx:latest
t3msb25rc8b6 hello-world replicated 2/2 tutum/hello-world:latest *:8080->80/tcp
随后,用一个docker service rm
命令删除这两个服务。如果命令成功,则输出删除的服务。
∼ $ docker service rm nginx hello-world
nginx
hello-world
全球服务无法扩展
全局服务在群中的每个节点上创建一个服务任务,并且不能扩展。使用docker service create
命令为 MySQL 数据库创建一个全局服务。该命令的显著区别是--mode
被设置为global
,并且不包括--replicas
选项。
∼ $ docker service create \
> --mode global \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --name mysql-global \
> mysql
nxhnrsiulymd9n4171cie9a8j
创建了全局服务,并且列出服务应该指示设置为global
的Mode
。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
nxhnrsiulymd mysql-global global 3/3 mysql:latest
在群中的每个节点上创建一个服务任务。
∼ $ docker service ps mysql-global
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
nfbmkqdh46k0 mysql-global.zkxle7kafwcmt1sd93kh5cy5e mysql:latest ip-172-31-13-155.ec2.internal Running Running 22 seconds ago
t55ba3bobwzf mysql-global.w5to186ipblpcq390625wyq2e mysql:latest ip-172-31-37-135.ec2.internal Running Running 22 seconds ago
kqg656m30lj3 mysql-global.ilru4f0i280w2tlsrg9hglwsj mysql:latest ip-172-31-10-132.ec2.internal Running Running 22 seconds ago
如果将另一个节点添加到群中,服务任务会在新节点上自动开始。
如果对全局服务运行docker service scale
命令,则服务不会扩展。相反,会输出以下消息。
∼ $ docker service scale mysql-global=5
mysql-global: scale can only be used with replicated mode
使用docker service rm
命令,可以像删除复制服务一样删除全局服务。
∼ $ docker service rm mysql-global
mysql-global
使用同一命令扩展多个服务
使用单个docker service scale
命令可以扩展多个服务。为了演示,创建两个服务:nginx
和mysql
。
∼ $ docker service create \
> --replicas 1 \
> --name nginx \
> nginx
u6i4e8eg720dwzz425inhxqrp
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --name mysql \
> mysql
1umb7e2gr68s54utujr6khjgd
请列出这两种服务。每个服务应该运行一个副本。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
1umb7e2gr68s mysql replicated 1/1 mysql:latest
u6i4e8eg720d nginx replicated 1/1 nginx:latest
用一个命令扩展nginx
服务和mysql
服务。不同的服务可以扩展到不同数量的副本。
∼ $ docker service scale mysql=5 nginx=10
mysql scaled to 5
nginx scaled to 10
mysql
服务扩展到 5 个任务,而nginx
服务扩展到 10 个副本。最初,服务的一些新任务可能还没有开始,比如nginx
服务,它只列出了 10 个任务中的 8 个正在运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
1umb7e2gr68s mysql replicated 5/5 mysql:latest
u6i4e8eg720d nginx replicated 8/10 nginx:latest
过一会儿,所有服务任务都应该被列为正在运行,如nginx
服务的10/10
所示。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
1umb7e2gr68s mysql replicated 5/5 mysql:latest
u6i4e8eg720d nginx replicated 10/10 nginx:latest
可以使用单个docker service ps
命令列出两个服务的服务任务。
∼ $ docker service ps nginx mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
f9g1tw88nppk mysql.1 mysql:latest ip-172-31-26-234.ec2.internal Running Running about a minute ago
zcl1qfdiqrvu nginx.1 nginx:latest ip-172-31-10-132.ec2.internal Running Running about a minute ago
vu4xo99xr0y4 nginx.2 nginx:latest ip-172-31-13-155.ec2.internal Running Running 40 seconds ago
xvxgfoacxjos mysql.2 mysql:latest ip-172-31-37-135.ec2.internal Running Running 41 seconds ago
yw0opq5y0x20 nginx.3 nginx:latest ip-172-31-13-155.ec2.internal Running Running 41 seconds ago
vb92hkua6eyo mysql.3 mysql:latest ip-172-31-13-155.ec2.internal Running Running 40 seconds ago
1cnqwtb24zvy nginx.4 nginx:latest ip-172-31-13-155.ec2.internal Running Running 41 seconds ago
hclu53xkosva mysql.4 mysql:latest ip-172-31-26-234.ec2.internal Running Running 40 seconds ago
2xjcw4i9xw89 nginx.5 nginx:latest ip-172-31-10-132.ec2.internal Running Running 41 seconds ago
ocvb2qctuids mysql.5 mysql:latest ip-172-31-10-132.ec2.internal Running Running 41 seconds ago
l8mlu3jpp9cx nginx.6 nginx:latest ip-172-31-10-132.ec2.internal Running Running 41 seconds ago
p84m8yh5if5t nginx.7 nginx:latest ip-172-31-37-135.ec2.internal Running Running 41 seconds ago
7yp8m7ytt7z4 nginx.8 nginx:latest ip-172-31-26-234.ec2.internal Running Running 24 seconds ago
zegs90r015nn nginx.9 nginx:latest ip-172-31-37-135.ec2.internal Running Running 41 seconds ago
qfkpvy28g1g6 nginx.10 nginx:latest ip-172-31-26-234.ec2.internal Running Running 24 seconds ago
离开群体的节点上的服务任务替换
Docker 群模式中的期望状态协调确保了如果资源可用,期望数量的副本正在运行。如果一个节点离开群,在该节点上运行的副本被调度到另一个节点上。从运行在三节点集群中每个节点上的mysql
服务副本开始,您可以让一个工作节点离开集群。
∼ docker swarm 1eave
Node left the swarm.
运行在shutdown
节点上的服务任务的替换服务任务被调度到另一个节点上。
∼ s docker service ps mysql
NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
6zu7a59ejdxip3y9oeu548hv5 mysql.l mysql ip-10-0-0-46.ec2.internal Running Running 3 minutes ago
441cuufa7sa9möeatqbiq7vi3 mysql.2 mysql ip-10-0-0-28.ec2.internal Running Running about a minute ago
blcdm8Bh6v86gl..pwp6zx3janv mysql.3 mysql ip-10-0-0-28.ec2.internal Running Running 4 seconds ago
Or3oki4acf3d6ils5iazmg425 \_ mysql.3 mysql ip-10-0-0-106.ec2.internal Shutdown Running about a minute ago
使其他工人节点也离开蜂群。另一个工作节点上的服务副本也被关闭,并被调度到群中唯一剩余的节点上。
∼ s docker service ps mysql
NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
6zu7a59ejdxip3y9oeu548hv5 mysql.1 mysql ip-10-0-0-46.ec2\. internal Running Running 5 minutes ago
dbdaxvl6lohlxrsxh5aobjxi8 mysq.2 mysql ip-10-0-0-46.ec2.internal Running Running 7 seconds ago
44tcuufa7sa9m6eatqbiq7vi3 \_ mysql.2 mysql ip-10-0-0-28.ec2.internal Shutdown Running 2 minutes ago
216iu28xh5hztm3bgtvy7ttk8 mysql.3 mysql ip-10-0-0-46.ec2.internal Running Running 7 seconds ago
blcdm88h6v86gLpwp6zx3janv \_ mysql.3 mysql ip-10-0-0-28.ec2.internal Shutdown Running about a minute ago
Or3oki4acf3d6ils5iazmg425 \_ mysql.3 mysql ip-10-0-0-106.ec2.internal Shutdown Running 2 minutes ago
如果仅列出所需状态为正在运行的复制副本,则所有复制副本都会在管理器节点上显示为正在运行。
∼s docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
6zu7a59ejdxip3y9oeu548hv5 mysql.1 mysql ip-10-0-0-46.ec2.internal Running Running 7 minutes ago
dbdaxvl6lohlxrsxh5aobjxi8 mysql.2 mysql ip-10-0-0-46.ec2.internal Running Running 2 minutes ago
216iu28xh5hztm3bgtvy7ttk8 mysql.3 mysql ip-10-0-0-46.ec2.internal Running Running 2 minutes ago
摘要
本章讨论了群模式下的服务扩展。只能扩展复制的服务,而不能扩展全局服务。一个服务可以扩展到资源可以支持的尽可能多的副本,也可以缩减到没有副本。使用同一命令可以扩展多个服务。期望状态协调确保期望数量的服务副本正在运行。下一章将介绍 Docker 服务挂载。
六、使用挂载
Swarm 中的服务任务容器可以访问从其 Docker 映像继承的文件系统。数据通过其 Docker 映像集成到 Docker 容器中。有时,Docker 容器可能需要在持久文件系统上存储或访问数据。虽然容器有一个文件系统,但是一旦容器退出,它就会被删除。为了在容器重启时存储数据,数据必须保存在容器之外的某个地方。
问题
仅存储在容器中的数据可能会导致以下问题:
- 数据不是持久的。当 Docker 容器停止时,数据将被删除。
- 数据不能与其他 Docker 容器或主机文件系统共享。
解决方案
基于单一责任原则(SRP)的模块化设计建议将数据从 Docker 容器中分离出来。Docker Swarm 模式提供了共享数据的挂载,并使数据在容器启动和关闭时保持持久。Docker Swarm 模式为服务提供了两种类型的挂载:
- 卷装载
- 绑定安装
默认值是卷装载。使用docker service create
命令的--mount
选项创建服务的挂载。
卷装载
卷装载是主机上装载到服务任务容器中的命名卷。即使在停止和删除容器后,主机上的命名卷仍然存在。可以在创建使用该卷的服务之前创建命名卷,或者可以在服务部署时创建该卷。在部署时创建的命名卷是在启动服务任务容器之前创建的。如果在服务部署时创建,并且未指定卷名,则命名卷会自动生成一个名称。图 6-1 显示了一个卷挂载的例子,其中一个在创建服务之前就存在的命名卷mysql-scripts
被挂载到目录路径/etc/mysql/scripts
下的服务任务容器中。
图 6-1。
Volume mount
服务中的每个容器都可以访问运行该容器的主机上的相同命名卷,但是该主机命名卷可以存储相同或不同的数据。
使用卷装载时,内容不会跨群集复制。例如,如果您将某些内容放入正在使用的mysql-scripts
目录,这些新文件将只能被运行在同一节点上的其他任务访问。在其他节点上运行的副本将无法访问这些文件。
绑定安装
绑定装载是要在其上计划服务任务的主机上的文件系统路径。主机文件系统路径被装载到服务任务容器的指定目录路径中。主机文件系统路径必须存在于群中的每个主机上,在创建服务之前可以在其上调度任务。如果使用节点约束将某些节点排除在服务部署之外,则绑定装载主机文件系统不必存在于这些节点上。使用绑定挂载时,请记住使用绑定挂载的服务本身是不可移植的。如果要在生产中部署服务,主机目录路径必须存在于生产集群中的每个主机上。
主机文件系统路径不必与任务容器中的目标目录路径相同。例如,在图 6-2 中,主机路径/db/mysql/data
作为绑定挂载被挂载到目录路径/etc/mysql/data
的服务容器中。默认情况下,绑定装载是读写的,但是在服务部署时可以设置为只读。服务中的每个容器都可以访问运行容器的主机上的相同目录路径,但是主机目录路径可以存储不同或相同的数据。
图 6-2。
Bind mount
群模式装载在主机上提供可共享的命名卷和文件系统路径,这些路径在服务任务启动和关闭期间保持不变。Docker 映像的文件系统仍然位于文件系统层次结构的根位置,并且只能在根文件系统内的目录路径上进行挂载。
本章涵盖以下主题:
- 设置环境
- 安装类型
- 创建命名卷
- 使用卷装载来获取有关卷的详细信息
- 删除卷
- 创建和使用绑定装载
设置环境
为基于 AWS 的群创建一个 Docker,由一个管理节点和两个工作节点组成,如第三章所述。AWS Swarm 的 Docker 将用于一种类型的挂载,即卷挂载。对于绑定挂载,在 CoreOS 实例上创建一个由一个管理器和两个工作者节点组成的三节点集群。在第二章中讨论了在 CoreOS 实例上创建蜂群。使用基于 CoreOS 的 Swarm 是因为 Docker for AWS Swarm 不支持开箱即用的绑定挂载。从 EC2 控制台获取 AWS Swarm Docker 的 manager 实例的公共 IP 地址,如图 6-3 所示。
图 6-3。
EC2 instances for Docker for AWS Swarm nodes
SSH 登录到管理器实例。
[root@localhost ∼]# ssh -i "docker.pem" docker@52.91.115.180
Welcome to Docker!
列出群体中的节点。列出了一个管理节点和两个工作节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
8ynq7exfo5v74ymoe7hrsghxh ip-172-31-33-230.ec2.internal Ready Active
o0h7o09a61ico7n1t8ooe281g * ip-172-31-16-11.ec2.internal Ready Active Leader
yzlv7c3qwcwozhxz439dbknj4 ip-172-31-25-163.ec2.internal Ready Active
创建命名卷
在服务中用作卷类型装载的命名卷可以在创建服务之前创建,也可以在部署时创建。使用以下命令语法创建新的命名卷。
docker volume create [OPTIONS] [VOLUME]
支持表 6-1 中讨论的选项。
表 6-1。
Options for the docker volume create Command for a Named Volume
| [计]选项 | 描述 | 类型 | 缺省值 | | --- | --- | --- | --- | | `--driver, -d` | 指定卷驱动程序名称 | `string` | `local` | | `--label` | 设置卷的元数据 | `value` | `[]` | | `--name` | 指定卷名 | `string` | | | `--opt, -o` | 设置驱动程序特定选项 | `value` | `map[]` |使用docker volume create
命令创建一个名为hello
的命名卷。
∼ $ docker volume create --name hello
hello
随后,使用docker volume ls
命令列出卷。除了可能存在的其他命名卷之外,还列出了hello
卷。
∼ $ docker volume ls
DRIVER VOLUME NAME
local hello
您可以使用以下命令找到有关该卷的详细信息。
docker volume inspect hello
除了卷名和驱动程序之外,还会列出卷的挂载点。
∼ $ docker volume inspect hello
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/hello/_data",
"Name": "hello",
"Options": {},
"Scope": "local"
}
]
本地驱动程序卷的范围是local
。另一个支持的范围是global
。在单个 Docker 主机上创建一个local
卷,在集群中的每个 Docker 主机上创建一个global
卷。
使用卷装载
使用带有--mount
选项的docker service create
命令中的hello
音量。表 6-2 中讨论的选项可用于绑定挂载和卷挂载。
表 6-2。
Options for Volume and Bind Mounts
| [计]选项 | 需要 | 描述 | 默认 | | `type` | 不 | 指定装载的类型。可以指定三个值之一:`volume-Mounts`是容器中的命名卷。`bind-Bind-mounts`是将一个目录或文件从主机放入一个容器中。`tmpfs-Mounts`是一只`tmpfs`变成的容器。 | `volume` | | `src`或`source` | 仅适用于`type=bind`。`type=volume`的否 | 源目录或卷。该选项对于不同类型的挂载有不同的含义。`type=volume: src`指定卷的名称。如果指定的卷不存在,则创建该卷。如果省略了`src`,则使用自动生成的名称创建命名卷,该名称在主机上是唯一的,但在群集范围内可能不是唯一的。当使用卷的容器被删除时,自动生成的命名卷也被删除。`docker service update`命令关闭任务容器并启动新的任务容器,扩展服务也是如此。`volume source`不能是绝对路径。`type=bind: src`指定要绑定挂载的目录或文件的绝对路径。目录路径必须是绝对路径,而不是相对路径。类型为`bind`的挂载需要`src`选项,如果没有指定,就会产生错误。不支持`type=tmpfs:`。 | | | `dst`或`destination`或`target` | 是 | 指定容器内的挂载路径。如果容器的文件系统中不存在该路径,Docker 引擎会在挂载绑定或卷挂载之前创建挂载路径。卷目标必须是相对路径。 | | | `readonly`或`ro` | 不 | 一个布尔值(真/假)或(1/0)来指示 Docker 引擎应该装入卷并绑定读写还是只读。如果未指定选项,引擎将装载绑定或卷读写。如果将该选项指定为 true 值、1 值或无值,则引擎会以只读方式装载卷或绑定。如果将该选项指定为值 false 或 0,则引擎会装载卷或绑定读写。 | |一些装载选项仅支持卷装载,并在表 6-3 中讨论。
表 6-3。
Options for Volume Mounts
| [计]选项 | 需要 | 描述 | 缺省值 | | --- | --- | --- | --- | | `volume-driver` | 不 | 指定用于卷的卷驱动程序插件的名称。如果在`src`中没有指定命名卷,则`volume-driver`用于创建一个命名卷。 | `local` | | `volume-` `label` | 不 | 指定要应用于卷的一个或多个逗号分隔的元数据标签。例子:`volume-label=label-1=hello-world,label-2=hello`。 | | | `volume-nocopy` | 不 | 适用于在文件和目录已经存在的装载路径的容器中装载的空卷。指定是否将挂载路径(`dst`)下容器的文件系统文件和目录复制到卷中。主机能够访问从容器复制到指定卷的文件和目录。true 或 1 值禁止将文件从容器的文件系统复制到主机卷。false 或 0 值表示允许复制。 | 真或 1 | | `volume-` `opt` | 不 | 指定在创建命名卷(如果不存在)时提供给`volume-driver`的选项。`volume-opt`选项被指定为逗号分隔的键/值对列表。例如:`volume-opt-1=option-1=value1,option-2=value2`。要装载卷类型装载的每个主机上都必须有一个命名卷。在 Swarm manager 上创建命名卷并不会在 worker 节点上创建命名卷。`volume-driver`和`volume-opt`选项用于在工作节点上创建命名卷。 | |表 6-4 中讨论的选项仅支持tmpfs
类型的安装。
表 6-4。
Options for the tmpfs Mount
| [计]选项 | 需要 | 描述 | 缺省值 | | --- | --- | --- | --- | | `tmpfs-size` | 不 | 以字节为单位的`tmpfs`挂载的大小 | Linux 的无限价值 | | `tmpfs-` `mode` | 不 | 以八进制指定`tmpfs`的文件模式 | `1777`在 Linux 中 |接下来,我们将在用 Docker 映像tutum/hello-world
创建的服务中使用命名卷hello
。在下面的docker service create
命令中,--mount
选项将src
指定为hello
,并包含一些卷的volume-label
标签。
∼ $ docker service create \
--name hello-world \
--mount src=hello,dst=/hello,volume-label="msg=hello",volume-label="msg2=world" \
--publish 8080:80 \
--replicas 2 \
tutum/hello-world
创建服务并输出服务 ID。
∼ $ docker service create \
> --name hello-world \
> --mount src=hello,dst=/hello,volume-label="msg=hello",volume-label="msg2=world" \
> --publish 8080:80 \
> --replicas 2 \
> tutum/hello-world
8ily37o72wyxkyw2jt60kdqoz
创建了两个服务副本。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
8ily37o72wyx hello-world replicated 2/2 tutum/hello-world:latest *:8080->80/tcp
∼ $ docker service ps hello-world
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
uw6coztxwqhf hello-world.1 tutum/hello-world:latest ip-172-31-25-163.ec2.internalRunning Running 20 seconds ago
cfkwefwadkki hello-world.2 tutum/hello-world:latest ip-172-31-16-11.ec2.internalRunning Running 21 seconds ago
命名的卷安装在服务的每个任务容器中。
服务定义列出了装载,包括装载标签。
∼ $ docker service inspect hello-world
[
...
"Spec": {
"ContainerSpec": {
"Image": "tutum/hello-world:latest@sha256:0d57def8055178aafb4c7669cbc25ec17f0acdab97cc587f30150802da8f8d85",
"Mounts": [
{
"Type": "volume",
"Source": "hello",
"Target": "/hello",
"VolumeOptions": {
"Labels": {
"msg": "hello",
"msg2": "world"
},
...
]
在前面的示例中,在卷装入中使用卷之前,创建了一个命名卷。又如,在部署时创建一个命名卷。在下面的docker service create
命令中,--mount
选项被设置为type=volume
,源被设置为nginx-root
。创建服务之前,命名卷nginx-root
不存在。
∼ $ docker service create \
> --name nginx-service \
> --replicas 3 \
> --mount type=volume,source="nginx-root",destination="/var/lib/nginx",volume-
label="type=nginx root dir" \
> nginx:alpine
rtz1ldok405mr03uhdk1htlnk
运行该命令时,会创建一个服务。服务描述包括mounts
中的卷挂载。
∼ $ docker service inspect nginx-service
[
...
"Spec": {
"Name": "nginx-service",
...
"Mounts": [
{
"Type": "volume",
"Source": "nginx-root",
"Target": "/var/lib/nginx",
"VolumeOptions": {
"Labels": {
"type": "nginx root dir"
},
...
]
命名卷nginx-root
不是在创建服务之前创建的,因此是在为服务任务启动容器之前创建的。命名卷nginx-root
仅在计划任务的节点上创建。三个节点中的每一个都计划了一个服务任务。
∼ $ docker service ps nginx-service
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
pfqinizqmgur nginx-service.1 nginx:alpine ip-172-31-33-230.ec2.internalRunning Running 19 seconds ago
mn8h3p40chgs nginx-service.2 nginx:alpine ip-172-31-25-163.ec2.internalRunning Running 19 seconds ago
k8n5zzlnn46s nginx-service.3 nginx:alpine ip-172-31-16-11.ec2.internalRunning Running 18 seconds ago
当在管理器节点上调度任务时,在管理器节点上创建一个名为nginx-root
的命名卷,如docker volume ls
命令的输出中所列。
∼ $ docker volume ls
DRIVER VOLUME NAME
local hello
local nginx-root
服务任务和任务容器在两个工作节点的每一个上启动。在每个工作节点上创建一个nginx-root
命名卷。列出工作节点上的卷会列出nginx-root
卷。
[root@localhost ∼]# ssh -i "docker.pem" docker@34.229.86.64
Welcome to Docker!
∼ $ docker volume ls
DRIVER VOLUME NAME
local hello
local nginx-root
[root@localhost ∼]# ssh -i "docker.pem" docker@52.91.200.241
Welcome to Docker!
∼ $ docker volume ls
DRIVER VOLUME NAME
local hello
local nginx-root
在前面的示例中,在src
中指定了一个命名卷。在下面的服务定义中,可以省略指定的卷。
∼ $ docker service create \
> --name nginx-service-2 \
> --replicas 3 \
> --mount type=volume,destination=/var/lib/nginx \
> nginx:alpine
q8ordkmkwqrwiwhmaemvcypc3
服务是用一个副本创建的,并在每个群节点上进行调度。
∼ $ docker service ps nginx-service-2
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
kz8d8k6bxp7u nginx-service-2.1 nginx:alpine ip-172-31-25-163.ec2.internalRunning Running 27 seconds ago
wd65qsmqixpg nginx-service-2.2 nginx:alpine ip-172-31-16-11.ec2.internalRunning Running 27 seconds ago
mbnmzldtaaed nginx-service-2.3 nginx:alpine ip-172-31-33-230.ec2.internalRunning Running 26 seconds ago
服务定义没有列出命名卷。
∼ $ docker service inspect nginx-service-2
[
"Spec": {
"Name": "nginx-service-2",
"ContainerSpec": {
"Mounts": [
{
"Type": "volume",
"Target": "/var/lib/nginx"
}
],
...
]
如果没有明确指定卷名,则会创建具有自动生成名称的命名卷。在运行服务任务的每个节点上,都会创建一个具有自动生成名称的自动生成的命名卷。manager 节点上列出的一个命名卷是自动生成的命名卷,具有自动生成的名称。
∼ $ docker volume ls
DRIVER VOLUME NAME
local 305f1fa3673e811b3b320fad0e2dd5786567bcec49b3e66480eab2309101e233
local hello
local nginx-root
作为在服务中使用命名卷作为挂载的另一个例子,为 MySQL 数据库服务创建一个名为mysql-scripts
的命名卷。
∼ $ docker volume create --name mysql-scripts
mysql-scripts
命名的卷已创建并列出。
∼ $ docker volume ls
DRIVER VOLUME NAME
local 305f1fa3673e811b3b320fad0e2dd5786567bcec49b3e66480eab2309101e233
local hello
local mysql-scripts
local nginx-root
卷描述将范围列为local
,并列出挂载点。
∼ $ docker volume inspect mysql-scripts
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/mysql-scripts/_data",
"Name": "mysql-scripts",
"Options": {},
"Scope": "local"
}
]
接下来,创建一个在卷装载中使用命名卷的服务。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --mount type=volume,src="mysql-scripts",dst="/etc/mysql/scripts",
el="msg=mysql",volume-label="msg2=scripts" \
> --publish 3306:3306\
> --replicas 2 \
> --name mysql \
> mysql
cghaz4zoxurpyqil5iknqf4c1
服务被创建并列出。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
8ily37o72wyx hello-world replicated 2/2 tutum/hello-world:latest *:8080->80/tcp
cghaz4zoxurp ysql replicated 1/2 mysql:latest *:3306->3306/tcp
列出服务任务表明任务被安排在管理器节点和一个工作者节点上。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
y59yhzwch2fj mysql.1 mysql:latest ip-172-31-33-230.ec2.internalRunning Preparing 12 seconds ago
zg7wrludkr84 mysql.2 mysql:latest ip-172-31-16-11.ec2.internalRunning Running less than a second ago
命名卷的目标目录在 Docker 容器中创建。管理节点上的 Docker 容器可以用docker ps
列出,容器上的 bash shell 可以用docker exec -it <containerid> bash
命令启动。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a855826cdc75 mysql:latest "docker-entrypoint..." 22 seconds ago Up 21 seconds 3306/tcp mysql.2.zg7wrludkr84zf8vhdkf8wnlh
∼ $ docker exec -it a855826cdc75 bash
root@a855826cd75:/#
将目录更改为容器中的/etc/mysql/scripts
。最初,目录是空的。
root@a855826cdc75:/# cd /etc/mysql/scripts
root@a855826cdc75:/etc/mysql/scripts# ls -l
total 0
root@a855826cdc75:/etc/mysql/scripts# exit
exit
在工作者节点之一上创建服务的任务容器,并且可以在工作者节点上列出该任务容器。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eb8d59cc2dff mysql:latest "docker-entrypoint..." 8 minutes ago Up 8 minutes 3306/tcp mysql.1.xjmx7qviihyq2so7n0oxi1muq
在 worker 节点上为 Docker 容器启动一个 bash shell。在 Docker 容器中创建了挂载指定卷的/etc/mysql/scripts
目录。
∼ $ docker exec -it eb8d59cc2dff bash
root@eb8d59cc2dff:/# cd /etc/mysql/scripts
root@eb8d59cc2dff:/etc/mysql/scripts# exit
exit
如果使用自动生成的命名卷的服务被扩展为在先前未运行任务的节点上运行任务,则命名卷也会在这些节点上自动生成。作为在服务中使用自动生成的命名卷作为挂载时发现扩展服务的效果的示例,创建一个带有卷挂载的 MySQL 数据库服务。在创建服务之前,卷mysql-scripts
不存在;移除mysql-scripts
卷(如果存在)。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --mount type=volume,src="mysql-scripts",dst="/etc/mysql/scripts"\
> --name mysql \
> mysql
088ddf5pt4yb3yvr5s7elyhpn
服务任务被安排在一个节点上。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xlix91njbaq0 mysql.1 mysql:latest ip-172-31-13-122.ec2.internalRunning Preparing 12 seconds ago
列出节点;安排服务任务的节点是管理器节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
o5hyue3hzuds8vtyughswbosl ip-172-31-11-41.ec2.internal Ready Active
p6uuzp8pmoahlcwexr3wdulxv ip-172-31-23-247.ec2.internal Ready Active
qnk35m0141lx8jljp87ggnsnq * ip-172-31-13-122.ec2.internal Ready Active Leader
在调度任务的 manager 节点上创建一个命名卷mysql-scripts
和一个具有自动生成名称的辅助命名卷。
∼ $ docker volume ls
DRIVER VOLUME NAME
local a2bc631f1b1da354d30aaea37935c65f9d99c5f084d92341c6506f1e2aab1d55
local mysql-scripts
工作节点没有列出mysql-scripts
命名卷,因为工作节点上没有调度任务。
∼ $ docker volume ls
DRIVER VOLUME NAME
将服务扩展到三个副本。在三个节点中的每一个节点上都安排了一个复制副本。
∼ $ docker service scale mysql=3
mysql scaled to 3
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xlix91njbaq0 mysql.1 mysql:latest ip-172-31-13-122.ec2.internal Running Running about a minute ago
ifk7xuvfp9p2 mysql.2 mysql:latest ip-172-31-23-247.ec2.internal Running Running less than a second ago
3c53fxgcjqyt mysql.3 mysql:latest ip-172-31-11-41.ec2.internal Running Running less than a second ago
由于计划了一个副本,因此在工作节点上创建了一个命名卷mysql-scripts
和一个具有自动生成名称的辅助命名卷。
[root@localhost ∼]# ssh -i "docker.pem" docker@54.165.69.9
Welcome to Docker!
∼ $ docker volume ls
DRIVER VOLUME NAME
local 431a792646d0b04b5ace49a32e6c0631ec5e92f3dda57008b1987e4fe2a1b561
local mysql-scripts
[root@localhost ∼]# ssh -i "docker.pem" docker@34.232.95.243
Welcome to Docker!
∼ $ docker volume ls
DRIVER VOLUME NAME
local afb2401a9a916a365304b8aa0cc96b1be0c161462d375745c9829f2b6f180873
local mysql-scripts
自动生成的命名卷是永久性的,不会在服务复制副本关闭时被删除。具有自动生成名称的命名卷不是永久卷。例如,将服务扩展回一个副本。两个复制副本关闭,包括管理器节点上的复制副本。
∼ $ docker service scale mysql=1
mysql scaled to 1
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
3c53fxgcjqyt mysql.3 mysql:latest ip-172-31-11-41.ec2.internal Running Running 2 minutes ago
但是管理器节点上的命名卷mysql-scripts
不会被删除,即使没有使用该卷的 Docker 容器正在运行。
∼ $ docker volume ls
DRIVER VOLUME NAME
local mysql-scripts
类似地,关闭服务副本的工作节点上的命名卷也不会被删除,即使没有使用该命名卷的 Docker 容器正在运行。当没有容器在使用时,具有自动生成名称的命名卷会被删除,但mysql-scripts
命名卷不会被删除。
移除卷mysql-scripts
仍未移除。
∼ $ docker service rm mysql
mysql
∼ $ docker volume ls
DRIVER VOLUME NAME
local mysql-scripts
删除卷
可以使用以下命令删除命名卷。
docker volume rm <VOL>
例如,删除命名卷mysql-scripts
。
∼ $ docker volume rm mysql-scripts
mysql-scripts
如果您尝试删除的卷在 Docker 容器中使用,则会生成一个错误,并且该卷不会被删除。如果正在容器中使用,即使是具有自动生成名称的命名卷也不能删除。
创建和使用绑定装载
在本节中,我们将创建一个 bind 类型的装载。如果需要从 Docker 容器中访问主机上已经存在的目录中的数据,则适合使用绑定装载。当创建具有绑定类型装载的服务时,必须使用--mount
选项指定type=bind
。主机源目录和卷目标必须都是绝对路径。创建服务之前,主机源目录必须存在。服务的每个 Docker 容器中的目标目录是自动创建的。在管理器节点上创建一个目录,然后向该目录添加一个名为createtable.sql
的文件。
core@ip-10-0-0-143 ∼ $ sudo mkdir -p /etc/mysql/scripts
core@ip-10-0-0-143 ∼ $ cd /etc/mysql/scripts
core@ip-10-0-0-143 /etc/mysql/scripts $ sudo vi createtable.sql
在示例 SQL 文件中保存一个 SQL 脚本,如图 6-4 所示。
图 6-4。
Adding a SQL script to the host directory
类似地,创建一个目录并向 worker 节点添加一个 SQL 脚本。
使用使用主机目录的绑定装载创建服务。目标目录被指定为/scripts
。
core@ip-10-0-0-143 ∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql' \
> --replicas 3 \
> --mount type=bind,src="/etc/mysql/scripts",dst="/scripts" \
> --name mysql \
> mysql
0kvk2hk2qigqyeem8x1r8qkvk
从调度任务的节点启动服务容器的 bash shell。列出目标目录/scripts
。
core@ip-10-0-0-143 ∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e71275e6c65c mysql:latest "docker-entrypoint.sh" 5 seconds ago Up 4 seconds 3306/tcp mysql.1.btqfrx7uffym2xvc441pubaza
core@ip-10-0-0-143 ∼ $ docker exec -it e71275e6c65c bash
root@e71275e6c65c:/# ls -l
drwxr-xr-x. 2 root root 4096 Jul 24 20:44 scripts
将目录(cd
)更改为目标挂载路径/scripts
。在绑定挂载的目标挂载路径中列出了createtable.sql
脚本。
root@e71275e6c65c:/# cd /scripts
root@e71275e6c65c:/scripts# ls -l
-rw-r--r--. 1 root root 1478 Jul 24 20:44 createtable.sql
每个服务任务 Docker 容器在主机上都有自己的文件副本。因为默认情况下,挂载是read-write
,所以挂载路径中的文件可能会被修改或删除。例如,从容器中删除createtable.sql
脚本。
core@ip-10-0-0-137 ∼ $ docker exec -it 995b9455aff2 bash
root@995b9455aff2:/# cd /scripts
root@995b9455aff2:/scripts# ls -l
total 8
-rw-r--r--. 1 root root 1478 Jul 24 20:45 createtable.sql
root@995b9455aff2:/scripts# rm createtable.sql
root@995b9455aff2:/scripts# ls -l
total 0
root@995b9455aff2:/scripts#
如前所述,通过在--mount
arg 中包含一个额外的选项,可以将挂载设置为只读。为了演示a readonly
挂载,首先删除已经运行的mysql
服务。创建一个服务并使用与之前相同的命令挂载一个readonly
绑定,除了包含一个额外的readonly
选项。
core@ip-10-0-0-143 ∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql' \
> --replicas 3 \
> --mount type=bind,src="/etc/mysql/scripts",dst="/scripts",readonly \
> --name mysql \
> mysql
c27se8vfygk2z57rtswentrix
安装了类型为readonly
的mount
活页夹。
访问调度任务的节点上的容器,并列出主机目录中的示例脚本。
core@ip-10-0-0-143 ∼ $ docker exec -it 3bf9cf777d25 bash
root@3bf9cf777d25:/# cd /scripts
root@3bf9cf777d25:/scripts# ls -l
-rw-r--r--. 1 root root 1478 Jul 24 20:44 createtable.sql
删除或尝试删除示例脚本。会产生一个错误。
root@3bf9cf777d25:/scripts# rm createtable.sql
rm: cannot remove 'createtable.sql': Read-only file system
摘要
本章介绍了蜂群模式下的坐骑。支持两种类型的装载—绑定装载和卷装载。绑定装载将预先存在的目录或文件从主机装载到服务的每个容器中。卷挂载将命名卷挂载到服务中的每个容器中,该命名卷在创建服务之前可能存在,也可能不存在。下一章讨论配置资源。
七、配置资源
Docker 容器在底层操作系统内核上独立运行,运行时需要资源。Docker Swarm 模式支持两种类型的资源——CPU 和内存——如图 7-1 所示。
图 7-1。
Types of resources supported by Docker Swarm mode
问题
默认情况下,Docker Swarm 模式对一个服务任务可以消耗多少资源(CPU 周期或内存)没有任何限制。Swarm 模式也不能保证最少的资源。如果在 Docker Swarm 模式下没有指定资源配置,会导致两个问题。
一些服务任务可能消耗不成比例的资源量,而其他服务任务由于缺乏资源而不能被调度。例如,考虑一个具有 3GB 资源容量和 3 个 CPU 的节点。在没有任何资源保证和限制的情况下,一个服务任务容器可以消耗大部分资源(2.8GB 和 2.8 CPUs),而另外两个服务任务容器每个只剩下 0.1GB 和 0.1 CPU 的资源可以使用,并且没有得到调度,如图 7-2 所示。没有足够资源进行调度的 Docker 服务任务被置于Pending
状态。
图 7-2。
Unequal allocation of resources
可能导致的第二个问题是,节点的资源容量可能被完全用尽,而没有任何供应来调度任何更多的服务任务。例如,一个资源容量为 9GB 和 9 个 CPU 的节点运行着三个服务任务容器,每个容器使用 3GB 和 3 个 CPU,如图 7-3 所示。如果为同一个或另一个服务创建了新的服务任务,它在节点上没有任何可用的资源。
图 7-3。
Fully resource-utilized node
解决方案
Docker Swarm 模式具有设置资源保证(或储备)和资源限制的规定,如图 7-4 所示。资源储备是为服务任务保证或保留的最小资源量。资源限制是服务任务可以使用的最大资源量,而不管有多少资源可用。
图 7-4。
Managing Swarm resources with resource reserves and limits
有了资源储备,在前面讨论的问题中,每个服务任务容器可以保证 1 个 CPU 和 1GB,如图 7-5 所示。
图 7-5。
Resource allocation with resource reserves set
并且,如果对服务任务容器实施资源限制,则多余的资源将可用于启动新的服务任务容器。在前面讨论的示例中,每个服务任务 2GB 和 2 个 CPU 的限制将使 3GB 和 3 个 CPU 的额外资源可用于新的服务任务容器,如图 7-6 所示。
图 7-6。
Resource allocation with resource limits set
本章涵盖以下主题:
- 设置环境
- 创建没有资源规范的服务
- 保留资源
- 设置资源限制
- 使用资源规范创建服务
- 扩展和资源
- 保留资源必须小于资源限制
- 设置资源限制和储备的滚动更新
- 资源使用和节点容量
设置环境
在 Docker 上为 AWS 创建一个三节点 Swarm,其中有一个管理节点和两个工作节点。在 Docker 上为 AWS 创建一个 Swarm 将在第三章中讨论。我们在本章中也使用了在第六章中创建的三节点蜂群。获取 Swarm manager 实例的公共 IP 地址,如图 7-7 所示。
图 7-7。
EC2 instances for Swarm nodes
以用户“docker”的身份通过 SSH 登录 manager 实例。
[root@localhost ∼]# ssh -i "docker.pem" docker@52.91.115.180
Welcome to Docker!
列出群节点;列出了一个管理节点和两个工作节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
8ynq7exfo5v74ymoe7hrsghxh ip-172-31-33-230.ec2.internal Ready Active
o0h7o09a61ico7n1t8ooe281g * ip-172-31-16-11.ec2.internal Ready Active Leader
yzlv7c3qwcwozhxz439dbknj4 ip-172-31-25-163.ec2.internal Ready Active
创建没有资源规范的服务
我们首先创建一个没有任何资源规范的服务。创建一个 MySQL 数据库服务,不设置任何资源储备或限制。
docker service create \
--env MYSQL_ROOT_PASSWORD='mysql'\
--replicas 1 \
--name mysql \
mysql
将创建一个服务副本。该命令的输出是服务 ID(以斜体显示)。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> mysql
2kcq6cf72t4wu94o00k3sax41
列出服务;列出了mysql
服务。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
2kcq6cf72t4w mysql replicated 1/1 mysql:latest
列出服务任务。唯一的服务任务正在工作节点上运行。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
sccqv4k9r22h mysql.1 mysql:latest ip-172-31-33-230.ec2.internal Running Running 10 seconds ago
在检查服务时,容器规范不包括任何资源、限制或保留。单个服务任务可能会使用调度它的节点上的所有可用资源。
∼ $ docker service inspect mysql
[
"Resources": {
"Limits": {},
"Reservations": {}
},
]
保留资源
Swarm 模式在docker service create
和docker service update
命令中提供了两个资源预留选项,如表 7-1 所示。
表 7-1。
Options for Resource Reserves
| [计]选项 | 描述 | 缺省值 | | --- | --- | --- | | `--reserve-cpu` | 储备 CPU。值`0.000`意味着没有设置储备。 | `0.000` | | `--reserve-memory` | 保留记忆。值`0`意味着没有设置储备。 | `0` |设置资源限制
群模式在docker service create
和docker service update
命令中提供了两个资源限制选项,如表 7-2 中所述。
表 7-2。
Options for Resource Limits
| [计]选项 | 描述 | 缺省值 | | --- | --- | --- | | `--limit-cpu` | 限制 CPU | `0.000` | | `--limit-memory` | 限制内存 | `0` |使用资源规范创建服务
接下来,使用资源规范创建一个服务。设置 0.25 个 CPU 和 128MB 的资源预留,以及 1 个 CPU 和 256MB 的资源限制。在创建定义了资源的新服务之前,删除先前创建的mysql
服务。该命令的输出是服务 ID(以斜体显示)。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> --reserve-cpu .25 --limit-cpu 1 --reserve-memory 128mb --limit-memory 256mb \
> mysql
abwq9budo7joyd00u32z2b047
在检查服务时,会列出资源限制和保留,这与创建服务时没有资源定义的空资源设置形成对比。
∼ $ docker service inspect mysql
[
"Resources": {
"Limits": {
"NanoCPUs": 1000000000,
"MemoryBytes": 268435456
},
"Reservations": {
"NanoCPUs": 250000000,
"MemoryBytes": 134217728
}
},
]
扩展和资源
在纵向扩展服务之前,根据 CPU 和内存资源确定节点容量可能是合适的。因为群中的所有三个节点都是相同的,所以一个节点上的节点容量与其他节点上的相同。节点容量是 1 个 CPU 和 1GB,如docker node inspect
命令的输出中所列。
∼ $ docker node inspect ip-172-31-16-11.ec2.internal
[
"Resources": {
"NanoCPUs": 1000000000,
"MemoryBytes": 1039040512
},
]
上一节中创建的每个服务任务的 CPU 限制也是 1 个 CPU。扩展时,节点上所有服务任务的资源限制总数可能会超过节点的容量。但是,资源预留总量不得超过节点容量。
例如,扩展到五个副本。
∼ $ docker service scale mysql=5
mysql scaled to 5
扩展到五个时,管理节点上有两个副本,一个工作节点上有两个副本,另一个工作节点上有一个副本。超过了工作节点上的资源限制的总和,但是资源保留的总和在节点的容量之内。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
npc5r7xf98fg mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 2 minutes ago
xokdhowntp0w mysql.2 mysql:latest ip-172-31-25-163.ec2.internal Running Running 13 seconds ago
b6h4bsf7xzdc mysql.3 mysql:latest ip-172-31-16-11.ec2.internal Running Running 12 seconds ago
j1d7ti7nb80u mysql.4 mysql:latest ip-172-31-33-230.ec2.internal Running Running 13 seconds ago
w6to9pxcdbm5 mysql.5 mysql:latest ip-172-31-25-163.ec2.internal Running Running 13 seconds ago
保留资源不得超过资源限制
在计划服务任务时,不考虑资源限制,只考虑资源储备。如果运行任务所需的资源在节点容量范围内,则不设置预留(无论是否设置限制以及限制是否超过节点容量)会调度服务任务。资源储备不得超过资源限制,否则服务任务可能无法调度,或者可能会在一段时间后失败。例如,删除mysql
服务,并在资源储备超过资源限制的情况下创建一个新服务。该命令的输出是服务 ID(以斜体显示)。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> --reserve-cpu .75 --limit-cpu .5 --reserve-memory 256mb --limit-memory 128mb \
> mysql
srot5vr8x7v7iml2awc3fxb1u
服务被创建,甚至被调度。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
pmcjrj6p3wfp mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 20 seconds ago
服务配置的资源储备超过了资源限制。
∼ $ docker service inspect mysql
[
},
"Resources": {
"Limits": {
"NanoCPUs": 500000000,
"MemoryBytes": 134217728
},
"Reservations": {
"NanoCPUs": 750000000,
"MemoryBytes": 268435456
}
},
]
资源储备在节点容量之内,但是因为资源限制小于资源储备,所以新启动的服务任务失败并被关闭。服务任务不断重启和关闭。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
vjcnjkwfdfkb mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 16 seconds ago
pxdku8pxviyn \_ mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Shutdown Failed 21 seconds ago "task: non-zero exit (1)"
pmcjrj6p3wfp \_ mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Shutdown Failed about a minute ago "task: non-zero exit (1)"
服务任务资源限制可以与资源储备相同。删除mysql
服务,并使用与资源保留相同的资源限制重新创建它。该命令的输出是服务 ID(以斜体显示)。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> --reserve-cpu .5 --limit-cpu .5 --reserve-memory 256mb --limit-memory 256mb \
> mysql
81bu63v97p9rm81xfyxv9k11e
创建服务并调度单个任务。当资源储备超过资源限制时,服务任务不会失败。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
4i1fpha53abs mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 33 seconds ago
并启动 Docker 容器。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
14d5553f0393 mysql:latest "docker-entrypoint..." 34 seconds ago Up 33 seconds 3306/tcp mysql.1.4i1fpha53absl4qky9dgafo8t
滚动更新以修改资源限额和储量
本节演示了设置新的 CPU 和内存限制和预留的滚动更新。在上一节中创建的服务用于本节中的更新。使用docker service update
命令,更新 CPU 和内存预留和限制。该命令的输出是服务名mysql
(以斜体显示)。
∼ $ docker service update --reserve-cpu 1 --limit-cpu 2 --reserve-memory 256mb
--limit-memory 512mb mysql
mysql
资源被更新。更新服务的资源规范会关闭服务副本,并使用新的资源规范启动新的副本。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
81bu63v97p9r mysql replicated 1/1 mysql:latest
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xkis4mirgbtv mysql.1 mysql:latest ip-172-31-33-230.ec2.internal Running Running 14 seconds ago
4i1fpha53abs \_ mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Shutdown Shutdown 15 seconds ago
服务资源配置已更新。
∼ $ docker service inspect mysql
[
},
"Resources": {
"Limits": {
"NanoCPUs": 2000000000,
"MemoryBytes": 536870912
},
"Reservations": {
"NanoCPUs": 1000000000,
"MemoryBytes": 268435456
}
},
]
资源使用和节点容量
资源使用不能超过节点容量。在三节点 Swarm 上(一个管理器和两个工作者节点),回想一下节点容量是 1GB 和 1 个 CPU。
删除已经运行的mysql
服务,并创建一个mysql
服务,它有三个请求 4GB 内存的副本。服务已创建。该命令的输出是服务 ID(以斜体显示)。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 3 \
> --name mysql \
> --reserve-memory=4GB\
> mysql
cgrihwij2znn4jkfe6hswxgr7
如0/3
的Replicas
列值所示,没有服务副本被调度,因为请求的容量大于单个节点的节点容量。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
cgrihwij2znn mysql replicated 0/3 mysql:latest
副本的Current State
列为Pending
。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
vm7z20krx3j6 mysql.1 mysql:latest Running Pending 19 seconds ago
exmsheo144ef mysql.2 mysql:latest Running Pending 19 seconds ago
kiset9poqz2s mysql.3 mysql:latest Running Pending 19 seconds ago
如果之前运行所有副本的服务被纵向扩展,一些或所有副本可能会被取消调度。如果运行新复制副本所需的资源超过了可用的节点容量,就会发生这种情况。例如,删除mysql
服务并创建一个新的mysql
服务,其资源设置在一个节点的供应中。该命令的输出是服务 ID(以斜体显示)。
∼ $ docker service rm mysql
mysql
∼ $
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> --reserve-cpu .5 --reserve-memory 512mb \
> mysql
ysef8n02mhuwa7sxerc9jwjqx
服务被创建,单个副本正在运行,如1/1
的Replicas
列值所示。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 1/1 mysql:latest
逐步扩展服务,以确定是否所有服务副本都已安排。首先,扩展到三个副本。
∼ $ docker service scale mysql=3
mysql scaled to 3
服务描述将3/3 Replicas
列为正在运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 3/3 mysql:latest
使用分散调度策略来调度服务副本,群中的每个节点上有一个副本,这将在第八章中详细讨论。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
8kkkdns0l690 mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 51 seconds ago
k209uge36bih mysql.2 mysql:latest ip-172-31-25-163.ec2.internal Running Running 16 seconds ago
oiublpclz9eu mysql.3 mysql:latest ip-172-31-33-230.ec2.internal Running Running 16 seconds ago
将 mysql 服务进一步扩展到副本。
∼ $ docker service scale mysql=10
mysql scaled to 10
只有 3/10 的副本被列为正在运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 3/10 mysql:latest
有些副本是Allocated
的,但由于资源不足,没有计划在任何节点上运行。没有运行的服务副本被列出,其中Current State
被设置为Pending
。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
8kkkdns0l690 mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running about a minute ago
k209uge36bih mysql.2 mysql:latest ip-172-31-25-163.ec2.internal Running Running 35 seconds ago
oiublpclz9eu mysql.3 mysql:latest ip-172-31-33-230.ec2.internal Running Running 35 seconds ago
u807b7h0qvqc mysql.4 mysql:latest Running Pending 7 seconds ago
jh2ep10sonxy mysql.5 mysql:latest Running Pending 7 seconds ago
8d19osxa4fwf mysql.6 mysql:latest Running Pending 7 seconds ago
k8hba8j5o9vi mysql.7 mysql:latest Running Pending 7 seconds ago
ettk65bpin3b mysql.8 mysql:latest Running Pending 7 seconds ago
i3otbqfsfvr7 mysql.9 mysql:latest Running Pending 7 seconds ago
sxdi970o6d3b mysql.10 mysql:latest Running Pending 7 seconds ago
添加一个或多个新的工作节点可能会使服务协调其所需的状态,并导致所有副本运行。为了进行下一步演示,我们扩展了 CloudFormation 栈,以增加工作节点的数量。
向上扩展栈
要纵向扩展 CloudFormation 栈,请在 CloudFormation ➤栈表中选择 Docker 栈,然后选择操作➤更新栈,如图 7-8 所示。
图 7-8。
Choosing Actions ➤ Update Stack
“更新 Docker 栈向导”启动。它类似于创建栈向导。在选择模板中,单击下一步,不修改任何设置。具体来说,增加集群工作节点的数量?至 10,如图 7-9 所示。点击下一步。
图 7-9。
Increasing the number of worker nodes to 10
在预览你的修改时,点击更新,如图 7-10 所示。
图 7-10。
Click Update to preview your changes
更新完成后,栈状态变为UPDATE_COMPLETE
,如图 7-11 所示。
图 7-11。
Stack update is complete
蜂群获得 8 个新的工作者节点,总共 10 个工作者节点。定期(间隔几秒钟后)列出服务描述,随着新工作节点的创建,新的副本开始将当前状态与所需状态相协调。Replicas
列中的副本数量在几秒钟内逐渐增加。如服务列表中的10/10
所示,mysql
服务的所有副本开始运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 3/10 mysql:latest
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 6/10 mysql:latest
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 9/10 mysql:latest
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 10/10 mysql:latest
列出服务副本会将所有副本列为Running
。先前的Pending
副本被安排在新节点上。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
8kkkdns0l690 mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 7 minutes ago
k209uge36bih mysql.2 mysql:latest ip-172-31-25-163.ec2.internal Running Running 6 minutes ago
oiublpclz9eu mysql.3 mysql:latest ip-172-31-33-230.ec2.internal Running Running 6 minutes ago
u807b7h0qvqc mysql.4 mysql:latest ip-172-31-11-105.ec2.internal Running Running 45 seconds ago
jh2ep10sonxy mysql.5 mysql:latest ip-172-31-13-141.ec2.internal Running Running about a minute ago
8d19osxa4fwf mysql.6 mysql:latest ip-172-31-24-10.ec2.internal Running Running about a minute ago
k8hba8j5o9vi mysql.7 mysql:latest ip-172-31-0-114.ec2.internal Running Running 55 seconds ago
ettk65bpin3b mysql.8 mysql:latest ip-172-31-5-127.ec2.internal Running Running about a minute ago
i3otbqfsfvr7 mysql.9 mysql:latest ip-172-31-35-209.ec2.internal Running Running 24 seconds ago
sxdi970o6d3b mysql.10 mysql:latest ip-172-31-21-57.ec2.internal Running Running 49 seconds ago
如果再次更新栈以减少工作节点的数量,一些副本将关闭并取消调度。在减少工作节点的数量后,Replicas
列只列出了正在运行的5/10
个副本。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ysef8n02mhuw mysql replicated 5/10 mysql:latest
一些服务任务被列为Shutdown
,因为一些工作者节点已经从群中移除。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
8kkkdns0l690 mysql.1 mysql:latest ip-172-31-16-11.ec2.internal Running Running 10 minutes ago
ulknt3e5zxy1 mysql.2 mysql:latest Ready Pending 3 seconds ago
k209uge36bih \_ mysql.2 mysql:latest ip-172-31-25-163.ec2.internal Shutdown Running 14 seconds ago
oiublpclz9eu mysql.3 mysql:latest ip-172-31-33-230.ec2.internal Running Running 9 minutes ago
mh2fpioi441k mysql.4 mysql:latest Running Pending 3 seconds ago
u807b7h0qvqc \_ mysql.4 mysql:latest v53huw84hskqsb3e8o0a2pmun Shutdown Running about a minute ago
jzghd72nk0zc mysql.5 mysql:latest Ready Pending 3 seconds ago
jh2ep10sonxy \_ mysql.5 mysql:latest ip-172-31-13-141.ec2.internal Shutdown Running 14 seconds ago
8d19osxa4fwf mysql.6 mysql:latest ip-172-31-24-10.ec2.internal Running Running 4 minutes ago
dlcgstxxkd9t mysql.7 mysql:latest Running Pending 3 seconds ago
ziqslz7u9d9l \_ mysql.7 mysql:latest ip-172-31-43-179.ec2.internal Shutdown Assigned 57 seconds ago
k8hba8j5o9vi \_ mysql.7 mysql:latest op1dzvmt5eyc74l6pcl5ut64p Shutdown Running about a minute ago
ettk65bpin3b mysql.8 mysql:latest ip-172-31-5-127.ec2.internal Running Running 4 minutes ago
i3otbqfsfvr7 mysql.9 mysql:latest ip-172-31-35-209.ec2.internal Running Running 3 minutes ago
sxdi970o6d3b mysql.10 mysql:latest ip-172-31-21-57.ec2.internal Running Running 12 seconds ago
摘要
本章讨论了基于资源储备和资源限制的 Docker Swarm 模式的资源模型。预留资源不能超过资源限制,分配给服务任务的资源受节点容量的限制。下一章讨论 Docker 群模式下的调度。
八、调度
在第二章,介绍了 Docker Swarm。在第四章中,介绍了 Docker 群服务。一个服务由零个或多个服务任务(副本)组成,它在集群中的节点上调度这些任务。服务的期望状态包括必须运行的任务数量。调度被定义为将需要运行的服务任务放置在集群中的一个节点上以保持服务的期望状态的过程,如图 8-1 所示。只能在工作节点上计划服务任务。默认情况下,管理器节点也是工作者节点。
图 8-1。
Scheduling
问题
如果没有调度策略,服务任务可能会被调度到群中的一个节点子集上。例如,一个服务中的所有三个任务都可以在一个群中的同一个节点上被调度,如图 8-2 所示。
图 8-2。
Avoid scheduling all tasks on one node
不使用调度策略可能会导致以下问题:
- 群中资源利用不足—如果所有任务都在单个节点或节点子集上调度,则其他节点的资源容量没有得到利用。
- 资源利用不均衡—如果所有任务都安排在单个节点或节点子集上,则安排任务的节点上的资源会被过度利用,任务甚至会用尽所有资源容量,而没有扩展副本的余地。
- 缺乏局部性—客户端基于节点位置访问服务的任务。如果所有服务任务都被安排在单个节点上,则在其他节点上访问服务的外部客户端不能本地访问服务,从而在访问相对远程的任务时导致网络开销。
- 单点故障—如果所有服务都在一个节点上运行,而该节点出现问题,就会导致停机。增加节点间的冗余可以避免这个问题。
解决方案
为了克服前面讨论的问题,Docker 群中的服务任务调度是基于一个内置的调度策略。Docker Swarm 模式使用分散调度策略对节点进行排序,以放置服务任务(副本)。为每个任务的调度计算节点排序,并且在具有最高计算排序的节点上调度任务。分布调度策略根据节点的可用 CPU、RAM 和已经在节点上运行的容器数量来计算节点等级。传播策略针对容器数量最少的节点进行优化。负载共享是分布策略的目标,它导致任务(容器)稀疏而均匀地分布在集群中的几台机器上。传播策略的预期结果是,如果单个节点或节点的一个小子集宕机或变得可用,则只有少数任务丢失,而群中的大多数任务继续可用。
Note
因为容器在所有状态下都会消耗资源,包括退出时,所以扩展策略不会考虑容器的状态。建议用户删除已停止的容器,因为如果某个节点有几个已停止的容器,那么该节点将变得不适合调度新任务。
扩展调度策略不考虑为哪个服务调度任务。只有可用的和请求的资源被用于调度新的任务。图 8-3 显示了使用扩展调度策略的调度。
图 8-3。
Using the spread scheduling policy
举个假设的例子:
- 从三个节点开始,每个节点有 3GB 的容量和 3 个 CPU,并且没有容器运行。
- 创建一个带有一个副本的
mysql
服务,它需要 1GB 和 1 个 CPU 的资源。第一个副本被随机安排在群中三个节点中的一个上,因为所有节点都具有相同的排名。如果所有的节点都有相同的等级,一个新的任务会被随机安排在其中一个节点上。 - 将
mysql
服务扩展为三个任务。由于其中一个节点已经加载,这两个新任务被安排在另外两个节点上,将一个任务分配给每个节点。 - 将
mysql
服务扩展到五个任务。必须启动两个新任务,并且所有节点都具有相同的等级,因为它们具有相同的可用资源容量和相同数量的正在运行的容器。这两个新任务随机安排在两个节点上。因此,两个节点各有两个任务,一个节点有一个任务。 - 为
nginx
服务器创建另一个服务,需要两个任务,每个任务需要 0.5GB 和 0.5 CPU。这两个任务都被安排在只有mysql
服务任务的节点上,因为它负载最小。因此,两个节点有两个mysql
服务任务和 1GB 和 1 个 CPU 的可用容量,一个节点有两个nginx
服务任务和一个mysql
服务任务以及 1GB 和 1 个 CPU 的可用资源容量。 - 将
nginx
服务扩展到三个。即使所有节点都有相同的可用 CPU 和 RAM,新任务也不会随机安排在三个节点中的一个上,而是安排在容器数量最少的节点上。结果,新的nginx
任务被随机安排在其中一个节点上,每个节点有两个mysql
任务。如果节点具有相同的可用 CPU 和 RAM,则选择具有较少容器(运行或停止)的节点来调度新任务。
本章涵盖以下主题:
- 设置环境
- 创建和计划服务—扩展计划
- 期望的国家和解
- 受节点资源容量限制的任务调度
- 添加服务调度约束
- 在特定节点上调度
- 添加多个日程排定限制
- 为调度添加节点标签
- 添加、更新和删除服务调度约束
- 扩展调度和全球服务
设置环境
使用 Docker for AWS 创建一个由一个管理节点和两个工作节点组成的 CloudFormation 栈。AWS 的 Docker 在第三章中介绍。栈如图 8-4 所示。
图 8-4。
CloudFormation stack
栈中的三个 EC2 实例如图 8-5 所示。
图 8-5。
EC2 instances for the Docker swarm
使用公共 IP 地址 SSH 登录到 Swarm manager,该地址可以从 EC2 控制台获得,如图 8-5 所示。
[root@localhost ∼]# ssh -i "docker.pem" docker@54.84.133.157
Welcome to Docker!
列出群体中的节点;应该列出三个节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
0waa5g3b6j641xtwsygvjvwc1 ip-172-31-0-147.ec2.internal Ready Active
e7vigin0luuo1kynjnl33v9pa ip-172-31-29-67.ec2.internal Ready Active
ptm7e0p346zwypos7wnpcm72d * ip-172-31-25-121.ec2.internal Ready Active Leader
创建和计划服务:扩展计划
首先,我们以 MySQL 数据库服务为例来讨论默认的扩展调度。从 Swarm manager 节点,运行以下命令为 MySQL 创建一个五副本服务。输出是服务 ID(以斜体显示)。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 5 \
> --name mysql \
> mysql
1onpemnoz4x1lh3sv5umab8uo
随后,使用docker service ls
列出服务。最初,REPLICAS
列可能是0/5
,表示没有副本被调度和运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
1onpemnoz4x1 mysql replicated 0/5 mysql:latest
过一会儿再次运行该命令;所有副本应该都在运行,如REPLICAS
列中的5/5
所示。使用docker service ps mysql
命令列出服务副本。任务应该正在运行或准备运行。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
fwjbu3gt2zn0 mysql.1 mysql:latest ip-172-31-0-147.ec2.internal Running Preparing 8 seconds ago
w0521ik1awjf mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Preparing 8 seconds ago
z9wn2nrzfzt8 mysql.3 mysql:latest ip-172-31-0-147.ec2.internal Running Preparing 8 seconds ago
tm8jbque3xbb mysql.4 mysql:latest ip-172-31-25-121.ec2.internal Running Preparing 8 seconds ago
7drxfy3vbmp5 mysql.5 mysql:latest ip-172-31-29-67.ec2.internal Running Preparing 8 seconds ago
按照分布调度策略,其中两个副本在一个工作节点上按计划列出,两个在另一个工作节点上,一个在管理器节点上。由于副本的数量为奇数,所以位置不能完全均匀分布,但是单个节点不会有两个以上的副本。
要查看分布调度策略如何在整个群中均匀地分布副本,请将服务扩展到六个副本。docker service scale
命令的输出以斜体显示。
∼ $ docker service scale mysql=6
mysql scaled to 6
随后,列出副本。每个节点上都安排了两个复制副本,因为扩展计划策略旨在计划。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
fwjbu3gt2zn0 mysql.1 mysql:latest ip-172-31-0-147.ec2.internal Running Running 13 seconds ago
w0521ik1awjf mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 12 seconds ago
z9wn2nrzfzt8 mysql.3 mysql:latest ip-172-31-0-147.ec2.internal Running Running 13 seconds ago
tm8jbque3xbb mysql.4 mysql:latest ip-172-31-25-121.ec2.internal Running Running 8 seconds ago
7drxfy3vbmp5 mysql.5 mysql:latest ip-172-31-29-67.ec2.internal Running Running 12 seconds ago
utjo8lwbtzf7 mysql.6 mysql:latest ip-172-31-25-121.ec2.internal Running Running 5 seconds ago
因为服务副本或任务只不过是运行容器的一个槽,所以每个节点为mysql
服务运行两个容器。
为了进一步演示扩展调度,将服务缩减为三个任务。命令输出以斜体显示。
∼ $ docker service scale mysql=3
mysql scaled to 3
列出服务任务。每个节点上运行一个任务,这也是一个均匀分布的任务调度。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
w0521ik1awjf mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 40 seconds ago
z9wn2nrzfzt8 mysql.3 mysql:latest ip-172-31-0-147.ec2.internal Running Running 41 seconds ago
utjo8lwbtzf7 mysql.6 mysql:latest ip-172-31-25-121.ec2.internal Running Running 33 seconds ago
期望的国家和解
当一个服务被创建或者被放大或缩小时,该服务最初在当前状态和期望状态之间存在差异。期望状态的不同值是ready
、running
、shutdown
和accepted
。Docker 服务是为期望的状态协调而设计的,这意味着群管理器持续监视集群状态,以协调服务的期望状态和当前状态之间的任何差异。任务的当前状态可以是assigned
、preparing
、ready
、running
、shutdown
或pending
。已经分配给节点但当前未运行的任务处于assigned
状态。期望状态为running
并准备运行的任务处于preparing
当前状态。如果群中没有节点可以运行任务,则该任务处于pending
状态。
在下面的任务列表中,一些任务具有期望状态和当前状态running
。这些任务已经协调了它们期望的状态。一个任务的期望状态被设置为running
,但是当前状态是pending
。另一个任务的期望状态设置为shutdown
,当前状态设置为assigned
。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
opxf4ne7iyy6 mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 9 minutes ago
x30y3jlea047 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 8 minutes ago
w4ivsbvwqqzq mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 4 minutes ago
j9lp08ojofj7 mysql.4 mysql:latest Running Pending 28 seconds ago
ph1zpsjsvp69 \_ mysql.4 mysql:latest ip-172-31-7-137.ec2.internal Shutdown Assigned 33 seconds ago
d3oxy6hxfjh3 \_ mysql.4 mysql:latest ip-172-31-40-70.ec2.internal Shutdown Running 43 seconds ago
ic331aasjpdm mysql.5 mysql:latest ip-172-31-44-104.ec2.internal Running Running 8 minutes ago
在早期的任务列表中,所有任务都处于当前状态preparing
和期望状态running
。
群模式旨在尽可能地协调所需的状态,这意味着如果节点资源可用,将运行所需数量的副本。为了进行演示,通过选择操作➤更新栈来更新 AWS CloudFormation 栈的 Docker,如图 8-6 所示。
图 8-6。
Updating a stack
将工作节点的数量从两个减少到一个,如图 8-7 所示。
图 8-7。
Decreasing the number of worker nodes to one
随后,列出来自 Swarm manager 节点的服务副本。
docker service ps mysql
在离开群的群工作者节点上运行的服务副本被列为shutdown
。在群中剩余的两个节点上开始新的副本以协调期望的状态。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
p14bbk7ij1mt mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 5 minutes ago
w0521ik1awjf mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 7 minutes ago
uatsaay7axlc mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running about a minute ago
z9wn2nrzfzt8 \_ mysql.3 mysql:latest 0waa5g3b6j641xtwsygvjvwc1 Shutdown Running 2 minutes ago
w1tlw0fom42q mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running about a minute ago
qc75buhzzct3 \_ mysql.4 mysql:latest 0waa5g3b6j641xtwsygvjvwc1 Shutdown Running 2 minutes ago
s09ts9s8np3d mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running 5 minutes ago
utjo8lwbtzf7 mysql.6 mysql:latest ip-172-31-25-121.ec2.internal Running Running 7 minutes ago
仅列出所需状态为running
的副本,六个副本在两个节点之间按计划平均列出—三个副本在管理节点上,三个副本在工作节点上。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
p14bbk7ij1mt mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 6 minutes ago
w0521ik1awjf mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 8 minutes ago
uatsaay7axlc mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 2 minutes ago
w1tlw0fom42q mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 2 minutes ago
s09ts9s8np3d mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running 6 minutes ago
utjo8lwbtzf7 mysql.6 mysql:latest ip-172-31-25-121.ec2.internal Running Running 8 minutes ago
如果新的节点被添加到群中,传播调度策略不重新调度已经运行的副本来实现群中的均匀传播。为了演示这一点,我们将工作节点的数量增加回两个,如图 8-8 所示。
图 8-8。
Re-adding a worker node to Swarm
将一个节点添加到群中不会关闭其他节点上的副本并在新节点上启动副本。列出正在运行的副本并不表示服务副本的替换。在添加新节点之前,服务副本继续在运行它们的节点上运行,其中三个在管理节点上,三个在工作节点上。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
p14bbk7ij1mt mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 15 minutes ago
w0521ik1awjf mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 17 minutes ago
uatsaay7axlc mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 12 minutes ago
z9wn2nrzfzt8 \_ mysql.3 mysql:latest 0waa5g3b6j641xtwsygvjvwc1 Shutdown Running 13 minutes ago
w1tlw0fom42q mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 12 minutes ago
qc75buhzzct3 \_ mysql.4 mysql:latest 0waa5g3b6j641xtwsygvjvwc1 Shutdown Running 13 minutes ago
s09ts9s8np3d mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running 15 minutes ago
utjo8lwbtzf7 mysql.6 mysql:latest ip-172-31-25-121.ec2.internal Running Running 17 minutes ago
受节点资源容量限制的任务调度
调度策略受到可用节点资源的限制,这意味着如果没有足够的节点资源(CPU 和内存)可用,就无法运行服务副本。资源使用不能超过节点容量。副本仍被分配给服务以定义所需的状态,但由于资源不足,可能不会运行。为了演示这一点,我们删除服务mysql
并使用指定的资源请求和限制再次创建服务。命令输出以斜体显示。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> --reserve-cpu 1 --limit-cpu 2 --reserve-memory 256mb --limit-memory 512mb mysql
0qe2thy0dlviroli6k8thist1
列出服务表明创建了服务的一个副本。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
0qe2thy0dlvi mysql replicated 1/1 mysql:latest
单个副本被调度在管理节点上,如果群中的所有节点具有相同的节点排名,则管理节点被随机选择。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
opxf4ne7iyy6 mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 8 seconds ago
接下来,为了潜在地使服务副本消耗比可用资源更多的资源,将服务扩展到五个副本。
∼ $ docker service scale mysql=5
mysql scaled to 5
列出服务表明3/5 Replicas
正在运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
0qe2thy0dlvi mysql replicated 3/5 mysql:latest
列出服务副本表明一些副本是pending
而不是running
。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
opxf4ne7iyy6 mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 4 minutes ago
x30y3jlea047 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 3 minutes ago
w4ivsbvwqqzq mysql.3 mysql:latest Running Pending 3 minutes ago
d3oxy6hxfjh3 mysql.4 mysql:latest Running Pending 3 minutes ago
ic331aasjpdm mysql.5 mysql:latest ip-172-31-44-104.ec2.internal Running Running 3 minutes ago
pending
状态意味着副本被分配给服务,但是还没有在任何节点上被调度。根据请求的资源和可用的节点资源,只能运行三个副本,每个节点一个。
因为由于缺乏资源而没有调度副本,所以我们添加一个或多个新的工作节点来潜在地调度副本以协调期望的状态。将工作节点的数量增加到 5 个,如图 8-9 所示。
图 8-9。
Increasing the number of worker nodes to five
在添加一个新节点后,Swarm 应该列出六个节点。随着资源变得可用于pending
任务,任务得到调度并开始运行。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
opxf4ne7iyy6 mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 5 minutes ago
x30y3jlea047 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 4 minutes ago
w4ivsbvwqqzq mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 21 seconds ago
d3oxy6hxfjh3 mysql.4 mysql:latest ip-172-31-40-70.ec2.internal Running Preparing 30 seconds ago
ic331aasjpdm mysql.5 mysql:latest ip-172-31-44-104.ec2.internal Running Running 4 minutes ago
如果工作者节点的数量减少,一些任务被重新调度,如shutdown
期望状态所示。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
opxf4ne7iyy6 mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 9 minutes ago
x30y3jlea047 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 8 minutes ago
w4ivsbvwqqzq mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 4 minutes ago
j9lp08ojofj7 mysql.4 mysql:latest Running Pending 28 seconds ago
ph1zpsjsvp69 \_ mysql.4 mysql:latest ip-172-31-7-137.ec2.internal Shutdown Assigned 33 seconds ago
d3oxy6hxfjh3 \_ mysql.4 mysql:latest ip-172-31-40-70.ec2.internal Shutdown Running 43 seconds ago
ic331aasjpdm mysql.5 mysql:latest ip-172-31-44-104.ec2.internal Running Running 8 minutes ago
更新服务以降低保留的 CPU 和内存资源使用率只会更新服务的UpdateConfig
。这不会降低已经运行的任务的资源使用率,也不会使pending
或shutdown
任务运行。例如,当一些任务由于缺乏资源而处于pending
或shutdown
时,降低mysql
服务的资源储备和限制。
∼ $ docker service update --reserve-cpu .1 --limit-cpu .5 --reserve-memory 64mb
--limit-memory 128mb mysql
mysql
UpdateConfig
被修改,但仅适用于在该点之后创建的新副本。
∼ $ docker service inspect mysql
[
},
"Resources": {
"Limits": {
"NanoCPUs": 500000000,
"MemoryBytes": 134217728
},
"Reservations": {
"NanoCPUs": 100000000,
"MemoryBytes": 67108864
}
},
]
mysql
服务中只有三个副本在实际运行。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
opxf4ne7iyy6 mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 10 minutes ago
x30y3jlea047 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 10 minutes ago
w4ivsbvwqqzq mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 5 minutes ago
rm9uj4qevt5b mysql.5 mysql:latest Running Pending 33 seconds ago
要强制服务任务使用新的资源设置,请将服务缩减为一个任务,然后再缩减为五个任务。
∼ $ docker service scale mysql=1
mysql scaled to 1
∼ $ docker service scale mysql=5
mysql scaled to 5
所有五个任务现在都在运行。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
anai3mptbnkp mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Running Running 17 seconds ago
opxf4ne7iyy6 \_ mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Shutdown Shutdown 18 seconds ago
lmkn8l50t334 mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running 10 seconds ago
7uz7q86wnzn4 mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 11 seconds ago
ubh4m39aw8m9 mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 11 seconds ago
56pnrzajogvs mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running 10 seconds ago
添加服务调度约束
Docker Swarm 支持放置或调度约束来调度新任务。服务放置约束是放置服务任务的附加标准,可以基于节点属性、元数据和引擎元数据。群调度器使用以下序列来调度服务任务。
- 节点是否满足所有放置约束?
- 一个节点是否满足均匀分布的调度策略要求?
- 节点是否有足够的资源来调度任务?
可通过docker service create
命令使用--constraint
选项添加一个布局约束。对于一个已经运行的服务,可以分别使用--constraint-add
和--constraint-rm
选项和docker service update
命令添加和删除约束。表 8-1 中讨论的节点属性可用于指定约束。
表 8-1。
Node Attributes for Constraints
| 节点属性 | 描述 | 例子 | | --- | --- | --- | | `node.id` | 指定节点 ID。使用`docker node ls`命令列出节点 id。 | `node.id==a3r56hj7y` | | `node.hostname` | 指定节点的主机名。使用`docker node ls`命令列出了节点的主机名。 | `node.hostname!=ip-10-0-0-ec2.internal` | | `node.role` | 指定节点角色,它是`worker`或`manager`之一。 | `node.role==manager` | | `node.labels` | 指定用户添加的节点标签。标签是一个键值对。添加节点标签时,`node.labels.`前缀将被省略,并自动添加。添加和使用节点标签将在后续章节中讨论。 | `node.labels.db==mysql` | | `engine.labels` | Docker 引擎标签,如驱动程序、操作系统、版本。 | `engine.labels.os==coreos` |接下来,我们讨论一些使用调度约束的例子。
在特定节点上调度
在本节中,我们将在集群中的特定节点上调度服务副本。用docker node ls
命令列出节点 id。Swarm 有以下三个可用于调度的节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
81h6uvu8uq0emnovzkg6v7mzg ip-172-31-2-177.ec2.internal Ready Active
e7vigin0luuo1kynjnl33v9pa ip-172-31-29-67.ec2.internal Ready Active
ptm7e0p346zwypos7wnpcm72d * ip-172-31-25-121.ec2.internal Ready Active Leader
我们可以按节点角色安排服务。创建一个mysql
服务,其位置约束是服务任务只能在工作节点上调度。首先,删除已经运行的mysql
服务
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 3 \
> --constraint node.role==worker \
> --name mysql \
> mysql
nzgte4zac1x8itx6t98y5gi42
创建了服务,并且仅在两个 worker 节点上调度了三个任务,如正在运行的服务任务中所列。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
f5t15mnrft0h mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 19 seconds ago
oxvq4ljuq6yz mysql.2 mysql:latest ip-172-31-2-177.ec2.internal Running Running 19 seconds ago
k5jo862lvsxf mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 19 seconds ago
接下来,我们使用节点 ID 来调度服务的任务。复制管理节点的节点 ID,它也是群中的领导者,是唯一的管理节点。在下面的命令中替换节点 ID,为 MySQL 数据库创建一个服务,并只在 manager 节点上调度副本。
docker service create \
--env MYSQL_ROOT_PASSWORD='mysql'\
--replicas 3 \
--constraint node.id ==<nodeid>
--name mysql \
mysql
创建服务有三个任务。命令输出以斜体显示。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 3 \
> --constraint node.id==ptm7e0p346zwypos7wnpcm72d\
> --name mysql \
> mysql
u1qi6zqnch9hn7x6k516axg7h
该服务的所有三个副本仅在管理器节点上进行调度。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
lbttu95qdjvy mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 21 seconds ago
89x0z94on0fb mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running 21 seconds ago
3s6508aimdaj mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 22 seconds ago
添加多个日程排定限制
还可以指定多个节点约束,并且必须使用AND
来满足每个约束表达式,以便调度程序在节点上调度副本。例如,我们创建一个具有两个角色的服务,一个将节点角色约束为 worker,另一个将节点主机名约束为不是特定的主机名ip-172-31-2-177.ec2.internal
。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 3 \
> --constraint node.role==worker \
> --constraint node.hostname!=ip-172-31-2-177.ec2.internal\
> --name mysql \
> mysql
87g0c8kauhz8yb4wv2ryc2vqr
服务被创建。列出服务会将3/3 replicas
列为正在运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
87g0c8kauhz8 mysql replicated 3/3 mysql:latest
列出服务任务表明所有任务都被安排在单个工作节点上。满足两个约束:该节点是一个工作节点,而不是主机名为ip-172-31-2-177.ec2.internal
的工作节点。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
jlfk79mb6m6a mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 13 seconds ago
if5y39ky884q mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 13 seconds ago
zctm6mzbl4du mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Running Running 13 seconds ago
如果mysql
服务被更新以移除约束,则扩展调度策略基于节点排序来重新调度任务。例如,更新服务以移除添加的两个放置约束。使用docker service update
命令的–constraint-rm
选项删除一个约束。
∼ $ docker service update \
> --constraint-rm node.role==worker \
> --constraint-rm node.hostname!=ip-172-31-2-177.ec2.internal\
> mysql
mysql
当服务被更新以移除约束时,所有服务任务被关闭,并且新的服务任务被启动。开始新的服务任务,在群中的三个节点上各一个。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
d22bkgteivot mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Ready Ready less than a second ago
jlfk79mb6m6a \_ mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Shutdown Running 1 second ago
mp757499j3io mysql.2 mysql:latest ip-172-31-2-177.ec2.internal Running Running 1 second ago
if5y39ky884q \_ mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Shutdown Shutdown 2 seconds ago
jtdxucteb0fl mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 4 seconds ago
zctm6mzbl4du \_ mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Shutdown Shutdown 5 seconds ago
仅列出正在运行的任务。列出了每个节点上运行的一个任务。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
d22bkgteivot mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 46 seconds ago
mp757499j3io mysql.2 mysql:latest ip-172-31-2-177.ec2.internal Running Running 49 seconds ago
jtdxucteb0fl mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 53 seconds ago
类似地,多节点约束可用于仅在管理器节点上运行副本。接下来,我们更新mysql
服务以在特定的管理器节点上运行。首先,将一个 worker 节点提升为 manager。
∼ $ docker node promote ip-172-31-2-177.ec2.internal
Node ip-172-31-2-177.ec2.internal promoted to a manager in the swarm.
随后,列出了两个管理器节点,如其中两个节点的Manager Status
所示。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
81h6uvu8uq0emnovzkg6v7mzg ip-172-31-2-177.ec2.internal Ready Active Reachable
e7vigin0luuo1kynjnl33v9pa ip-172-31-29-67.ec2.internal Ready Active
ptm7e0p346zwypos7wnpcm72d * ip-172-31-25-121.ec2.internal Ready Active Leader
更新mysql
服务以添加多个节点约束,从而仅在特定的管理器节点上运行副本。使用docker service update
命令的--constraint-add
选项添加约束。
∼ $ docker service update \
> --constraint-add node.role==manager \
> --constraint-add node.hostname==ip-172-31-2-177.ec2.internal\
> mysql
mysql
同样,所有服务任务被关闭,新任务被启动,所有这些都在从 worker 节点升级的指定 manager 节点上进行。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
eghm1or6yg5g mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Running Running 28 seconds ago
bhfngac5ssm7 mysql.2 mysql:latest ip-172-31-2-177.ec2.internal Running Running 22 seconds ago
ts3fgvq900os mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 25 seconds ago
为调度添加节点标签
接下来,我们讨论如何使用节点标签来指定服务放置约束。可以使用以下命令语法向节点添加标签,其中变量为<LABELKEY>
、<LABELVALUE>
和<NODE>
。<NODE>
是节点 ID 或主机名。
docker node update --label-add <LABELKEY>=<LABELVALUE> <NODE>
例如,将标签db=mysql
添加到主机名设置为ip-172-31-25-121.ec2.internal
的节点,该节点是领导者节点。
∼ $ docker node update --label-add db=mysql ip-172-31-25-121.ec2.internal
ip-172-31-25-121.ec2.internal
添加一个节点标签。检查节点时,标签列在Labels
字段中。
∼ $ docker node inspect ip-172-31-25-121.ec2.internal
[
"Spec": {
"Labels": {
"db": "mysql"
},
"Role": "manager",
"Availability": "active"
},
]
接下来,创建一个使用节点标签添加放置约束的服务。标签的- constraint 选项必须包括前缀 node.labels。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 3 \
> --constraint node.labels.db==mysql \
> --name mysql \
> mysql
2hhccmj9senseazbet11dekoa
服务已创建。列出任务会列出领导者管理器节点上的所有任务,这是节点标签约束所指定的。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
g5jz9im3fufv mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 18 seconds ago
bupr27bs57h1 mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running 18 seconds ago
5bb2yf8aehqn mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 18 seconds ago
添加的标签可通过docker node update
命令的--label-rm
选项移除,其中仅指定了标签键。
docker node update --label-rm db ip-172-31-25-121.ec2.internal
添加、更新和删除服务调度约束
在前面的部分中,我们讨论了在使用docker service create
创建服务时添加位置约束。可使用--constraint-add
和--constraint-rm
选项通过docker service update
命令添加/删除布局约束。为了讨论一个更新布局约束的例子,我们创建了一个mysql
服务,它有三个副本,开始时没有布局约束。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 3 \
> --name mysql \
> mysql
az3cq6sxwrrk4mxkksdu21i25
一个mysql
服务被创建,在集群中的三个节点上调度三个副本,使用传播策略。
接下来,用docker service update
命令更新服务,为服务副本添加一个仅在管理器节点上运行的约束。
∼ $ docker service update \
> --constraint-add node.role==manager \
> mysql
mysql
在具有两个管理节点的群中,所有服务任务都被关闭,新任务只在管理节点上启动。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
pjwseruvy4rj mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Running Running 4 seconds ago
s66g9stz9af5 \_ mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Shutdown Shutdown 4 seconds ago
yqco9zd0vq79 mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running 9 seconds ago
8muu6gbghhnd \_ mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Shutdown Shutdown 10 seconds ago
8x7xlavcxdau mysql.3 mysql:latest ip-172-31-25-121.ec2.internal Running Running 7 seconds ago
qx95vwi2h547 \_ mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Shutdown Shutdown 7 seconds ago
可以在同一个docker service update
命令中添加和删除调度约束。例如,删除节点成为管理者的约束,并添加节点成为工作者的约束。
∼ $ docker service update \
> --constraint-rm node.role==manager \
> --constraint-add node.role==worker \
> mysql
mysql
又来了。所有服务任务都将关闭,新任务仅在工作节点上启动。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
6ppgmvw9lv75 mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 9 seconds ago
qm0loki65v9s mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 17 seconds ago
ypl0tc1ft92o mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Running Running
如果将节点角色指定为工作者的唯一调度约束被移除,则分布调度策略开始在整个群中均匀分布新任务。要进行演示,请删除节点角色成为工作者的约束。
∼ $ docker service update --constraint-rm node.role==worker mysql
mysql
随后,新的任务在群体中的节点间传播。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
jpx4jjw6l9d5 mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 5 seconds ago
ngajiik1hugb mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running 12 seconds ago
40eaujzlux88 mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 8 seconds ago
扩展调度和全球服务
全局服务在集群中的每个节点上运行一个任务。全局服务不能被扩展以创建更多/更少的任务。因此,分布调度策略概念不适用于全局服务。然而,节点约束可以应用于全局服务。例如,我们为mysql
数据库创建了一个全局服务。应用一个放置约束,即服务应该只在工作节点上可用。
∼ $ docker service create \
> --mode global \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --constraint node.role==worker \
> --name mysql \
> mysql
jtzcwatp001q9r26n1uubd8me
创建了全局服务。将具有期望状态的任务的服务任务列为running
仅列出工作节点上的任务。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
o5nskzpv27j9 mysql.e7vigin0luuo1kynjnl33v9pa mysql:latest ip-172-31-29-67.ec2.internal Running Running 17 seconds ago
如果创建时没有仅在工作节点上调度的约束,全局服务将在每个节点上调度一个任务,如下例所示。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --mode global \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --name mysql \
> mysql
mv9yzyyntdhzz41zssbutcsvw
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
mc87btddhmpl mysql.e7vigin0luuo1kynjnl33v9pa mysql:latest ip-172-31-29-67.ec2.internal Running Running 19 seconds ago
o0wfdq9sd8yt mysql.ptm7e0p346zwypos7wnpcm72d mysql:latest ip-172-31-25-121.ec2.internal Running Running 19 seconds ago
wt2q5k2dhqjt mysql.81h6uvu8uq0emnovzkg6v7mzg mysql:latest ip-172-31-2-177.ec2.internal Running Running 19 seconds ago
摘要
本章讨论了 Docker Swarm 模式中使用的分布调度策略,通过这种策略,服务副本基于节点排序均匀地分布在集群中的节点上;较高的节点等级获得服务副本放置优先级。我们还讨论了有限节点资源容量的影响,以及如何通过向群集添加新节点来缓解这种影响。我们讨论了调度新副本的放置约束。分布调度策略与全局服务无关,因为默认情况下,全局服务在每个节点上创建一个服务任务。然而,调度约束可以与全局服务一起使用。在下一章,我们将讨论 Docker 服务的滚动更新。
九、滚动更新
Docker 群模式提供由在群中的节点上运行的副本组成的服务。服务定义是在首次创建/定义服务时创建的。使用docker service create
命令创建服务定义。该命令提供了几个选项,包括用于添加放置约束、容器标签、服务标签、DNS 选项、环境变量、资源保留和限制、日志记录驱动程序、装载、副本数量、重启条件和延迟、更新延迟、故障操作、最大故障率和并行性的选项,其中大部分在第四章中讨论。
问题
创建服务定义后,可能需要更新一些服务选项,例如增加/减少副本数量、添加/删除放置约束、更新资源保留和限制、添加/删除装载、添加/删除环境变量、添加/删除容器和服务标签、添加/删除 DNS 选项,以及修改重启和更新参数。如果需要整体关闭服务来更新服务定义选项,则会导致服务中断。
解决方案
Docker Swarm 模式包括滚动更新。在滚动更新中,服务不会关闭,但是服务中的单个副本/任务会一次关闭一个,并且基于新服务定义的新服务副本/任务会一次启动一个,如图 9-1 所示。因此,该服务在滚动更新期间继续可用。在滚动更新期间,提供给客户端的服务任务可以来自旧的和新的服务定义。例如,如果滚动更新对更近的映像标签执行更新,则在滚动更新期间提供给外部客户端的一些任务可能来自旧映像标签和新映像标签的混合。
图 9-1。
Rolling update
滚动更新为服务创建新的服务定义和新的期望状态。滚动更新包括关闭所有服务副本和启动所有新的服务副本,但不适用于尚未计划的服务副本,例如由于缺乏资源。在滚动更新中,即使只更新副本的数量,也会关闭所有旧副本或使其失败,并启动所有新副本。
在滚动更新期间,调度程序使用以下顺序。
- 第一个任务停止。
- 已计划对停止的任务进行更新。
- 启动更新任务的 Docker 容器。
- 如果任务更新返回
RUNNING
,等待--update-delay
中指定的持续时间,并开始下一个任务的更新。 - 如果在更新过程中,任务返回
FAILED
,执行--update-failure-action
,默认暂停更新。 - 用
docker service update <SERVICE-ID>
重启暂停的更新。 - 如果更新失败重复出现,请找到失败的原因,并通过向 docker 服务更新提供其他选项来重新配置服务。
设置环境
使用 Docker for AWS 创建一个由一个管理节点和两个工作节点组成的 Docker 群,如第三章所述。从 EC2 控制台获取 manager 实例的公共 IP 地址,然后 SSH 登录到该实例。
[root@localhost ∼]# ssh -i "docker.pem" docker@54.84.133.157
Welcome to Docker!
列出群体节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
81h6uvu8uq0emnovzkg6v7mzg ip-172-31-2-177.ec2.internal Ready Active
e7vigin0luuo1kynjnl33v9pa ip-172-31-29-67.ec2.internal Ready Active
ptm7e0p346zwypos7wnpcm72d * ip-172-31-25-121.ec2.internal Ready Active Leader
使用滚动更新策略创建服务
滚动更新策略或更新配置由表 9-1 中讨论的服务定义选项组成。
表 9-1。
Rolling Update Options
| [计]选项 | 描述 | 缺省值 | | --- | --- | --- | | `--update-delay` | 更新之间的延迟(ns|us|ms|s|m|h)。 | 0 秒 | | `--update-failure-action` | 更新失败时的操作。值可以是`pause`或`continue`。 | 中止 | | `--update-max-failure-ratio` | | | | `--update-monitor` | 每次任务更新后监视失败的持续时间(ns|us|ms|s|m|h)。 | 0 秒 | | `--update-` `parallelism` | 同时更新的最大任务数。值为 0 会一次更新所有内容。 | one |要在服务部署时配置滚动更新策略,必须在创建服务时提供要配置的选项。例如,为 MySQL 数据库创建一个服务,并指定更新策略选项--update-delay
和--update-parallelism
。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> --update-delay 10s \
> --update-parallelism 1 \
> mysql:5.6
wr0z48v1uguk1c40pa42ywrpn
服务已创建。列出服务可能不会列出最初运行的所有副本,如REPLICAS
列中的0/1
所示。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
wr0z48v1uguk mysql replicated 0/1 mysql:5.6
过一会儿运行相同的命令应该会将所有副本列为正在运行,如REPLICAS
列中的1/1
所示。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
wr0z48v1uguk mysql replicated 1/1 mysql:5.6
单个服务副本被调度在管理器节点本身上,并且副本的 Docker 容器被启动。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
38dm9gm6cmvk mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 13 seconds ago
使用滚动更新选项创建服务本身并不演示滚动更新。它仅定义服务的UpdateConfig
设置。在下一节中,我们将执行滚动更新。
滚动更新以增加副本数量
滚动更新可用于通过对docker service update
命令使用--replicas
选项来更新副本的数量。滚动更新更新首次部署服务时应用的UpdateConfig
策略。接下来,我们从上一节中创建的一个副本更新基于mysql:5.6
映像的服务的副本数量。运行以下命令,将服务定义从一个副本更新到五个副本。--update-delay
和--update-parallelism
选项修改服务定义的UpdateConfig
。如果更新成功,则docker service update
命令输出服务名。
∼ $ docker service update \
> --replicas 5 \
> --update-delay 20s \
> --update-parallelism 1 \
> mysql
mysql
随后,服务列表可能会在docker service ls
命令的输出中列出一些尚未启动的副本。但是,过一会儿再次运行该命令应该会将所有复制副本列为正在运行。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
wr0z48v1uguk mysql replicated 5/5 mysql:5.6
在滚动更新期间,所有正在运行的任务都将关闭,并启动新的任务。mysql.1
任务的期望状态被更新为shutdown
,当前状态被设置为failed
。新任务mysql.1
开始。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
ydqj6vf9rsgw mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 26 seconds ago
38dm9gm6cmvk \_ mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed 31 seconds ago "task: non-zero exit (137)"
7bns96iu8ygz mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 32 seconds ago
62wfdbcv3cr4 mysql.3 mysql:5.6 ip-172-31-2-177.ec2.internal Running Running 33 seconds ago
ql66z5x0a2lf mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 14 seconds ago
3n3b1j7ey732 \_ mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed 19 seconds ago "task: non-zero exit (137)"
bl1365y60vuu mysql.5 mysql:5.6 ip-172-31-2-177.ec2.internal Running Running 33 seconds ago
当从一个复制副本扩展到五个复制副本时,首先启动几个新任务,然后关闭最初运行的任务,以便在滚动更新期间服务继续可用。如果服务中的唯一任务在开始任何新任务之前被首先关闭,那么服务在短时间内不会有任何正在运行的任务。
在滚动更新期间,运行五个副本的理想状态不会立即协调。滚动更新正在进行时,运行的任务可能少于五个。列出正在运行的服务任务只列出三个任务作为running
。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
ydqj6vf9rsgw mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 35 seconds ago
7bns96iu8ygz mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 40 seconds ago
ql66z5x0a2lf mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 22 seconds ago
当滚动更新完成时,五个任务正在运行。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
u8falo7q95cq mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 20 seconds ago
luabknwzwqoj mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 13 seconds ago
ce4l2qvtcanv mysql.3 mysql:5.6 ip-172-31-2-177.ec2.internal Running Running 25 seconds ago
iw8vwsxq3tjz mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 6 seconds ago
qfi5fionjt2v mysql.5 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 25 seconds ago
检查服务应该会列出更新后的副本数量。UpdateConfig
也与docker service inspect
命令一起列出。
∼ $ docker service inspect mysql
[
...
"Spec": {
"Name": "mysql",
...
},
"Mode": {
"Replicated": {
"Replicas": 5
}
},
"UpdateConfig": {
"Parallelism": 1,
"Delay": 20000000000,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"RollbackConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
...
]
滚动更新到不同的映像标签
滚动更新的一个用例是更新到较新的映像标签。例如,为mysql
服务执行滚动更新,从mysql:5.6
更新到 Docker 映像mysql:latest
。Update parallelism
设置为 2,一次更新两个副本。
∼ $ docker service update --image mysql:latest --update-parallelism 2 mysql
mysql
服务滚动更新开始。列出服务复制副本时,会将基于映像的复制副本列为正在关闭,如shutdown
期望状态所示,并将基于映像的复制副本列为正在启动,如running
期望状态所示。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
vqc6rhzw5uxz mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Ready Ready 7 seconds ago
80kswuu4d5gc \_ mysql.1 mysql:5.6 ip-172-31-2-177.ec2.internal Shutdown Running 7 seconds ago
u8falo7q95cq \_ mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed 12 seconds ago "task: non-zero exit (1)"
ydqj6vf9rsgw \_ mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed 56 seconds ago "task: non-zero exit (1)"
38dm9gm6cmvk \_ mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed about a minute ago "task: non-zero exit (137)"
tvxjmahy08uh mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 2 seconds ago
luabknwzwqoj \_ mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Shutdown Failed 8 seconds ago "task: non-zero exit (137)"
7bns96iu8ygz \_ mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Shutdown Failed 50 seconds ago "task: non-zero exit (137)"
u2ea4xq4yx6t mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 4 seconds ago
ce4l2qvtcanv \_ mysql.3 mysql:5.6 ip-172-31-2-177.ec2.internal Shutdown Shutdown 4 seconds ago
62wfdbcv3cr4 \_ mysql.3 mysql:5.6 ip-172-31-2-177.ec2.internal Shutdown Failed about a minute ago "task: non-zero exit (1)"
iw8vwsxq3tjz mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 37 seconds ago
ql66z5x0a2lf \_ mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed 43 seconds ago "task: non-zero exit (137)"
3n3b1j7ey732 \_ mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Shutdown Failed about a minute ago "task: non-zero exit (137)"
f5vcf9mgluqe mysql.5 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 14 seconds ago
qfi5fionjt2v \_ mysql.5 mysql:5.6 ip-172-31-29-67.ec2.internal Shutdown Failed 19 seconds ago "task: non-zero exit (1)"
bl1365y60vuu \_ mysql.5 mysql:5.6 ip-172-31-2-177.ec2.internal Shutdown Failed about a minute ago "task: non-zero exit (1)"
当滚动更新正在进行时,一些正在运行的任务可以基于先前的服务规范(mysql:5.6
),而其他的基于新的服务规范(mysql:latest
)。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
vqc6rhzw5uxz mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Running Running 4 seconds ago
tvxjmahy08uh mysql.2 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 11 seconds ago
u2ea4xq4yx6t mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 13 seconds ago
iw8vwsxq3tjz mysql.4 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 46 seconds ago
f5vcf9mgluqe mysql.5 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 23 seconds ago
当滚动更新完成时,所有正在运行的任务都基于新的服务规范。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
vqc6rhzw5uxz mysql.1 mysql:latest ip-172-31-2-177.ec2.internal Running Running 45 seconds ago
53choz0dd967 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running less than a second ago
u2ea4xq4yx6t mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running 53 seconds ago
tyo6v0yen7ev mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 21 seconds ago
upt212osx7au mysql.5 mysql:latest ip-172-31-29-67.ec2.internal Running Running 25 seconds ago
添加和删除环境变量的滚动更新
Docker 镜像mysql
需要一个强制的环境变量MYSQL_ROOT_PASSWORD
作为根密码,并支持其他一些可能被指定的环境变量。其他环境变量是 MySQL 数据库的MYSQL_DATABASE
、MYSQL
用户的MYSQL_USER
、MySQL 密码的MYSQL_PASSWORD
以及是否允许 root 密码为空的MYSQL_ALLOW_EMPTY_PASSWORD
。创建mysql
服务时已经设置了MYSQL_ROOT_PASSWORD
。使用docker service update
命令的--env-add
选项,我们可以添加其他环境变量。
∼ $ docker service update --env-add MYSQL_DATABASE='mysqldb' --env-add MYSQL_USER='mysql' --env-add MYSQL_PASSWORD='mysql' --env-add MYSQL_ALLOW_EMPTY_PASSWORD='no' --update-parallelism 1 mysql
mysql
mysql
的输出意味着命令成功运行。
滚动更新状态是通过docker service inspect
命令找到的,该命令除了列出添加到Env
JSON 对象中的env
变量之外,还列出了UpdateStatus
。更新状态的State
为updating
,消息为“update in progress”
。
∼ $ docker service inspect mysql
[
{...
"Spec": {
"Name": "mysql",
"ContainerSpec": {
...
"Env": [
"MYSQL_ROOT_PASSWORD=mysql",
"MYSQL_DATABASE=mysqldb",
"MYSQL_USER=mysql",
"MYSQL_PASSWORD=mysql",
"MYSQL_ALLOW_EMPTY_PASSWORD=no"
],
... },
"UpdateStatus": {
"State": "updating",
"StartedAt": "2017-07-25T19:18:11.44139778Z",
"Message": "update in progress"
}
}
]
当更新完成后,UpdateStatus
状态变为"completed"
,Message
变为"update completed"
。
∼ $ docker service inspect mysql
[
... },
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T19:18:11.44139778Z",
"CompletedAt": "2017-07-25T19:20:37.912993431Z",
"Message": "update completed"
}
}
]
如StartedAt
和CompletedAt
时间戳所示,滚动更新大约需要两分钟。只列出期望状态为running
的任务表明一个任务已经running
了 21 秒,另一个任务已经运行了两分钟。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
3zhf94kklu6r mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 21 seconds ago
ta16ch5kjlr9 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 2 minutes ago
fc7uxvwvcmk3 mysql.3 mysql:latest ip-172-31-2-177.ec2.internal Running Running about a minute ago
jir97p344kol mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running about a minute ago
5rly53mcc8yq mysql.5 mysql:latest ip-172-31-2-177.ec2.internal Running Running 45 seconds ago
添加的环境变量可以用另一个docker service update
命令和每个要删除的环境变量的--env-rm
选项删除。在--env-rm
中只指定了env
变量名,而不是env
值。
∼ $ docker service update --env-rm MYSQL_DATABASE --env-rm MYSQL_USER --env-rm
MYSQL_PASSWORD --env-rm MYSQL_ALLOW_EMPTY_PASSWORD mysql
mysql
执行另一个滚动更新。关闭所有服务任务,并启动基于新服务规范的新服务任务。服务定义只列出了强制的环境变量MYSQL_ROOT_PASSWORD
。
∼ $ docker service inspect mysql
[...
"Env": [
"MYSQL_ROOT_PASSWORD=mysql"
],
},
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T19:20:57.968668604Z",
"CompletedAt": "2017-07-25T19:22:59.18517919Z",
"Message": "update completed"
}
}
]
滚动更新以设置 CPU 和内存限制和预留
滚动更新可用于设置新的资源限制和储备。
∼ $ docker service update --reserve-cpu 1 --limit-cpu 2 --reserve-memory 256mb -
-limit-memory 512mb mysql
mysql
配置新的资源限制和预留,如服务规范中所列。PreviousSpec
表示没有Resources Limits
和Reservations
被配置开始。
∼ $ docker service inspect mysql
[
...
"Spec": {
"Name": "mysql",
...
"ContainerSpec": {
... },
"Resources": {
"Limits": {
"NanoCPUs": 2000000000,
"MemoryBytes": 536870912
},
"Reservations": {
"NanoCPUs": 1000000000,
"MemoryBytes": 268435456
}
},
... },
"PreviousSpec": {
...
"Name": "mysql",
"Resources": {
"Limits": {},
"Reservations": {}
},
"UpdateStatus": {
"State": "updating",
"StartedAt": "2017-07-25T19:23:44.004458295Z",
"Message": "update in progress"
}
}
]
设置新的资源限制和保留受节点容量限制的约束。如果请求的资源超过了节点容量,滚动更新可能会继续运行而无法完成,一些任务处于pending
当前状态。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
5u7zifw15n7t mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running about an hour ago
2kgsb16c8m8u mysql.2 mysql:latest Running Pending about an hour ago
mu08iu9qzqlh mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Running Running about an hour ago
aakxr8dw5s15 mysql.4 mysql:latest ip-172-31-2-177.ec2.internal Running Running about an hour ago
z6045639f20p mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running about an hour ago
如果一些任务是pending
,向集群添加资源可以使pending
任务运行。我们可以更新 CloudFormation 栈,将工作节点的数量从 2 个增加到 3 个,如图 9-2 所示。
图 9-2。
Increasing the number of worker nodes in the Swarm
随后,Swarm 应该列出四个节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
81h6uvu8uq0emnovzkg6v7mzg ip-172-31-2-177.ec2.internal Ready Active
e7vigin0luuo1kynjnl33v9pa ip-172-31-29-67.ec2.internal Ready Active
ptm7e0p346zwypos7wnpcm72d * ip-172-31-25-121.ec2.internal Ready Active Leader
t4d0aq9w2a6avjx94zgkwc557 ip-172-31-42-198.ec2.internal Ready Active
随着群中资源的增加,pending
任务也开始运行。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
5u7zifw15n7t mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running about an hour ago
2kgsb16c8m8u mysql.2 mysql:latest ip-172-31-2-177.ec2.internal Running Running 7 minutes ago
mu08iu9qzqlh mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Running Running about an hour ago
i5j2drlcm75f mysql.4 mysql:latest ip-172-31-42-198.ec2.internal Running Running 4 seconds ago
z6045639f20p mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running about an hour ago
滚动更新到不同的映像
滚动更新也可用于更新到完全不同的 Docker 映像。例如,对mysql
服务执行滚动更新,以使用 Docker 映像postgres
而不是它正在使用的mysql
映像。也可以设置其他选项,如--update-parallelism
。
∼ $ docker service update --image postgres --update-parallelism 1 mysql
mysql
mysql:latest
基于映像的任务开始关闭,postgres
基于映像的替换任务开始一次启动一个任务。滚动更新不会立即完成,将具有所需状态的服务任务列为running
会列出一些基于postgres:latest
映像的任务,而其他任务仍然使用mysql:latest
映像。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
9tzm5pa6pcyx mysql.1 postgres:latest ip-172-31-2-177.ec2.internal Running Running 39 seconds ago
xj23fu5svv9d mysql.2 postgres:latest ip-172-31-42-198.ec2.internal Running Running about a minute ago
mu08iu9qzqlh mysql.3 mysql:latest ip-172-31-29-67.ec2.internal Running Running about an hour ago
skzxi33c606o mysql.4 postgres:latest ip-172-31-2-177.ec2.internal Running Running 13 seconds ago
z6045639f20p mysql.5 mysql:latest ip-172-31-25-121.ec2.internal Running Running about an hour ago
一次一个复制副本,关闭mysql
基于映像的复制副本,启动postgres
基于映像的复制副本。大约两分钟后,所有任务都更新到了postgres:latest
的映像上。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
9tzm5pa6pcyx mysql.1 postgres:latest ip-172-31-2-177.ec2.internal Running Running about a minute ago
xj23fu5svv9d mysql.2 postgres:latest ip-172-31-42-198.ec2.internal Running Running about a minute ago
kd9pk31vpof2 mysql.3 postgres:latest ip-172-31-42-198.ec2.internal Running Running 35 seconds ago
skzxi33c606o mysql.4 postgres:latest ip-172-31-2-177.ec2.internal Running Running 59 seconds ago
umtitiuvt5gg mysql.5 postgres:latest ip-172-31-25-121.ec2.internal Running Running 8 seconds ago
服务名称保持不变,副本名称也包含前缀mysql
。mysql
服务定义ContainerSpec
将映像列为postgres
。将映像更新为postgres
并不意味着为新映像更新所有其他服务定义设置。postgres
映像不使用MYSQL_ROOT_PASSWORD
,但是环境变量仍然在服务规范中。
∼ $ docker service inspect mysql
[
"Spec": {
"Name": "mysql",
"ContainerSpec": {
"Image": "postgres:latest@sha256:e92fe21f695d27be7050284229a1c8c63ac10d88cba58d779c243566e125aa34",
"Env": [
"MYSQL_ROOT_PASSWORD=mysql"
],
"PreviousSpec": {
"Name": "mysql",
"ContainerSpec": {
"Image": "mysql:latest@sha256:75c563c474f1adc149978011fedfe2e6670483d133b22b07ee32789b626f8de3",
"Env": [
"MYSQL_ROOT_PASSWORD=mysql"
... },
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T20:39:45.230997671Z",
"CompletedAt": "2017-07-25T20:42:04.186537673Z",
"Message": "update completed"
}
}
]
可以用另一个update
命令删除MYSQL_ROOT_PASSWORD
环境变量。
∼ $ docker service update --env-rm MYSQL_ROOT_PASSWORD mysql
mysql
随后,ContainerSpec
不包括MYSQL_ROOT_PASSWORD
环境变量。
∼ $ docker service inspect mysql
[
...
"Spec": {
"Name": "mysql",
...
"ContainerSpec": {
"Image": "postgres:latest@sha256:e92fe21f695d27be7050284229a1c8c63ac10d88cba58d779c243566e125aa34",
"StopGracePeriod": 10000000000,
"DNSConfig": {}
},
... },
"PreviousSpec": {
"ContainerSpec": {
"Image": "postgres:latest@sha256:e92fe21f695d27be7050284229a1c8c63ac10d88cba58d779c243566e125aa34",
"Env": [
"MYSQL_ROOT_PASSWORD=mysql"
],
... },
"UpdateStatus": {
"State": "updating",
"StartedAt": "2017-07-25T20:42:56.651025816Z",
"Message": "update in progress"
}
}
]
删除环境变量的滚动更新涉及关闭所有服务任务并启动所有新任务。更新大约需要两分钟完成。
∼ $ docker service inspect mysql
[
},
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T20:42:56.651025816Z",
"CompletedAt": "2017-07-25T20:44:55.078906359Z",
"Message": "update completed"
}
}
]
列出正在运行的任务表明任务最多只运行了两分钟。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
menpo2zgit5u mysql.1 postgres:latest ip-172-31-2-177.ec2.internal Running Running about a minute ago
adnid3t69sue mysql.2 postgres:latest ip-172-31-25-121.ec2.internal Running Running about a minute ago
we92apfuivil mysql.3 postgres:latest ip-172-31-42-198.ec2.internal Running Running 46 seconds ago
ed7vh4ozefm5 mysql.4 postgres:latest ip-172-31-29-67.ec2.internal Running Running 2 minutes ago
i2x2377ad7u0 mysql.5 postgres:latest ip-172-31-25-121.ec2.internal Running Running about a minute ago
通过移除env
变量MYSQL_ROOT_PASSWORD
,mysql
服务被更新以使用 Docker 映像postgres
。服务名称本身无法更新。该服务可以被更新回mysql
映像,并且强制环境变量MYSQL_ROOT_PASSWORD
被添加另一个滚动更新。
∼ $ docker service update --image mysql --env-add MYSQL_ROOT_PASSWORD='mysql' mysql
mysql
同样,将具有所需状态的副本列为running
会列出由mysql
基于映像的副本替换的postgres
基于映像的副本。一次一个副本,postgres
基于映像的副本被mysql
基于映像的副本替换。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
menpo2zgit5u mysql.1 postgres:latest ip-172-31-2-177.ec2.internal Running Running 2 minutes ago
adnid3t69sue mysql.2 postgres:latest ip-172-31-25-121.ec2.internal Running Running 2 minutes ago
we92apfuivil mysql.3 postgres:latest ip-172-31-42-198.ec2.internal Running Running about a minute ago
pjvj50j822xr mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 12 seconds ago
i2x2377ad7u0 mysql.5 postgres:latest ip-172-31-25-121.ec2.internal Running Running 2 minutes ago
在一两分钟内,所有的postgres
映像副本都被基于mysql
映像的副本所取代。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
sobd90v7gbmz mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running about a minute ago
st5t7y8rdgg1 mysql.2 mysql:latest ip-172-31-29-67.ec2.internal Running Running 57 seconds ago
upekevrlbmgo mysql.3 mysql:latest ip-172-31-42-198.ec2.internal Running Running about a minute ago
pjvj50j822xr mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 2 minutes ago
nmrmdug87cy0 mysql.5 mysql:latest ip-172-31-2-177.ec2.internal Running Running 2 minutes ago
服务规范被更新为mysql
映像,并添加了强制环境变量MYSQL_ROOT_PASSWORD
。更新完成后,UpdateStatus State
变为completed
。
∼ $ docker service inspect mysql
[
"Spec": {
"Name": "mysql",
...
"Image": "mysql:latest@sha256:75c563c474f1adc149978011fedfe2e6670483d133b22b07ee32789b626f8de3",
"Env": [
"MYSQL_ROOT_PASSWORD=mysql"
],
... },
"PreviousSpec": {
"Name": "mysql",
"ContainerSpec": {
"Image": "postgres:latest@sha256:e92fe21f695d27be7050284229a1c8c63ac10d88cba58d779c243566e125aa34",
... },
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T20:45:54.104241339Z",
"CompletedAt": "2017-07-25T20:47:47.996420791Z",
"Message": "update completed"
}
}
]
滚动重启
Docker 1.13 增加了一个新选项,即使根据更新选项不需要更新,也可以执行滚动重启。例如,从更新配置为--update-parallelism
1 和--update-delay
20s 的mysql
服务开始,下面的update
命令不会执行任何滚动更新,因为没有对服务进行任何更改。
∼ $ docker service update --update-parallelism 1 --update-delay 20s mysql
mysql
要强制滚动重启,包括--force
选项。
∼ $ docker service update --force --update-parallelism 1 --update-delay 20s mysql
mysql
服务任务开始被关闭,并且新的服务任务开始,即使没有对服务规范进行更新。一些任务被列为几秒钟前已经开始。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
sobd90v7gbmz mysql.1 mysql:latest ip-172-31-25-121.ec2.internal Running Running 3 minutes ago
trye9chir91l mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running 23 seconds ago
uu7sfp147xnu mysql.3 mysql:latest ip-172-31-42-198.ec2.internal Running Running less than a second ago
pjvj50j822xr mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 4 minutes ago
nmrmdug87cy0 mysql.5 mysql:latest ip-172-31-2-177.ec2.internal Running Running 3 minutes ago
滚动重启可能需要 1-2 分钟才能完成。
∼ $ docker service inspect mysql
[
...
},
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T20:49:34.716535081Z",
"CompletedAt": "2017-07-25T20:51:36.880045931Z",
"Message": "update completed"
}
}
]
滚动重启完成后,该服务具有所有新的服务任务,如下所示。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
z2n2qcgfsbke mysql.1 mysql:latest ip-172-31-29-67.ec2.internal Running Running 6 seconds ago
trye9chir91l mysql.2 mysql:latest ip-172-31-25-121.ec2.internal Running Running about a minute ago
uu7sfp147xnu mysql.3 mysql:latest ip-172-31-42-198.ec2.internal Running Running about a minute ago
1aovurxkteq1 mysql.4 mysql:latest ip-172-31-29-67.ec2.internal Running Running 29 seconds ago
r0lslq6jibvp mysql.5 mysql:latest ip-172-31-2-177.ec2.internal Running Running 52 seconds ago
添加和删除装载的滚动更新
滚动更新还可以用于添加和删除挂载。例如,我们添加一个类型为volume
的挂载,其中源卷由src
指定,目标目录由dst
指定。
∼ $ docker service update \
> --mount-add type=volume,src=mysql-scripts,dst=/etc/mysql/scripts \
> mysql
mysql
装载将添加到服务中,并在服务定义中列出。添加装载涉及关闭所有服务任务并启动新任务。滚动更新可能需要 1-2 分钟。
∼ $ docker service inspect mysql
[
"Spec": {
"ContainerSpec": {
...
"Mounts": [
{
"Type": "volume",
"Source": "mysql-scripts",
"Target": "/etc/mysql/scripts"
}
],
...
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T20:51:55.205456644Z",
"CompletedAt": "2017-07-25T20:53:56.451313826Z",
"Message": "update completed"
}
}
]
添加的挂载可以用docker service update
命令的--mount-rm
选项删除,只需提供挂载目标目录作为参数。
∼ $ docker service update \
> --mount-rm /etc/mysql/scripts \
> mysql
mysql
执行另一次滚动更新,并删除挂载。它不会在服务定义中列出。PreviousSpec
列出了挂载。UpdateStatus
表示滚动更新的状态。
∼ $ docker service inspect mysql
[
"Spec": {
"Name": "mysql",
"ContainerSpec": {
...
"PreviousSpec": {
"Name": "mysql",
...
"Mounts": [
{
"Type": "volume",
"Source": "mysql-scripts",
"Target": "/etc/mysql/scripts"
}
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T20:55:56.30844324Z",
"CompletedAt": "2017-07-25T20:57:58.489349432Z",
"Message": "update completed"
}
}
]
滚动更新失败操作
docker service create
和docker service update
命令的--update-failure-action
选项指定如果任务更新失败并返回FAILED
时要采取的后续动作。我们将mysql
服务的UpdateConfig
设置为包含一个pause
的--update-failure-action
(默认)。另一个选项设置是continue
,它不会暂停滚动更新,而是继续下一个任务的更新。为了演示一个更新失败动作,指定一个不存在的 Docker 映像,比如mysql:5.9
。
∼ $ docker service update \
> --replicas 10 \
> --image mysql:5.9 \
> --update-delay 10s \
> --update-failure-action pause \
> mysql
image mysql:5.9 could not be accessed on a registry to record
its digest. Each node will access mysql:5.9 independently,
possibly leading to different nodes running different
versions of the image.
mysql
滚动更新仍在开始,更新状态显示更新为paused
。更新状态消息指示“由于失败或任务提前终止,更新暂停”。
∼ $ docker service inspect mysql
[
"Spec": {
"Name": "mysql",
},
"UpdateConfig": {
"Parallelism": 1,
"Delay": 10000000000,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"RollbackConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
... },
"UpdateStatus": {
"State": "paused",
"StartedAt": "2017-07-25T20:58:51.695333064Z",
"Message": "update paused due to failure or early termination of task s1p1n0x3k67uwpoj7qxg13747"
}
}
]
如果滚动更新因任务更新失败而暂停,则有两个选项可用。
- 使用
docker service update <SERVICE-ID>
重启暂停的更新。 - 如果更新失败重复出现,找到失败的原因,并通过向
docker service update <SERVICE-ID>
命令提供其他选项来重新配置服务。
回滚到以前的规格
Docker 1.13 Swarm mode 增加了回滚到之前服务定义的特性。例如,执行滚动更新,将mysql
服务的映像更新为postgres
。基于mysql
的副本开始关闭,基于postgres
的副本开始启动。在从mysql
映像到postgres
映像的滚动更新过程中的任何时候,或者在对postgres
映像的更新完成之后,如果确定滚动更新不应该开始或执行,可以使用以下命令回滚滚动更新。为了演示回滚,我们首先启动一个mysql
服务。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 5 \
> --name mysql \
> --update-delay 10s \
> --update-parallelism 1 \
> mysql:5.6
xkmrhnk0a444zambp9yh1mk9h
我们从mysql
映像开始对postgres
映像进行滚动更新。
∼ $ docker service update --image postgres mysql
mysql
随后,一些任务基于postgres
映像,一些基于mysql
映像。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
mnm5pg9ha61u mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 58 seconds ago
9y0fzn4sgiv0 mysql.2 postgres:latest ip-172-31-2-177.ec2.internal Ready Ready 2 seconds ago
ewl7zxwi07gc \_ mysql.2 mysql:5.6 ip-172-31-2-177.ec2.internal Shutdown Running 2 seconds ago
l3ock28cmtzx mysql.3 mysql:5.6 ip-172-31-42-198.ec2.internal Running Running 22 seconds ago
1vqs3lcqvbt5 mysql.4 postgres:latest ip-172-31-29-67.ec2.internal Running Running 12 seconds ago
wu11jjbszesy \_ mysql.4 mysql:5.6 ip-172-31-29-67.ec2.internal Shutdown Shutdown 13 seconds ago
g3tr6z9l5vzx mysql.5 mysql:5.6 ip-172-31-42-198.ec2.internal Running Running 22 seconds ago
开始回滚以恢复到mysql
映像。
∼ $ docker service update --rollback mysql
mysql
postgres
基于映像的任务开始关闭,而mysql
基于映像的任务开始。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
mnm5pg9ha61u mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running about a minute ago
gyqgtoc4ix3y mysql.2 mysql:5.6 ip-172-31-2-177.ec2.internal Running Running 14 seconds ago
9y0fzn4sgiv0 \_ mysql.2 postgres:latest ip-172-31-2-177.ec2.internal Shutdown Shutdown 15 seconds ago
ewl7zxwi07gc \_ mysql.2 mysql:5.6 ip-172-31-2-177.ec2.internal Shutdown Shutdown 23 seconds ago
l3ock28cmtzx mysql.3 mysql:5.6 ip-172-31-42-198.ec2.internal Running Running 46 seconds ago
ecvh8fd5308k mysql.4 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 16 seconds ago
1vqs3lcqvbt5 \_ mysql.4 postgres:latest ip-172-31-29-67.ec2.internal Shutdown Shutdown 16 seconds ago
wu11jjbszesy \_ mysql.4 mysql:5.6 ip-172-31-29-67.ec2.internal Shutdown Shutdown 37 seconds ago
m27d3gz4g6dy mysql.5 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 1 second ago
g3tr6z9l5vzx \_ mysql.5 mysql:5.6 ip-172-31-42-198.ec2.internal Shutdown Failed 6 seconds ago "task: non-zero exit (1)"
从mysql
到postgres
的滚动更新被回滚。回滚完成后,所有副本都是基于mysql
映像的,这是服务开始时的理想状态。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xamxi29okj74 mysql.1 mysql:5.6 ip-172-31-25-121.ec2.internal Running Running 30 seconds ago
gyqgtoc4ix3y mysql.2 mysql:5.6 ip-172-31-2-177.ec2.internal Running Running 56 seconds ago
l3ock28cmtzx mysql.3 mysql:5.6 ip-172-31-42-198.ec2.internal Running Running about a minute ago
ecvh8fd5308k mysql.4 mysql:5.6 ip-172-31-29-67.ec2.internal Running Running 58 seconds ago
全球服务的滚动更新
也可以在全局服务上执行滚动更新。为了演示,我们为mysql:latest
映像创建了一个全局服务。
∼ $ docker service rm mysql
mysql
∼ $ docker service create \
> --mode global \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --name mysql \
> mysql
7nokncnti3izud08gfdovwxwa
Start a rolling update to Docker image mysql:5.6\. ∼ $ docker service update \
> --image mysql:5.6 \
> --update-delay 10s \
> mysql
mysql
服务已更新。Spec>ContainerSpec>Image
从mysql:latest
的PreviousSpec>ContainerSpec>Image
更新为mysql:5.6
。
∼ $ docker service inspect mysql
[
"Spec": {
"Name": "mysql",
"ContainerSpec": {
"Image": "mysql:5.6@sha256:6ad5bd392c9190fa92e65fd21f6debc8b2a76fc54f13949f9b5bc6a0096a5285",
},
"PreviousSpec": {
"Name": "mysql",
"ContainerSpec": {
"Image": "mysql:latest@sha256:75c563c474f1adc149978011fedfe2e6670483d133b22b07ee32789b626f8de3",
"UpdateStatus": {
"State": "completed",
"StartedAt": "2017-07-25T21:06:46.973666693Z",
"CompletedAt": "2017-07-25T21:07:46.656023733Z",
"Message": "update completed"
}
}
]
一分钟之内,所有基于mysql:5.6
的新服务任务开始。
∼ $ docker service ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
ybf4xpofte8l mysql.81h6uvu8uq0emnovzkg6v7mzg mysql:5.6 ip-172-31-2-177.ec2.internal Running Running 46 seconds ago
7nq99jeil9n0 mysql.t4d0aq9w2a6avjx94zgkwc557 mysql:5.6 ip-172-31-42-198.ec2.internal Running Running about a minute ago
wcng24mq7e8m mysql.e7vigin0luuo1kynjnl33v9pa mysql:5.6 ip-172-31-29-67.ec2.internal Running Running about a minute ago
q14t2pyhra3w mysql.ptm7e0p346zwypos7wnpcm72d mysql:5.6 ip-172-31-25-121.ec2.internal Running Running about a minute ago
不能在全局服务上执行滚动更新以使用--replicas
选项设置副本,如下面的docker service update
命令中的消息所示。
∼ $ docker service update \
> --image mysql \
> --replicas 1 \
> mysql
replicas can only be used with replicated mode
如输出所示,虽然副本是在复制的服务mysql
上设置的,但副本不是在全局服务上设置的。
摘要
本章讨论了服务的滚动更新。服务的滚动更新包括关闭以前的服务任务,并更新服务定义以启动新任务。在下一章,我们将讨论在群组模式下配置网络。
十、网络连接
Docker 引擎上的网络是由桥接网络提供的,即docker0
桥。docker0
桥在 Docker 主机的范围内是本地的,并且在安装 Docker 时默认安装。所有 Docker 容器都运行在 Docker 主机上,并连接到docker0
桥网络。他们通过网络互相交流。
问题
默认的docker0
桥接网络有以下限制:
-
桥接网络的范围限于本地 Docker 主机,以提供容器到容器的联网,而不是多主机联网。
-
The bridge network isolates the Docker containers on the host from external access. A Docker container may expose a port or multiple ports and the ports may be published on the host for an external client host access, as illustrated in Figure 10-1, but by default the
docker0
bridge does not provide any external client access outside the network.图 10-1。
The default docker0 bridge network
解决方案
群模式(Docker 引擎> =1.12)为群中的节点创建一个名为ingress
的overlay
网络。ingress
覆盖网络是一个多主机网络,用于将ingress
流量路由到群;外部客户端使用它来访问群服务。如果服务发布了一个端口,它们就会被添加到ingress
网络中。ingress
覆盖网络具有默认网关和子网,并且ingress
网络中的所有服务都暴露在群中的所有节点上,无论服务是否在每个节点上有任务调度。除了ingress
网络,可使用覆盖驱动程序创建自定义覆盖网络。自定义覆盖网络在群中的 Docker 守护进程之间提供网络连接,并用于服务对服务的通信。Ingress
是一种特殊类型的覆盖网络,不用于服务或任务之间的网络流量。群模式网络如图 10-2 所示。
图 10-2。
The Swarm overlay networks
以下 Docker 网络用于或可用于群组模式。
入口网络
初始化群组模式时,会自动创建ingress
网络。在 Docker for AWS 上,ingress
网络是现成可用的,因为托管服务默认启用了群组模式。名为ingress
的默认覆盖网络扩展到群中的所有节点,无论该节点是否有预定的服务任务。ingress
提供服务任务之间的负载均衡。所有发布端口的服务都被添加到ingress
网络中。如果服务发布一个端口,甚至在内部网络中创建的服务也被添加到ingress
。如果服务没有发布端口,它不会被添加到ingress
网络中。服务使用下面的docker service create
命令语法发布带有--publish
或–p
选项的端口。
docker service create \
--name <SERVICE-NAME> \
--publish <PUBLISHED-PORT>:<TARGET-PORT> \
<IMAGE>
如果省略了<PUBLISHED-PORT>
,群组管理器选择 30000-32767 范围内的一个端口来发布服务。
要使用ingress
网络,群组节点之间必须开放以下端口。
- 端口 7946 TCP/UDP 用于容器网络发现
- 端口 4789 UDP 用于容器
ingress
网络
自定义覆盖网络
使用覆盖驱动器创建定制覆盖网络,并且可以在覆盖网络中创建服务。使用docker service create
命令的--network
选项在覆盖网络中创建服务。覆盖网络提供服务对服务的通信。覆盖网络中的一个对接容器可以直接与网络中的另一个对接容器通信,无论该容器是在同一节点上还是在不同的节点上。只有用于群服务任务的 Docker 容器可以使用覆盖网络相互连接,而不仅仅是群中主机上运行的任何 Docker 容器。例如,用docker run <img>
命令启动的 Docker 容器不能连接到 Swarm 覆盖网络,例如使用docker network connect <overlay network> <container>
。不在群中的对接主机上的对接容器也不能与群中的对接容器直接连接和通信。不同群覆盖网络中的 Docker 容器不能彼此直接通信,因为每个群覆盖网络与其他网络隔离。
尽管群中的默认覆盖网络ingress
扩展到群中的所有节点,而不管服务任务是否在其上运行,但是其范围也是群的定制覆盖网络默认不扩展到群中的所有节点。定制的群覆盖网络仅扩展到群中运行由定制的群覆盖网络创建的服务任务的那些节点。
“覆盖”网络覆盖主机的底层网络,覆盖网络的范围是群。覆盖网络中的服务容器具有不同的 IP 地址,并且每个覆盖网络分配有不同范围的 IP 地址。在现代内核中,覆盖网络可以与底层网络重叠,因此,多个网络可以拥有相同的 IP 地址。
docker_gwbridge 网络
初始化群组模式时自动创建的另一个网络(除了ingress
网络)是docker_gwbridge
网络。docker_gwbr
idge 网络是一个桥接网络,它将包括ingress
网络在内的所有覆盖网络连接到 Docker 守护进程的主机网络。每个服务容器都连接到本地 Docker 守护进程主机的docker_gwbridge
网络。
桥接网络
网桥网络是由 Docker 管理的主机上的网络。主机上的 Docker 容器通过桥接网络相互通信。不公布端口的群模式服务也在桥接网络中创建。以docker run
命令开始的 Docker 容器也是如此。这意味着不发布端口的群模式 Docker 服务与用docker run
命令启动的 Docker 容器在同一个网络中。
本章涵盖以下主题:
- 设置环境
- Swarm 模式下的联网
- 使用默认覆盖网络
ingress
创建服务 - 创建自定义叠加网络
- 使用自定义覆盖网络创建服务
- 连接到同一覆盖网络中的另一个 Docker 容器
- 创建内部网络
- 删除网络
设置环境
在 Docker 上为 AWS 创建一个三节点 Docker 群,如第三章所述。如图 10-3 所示,一个自动气象站云形成栈被用来创建一个蜂群。
图 10-3。
AWS CloudFormation stack
获取群管理器节点的公共 IP 地址,如图 10-4 所示。
图 10-4。
Obtaining the public IP address of a Swarm manager node instance
SSH 登录到 Swarm manager 实例。
[root@localhost ∼]# ssh -i "docker.pem" docker@174.129.48.148
Welcome to Docker!
列出群节点——一个管理者和两个工作者节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
npz2akark8etv4ib9biob5yyk ip-172-31-47-123.ec2.internal Ready Active
p6wat4lxq6a1o3h4fp2ikgw6r ip-172-31-3-168.ec2.internal Ready Active
tb5agvzbi0rupq7b83tk00cx3 * ip-172-31-47-15.ec2.internal Ready Active Leader
Swarm 模式下的联网
群组模式提供了一些默认网络,可以用docker network ls
命令列出。这些网络不仅可以在 Docker for AWS 上使用,还可以在任何平台(如 CoreOS)上以 Swarm 模式使用。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
34a5f77de8cf bridge bridge local
0e06b811a613 docker_gwbridge bridge local
6763ebad69cf host host local
e41an60iwval ingress overlay swarm
eb7399d3ffdd none null local
我们在上一节中讨论了这些网络中的大多数。“host
”网络是主机的网络栈。“none
”网络不提供 Docker 容器和主机网络栈之间的网络,并且创建没有网络访问的容器。
即使在调度任何服务任务之前,默认网络在群管理器节点和群工作者节点上也是可用的。
可使用设置为overlay
的驱动过滤器过滤列出的网络。
docker network ls --filter driver=overlay
仅列出了ingress
网络。默认情况下,不会提供其他覆盖网络。
∼ $ docker network ls --filter driver=overlay
NETWORK ID NAME DRIVER SCOPE
e41an60iwval ingress overlay swarm
感兴趣的网络是称为ingress
的覆盖网络,但是除了在章节介绍中讨论之外,所有默认网络都在表 10-1 中讨论。
表 10-1。
Docker Networks
| 网络 | 描述 | | `bridge` | `bridge`网络是在所有 Docker 主机上创建的`docker0`网络。默认情况下,Docker 守护进程将容器连接到`docker0`网络。任何以`docker run command`开头的 Docker 容器,甚至在一个群节点上,都连接到`docker0`桥网络。 | | `docker_` `gwbridge` | 用于不同主机上的群节点之间的通信。该网络用于向缺少用于连接外部网络和其他群节点的替代网络的容器提供外部连接。当一个容器连接到多个网络时,它的外部连接是通过第一个非内部网络按词汇顺序提供的。 | | `host` | 将容器添加到主机的网络栈中。容器内部的网络配置与主机的配置相同。 | | `ingress` | Swarm 用于`ingress`的覆盖网络,是外部接入。`ingress`网络仅用于路由网状网/ `ingress`流量。 | | `none` | 将容器添加到特定于容器的网络栈中,该容器缺少网络接口。 |默认网络不能删除,除了ingress
网络,用户不需要直接连接或使用其他网络。要查找关于ingress
网络的详细信息,请运行以下命令。
docker network inspect ingress
ingress
网络的范围是 Swarm,使用的驱动程序是overlay
。子网和网关分别是10.255.0.0/16
和10.255.0.1
。false
的内部设置显示ingress
网络不是内部网络,这意味着该网络连接到外部网络。ingress
网络有一个 IPv4 地址,但该网络不支持 IPv6。
∼ $ docker network inspect ingress
[
{
"Name": "ingress",
"Id": "e41an60iwvalbeq5y3stdfem9",
"Created": "2017-07-26T18:38:29.753424199Z",
"Scope": "swarm",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "10.255.0.0/16",
"Gateway": "10.255.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": true,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"ingress-sbox": {
"Name": "ingress-endpoint",
"EndpointID": "f646b5cc4316994b8f9e5041ae7c82550bc7ce733db70df3f66b8d771d0f53c4",
"MacAddress": "02:42:0a:ff:00:02",
"IPv4Address": "10.255.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.driver.overlay.vxlanid_list": "4096"
},
"Labels": {},
"Peers": [
{
"Name": "ip-172-31-47-15.ec2.internal-17c7f752fb1a",
"IP": "172.31.47.15"
},
{
"Name": "ip-172-31-47-123.ec2.internal-d6ebe8111adf",
"IP": "172.31.47.123"
},
{
"Name": "ip-172-31-3-168.ec2.internal-99510f4855ce",
"IP": "172.31.3.168"
}
]
}
]
使用默认桥接网络创建服务
要使用默认桥接网络在群组模式下创建服务,不需要指定任何特殊选项。不得指定--publish
或–p
选项。为mysql
数据库创建一个服务。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --name mysql \
> mysql
likujs72e46ti5go1xjtksnky
创建服务,并在其中一个节点上安排服务任务。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
likujs72e46t mysql replicated 1/1 mysql:latest
该服务可以扩展到在整个群体中运行任务。
∼ $ docker service scale mysql=3
mysql scaled to 3
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
v4bn24seygc6 mysql.1 mysql:latest ip-172-31-47-15.ec2.internal Running Running 2 minutes ago
29702ebj52gs mysql.2 mysql:latest ip-172-31-47-123.ec2.internal Running Running 3 seconds ago
c7b8v16msudl mysql.3 mysql:latest ip-172-31-3-168.ec2.internal Running Running 3 seconds ago
创建的mysql
服务没有添加到ingress
网络,因为它没有发布端口。
在入口网络中创建服务
在本节中,我们将在ingress
网络中创建一个 Docker 服务。不使用docker service create
的--network
选项指定ingress
网络。服务必须发布一个要在ingress
网络中创建的端口。创建一个在端口8080
上发布的 Hello World 服务。
∼ $ docker service rm hello-world
hello-world
∼ $ docker service create \
> --name hello-world \
> -p 8080:80\
> --replicas 3 \
> tutum/hello-world
l76ukzrctq22mn97dmg0oatup
该服务创建三个任务,群中的每个节点一个。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
l76ukzrctq22 hello-world replicated 3/3 tutum/hello-world:latest *:8080->80/tcp
∼ $ docker service ps hello-world
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
5ownzdjdt1yu hello-world.1 tutum/hello-world: latest ip-172-31-14-234.ec2.internal Running Running 33 seconds ago
csgofrbrznhq hello-world.2 tutum/hello-world:latest ip-172-31-47-203.ec2.internal Running Running 33 seconds ago
sctlt9rvn571 hello-world.3 tutum/hello-world:latest ip-172-31-35-44.ec2.internal Running Running 32 seconds ago
可以使用<Public DNS>:<8080>
URL 在端口 8080 上的 Swarm 中的任何节点实例上访问该服务。如果创建了弹性负载均衡器,对于 AWS 的 Docker,可以在<LoadBalancer DNS>:<8080>
访问服务,如图 10-5 所示。
图 10-5。
Invoking a Docker service in the ingress network using EC2 elastic load balancer public DNS
在docker service create
命令中可以省略<PublishedPort> 8080
。
∼ $ docker service create \
> --name hello-world \
> -p 80\
> --replicas 3 \
> tutum/hello-world
pbjcjhx163wm37d5cc5au2fog
在整个蜂群中启动三个服务任务。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
pbjcjhx163wm hello-world replicated 3/3 tutum/hello-world:latest *:0->80/tcp
∼ $ docker service ps hello-world
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xotbpvl0508n hello-world.1 tutum/hello-world:latest ip-172-31-37-130.ec2.internal Running Running 13 seconds ago
nvdn3j5pzuqi hello-world.2 tutum/hello-world:latest ip-172-31-44-205.ec2.internal Running Running 13 seconds ago
uuveltc5izpl hello-world.3 tutum/hello-world:latest ip-172-31-15-233.ec2.internal Running Running 14 seconds ago
群组管理器自动分配一个发布端口(30000
),如docker service inspect
命令中所列。
∼ $ docker service inspect hello-world
[
"Spec": {
"Name": "hello-world",
...
"EndpointSpec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishMode": "ingress"
}
]
}
},
"Endpoint": {
"Spec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishMode": "ingress"
}
]
},
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 30000,
"PublishMode": "ingress"
}
],
"VirtualIPs": [
{
"NetworkID": "bllwwocjw5xejffmy6n8nhgm8",
"Addr": "10.255.0.5/16"
}
]
}
}
]
即使服务发布了一个端口(30000 或 30000-32767 范围内的其他可用端口),AWS 群组 Docker 的 AWS 弹性负载均衡器也不会为发布的端口(30000 或 30000-32767 范围内的其他可用端口)添加侦听器。我们添加一个<Load Balancer Port:Instance Port>
映射为 30000:30000 的监听器,如图 10-6 所示。
图 10-6。
Adding a load balancer listener
在<Load Balancer DNS>:<30000>
URL 调用服务,如图 10-7 所示。
图 10-7。
Invoking a Hello World service on port 30000
创建自定义叠加网络
我们使用默认的覆盖网络ingress
在 Swarm 模式下提供。ingress
网络仅用于包含所有节点的群模式路由网格。提供群路由网格,使得群中的每个节点可以接受群中服务的公布端口上的连接,即使服务没有在节点上运行任务。ingress
网络不是用于服务对服务的通信。
定制的覆盖网络可以在群组模式中用于服务到服务的通信。接下来,使用一些高级选项创建一个覆盖网络,包括使用--subnet
选项设置子网,使用--gateway
选项设置默认网关,以及使用--ip-range
选项设置 IP 范围。--driver
选项必须设置为overlay
,网络必须以群组模式创建。指定 IP 范围的匹配子网必须可用。子网是 IP 网络的逻辑细分。网关是将主机的子网连接到其他网络的路由器。以下命令必须从管理器节点运行。
∼ $ docker network create \
> --subnet=192.168.0.0/16 \
> --subnet=192.170.0.0/16 \
> --gateway=192.168.0.100 \
> --gateway=192.170.0.100 \
> --ip-range=192.168.1.0/24 \
> --driver overlay \
> mysql-network
mkileuo6ve329jx5xbd1m6r1o
自定义覆盖网络被创建并在网络中作为具有群组范围的覆盖网络列出。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
34a5f77de8cf bridge bridge local
0e06b811a613 docker_gwbridge bridge local
6763ebad69cf host host local
e41an60iwval ingress overlay swarm
mkileuo6ve32 mysql-network overlay swarm
eb7399d3ffdd none null local
仅列出覆盖网络应列出ingress
网络和自定义mysql-network
。
∼ $ docker network ls --filter driver=overlay
NETWORK ID NAME DRIVER SCOPE
e41an60iwval ingress overlay swarm
mkileuo6ve32 mysql-network overlay swarm
关于自定义覆盖网络的详细信息mysql-network
列出了子网和网关。
∼ $ docker network inspect mysql-network
[
{
"Name": "mysql-network",
"Id": "mkileuo6ve329jx5xbd1m6r1o",
"Created": "0001-01-01T00:00:00Z",
"Scope": "swarm",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "192.168.0.0/16",
"IPRange": "192.168.1.0/24",
"Gateway": "192.168.0.100"
},
{
"Subnet": "192.170.0.0/16",
"Gateway": "192.170.0.100"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": null,
"Options": {
"com.docker.network.driver.overlay.vxlanid_list": "4097,4098"
},
"Labels": null
}
]
只能为特定的子网、网关和 IP 范围创建一个覆盖网络。使用不同的子网、网关或 IP 范围,可以创建不同的覆盖网络。
∼ $ docker network create \
> --subnet=10.0.0.0/16 \
> --gateway=10.0.0.100 \
> --ip-range=10.0.1.0/24 \
> --driver overlay \
> mysql-network-2
qwgb1lwycgvogoq9t62ea4ny1
创建mysql-network-2
并将其添加到网络列表中。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
34a5f77de8cf bridge bridge local
0e06b811a613 docker_gwbridge bridge local
6763ebad69cf host host local
e41an60iwval ingress overlay swarm
mkileuo6ve32 mysql-network overlay swarm
qwgb1lwycgvo mysql-network-2 overlay swarm
eb7399d3ffdd none null local
新的覆盖网络仅对具有使用覆盖的容器的工作者节点可用。虽然新的覆盖网络mysql-network
和mysql-network-2
在管理节点上可用,但是网络没有扩展到两个工作节点。SSH 登录到一个工作节点。
[root@localhost ∼]# ssh -i "docker.pem" docker@54.209.159.170
Welcome to Docker!
工作节点上没有列出mysql-network
和mysql-network-2
网络。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
255542d86c1b bridge bridge local
3a4436c0fb00 docker_gwbridge bridge local
bdd0be4885e9 host host local
e41an60iwval ingress overlay swarm
5c5f44ec3933 none null local
要将自定义覆盖网络扩展到工作节点,请在网络中创建一个在工作节点上运行任务的服务,这将在下一节中讨论。
默认情况下,群组模式覆盖网络是安全的。gossip
协议用于在群节点之间交换覆盖网络信息。节点在 GCM 模式下使用 AES 算法对交换的信息进行加密和认证。默认情况下,管理节点每 12 小时轮换一次八卦数据的加密密钥。覆盖网络上不同节点上的容器之间交换的数据也可以使用--opt encrypted
选项来加密,这在调度任务的所有节点之间创建 IPSEC 隧道。IPSEC 隧道还在 GCM 模式下使用 AES 算法,并每 12 小时轮换一次 gossip 数据的加密密钥。以下命令创建一个加密网络。
∼ $ docker network create \
> --driver overlay \
> --opt encrypted \
> overlay-network-2
aqppoe3qpy6mzln46g5tunecr
创建加密的群范围网络。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
34a5f77de8cf bridge bridge local
0e06b811a613 docker_gwbridge bridge local
6763ebad69cf host host local
e41an60iwval ingress overlay swarm
mkileuo6ve32 mysql-network overlay swarm
qwgb1lwycgvo mysql-network-2 overlay swarm
eb7399d3ffdd none null local
aqppoe3qpy6m overlay-network-2 overlay swarm
使用自定义覆盖网络创建服务
如果使用自定义覆盖网络来创建服务,则必须指定--network
。下面的命令使用自定义 Swarm 作用域覆盖网络mysql-network
在 Swarm 模式下创建一个 MySQL 数据库服务。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 1 \
> --network mysql-network \
> --name mysql-2\
> mysql
ocd9sz8qqp2becf0ww2rj5p5n
创建了mysql-2
服务。将mysql-2
服务扩展到三个副本,并列出该服务的服务任务。
∼ $ docker service scale mysql-2=3
mysql-2 scaled to 3
两种服务的两个不同网络中的 Docker 容器— mysql
(桥接网络)和mysql-2
(MySQL-网络覆盖网络)—同时运行在同一个节点上。
定制覆盖网络不扩展到群中的所有节点,直到节点具有使用定制网络的服务任务。直到在节点上为mysql-2
安排了服务任务之后,mysql-network
才被扩展到工作节点并在其上列出。
由默认对接引擎桥接网络docker0
管理的对接容器不能与群范围覆盖网络中的对接容器连接。不支持在docker run
命令中使用群组覆盖网络,使用docker network connect
命令连接群组覆盖网络,或者使用docker network connect
命令的--link
选项链接 Docker 容器和群组覆盖网络。群范围内的覆盖网络只能由群中的 Docker 服务使用。
对于服务容器之间的连接:
- 同一群范围覆盖网络中的相同或不同服务的对接容器能够彼此连接。
- 不同群范围覆盖网络中相同或不同服务的对接容器不能相互连接。
在下一节中,我们将讨论内部网络,但在此之前,应该先介绍一下外部网络。我们已经创建的 Docker 容器是外部网络容器。ingress
网络和自定义覆盖网络mysql-
network
是外部网络。外部网络提供通往网关的默认路由。主机和更广泛的互联网网络可以连接到ingress
或定制覆盖网络中的 Docker 容器。例如,运行以下命令从 Docker 容器的 bash shell pinggoogle.com
;Docker 容器应该在ingress
覆盖网络或定制的 Swarm 覆盖网络中。
docker exec –it <containerid> ping –c 1 google.com
建立连接并交换数据。命令输出以斜体显示。
∼ $ docker exec -it 3762d7c4ea68 ping -c 1 google.com
PING google.com (172.217.7.142): 56 data bytes
64 bytes from 172.217.7.142: icmp_seq=0 ttl=47 time=0.703 ms
--- google.com ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.703/0.703/0.703/0.000 ms
创建内部覆盖网络
在本节中,我们将讨论如何创建和使用内部覆盖网络。内部网络不提供外部连接。使网络内部化的原因是,没有为来自主机或更广泛的互联网的外部连接提供通往网关的默认路由。
首先,使用docker network create
命令的--internal
选项创建一个内部覆盖网络。添加一些其他选项,如--label,
,这些选项与内部网络无关。它是用docker network create
命令的--internal
选项配置的。
∼ $ docker network create \
> --subnet=10.0.0.0/16 \
> --gateway=10.0.0.100 \
> --internal \
> --label HelloWorldService \
> --ip-range=10.0.1.0/24 \
> --driver overlay \
> hello-world-network
pfwsrjeakomplo5zm6t4p19a9
内部网络的创建和列出方式与外部网络完全相同。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
194d51d460e6 bridge bridge local
a0674c5f1a4d docker_gwbridge bridge local
pfwsrjeakomp hello-world-network overlay swarm
03a68475552f host host local
tozyadp06rxr ingress overlay swarm
3dbd3c3ef439 none null local
在网络描述中,internal
被设置为true
。
core@ip-172-30-2-7 ∼ $ docker network inspect hello-world-network
[
{
"Name": "hello-world-network",
"Id": "58fzvj4arudk2053q6k2t8rrk",
"Scope": "swarm",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "10.0.0.0/16",
"IPRange": "10.0.1.0/24",
"Gateway": "10.0.0.100"
}
]
},
"Internal": true,
"Containers": null,
"Options": {
"com.docker.network.driver.overlay.vxlanid_list": "257"
},
"Labels": {
"HelloWorldService": ""
}
}
]
使用--network
选项创建一个使用内部网络的服务。
∼ $ docker service create \
> --name hello-world \
> --network hello-world-network \
> --replicas 3 \
> tutum/hello-world
hm5pf6ftcvphdrd2zm3pp4lpj
创建服务并安排副本。
获取服务任务之一的容器 ID,d365d4a5ff4c
。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d365d4a5ff4c tutum/hello-world:latest "/bin/sh -c 'php-f..." About a minute ago Up About a minute hello-world.3.r759ddnl1de11spo0zdi7xj4z
和以前一样,从 Docker 容器中 ping google.com
。
docker exec –it <containerid> ping –c 1 google.com
未建立连接,这是因为容器位于内部覆盖网络中。
∼ $ docker exec -it d365d4a5ff4c ping -c 1 google.com
ping: bad address 'google.com'
在同一内部网络中的容器之间建立连接,因为限制仅在外部连接性上。为了进行演示,获取同一内部网络中另一个容器的容器 ID。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b7b505f5eb8d tutum/hello-world:latest "/bin/sh -c 'php-f..." 3 seconds ago Up 2 seconds hello-world.6.i60ezt6da2t1odwdjvecb75fx
57e612f35a38 tutum/hello-world:latest "/bin/sh -c 'php-f..." 3 seconds ago Up 2 seconds hello-world.7.6ltqnybn8twhtblpqjtvulkup
d365d4a5ff4c tutum/hello-world:latest "/bin/sh -c 'php-f..." 7 minutes ago Up 7 minutes hello-world.3.r759ddnl1de11spo0zdi7xj4z
连接同一个内部网络中的两个容器。建立了连接。
∼ $ docker exec -it d365d4a5ff4c ping -c 1 57e612f35a38
PING 57e612f35a38 (10.0.1.7): 56 data bytes
64 bytes from 10.0.1.7: seq=0 ttl=64 time=0.288 ms
--- 57e612f35a38 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.288/0.288/0.288 ms
如果在内部网络中创建的服务发布(公开)了一个端口,该服务将被添加到ingress
网络中,即使该服务位于内部网络中,也会提供外部连接。例如,我们添加了docker service create
命令的--publish
选项,以在端口8080
上发布服务。
∼ $ docker service create \
> --name hello-world \
> --network hello-world-network \
> --publish 8080:80 \
> --replicas 3 \
> tutum/hello-world
mqgek4umisgycagy4qa206f9c
查找服务任务的 Docker 容器 ID。
∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c52804dc256 tutum/hello-world:latest "/bin/sh -c 'php-f..." 28 seconds ago Up 27 seconds 80/tcp hello-world.1.20152n01ng3t6uaiahpex9n4f
例如,在google.com
从内部网络中的容器连接到更大的外部网络。建立了连接。命令输出以斜体显示。
∼ $ docker exec -it 1c52804dc256 ping -c 1 google.com
PING google.com (172.217.7.238): 56 data bytes
64 bytes from 172.217.7.238: seq=0 ttl=47 time=1.076 ms
--- google.com ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 1.076/1.076/1.076 ms
删除网络
可以使用 docker network rm <networkid>
命令删除未使用的网络。可以在同一个命令中删除多个网络。例如,我们可以列出和删除多个网络。
∼ $ docker network ls
NETWORK ID NAME DRIVER SCOPE
34a5f77de8cf bridge bridge local
0e06b811a613 docker_gwbridge bridge local
wozpfgo8vbmh hello-world-network swarm
6763ebad69cf host host local
e41an60iwval ingress overlay swarm
mkileuo6ve32 mysql-network overlay swarm
qwgb1lwycgvo mysql-network-2 overlay swarm
eb7399d3ffdd none null local
aqppoe3qpy6m overlay-network-2 overlay swarm
服务正在使用的网络不会被删除。命令输出以斜体显示。
∼ $ docker network rm hello-world-network mkileuo6ve32 qwgb1lwycgvo overlay-network-2
hello-world-network
Error response from daemon: rpc error: code = 9 desc = network mkileuo6ve329jx5xbd1m6r1o is in use by service ocd9sz8qqp2becf0ww2rj5p5nqwgb1lwycgvo
overlay-network-2
摘要
本章讨论了 Docker 群组模式使用的网络。Swarm 模式中使用的默认网络是覆盖网络ingress
,这是一个跨同一 Swarm 中所有 Docker 节点的多主机网络,为每个节点提供路由网格,以便能够接受发布端口上服务的ingress
连接。自定义覆盖网络可用于创建 Docker 服务,不同之处在于,自定义覆盖网络提供服务对服务的通信,而不是ingress
通信,并且仅当使用网络的服务任务被调度在节点上时,才扩展到群工作器节点。本章还讨论了内部网络和外部网络的区别。在下一章中,我们将讨论 Docker 群组模式下的日志记录和监控。
十一、日志和监控
Docker 包括几个内置的容器日志驱动,比如json-file
、syslog、journald、gelf、fluentd 和 awslogs。Docker 还提供了docker logs
命令来获取容器的日志。Docker 1.13 包括一个实验性的特性,使用docker service logs
命令获取 Docker 服务日志。
问题
Docker Swarm 模式不包括针对 Docker 服务和容器的本地监控服务。此外,获取服务日志的实验性特性是一个命令行特性,并且每个服务都需要运行该特性。缺少一个日志记录服务,通过它可以收集所有服务的日志和指标,并在仪表板中查看。
解决方案
Sematext 是一个集成的数据分析平台,为指标和事件收集提供 SPM 性能监控,为日志收集提供 Logsene,包括性能指标、日志和事件之间的关联。Logsene 是一个托管的 ELK (Elasticsearch,Logtash,Kibana)栈。需要在群中的每个群节点上安装 Sematext Docker 代理,用于持续收集日志、指标和事件,如图 11-1 所示。
图 11-1。
Sematext Docker agent on each Swarm node
本章涵盖以下主题:
- 设置环境
- 创建 SPM 应用
- 创建 Logsene 应用
- 将 Sematext Docker 代理部署为服务
- 在 Docker Swarm 上创建 MySQL 数据库部署
- 监控 Docker 群体指标
- 获取 Logsene 中的 Docker 群集日志
设置环境
使用 Docker for AWS 启动一个由一个管理器和两个工作者节点组成的三节点集群。(这将在第三章中讨论。)从 EC2 控制台获取 manager 节点实例的公共 IP 地址,并通过 SSH 登录到该实例。
[root@localhost ∼]# ssh -i "docker.pem" docker@54.227.123.67
Welcome to Docker!
使用 Sematext SPM 和 Logsene 通过 Docker Swarm 进行日志记录和监控的过程如下。
- 在
https://apps.sematext.com/ui/registration
开户。 - 在
https://apps.sematext.com/ui/login
登录用户账号。 - 从
https://apps.sematext.com/ui/integrations?newUser
中选择集成(Logsene app 和 SPM Docker app),如步骤 4 和 5 所列。 - 创建一个 SPM(一个性能监控应用)。应用就像是数据的命名空间。生成 SPM 令牌,用于在每个群节点上安装 Sematext 代理。
- 创建一个 Logsene 应用。生成一个 Logsene 令牌,该令牌也用于在每个群节点上安装 Sematext 代理。
- 在每个群节点上安装一个 Sematext 代理。在 SPM 仪表板和 Logsene 仪表板中开始收集 Docker 群体指标、日志和事件。
创建 SPM 应用
在 https://apps.sematext.com/ui/integrations?newUser
登录 Sematext 账户,显示整合页面。对于 SPM Docker 应用,请从基础架构和应用性能监控中选择 Docker。在添加 SPM Docker App 对话框中,指定一个应用名称(DockerSwarmSPM
),如图 11-2 所示。点击创建应用。
图 11-2。
Adding a SPM Docker app
创建一个 SPM App,如图 11-3 所示。列出了几种客户端配置。
图 11-3。
SPM app is created
点击 Docker Swarm 的客户端配置选项卡,如图 11-4 所示。Docker Swarm 选项卡显示docker service create
命令,为 Sematext Docker 代理创建服务;复制命令。该命令包含一个SPM_TOKEN
,它对于每个 SPM 应用都是唯一的。
图 11-4。
Docker Swarm configuration
SPM app 被添加到仪表盘中,如图 11-5 所示。单击应用链接导航至应用报告,其中显示了 SPM 应用收集的监控数据、指标和事件,以及根据这些数据生成的图表。
图 11-5。
DockerSwarmSPM app on the dashboard
如图 11-6 所示,app 还没有收到任何数据。所有度量图形最初都是空的,但是当开始接收数据时,它们会显示图形。
图 11-6。
The DockerSwarmSPM app has not received any data
创建 Logsene 应用
要创建 Logsene app,在 https://apps.sematext.com/ui/integrations?newUser
的集成页面中选择 Logs App,如图 11-7 所示。
图 11-7。
Selecting the Logs app
在添加登录应用对话框中,指定应用名称(DockerSwarmLogsene
)并点击创建应用,如图 11-8 所示。
图 11-8。
Adding the Logsene app
一个名为DockerSwarmLogsene
的新 Logsene 应用被创建,如图 11-9 所示。复制生成的LOGSENE_TOKEN
,我们将使用它在 Docker 群中创建一个 Sematext Docker 代理服务。
图 11-9。
The Logsene app is added and LOGSENE_TOKEN is generated
一个名为DockerSwarmLogsene
的新 Logsene 应用被添加到仪表板中,如图 11-10 所示。
图 11-10。
The DockerSwarmLogsene app
点击DockerSwarmLogsene
app 链接,显示 app 采集的日志数据。最初,应用没有接收到任何数据,如图 11-11 中的消息所示,因为我们还没有在 Docker Swarm 上配置 Sematext Docker 代理服务。Logsene UI 与 Kibana 仪表板集成在一起。
图 11-11。
The app does not receive any data at first
连接 SPM 和 Logsene 应用
接下来,连接 SPM 和 Logsene 应用,以便 SPM 收集的指标和事件与 Logsene 应用集成。选择集成➤互联应用,如图 11-12 所示。
图 11-12。
Choosing Integrations ➤ Connected Apps
选择 DockerSwarmSPM 作为第一个 app,DockerSwarmLogsene 作为第二个 app,如图 11-13 所示。然后点击连接应用。
图 11-13。
DockerSwarmLogsene
已连接的应用被列出,如图 11-14 所示。
图 11-14。
The connected apps
将 Sematext Docker 代理部署为服务
之前复制的docker service create
命令只包含了SPM_TOKEN
令牌。添加从 Logsene 应用获得的–e LOGSENE_TOKEN
。在群管理器节点上运行docker service create
命令。
∼ $ docker service create --mode global \
> --restart-condition any \
> --name sematext-agent-docker \
> --mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
> --mount type=bind,src=/,dst=/rootfs,readonly=true \
> -e SPM_TOKEN=9b5552fd-001d-44f0-9452-76046d4a3413 \
> -e LOGSENE_TOKEN=81ac5395-fe8f-47d9-93b2-dc00c649116a \
> sematext/sematext-agent-docker
oubjk53mpdnjgak5dgfdxs4ft
为语义对接代理创建服务;使用docker service ls
列出。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
oubjk53mpdnj sematext-agent-docker global 3/3 sematext/sematext-agent-docker:latest
列出服务任务。因为这是一个全局服务,所以在每个节点上启动一个任务。
∼ $ docker service ps sematext-agent-docker
ID NAME IMAGENODE DESIRED STATE CURRENT STATE ERROR PORTS
5jvl7gnvl0te sematext-agent-docker.8d0qv1epqu8xop4o2f94i8j40 sematext/sematext-agent-docker:latest
ip-172-31-8-4.ec2.internal Running Running 2 minutes ago
y53f20d3kknh sematext-agent-docker.xks3sw6qgwbcuacyypemfbxyj sematext/sematext-agent-docker:latest
ip-172-31-31-117.ec2.internal Running Running 2 minutes ago
t5w2pxy4fc9l sematext-agent-docker.r02ftwtp3n4m0cl7v2llw4gi8 sematext/sematext-agent-docker:latest
ip-172-31-44-8.ec2.internal Running Running 2 minutes ago
如果额外的节点被添加到群中,则 Sematext Docker 代理在新节点上启动服务任务。例如,更新 CloudFormation 栈,将管理节点的数量增加到 3 个,将工作节点的数量增加到 5 个,如图 11-15 所示。
图 11-15。
Increasing the number of worker nodes
当栈更新完成时,群节点增加到三个管理节点和五个工作节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
8d0qv1epqu8xop4o2f94i8j40 ip-172-31-8-4.ec2.internal Ready Active
9rvieyqnndgecagbuf73r9gs5 ip-172-31-35-125.ec2.internal Ready Active Reachable
j4mg3fyzjtsdcnmr7rkiytltj ip-172-31-18-156.ec2.internal Ready Active
mhbbunhl358chah1dmr0y6i71 ip-172-31-7-78.ec2.internal Ready Active Reachable
r02ftwtp3n4m0cl7v2llw4gi8 ip-172-31-44-8.ec2.internal Ready Active
vdamjjjrz7a3ri3prv9fjngvy ip-172-31-6-92.ec2.internal Ready Active
xks3sw6qgwbcuacyypemfbxyj * ip-172-31-31-117.ec2.internal Ready Active Leader
xxyy4ys4oo30bb4l5daoicsr2 ip-172-31-21-138.ec2.internal Ready Active
向群组添加节点会在添加的节点上启动一个语义代理。
∼ $ docker service ps sematext-agent-docker
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
cgaturw05p59 sematext-agent-docker.xxyy4ys4oo30bb4l5daoicsr2 sematext/sematext-agent-docker:latest ip-172-31-21-138.ec2.internal RunningRunning 2 minutes ago
lj4f46q3ydv1 sematext-agent-docker.j4mg3fyzjtsdcnmr7rkiytltj sematext/sematext-agent-docker:latest ip-172-31-18-156.ec2.internal RunningRunning 2 minutes ago
v54bjs3c8u5r sematext-agent-docker.vdamjjjrz7a3ri3prv9fjngvy sematext/sematext-agent-docker:latest ip-172-31-6-92.ec2.internal Running Running 2 minutes ago
s7arohbeoake sematext-agent-docker.9rvieyqnndgecagbuf73r9gs5 sematext/sematext-agent-docker:latest ip-172-31-35-125.ec2.internal Running Running 3 minutes ago
ixpri65xwpds sematext-agent-docker.mhbbunhl358chah1dmr0y6i71 sematext/sematext-agent-docker:latest ip-172-31-7-78.ec2.internal Running Running 4 minutes ago
5jvl7gnvl0te sematext-agent-docker.8d0qv1epqu8xop4o2f94i8j40 sematext/sematext-agent-docker:latest ip-172-31-8-4.ec2.internal Running Running 15 minutes ago
y53f20d3kknh sematext-agent-docker.xks3sw6qgwbcuacyypemfbxyj sematext/sematext-agent-docker:latest ip-172-31-31-117.ec2.internal Running Running 15 minutes ago
t5w2pxy4fc9l sematext-agent-docker.r02ftwtp3n4m0cl7v2llw4gi8 sematext/sematext-agent-docker:latest ip-172-31-44-8.ec2.internal Running Running 15 minutes ago
在 Docker Swarm 上创建 MySQL 数据库服务
在本节中,我们将创建一个 MySQL 数据库服务,使用我们安装的 Sematext Docker 代理,通过 Sematext SCM 和 Logsene 从该服务中收集指标、日志和事件。首先,运行下面的命令来创建一个有 10 个副本的mysql
服务。
∼ $ docker service create \
> --env MYSQL_ROOT_PASSWORD='mysql'\
> --replicas 10 \
> --name mysql \
> mysql
rmy45fpa31twkyb3dowzpc74a
除了 Sematext Docker 代理服务之外,还会创建并列出该服务。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
oubjk53mpdnj sematext-agent-docker global 8/8 sematext/sematext-agent-docker:latest
rmy45fpa31tw mysql replicated 10/10 mysql:latest
还列出了mysql
服务的服务任务。
∼ $ docker service ps mysql
ID NAME IMAGE NODE DESIRED STATECURRENT STATE ERROR PORTS
x8j221ws4kx2 mysql.1 mysql:latest ip-172-31-21-138.ec2.internal Running Running 13 seconds ago
98rbd6nwspqz mysql.2 mysql:latest ip-172-31-44-8.ec2.internal Running Running 11 seconds ago
vmq0lylni8or mysql.3 mysql:latest ip-172-31-8-4.ec2.internal Running Running 24 seconds ago
0vb6oda3yh3d mysql.4 mysql:latest ip-172-31-7-78.ec2.internal Running Running 23 seconds ago
vdpplkyxy1uy mysql.5 mysql:latest ip-172-31-6-92.ec2.internal Running Running 23 seconds ago
9ser7fwz6998 mysql.6 mysql:latest ip-172-31-18-156.ec2.internal Running Running 17 seconds ago
vfsfvanghns0 mysql.7 mysql:latest ip-172-31-18-156.ec2.internal Running Running 17 seconds ago
v71qwpvjhhzn mysql.8 mysql:latest ip-172-31-6-92.ec2.internal Running Running 23 seconds ago
j7172i5ml43d mysql.9 mysql:latest ip-172-31-31-117.ec2.internal Running Running 24 seconds ago
5p5mg2wnbb0o mysql.10 mysql:latest ip-172-31-35-125.ec2.internal Running Running 20 seconds ago
在 Swarm 上启动 Sematext Docker 代理服务和 MySQL 数据库服务后,SPM 和 Logsene 应用都开始接收数据,如仪表板中的Data Received
列所示。参见图 11-16 。
图 11-16。
DockerSwarmSPM overview
监控 Docker 群体指标
在 Swarm 上启动mysql
服务后,服务的指标开始加载到 SPM——性能监控仪表板中。一旦安装了 Sematext Docker 代理,并且部署中的新指标可用,就会出现这种情况。显示不同指标的图表,包括主机 CPU、容器 CPU、容器内存、容器计数、容器内存故障计数器、容器交换、容器 I/O 吞吐量、容器网络流量和容器网络错误,如图 11-17 所示。
图 11-17。
Docker Swarm SPM overview
通过在导航中选择 Docker,可以显示 Docker 容器指标,包括容器计数、容器 CPU、容器磁盘、容器内存和容器网络。Docker 容器计数指标如图 11-18 所示。
图 11-18。
Docker metrics
Docker ➤容器网络选项显示接收和传输的网络流量、接收速率和传输速率。通过选择操作系统➤磁盘,可以显示已使用的操作系统磁盘空间。指标收集粒度可以设置为自动粒度(默认)、按月、按周、按天、按小时、按 5 分钟或按 1 分钟。可以使用日志按钮显示日志概述。
如果图表未设置为默认的自动刷新,请单击刷新图表按钮刷新图表。
使用 Logsene UI 或 Kibana 4 显示详细的日志,我们将在下一节中讨论。
获取 Logsene 中的 Docker 群集日志
在利润导航中选择日志 dockerswarmLogsene 以显示 logsene 收集的日志。显示日志计数、日志事件和过滤字段,如图 11-19 所示。要搜索由mysql
服务生成的日志,请将“mysql
”添加到搜索字段,然后单击搜索按钮。显示由mysql
Docker 服务生成的日志,包括状态消息,如"mysqld ready for connections"
。单击刷新按钮刷新日志。
图 11-19。
Logs generated by the mysql Docker Service
Logsene 收集所有的 Docker 事件,例如mysql:latest
映像的 Docker pull 事件,如图 11-20 所示。
图 11-20。
Logs for Docker event for mysql image pull
另一个 Docker 事件(卷装载)的日志如图 11-21 所示。
图 11-21。
Logs for Docker event volume mount
摘要
本章讨论了使用 Sematext SPM 性能监控和 Logsene 日志管理对 Docker 群进行连续日志记录和监控。首先,您学习了如何创建 SPM 应用和 Logsene 应用。然后,您在每个群节点上安装了 Sematext 代理服务,并在 SPM 仪表板中监控指标和事件。您还了解了如何在 Logsene UI 或 Kibana 4 仪表板中监控日志。下一章将讨论 Docker 集群中的负载均衡。
十二、负载均衡
Docker 群模式服务提供了一种可以跨节点集群扩展的分布式应用。群模式基于服务的 DNS 名称在群中的不同服务之间提供内部负载均衡。如果服务在主机端口上发布,群模式还在服务的不同任务之间提供入口负载均衡。此外,可以使用放置约束在特定节点上调度服务任务。
服务发现
一个群有一个嵌入其中的 DNS 服务器。服务发现基于 DNS 名称。群管理器为群中的每个服务分配一个唯一的 DNS 名称条目。Swarm manager 使用内部负载均衡,根据服务的 DNS 名称为群中的不同服务分配请求。
自定义计划
默认情况下,服务副本使用分散调度策略在集群中的节点上进行调度。用户可以为服务配置放置约束,以便在特定节点上调度副本。第六章讨论了使用约束的调度。
入口负载均衡
默认情况下,在发布的端口上为外部访问公开的每个服务都被添加到ingress
覆盖网络中。用户可以使用--publish
或-p
选项指定任何可用的端口来公开服务。--publish
( -p
)选项的语法是--publish <PublishedPort>:<TargetPort>
,其中<PublishedPort>
变量用于主机上发布的端口,<TargetPort>
变量用于容器端口。如果--publish
,-p
选项没有指定<PublishedPort>
端口来在群上发布服务,管理器自动在从范围 30000-32767 中选择的发布端口上公开服务。
问题
入口负载均衡用于在服务任务之间分配负载,即使群由单个节点组成也要使用。图 12-1 显示了多节点集群的入口负载均衡。客户端可以访问群中的任何节点,无论该节点是否调度了服务任务,并且使用入口负载均衡将客户端请求转发到服务任务之一。
图 12-1。
Ingress load balancing
单个客户端访问单个节点,结果,在跨群节点分布外部客户端负载方面,群未被充分利用。客户端负载在群节点之间并不均衡。单个节点不提供任何容错。如果该节点出现故障,访问该节点服务的外部客户端将无法使用该服务。
解决方案
AWS 弹性负载均衡器(ELB)用于在多个 EC2 实例之间分配客户端负载。当用于 Docker 群模式时,AWS 弹性负载均衡器将客户端负载分布在托管群节点的不同 EC2 实例上。外部负载均衡器使用 LB 监听器访问(监听)群中运行的服务的发布端口处的每个 EC2 实例上的群。每个 LB 侦听器都有一个 LB 端口映射到每个 EC2 实例上的一个实例端口(服务的发布端口)。蜂群中的 ELB 如图 12-2 所示。
图 12-2。
External load balancer
由于即使单个节点停机或变得不可用,客户端也不访问单个主机上的服务,所以群不会变得不可用,因为外部负载均衡器将客户端请求定向到群中的不同节点。即使所有节点都可用,客户端流量也会分布在不同的节点上。例如,客户端可能在特定时间从一个节点接受服务,此后不久从另一个节点接受服务。因此,外部负载均衡器提供两种功能:负载均衡和容错。此外,托管群的云提供商可以提供额外的功能,如安全和灵活的外部负载均衡。AWS 弹性负载均衡器提供的弹性负载均衡根据客户端流量来调整请求处理能力。
本章讨论 CoreOS 上用户创建的 Swarm 的负载均衡。它还讨论了 Docker 上为 AWS 托管服务自动提供的弹性负载均衡器。
设置环境
启动三个 CoreOS 实例——一个用于管理节点,两个用于工作节点——如图 12-3 所示。从 EC2 仪表板中获取 manager 实例的公共 IP 地址,如图 12-3 所示。
图 12-3。
CoreOS instances on EC2 for a manager and two worker nodes
SSH 登录到管理器节点以启动群组模式。在第二章中讨论了在 CoreOS 上初始化一个群组模式以及将工人节点加入群组。复制docker swarm join
命令输出,将工作者节点加入到群中。用docker node ls
命令列出群节点。
core@ip-10-0-0-226 ∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
9iqh5tg7hxy8u43tlifd1ri0q ip-10-0-0-203.ec2.internal Ready Active
aoe1b2623qj03852mrc5cax97 ip-10-0-0-198.ec2.internal Ready Active
dsyo3b6553ueishozhfb1apad * ip-10-0-0-226.ec2.internal Ready Active Leader
创建 Hello World 服务
接下来,用docker service create
命令创建一个 hello world 服务。使用--publish
选项在端口 8080 公开服务。使用--publish
或-p
发布服务的语法如下。
docker service create \
--name <SERVICE-NAME> \
--publish <PUBLISHED-PORT>:<TARGET-PORT> \
<IMAGE>
<PUBLISHED-PORT>
是主机上公开的端口,而<TARGET-PORT>
是 Docker 容器公开服务的端口。使用tutum/hello-world
Docker 映像、<PUBLISHED-PORT>
作为 8080、<TARGET-PORT>
作为 80、<SERVICE-NAME>
作为hello-world
,运行下面的命令来创建服务。
core@ip-10-0-0-226 ∼ $ docker service create \
> --name hello-world \
> --publish 8080:80 \
> --replicas 3 \
> tutum/hello-world
0gk3wom7z91fpm5o9e6optmb5
服务被添加到入口覆盖网络,并且服务被展示在群上的每个节点处,无论服务任务是否正在该节点上运行。hello-world
服务列出了 3/3 的副本。
core@ip-10-0-0-226 ∼ $ docker service ls
ID NAME REPLICAS IMAGE COMMAND
0gk3wom7z91f hello-world 3/3 tutum/hello-world
使用docker service ps hello-world
命令列出服务任务,三个任务按计划列出,每个节点一个。
core@ip-10-0-0-226 ∼ $ docker service ps hello-world
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
di5oilh96jmr6fd5haevkkkt2 hello-world.1 tutum/hello-world ip-10-0-0-198.ec2.internal Running Running 24 seconds ago
5g5d075yib2td8466mh7c01cz hello-world.2 tutum/hello-world ip-10-0-0-226.ec2.internal Running Running 24 seconds ago
5saarf4ngju3xr7uh7ninho0o hello-world.3 tutum/hello-world ip-10-0-0-203.ec2.internal Running Running 23 seconds ago
一个 Docker 容器正在 manager 节点上运行。
core@ip-10-0-0-226 ∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b73cbcd0c37e tutum/hello-world:latest "/bin/sh -c 'php-fpm " 34 seconds ago Up 32 seconds 80/tcp hello-world.2.5g5d075yib2td8466mh7c01cz
一个 Docker 容器正在一个 worker 节点上运行。
core@ip-10-0-0-198 ∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8bf11f2df213 tutum/hello-world:latest "/bin/sh -c 'php-fpm " 38 seconds ago Up 36 seconds 80/tcp hello-world.1.di5oilh96jmr6fd5haevkkkt2
第三个 Docker 容器运行在另一个工作节点上。
core@ip-10-0-0-203 ∼ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a461bfc8d4f9 tutum/hello-world:latest "/bin/sh -c 'php-fpm " 40 seconds ago Up 38 seconds 80/tcp hello-world.3.5saarf4ngju3xr7uh7ninho0o
调用 Hello World 服务
在没有外部负载均衡器的情况下,可以在发布端口的每个节点处建立入口连接。要调用管理器节点的服务,从 EC2 控制台获取 Swarm manager 实例的公共 DNS,如图 12-3 所示。
在 web 浏览器中调用位于<PublicDNS>:<PublishedPort>
URL 的服务,如图 12-4 所示。
图 12-4。
Invoking the service in a browser
类似地,要在 worker 节点调用服务,从 EC2 控制台获取 worker 实例的公共 DNS,并在 web 浏览器的<PublicDNS>:<PublishedPort>
URL 调用服务,如图 12-5 所示。
图 12-5。
Invoking the service at a worker node
类似地,要在另一个 worker 节点调用服务,从 EC2 控制台获取 worker 实例的公共 DNS,并在 web 浏览器中的<PublicDNS>:<PublishedPort>
URL 调用服务,如图 12-6 所示。
图 12-6。
Invoking the service at the other worker node
外部 AWS 弹性负载均衡器在 EC2 实例之间分配负载,而入口负载均衡器在服务任务之间分配负载。在前面的例子中,当在群管理器实例和群工作器实例调用服务时,调用相同的服务任务,如相同的主机名所示(图 12-4 和 12-6 )。这演示了入口负载均衡。
如果在同一主机上调用服务,则可能会调用不同的服务任务。例如,再次调用 Swarm manager 实例上的服务。服务于不同的服务任务,如图 12-7 中不同的主机名所示。这与之前在图 12-4 中提供的主机名进行了比较,再次证明了入口负载均衡。
图 12-7。
Different hostname served when invoking the service at the manager node again
创建外部弹性负载均衡器
在本节中,我们将在 AWS 云上创建一个外部弹性负载均衡器。单击 EC2 仪表板中的负载均衡器。然后点击 Create Load Balancer 创建一个新的负载均衡器,如图 12-8 所示。
图 12-8。
Creating a new load balancer
AWS 弹性负载均衡提供两种类型的负载均衡器——经典负载均衡器和应用负载均衡器。传统负载均衡器基于应用或网络级别的信息路由流量,而应用负载均衡器基于高级应用级别的信息路由流量。经典的负载均衡器应该能够满足对多个 EC2 实例的流量进行最简单的负载均衡,并且是我们用于 Docker Swarm 实例的负载均衡器。选择经典负载均衡器,然后点击继续,如图 12-9 所示。
图 12-9。
Selecting the classic load balancer option
在定义负载均衡器对话框中,指定一个负载均衡器名称(HelloWorldLoadBalancer
),并选择一个 VPC 在其中创建负载均衡器,如图 12-10 所示。VPC 必须在创建负载均衡器之前存在,并且必须是创建要进行负载均衡的 EC2 实例的位置。默认情况下,负载均衡器协议是 HTTP,实例协议也是。保留 HTTP 协议的默认设置,将负载均衡器端口和实例端口指定为 8080,因为 Hello World 服务在端口 8080 公开。
图 12-10。
Selecting the load balancer protocol
在选择子网选项卡中,单击可用子网表中列出的一个或多个子网。子网被添加到选中的子网中,如图 12-11 所示。点击下一步。要提供高可用性,请在不同的可用性区域中至少选择两个子网。
图 12-11。
Selecting subnets
在分配安全组选项卡中,选择创建新的安全组,如图 12-12 所示。在类型中,选择自定义 TCP 规则。选择 TCP 协议和端口范围 8080。选择任意位置作为源,其值为 0.0.0.0/0。点击下一步。
图 12-12。
Assigning security groups
单击“配置安全设置”中的“下一步”,因为我们没有使用 HTTPS 或 SSL 协议。在配置运行状况检查选项卡中,为 ping 协议选择 HTTP,为 ping 端口选择 8080。指定 ping 路径为/
,如图 12-13 所示。在“高级详细信息”区域保留默认值,然后单击“下一步”。
图 12-13。
Configuring a health check
选择列出的三个 Swarm 实例,如图 12-14 所示。另外,选择启用跨区域负载均衡,这将在所有可用性区域中的所有后端实例之间平均分配流量。点击下一步。
图 12-14。
Adding EC2 instances
在“添加标签”选项卡中,不需要添加任何标签。在审核选项卡中,点击创建,如图 12-15 所示。如上所述,负载均衡器是面向互联网的类型。
图 12-15。
Review your settings then create the load balancer
负载均衡器创建完成,如图 12-16 所示。
图 12-16。
The load balancer has been created
从 EC2 控制台获取负载均衡器的 DNS 名称,如图 12-17 所示。最初,状态将是“3 个实例中的 0 个在服务中”,因为注册仍在进行中。
图 12-17。
Obtaining the DNS name of the load balancer
过一会儿,状态应该变成“3 个实例中的 3 个在服务中”,所有实例都应该在服务中,如图 12-18 所示。
图 12-18。
Status indicates three of three instances InService
Hello World 服务可以从 web 浏览器中的<DNSname>:<LoadBalancerPort>
URL 调用,如图 12-19 所示。
图 12-19。
Invoking the Hello World service
外部弹性负载均衡器平衡集群中 EC2 实例之间的负载。因为入口负载均衡器在不同的服务任务之间平衡负载,如果在 ELB DNS 名称上再次调用服务,则可能会调用不同的服务任务,如图 12-20 所示。
图 12-20。
Different service task served
AWS Docker 中的负载均衡
虽然在使用命令行创建 Docker 群组时必须创建外部弹性负载均衡器(首先启动群组模式,然后将工作节点加入群组),但在第三章中介绍的 AWS 托管服务 Docker 会自动创建弹性负载均衡器。
使用 Docker for AWS 创建一个有三个管理器节点和五个工作者节点的 Swarm(之前创建的 Swarm 可能会被更新),如图 12-21 所示。如图 12-21 中的资源选项卡所示,创建一个外部弹性负载均衡器作为群资源之一。
图 12-21。
CloudFormation stack for a Docker Swarm
创建一个面向互联网的弹性负载均衡器,如图 12-22 所示。负载均衡器的公共 DNS 可以用于访问群,如后面所讨论的。
图 12-22。
Load balancer for the Swarm created with Docker for AWS
选择“实例”选项卡。列出了群中的所有实例,经理或工人。所有的实例都应处于使用状态,如图 12-23 所示。
图 12-23。
Instances status is InService
更新 listeners 选项卡中的负载均衡器侦听器,添加/修改一个侦听器,其负载均衡器端口设置为 8080,实例端口设置为 8080,这是我们创建的 Hello World 服务的发布端口,如图 12-24 所示。
图 12-24。
The Listeners tab
从 EC2 控制台获取其中一个管理器节点的公共 IP 地址。
SSH 登录到管理器节点。
[root@localhost ∼]# ssh -i "docker.pem" docker@34.205.43.53
Welcome to Docker!
列出群体节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
8d0qv1epqu8xop4o2f94i8j40 ip-172-31-8-4.ec2.internal Ready Active
8eckb0twpbuoslfr58lbibplh ip-172-31-32-133.ec2.internal Ready Active
b6f18h4f3o44gkf5dhkzavoy3 ip-172-31-2-148.ec2.internal Ready Active
k9nl2zcmjzobbqu5c5bkd829g ip-172-31-21-41.ec2.internal Ready Active
p0d70jwh5vpjwximc1cpjfjkp * ip-172-31-1-130.ec2.internal Ready Active Leader
r02ftwtp3n4m0cl7v2llw4gi8 ip-172-31-44-8.ec2.internal Ready Active
rd8d0kksuts3aa07orhgkri3i ip-172-31-41-86.ec2.internal Ready Active Reachable
xks3sw6qgwbcuacyypemfbxyj ip-172-31-31-117.ec2.internal Ready Active Reachable
创建一个 Hello World 服务,并在端口 8080(发布端口)公开该服务。
∼ $ docker service create \
> --name hello-world \
> --publish 8080:80 \
> --replicas 10 \
> tutum/hello-world
n4hmfognhjrasf5nhukr55krb
服务任务在整个群体中进行调度。
∼ $ docker service ps hello-world
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
y1fetn3kpwwn hello-world.1 tutum/hello-world:latest ip-172-31-2-148.ec2.internalRunning Running 15 seconds ago
5i15zl9dickd hello-world.2 tutum/hello-world:latest ip-172-31-44-8.ec2.internalRunning Running 17 seconds ago
k9glaavn0gzg hello-world.3 tutum/hello-world:latest ip-172-31-8-4.ec2.internalRunning Running 17 seconds ago
n83f89ijlokn hello-world.4 tutum/hello-world:latest ip-172-31-41-86.ec2.internalRunning Running 17 seconds ago
nelf275h9tp1 hello-world.5 tutum/hello-world:latest ip-172-31-8-4.ec2.internalRunning Running 16 seconds ago
w4c8zcvlq5v7 hello-world.6 tutum/hello-world:latest ip-172-31-32-133.ec2.internalRunning Running 17 seconds ago
b5qvbbgkrpd5 hello-world.7 tutum/hello-world:latest ip-172-31-21-41.ec2.internalRunning Running 16 seconds ago
qlm8dt9fuv92 hello-world.8 tutum/hello-world:latest ip-172-31-31-117.ec2.internalRunning Running 17 seconds ago
t3tenhpahh7g hello-world.9 tutum/hello-world:latest ip-172-31-44-8.ec2.internalRunning Running 17 seconds ago
up64ekxqeftk hello-world.10 tutum/hello-world:latest ip-172-31-1-130.ec2.internalRunning Running 17 seconds ago
可以在不明确指定发布端口的情况下创建hello-world
服务。
∼ $ docker service create \
> --name hello-world \
> --publish 80 \
> --replicas 3 \
> tutum/hello-world
群组管理器自动分配 30000-32767 范围内的公布端口;默认端口是 30000(如果可用的话)。AWS Swarm Docker 的负载均衡器中的监听器可能需要修改,以便为LoadBalancerPort:ServiceInstancePort
添加映射,比如 30000:30000。
获取自动创建的弹性负载均衡器的公共 DNS,如图 12-25 所示。
图 12-25。
Obtaining the public DNS of the ELB
在 web 浏览器中访问<PublicDNS>:<PublishedPort>
处的服务,如图 12-26 所示。该请求被转发到群中的一个实例上的入口负载均衡器。外部请求转发到的实例不一定要承载服务任务。寻找服务任务是入口负载均衡器的工作。
图 12-26。
Accessing a Docker service at the elastic load balancer DNS
摘要
本章讨论了群模式下的负载均衡。入口负载均衡器用于在服务的任务之间分配负载。群中的每个服务被分配一个 DNS 名称,内部负载均衡器基于 DNS 名称在服务之间平衡服务请求。我们还为 AWS EC2 实例创建了一个外部负载均衡器,以便在 EC2 实例之间分配负载。Docker for AWS 会在 AWS 上自动创建一个外部负载均衡器。在下一章,我们将讨论开发一个基于 Docker Swarm 的高可用性网站。
十三、开发高可用性网站
网站的高可用性是指网站持续可用,没有服务中断。通过在 Docker Swarm 应用中提供容错功能,网站变得高度可用。高可用性在不同的级别上提供。入口负载均衡器在多个服务任务之间平衡传入的客户端请求,并在任务级别提供容错。如果一个服务任务失败,客户端流量将被路由到另一个服务任务。为跨多个可用性区域托管的 Docker 群使用外部负载均衡器是提供高可用性的另一种方法。外部负载均衡器在节点级别提供容错。如果一个节点出现故障,客户端流量将被路由到另一个节点上的群节点。
问题
使用外部负载均衡器(如 AWS 弹性负载均衡器)可以在 AWS 区域的多个可用性区域中提供容错能力。客户端主机可以通过其 DNS 名称访问弹性负载均衡器,如图 13-1 所示。Swarm 的可用性不高,因为单个 AWS 区域的故障会导致网站不可用。
图 13-1。
The elastic load balancer may be accessed at its DNS name by a client host
解决方案
Amazon Route 53 通过各种 DNS 故障转移选项提供高可用性,包括使用别名资源记录集的主动-主动和主动-被动故障转移。Amazon Route 53 提供跨地理上分散的 AWS 区域的 DNS 故障转移,如图 13-2 所示。我们使用 Amazon Route 53 主动-被动故障转移配置,该配置基于负载均衡器 DNS 的主-从架构模式。
图 13-2。
Amazon Route 53 provides DNS failover across AWS regions
本章涵盖以下主题:
- 设置环境
- 创建多个停靠站群
- 部署 Docker 群服务
- 创建 AWS 路线 53
- 创建托管区域
- 配置名称服务器
- 创建记录集
- 测试高可用性
- 删除托管区域
设置环境
我们使用两个 Docker for AWS 托管集群,为主动-被动 DNS 故障转移配置提供两个 DNS。路由 53 为两个 DNS 提供了主-从架构模式。唯一的先决条件是一个 AWS 帐户,可以在 https://aws.amazon.com/resources/create-account/
创建。创建一个密钥对(Swarm ),用于 SSH 登录到 Swarm manager 节点,如图 13-3 所示。仅由所有者使用chmod 400 swarm.pem
命令将密钥对的权限设置为只读。
图 13-3。
Key pair
必须注册域名才能用于创建 Amazon Route 53 托管区域。
创建多个停靠站群
在 https://docs.docker.com/docker-for-aws/
使用 AWS 托管服务的 Docker 创建两个 Docker 群组。这两个 Docker 集群必须位于两个不同的 AWS 区域,才能使用地理上分布的 AWS 区域提供的高可用性。创建一个 Docker Swarm Oregon 地区作为例子,如图 13-4 所示。
图 13-4。
CloudFormation stack for Docker Swarm
每个 Docker 群都有分布在 AWS 区域中的 AWS 可用性区域的管理器和工作者节点。管理节点的公共 IP 可以从 EC2 控制台获取,如图 13-5 所示。
图 13-5。
Obtaining the public IP of the Swarm manager node
使用第一个停靠站群中管理节点的公共 IP 地址,SSH 登录到管理节点 EC2 实例。
[root@localhost ∼]# ssh -i "swarm.pem" docker@54\. 149.86.148
Welcome to Docker!
∼$
以俄亥俄州 AWS 地区创建其他 Docker Swarm 为例,如图 13-6 所示。对于不同的用户,区域可能是不同的。
图 13-6。
CloudFormation stack for the Docker Swarm in one region
第二停靠站群的群节点 EC2 实例也分布在第二 AWS 区域中的 AWS 可用性区域,如图 13-7 所示。获取管理器节点的公共 IP。
图 13-7。
The Availability Zone column lists multiple zones
SSH 登录到实例。
[root@1oca1.host —]# ssh -i “docker.pem” docker@52.14.23.163
Welcome to Docker!
∼$
用 Docker 节点列出 Docker 群中的群节点。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
fncv7ducej3ind4u2sy9xtwi7 ip-172-31-34-223.us-east-2.compute.internal. Ready Active Reachable
grdeu2x49yi2fmvuy9lmoogqg ip-172-31-43-174.us-east-2.compute.internal Ready Active
keOd75qef9bg8t22eqv9spdpm ip-172-31-30-180.us-east-2.compute.internal. Ready Active Reachable
m2mmifbrnjbdriub5r36zxyjc * ip-172-31-8-11.us-east-2.compute.internal Ready Active Leader
qenbfrmsOxv7wom6wpw9yspw4 ip-172-31-27-178.us-east-2.compute.ìnternal Ready Active
tipzy29hgh3m6og5bzkgsego8 ip-172-31-12-37.us-east-2.compute.internal Ready Active
v4xdl4jvthovrzsamujoxy3ju ip-172-31-7-219.us-east-2.compute.internal Ready Active
vuq68yex58vzgx3audj3sm23a ip-172-31-28-182.us-east-2.compute.internal Ready Active
部署 Docker 群服务
接下来,我们部署一个 Hello World 服务,它将被托管在一个网站上。在管理器实例上为DockerSwarm-1
Swarm 运行以下命令,创建一个tutum/hello-world
服务,在主机节点的端口 8080 公开两个副本。
docker service create \
--name hello-world \
--publish 8080:80 \
--replicas 2 \
tutum/hello-world
创建了一个具有两个服务任务的 Docker 服务。
∼ $ docker service create \
> --name hello-world \
> --publish 8080:80 \
> -- replicas 2 \
> tutum/hello-world
vn5fl8h7t65sjwk54dwcoklhu
∼ $ docker service 1s
ID NAME MODE REPLICAS IMAGE
vn5tl8h7t65s hello-world replicated 2/2 tutum/hello-world:latest
∼ $ docker service ps hello-world
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
ac9ks5y9duni2 hello-world.l tutum/hello-wor1d:latest ip-172-31-19-220.us-west-2.compute.internal Running Running 13 seconds ago
8s6r48wUui9 hello-world.2 tutum/hello-world:latest ip-172-31-24-250.us-west-2.compute.internal Running Running 13 seconds ago
Scale the service to 10 replicas to provide load distribution. Subsequently, list the services to list 10/10 replicas as running.∼ $ docker service scale hello-world=10
hello-world scaled to 10
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE
vn5U8h7t65s hello-world replicated 10/10 tutum/hello-world:latest
∼ $
10 个服务任务副本跨群节点进行调度,如图 13-8 所示。
图 13-8。
Service tasks scheduled across the Swarm nodes
从 EC2 仪表板获取第一个 Docker 群组的负载均衡器 DNS,如图 13-9 所示。
图 13-9。
Docker Swarm load balancer
在 web 浏览器中访问<DNS>:<LoadBalancerPort>
处的服务,如图 13-10 所示;负载均衡器端口设置为 8080,该端口用于公开服务。
图 13-10。
Accessing the service in a browser
类似地,对于第二个 Docker Swarm,创建一个tutum/hello-world
服务,将发布端口设置为 8080。将服务扩展到 10 个副本,以便在整个集群中分配负载。
S docker service create \
> --name hello-world \
> --publish 8080:80 \
,> --replicas 2 \
> tutum/hello-world
woqx2ltuibv53ctmuvssrsq8j
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE
woqx2ltuibv5 hello-world replicated 2/2 tutum/hello-world:latest
∼ $ docker service ps hello-world
NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
ny9ermdgb7a4 hello-world.1 tutum/hello-world:latest ip-172-31-34-223.us-east-2.compute.internal Running Running 15 seconds ago
5w3thlgleinme hello-world.2 tutum/hello-world:latest ip-172-31-30-180.us-east-2.compute.internal Running Running 15 seconds ago
∼ $ docker service scale hello-world=10
hello-world scaled to 10
服务副本分布在群节点上,如图 13-11 所示。
图 13-11。
Service replicas distributed across the Swarm
获取第二个集群的弹性负载均衡器的 DNS,如图 13-12 所示。
图 13-12。
Obtaining the DNS name for the Swarm ELB
在 web 浏览器中访问<DNS>:<LoadBalancerPort>
处的服务,如图 13-13 所示。
图 13-13。
Accessing the service in a browser
创建亚马逊路线 53
Amazon Route 53 是一个高度可用和可扩展的云域名服务(DNS ) web 服务,它将用户请求连接到运行在 AWS 上的基础设施,包括 Amazon EC2 实例、负载均衡器和 Amazon S3 存储桶。我们已经使用 Docker AWS 托管服务创建了两个托管相同 Docker 服务的 Docker 群组,该服务会自动为每个 Docker 群组创建一个 AWS ELB。
在本节中,我们创建一个 Amazon Route 53,将用户对nosqlsearch.com
域的请求路由到两个 Docker 集群的弹性负载均衡器。在 Amazon Route 53 中,我们创建了两个资源记录集,指向为故障转移配置的两个不同的 elb,其中一个 elb 是主资源记录集,另一个是辅助资源记录集。
当在 web 浏览器中打开nosqlsearch.com
域时,路由 53 将请求路由到主资源记录集。如果主记录集失败,Route 53 将用户请求路由到辅助记录集,实际上提供了托管在nosqlsearch.com
域上的 Hello World Docker 服务的高可用性。要创建 AWS 路线 53,从 AWS 服务中选择路线 53,如图 13-14 所示。
图 13-14。
Selecting the Amazon Route 53 service
创建托管区域
托管区域是一种配置,用于确定如何路由互联网上某个域的流量。要创建托管区域,在网页浏览器中打开 https://console.aws.amazon.com/route53/
,在 DNS 管理中点击创建托管区域,如图 13-15 所示。
图 13-15。
Creating the hosted zone
或者,选择托管区域或在浏览器中打开 https://console.aws.amazon.com/route53/home#hosted-zones
,点击创建托管区域,如图 13-16 所示。
图 13-16。
Creating a hosted zone
再次点击创建托管区域,如图 13-17 所示。
图 13-17。
Creating a hosted zone
在创建托管区域对话框中,指定域名(nosqlsearch.com
)。域名必须向用户注册。类型选择公共托管区域,如图 13-18 所示。
图 13-18。
Configuring the hosted zone
创建一个新的公共托管区域,如图 13-19 所示。托管区域的名称服务器(默认情况下有四个)已分配。
图 13-19。
The new public hosted zone
配置名称服务器
接下来,我们需要通过域名注册机构来配置域名服务器。不同的域名注册商配置域名服务器的程序是不同的,但是应该提供一个为域名添加区域记录的选项。
将记录类型指定为名称服务器,如图 13-20 所示。将主机指定为@
。每个区域记录应该指向一个名称服务器,该服务器可以从我们之前创建的公共托管区域获得。
图 13-20。
Adding a name server record
为要创建托管区域的域添加四台名称服务器(统称为委托集),如图 13-21 所示。
图 13-21。
Name servers configured on a domain
创建资源记录集
创建和配置托管区域后,创建一个或多个资源记录集。资源记录集是用于将流量路由到域的域名系统(DNS)配置。点击创建记录集,创建一个资源记录集,如图 13-22 所示。
图 13-22。
Creating a record set
在创建记录集选项卡中,类型应设置为–IP v4 地址,如图 13-23 所示。每个记录集的名称都以域名结尾。为别名选择是。
图 13-23。
Configuring a record set
接下来,选择别名目标作为其中一个 Docker 集群的 AWS 弹性负载均衡器 DNS,如图 13-24 所示。
图 13-24。
Selecting an alias target
接下来,选择路由策略,如图 13-25 所示。
图 13-25。
Selecting a routing policy
为路由策略选择故障转移。这将配置 DNS 故障转移,如图 13-26 所示。选择故障转移记录类型为主记录类型。
图 13-26。
Selecting failover record type
对于评估目标健康,选择是,如图 13-27 所示。
图 13-27。
Selecting the Evaluate Target Health option
对于关联健康检查,选择否,点击创建,如图 13-28 所示。
图 13-28。
Creating a record set
创建一个主记录集,如图 13-29 所示;“主要”意味着网站流量将首先被路由到记录集。
图 13-29。
Primary record set
要创建辅助记录集,再次点击创建记录集,如图 13-30 所示。
图 13-30。
Creating another record set
选择类型为–IP v4 地址,并为别名选择是。选择别名目标作为第二个 ELB DNS,如图 13-31 所示。
图 13-31。
Selecting an alias target
选择故障转移路由策略和辅助故障转移记录类型,如图 13-32 所示。
图 13-32。
Selecting failover record type as secondary
为评估目标健康状况选择是,为与健康状况检查关联选择否。点击创建,如图 13-33 所示。
图 13-33。
Creating a secondary record set
创建辅助记录集;“次要”意味着如果主要记录集出现故障,流量将被路由到该记录集,如图 13-34 所示。点击返回托管区域。
图 13-34。
Secondary record set is created
域(nosqlsearch.com
)配置了四个记录集,如图 13-35 所示。
图 13-35。
Hosted zone created
测试高可用性
接下来,我们测试我们配置的高可用性。在 web 浏览器中打开域,包括服务发布端口(nosqlsearch.com:8080
),如图 13-36 所示。应该会显示 Docker 服务输出。
图 13-36。
Invoking a service in a browser
为了测试高可用性,删除与主记录集关联的 Docker Swarm 的 CloudFormation 栈,如图 13-37 所示。
图 13-37。
Deleting a stack
在删除栈对话框中单击是,删除。栈应该开始被删除,如图 13-38 中的DELETE_IN_PROGRESS
状态所示。
图 13-38。
The delete is in progress
DNS 故障转移到二级资源记录集,域继续服务 Docker 服务,如图 13-39 所示。
图 13-39。
Domain continues to serve
如果请求被转发到不同的服务任务副本,浏览器中的主机名可能会变得不同,如图 13-40 所示。但是无论是否启动了故障转移,主机名也可能变得不同,因为入口负载均衡器在不同的服务副本之间分配流量。
图 13-40。
Different hostname
删除托管区域
在删除托管区域之前,必须删除与托管区域关联的所有资源记录集。选择要删除的资源记录集,点击【删除记录集】,如图 13-41 所示。
图 13-41。
Deleting the record sets
在确认对话框中点击确认,如图 13-42 所示。
图 13-42。
Confirmation dialog
点击返回托管区域,如图 13-43 所示。
图 13-43。
Going back to the hosted zones
选择要删除的托管区域,点击删除托管区域,如图 13-44 所示。
图 13-44。
Deleting a hosted zone
在确认对话框中点击确认,如图 13-45 所示。
图 13-45。
Confirmation dialog for deleting a hosted zone
托管区域被删除。
摘要
本章使用 Amazon Route 53 托管区域开发了一个高度可用的网站。首先,我们使用 Docker for AWS 托管服务创建了两个 Docker 群组,并在每个群组上部署了相同的 Docker 服务。每个 Docker 群服务可以使用由 Docker for AWS 自动创建的 Docker 群的 AWS 弹性负载均衡器来访问。路由 53 托管区域将为域创建一个托管区域,以将流量路由到在主/辅助故障转移模式中配置的 DNS。随后,我们测试了如果主记录集的 Docker Swarm 关闭,网站仍然可用,因为托管区域会将流量路由到辅助 ELB DNS。在下一章,我们将讨论在 Docker Cloud 中使用 Docker Swarm 模式。
十四、在 Docker Cloud 中使用 Swarm 模式
Docker for AWS 是 Docker Swarm 基于定制 Linux 发行版的托管服务,托管在 AWS 上,具有与 AWS 云平台集成的所有固有优势,如 CloudWatch 的集中式日志记录、定制调试、自动扩展组、弹性负载均衡和 DynamoDB 数据库。
问题
虽然 AWS 是一个托管云平台,但它本身不是 Docker 容器、映像和服务的托管服务。Docker 的构建和测试仍然需要集成。
解决方案
Docker Cloud 是一项托管服务,用于测试代码和构建 Docker 映像,并在 Docker Cloud 注册表中创建和管理 Docker 映像库。Docker Cloud 还管理 Docker 容器、服务、栈、节点和节点集群。栈是服务的集合,而服务是容器的集合。Docker Cloud 是一个集成的云服务,可以管理构建和映像、基础架构、节点和应用。
Docker Cloud 还引入了 Swarm 模式来管理 Docker Swarms。在 Swarm 模式下,Docker Cloud 与 Docker for AWS 集成。因此,Docker 云群模式是两种托管服务的集成——Docker for AWS 和 Docker Cloud。
Docker Cloud 提供了一些 Docker 映像,用于在 Docker 群和 Docker 主机客户端之间进行交互,如表 14-1 中所述。
表 14-1。
Docker Images for Docker Swarm
| Docker 映像 | 描述 | | --- | --- | | `dockercloud/client` | 在客户端使用 Docker ID 凭证启动交互式 shell 以连接到远程 docker Swarm 集群。 | | `dockercloud/client-proxy` | 在客户端使用,通过在每个请求上注入 Docker ID 授权信息,将本地 docker API 调用转发到远程群集群。 | | `dockercloud/server-proxy` | 验证和授权传入的 Docker API 调用,并将它们转发到本地 Docker 引擎。 | | `dockercloud/registration` | 向 Docker Cloud 注册一个群集群并启动一个服务器代理。 |在本章中,我们将讨论 Docker 云群模式,以向 Docker 云群提供 AWS 上托管的基础架构。本章涵盖以下主题:
- 设置环境
- 创建 IAM 角色
- 在 Docker 云中创建 Docker 群
- 从 Docker 主机连接到 Docker 群组
- 从群组管理器连接到 Docker 群组
- 将蜂群带入 Docker 云
设置环境
由于 Docker Cloud 是一项托管服务,因此所需要的只是一个帐户,该帐户可以在 https://cloud.docker.com/
创建。AWS 帐户也是必需的,可以在 https://aws.amazon.com/resources/create-account/
创建。还要在 Docker Swarm 的 EC2 实例将要运行的区域中创建一个密钥对,如图 14-1 所示。
图 14-1。
Creating a key pair on AWS EC2
创建 IAM 角色
Docker 云群模式需要一个具有新策略的 AWS 角色,这是 Docker for AWS 的嵌入式策略。要创建 IAM 角色,请在 web 浏览器中导航至 https://console.aws.amazon.com/iam/home?#roles
。点击新建角色,如图 14-2 所示。
图 14-2。
Creating a new role
指定一个角色名称(dockercloud-swarm-role
),如图 14-3 ,点击下一步。
图 14-3。
Specifying a role name
显示选择角色类型页面,如图 14-4 所示。由于我们链接了两个服务——Docker Cloud 和 Docker for AWS——我们不需要选择 AWS 服务角色。
图 14-4。
Select the role type
选择跨账户访问的角色,如图 14-5 所示,并使用选择按钮选择名为“在您的 AWS 账户和第三方 AWS 账户之间提供访问”的子选项。
图 14-5。
Role for cross-account access
接下来,指定 IAM 用户将访问 AWS 帐户的第三方 AWS 帐户的帐户 ID。已经为 Docker 云服务设置了一个第三方 AWS 帐户,其帐户 ID 为689684103426
,任何人(AWS 用户)都可以将 Docker 云服务链接到其 AWS 帐户。指定账户 ID 为689684103426
,如图 14-6 所示。外部 ID 是在 https://cloud.docker.com/
创建的 Docker 云服务账户的用户 Docker ID。虽然每个人的帐户 ID 都是相同的(689684103426
),但是不同用户的外部 ID 是不同的。保持要求 MFA 复选框未选中。点击下一步。
图 14-6。
Specifying account and external IDs
由于我们正在嵌入自定义策略,因此不要从“Attach Policy”中列出的任何策略中进行选择。点击下一步,如图 14-7 所示。
图 14-7。
Do not select a policy
在审核页面,点击创建角色,如图 14-8 所示。
图 14-8。
Creating a role
一个名为dockercloud-swarm-role
的新 AWS IAM 角色被创建,如图 14-9 所示。单击 dockercloud-swarm-role 角色名称。
图 14-9。
New role
接下来,我们将添加一个嵌入式(也称为内联)策略。默认情况下,应选择“权限”选项卡。点击 v 图标,展开内嵌策略部分,如图 14-10 所示。
图 14-10。
Expanding the inline policies
首先,没有列出内联策略。点击点击此处链接,添加一个内嵌策略,如图 14-11 所示。
图 14-11。
Click on the Click Here link to add an inline policy
在设置权限界面,使用选择按钮选择自定义策略,如图 14-12 所示。
图 14-12。
Selecting a custom policy
策略文档列出了一些权限,IAM 角色使用 Docker for AWS 的策略文档可从 https://docs.docker.com/docker-for-aws/iam-permissions/
获得。点击【验证策略】对策略进行验证,如图 14-13 所示。
图 14-13。
Validating the policy
点击应用策略,如图 14-14 所示。
图 14-14。
Applying the policy
为dockercloud-swarm-role
角色添加了一个新的内联策略,如图 14-15 所示。
图 14-15。
The new inline policy is added
复制图 14-16 中列出的角色 ARN 字符串,因为我们需要 ARN 字符串从 Docker Cloud 连接到 AWS 云提供商。
图 14-16。
Role ARN
在 Docker 云中创建 Docker 群
在本节中,我们将从 Docker 云服务中创建一个 Docker Swarm。在 https://cloud.docker.com/
登录 Docker 云服务。云注册表页面应该显示在 https://cloud.docker.com/app/dvohra/dashboard/onboarding/cloud-registry
。空白处有一个群组模式选项,默认关闭,如图 14-17 所示。
图 14-17。
The Swarm Mode slider
点击 Swarm 模式滑块;应启用群组模式,如图 14-18 所示。
图 14-18。
Switching to Swarm mode
增加一个群组工具栏选项,如图 14-19 所示。
图 14-19。
Swarms toolbar option
有两种选择——带上你自己的蜂群或者创建一个新的蜂群。点击创建创建一个新的蜂群,如图 14-20 所示。
图 14-20。
Creating a new Swarm
接下来,我们将配置群组,包括指定群组名称、选择云提供商和选择云提供商选项。支持两个云服务提供商:Amazon Web Services (AWS)和 Microsoft Azure(尚不可用)。我们在本章中使用 AWS。我们需要用之前复制的 ARN 字符串来配置 AWS 的云设置。云设置可以通过以下两个选项之一进行配置。一种选择是从账户中选择云设置,如图 14-21 所示。
图 14-21。
Cloud settings
在云设置页面,点击表示亚马逊网络服务提供商 Connect Provider 的插件图标,如图 14-22 所示。
图 14-22。
Connecting the provider
显示添加 AWS 凭证对话框,如图 14-23 所示。
图 14-23。
Adding AWS credentials
配置云设置的另一个选项是单击 Amazon Web Service 服务提供商图标,如图 14-24 所示,这也会显示添加 AWS 凭证对话框。
图 14-24。
Connecting to an Amazon web services provider
指定之前从添加 AWS 凭证对话框中复制的 ARN 字符串并点击保存,如图 14-25 所示。
图 14-25。
Saving the AWS credentials
无论选择哪种方式,服务提供商 Amazon Web Services 都应该连接,如连接提供商图标变为已连接所示,如图 14-26 所示。
图 14-26。
Amazon Web Services provider in connected mode
亚马逊网络服务选项应该显示connected
,如图 14-27 所示。
图 14-27。
Amazon Web Services provider connected
指定群组名称。该名称不得包含除“,
”、,
、-
、_
之外的任何空格、大写字母或特殊字符,如图 14-28 所示。
图 14-28。
Specifying a Swarm name
指定一个有效的群名称(docker-cloud-swarm
),选择已经连接好的 Amazon Web Services 服务提供商,点击创建,如图 14-29 所示。
图 14-29。
Creating a Docker Swarm using the AWS service provider
在区域中,选择一个区域(us-east-2
)、群管理器数量(3)、群工作器数量(5)、群管理器实例类型(t2.micro
)、代理工作器实例类型(t2.micro
)和 SSH 密钥。点击创建,如图 14-30 所示。
图 14-30。
Configuring and creating a Swarm
蜂群应该开始部署,如图 14-31 中的DEPLOYING
消息所示。
图 14-31。
Deploying a Swarm
当蜂群部署完毕后,消息变成Deployed
,如图 14-32 所示。
图 14-32。
The Swarm is now deployed
为 Swarm 创建和配置 AWS 基础设施。生成一个云状栈,如图 14-33 所示。
图 14-33。
CloudFormation stack for the created Swarm
为群组添加了一个新的代理 AWS IAM 角色,如图 14-34 所示。
图 14-34。
Proxy role and Docker Cloud Swarm AWS role
启动群管理器和工作节点的 EC2 实例。每个 EC2 实例都以自动创建的代理 IAM 角色启动,如图 14-35 中的管理节点所示。
图 14-35。
IAM role for EC2 instances
每个 Docker 云帐户名称空间必须只与一个 AWS IAM 角色相关联。如果多个 Docker 云帐户要访问同一个 AWS 帐户,则必须为每个 Docker 云帐户或 Docker 云帐户名称空间创建多个角色。Docker Cloud 访问 AWS 的每个 AWS IAM 角色都与一个 ARN 字符串相关联。可以使用编辑端点链接编辑已部署群组的 ARN 字符串,如图 14-36 所示。
图 14-36。
Edit Endpoint link
如果要修改群组端点,请在“编辑端点”对话框中指定新的 ARN 字符串(用于与不同 Docker 云名称空间关联的不同 IAM 角色)。点击保存,如图 14-37 所示。
图 14-37。
Editing the endpoint
接下来,我们连接到 Docker 群。有两种方法可以做到:
- 从任何 Docker 主机直接连接
- 从 EC2 仪表板获取群管理器的公共 IP 地址,并通过 SSH 登录群管理器
我们讨论这些选项中的每一个。
从 Docker 主机连接到 Docker 群组
点击 Docker 云仪表盘中的 Docker Swarm。应显示带有docker run
命令的连接对话框,如图 14-38 所示。复制docker run
命令。
图 14-38。
Listing and copying the docker run command to connect to the Swarm
用预装 Docker 的 CoreOS AMI 启动一个 EC2 实例,如图 14-39 所示。
图 14-39。
Creating an EC2 instance with CoreOS AMI
从 EC2 控制台获取 CoreOS 实例的公共 IP 地址,如图 14-40 所示。
图 14-40。
Displaying EC2 instance detail
SSH 登录到 CoreOS 实例。
ssh -i "coreos.pem" core@34.207.220.127
运行之前复制的命令来连接 Docker Swarm。
docker run --rm -ti -v /var/run/docker.sock:/var/run/docker.sock -e DOCKER_HOST dockercloud/client dvohra/docker-cloud-swarm
下载用于连接 Docker Cloud 的dockercloud/client
Docker 映像。应该显示用户名和密码提示。指定创建群组的 Docker 云帐户的用户名和密码。
Container Linux by CoreOS stable (1298.5.0)
$ docker run --rm -ti -v /var/run/docker.sock:/var/run/docker.sock -e DOCKER_HOST dockercloud/client dvohra/docker-cloud-swarm
Unable to find image 'dockercloud/client:latest' locally
latest: Pulling from dockercloud/client
b7f33ccOb4Be: Pull complete
91b7430c5c68: Pull complete
b686674c0e39: Pull complete
l9aaa3õbba7a: Pull complete
Digest: sha2S6: 11d3cc5e1a62c7324]2a6e038]ccffi9]53tc91d0b1c69c8D1d3b68629337558a6
Status: Downloaded newer image for dockercloud/client:latest
Use your Docker ID credentials to authenticate:
Username: dvohra
Password:
输出一个export
命令以连接到群。复制命令。
Use your Docker ID credentials to authenticate:
Username: dvohra
Password:
=> You can now start using the swarm dvohra/docker-cloud-swarm by executing:
export DOCKER_HOST=tcp://127.0.0.1:32768
运行命令。蜂群连接到 CoreOS Docker 主机。使用docker node ls
命令列出群节点。
>export DOCKER_HOST=tcp://127.0.0.1:32768
>docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
liuomlmb6n6xtq4apxayumsx3 ip-172-31-0-251.us-east-2.cornpute.internal. Ready Active
bchea5x85m82jtzoq336trn8y ip-172-31-47-61.us-east-2.compute.internat. Ready Active
e2bl785z5pqouakdceomdpsbi ip-172-31-42-130.us-east-2.compute.internal. Ready Active
hzxb8choml.7gylaqtrjrh6phx ip-172-31-26-90.us-east-2.compute.internal. Ready Active
pcnple9l29w88ueonhdwUcoc ip-172-31-27-18.us-east-2.compute.internal. Ready Active
rupjaojommfchjgcshffdobhf * ip-172-31-10-153.us-east-2.compute.internal Ready Active Leader
uyl5xv7mhb6c8jam5ofncplyh ip-172-31-25-137.us-east-2.compute.internal. Ready Active Reachable
wi6zurda4nawf9mgku3enf6io ip-172-31-34-33.us-east-2.cornpute.ìnternal Ready Active Reachable
从群组管理器连接到 Docker 群组
另一种选择是使用其公共 IP 地址连接到群管理器。首先,我们从 EC2 控制台获取一个群组管理器的公共 IP 地址,如图 14-41 所示。
图 14-41。
Obtaining the public IP of a Swarm manager
SSH 登录到 Swarm manager。
ssh -i "docker.pem" docker@52.14.146.223
群组管理器登录,并显示群组命令提示符。
[root@1ocathost —]# ssh -i "docker.pem" docker@52.14.146.223
The authenticity of host 52.14.146.223 (52.14.146.223)1 cant be established.
RSA key fingerprint is e9:7f:d2:3c:de:6d:5d:94:06:e2:09:56:b7:2a:c6:9a.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '52.14.146.223 (RSA) to the list of known hosts.
Welcome to Docker!
使用docker node ls
命令列出群节点。
Welcome to Docker!
∼ $ docker node l.s
ID HOSTNAME STATUS
AVAILABILITY MANAGER STATUS
liuomlmb6n6xtq4apxayumsx3 ip-172-31-O-251.us-east-2.compute.internal Ready Active
bchea5x85m82jtzoq336trn8y ip-172-31-47-61.us-east-2.cornpute.internal Ready Active
e2bl785z5pqouakdceonìdpsbi ip-172-31-42-130.us-east-2.compute.internal Ready Active
hzxb8chomt7gyl.aqtrj rh6phx ip-172-31-26-90.us-east-2.compute.interna1 Ready Active
pcnple9l29w88ueenhdwflcoc ip-172-31-27-18.us-east-2.compute.internal Ready Active
rupjaejommfchjgcshffdobhf * ip-172-31-1O-153.us-east-2.compute.internal. Ready Active Leader
uyl5xv7mhb6c8jain5ofncplyh ip-172-31-25-137.us-east-2.compute.internal. Ready Active Reachable
wi6zurda4nawf9mgku3enf6ie ip-172-31-34-33.us-east-2.compute.internal Ready Active Reachab1e
使用docker service create
命令创建一个服务,并用docker service ls
列出该服务。
docker service create \
--name hello-world \
--publish 8080:80 \
--replicas 1 \
tutum/hello-world
创建了hello-world
服务。还列出了 Docker 云服务器代理服务。
∼ $ docker service create \
> --name hello-world \
> --publish 8080:80 \
> - - replicas 1 \
> tutum/hello-world
hbiejbua8u5øskabun3dzkxk4
∼ $ docker service 1s
ID NAME MODE REPLICAS IMAGE
0gzua3p56myx dockerdoud-server-proxy global 3/3 dockercioud/server-proxy:latest
hbiejbua8u50 hello-world replicated 1/1 tutum/hello-world:latest
将蜂群带入 Docker 云
Docker Cloud Swarm 模式还可以将现有的 Swarm 导入 Docker Cloud。要导入的群必须具备以下先决条件:
- 基于 Docker 引擎 1.13 或更高版本的节点
- 群管理器传入端口 2376 已解除阻塞
在本节中,我们创建一个群,并将该群导入 Docker Cloud。首先,运行docker --version
命令来确定 Docker 主机版本是否是 1.13 或更高版本。Docker 为 AWS 提供的 EC2 实例之一可用于创建和导入群,因为定制 Linux 发行版上的 Docker 版本是>Docker 1.13;在创建新的群之前,必须使节点离开群。使用 EC2 实例的私有 IP 地址,启动一个新的群。
docker swarm init --advertise-addr 172.31.23.196
复制docker swarm join
命令输出以加入工作节点。
∼ $ docker --version
Docker version 17.03.0-ce, build 60ccb22
∼ $ docker swarm init --advertise-addr 172.31.23.196
Swarm initialized: current node (ylzc3h3slxO5ztbujtl3yf86p) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-23snf1iuieafnyd1zzgf37ucwuz1.khg9atqsmysmvv6iw1.arw0-do29n83jptkkdwss5fjsd3rt \
172.31.23.196:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
使用 Docker 1.13 或更高版本在另一个 EC2 实例上加入一个 worker 节点。
docker swarm join \
--token SWMTKN-1-61gcsgkr1ildxz580ftdl3rq0s9p7h30n12byktgvbd6y3dk7r-cpes7ofdsq8abhxtznh92tjrz \
10.0.0.176:2377
工作者节点加入群体。
创建了一个有两个节点的群,如在群管理器节点上运行的docker node ls
命令的输出中所列。
∼$ docker node 1s
HOSTNAME STATUS
AVAILABILITY MANAGER STATUS
trgb2t4ehs2gp3cjbrnqhs7a5 ip-172-31-6-64.us-east-2.compute.internal. Ready Active
yl.ic3h3stxo5ztbujtl3yf86p ip-172-31-23-196.us-east-2.compute.internal Ready Active Leader
∼$
接下来,将 Swarm 导入 Docker Cloud。从群组管理器节点,运行以下命令。
docker run -ti --rm -v /var/run/docker.sock:/var/run/docker.sock dockercloud/registration
在用户名提示符下指定 Docker ID,在密码提示符下指定密码。
∼ S docker run -ti --rm -v /var/run/docker.sock:/var/run/docker.sock dockercloud/registration
Unable to find image dockercloud/registration:latest’ locally
latest: Pulling from dockercloud/registration
b7f33ccOb48e: Pull complete
b52875cf8fd4: Pull complete
23f82c866468: Pull complete
Digest: sha256: a3f39de96d2763b957e7bel22ce99b8lfbbaO3fbd6b2e54bd6O7lcafbelcabcl
Status: Downloaded newer image for dockercloud/registratìon:latest
Use your Docker ID credentials to authenticate:
Username: dvohra
Password:
为导入 Docker Cloud 的群组指定一个集群名称,或使用默认名称。将集群指定为dvohra/dockercloudswarm
。蜂群在 Docker Cloud 注册。至于在 Docker 云群模式中创建的群,可以从输出命令的任何 Docker 主机访问该群。
Enter name for the new cluster [dvohra/wkhøtlq8cw5u44x22qp6r4eau]: dvohra/dockercloudswarm
You can now access this cluster using the following command in any Docker Engine
docker run -rm -ti -v /var/run/docker.sock:/var/run/docker.sock -e DOCKER HOST dockerctoud/client dvohra/dockerctoudswarm
要将蜂群带入 Docker Cloud,在蜂群模式下点击自带蜂群按钮,如图 14-42 所示。
图 14-42。
Bring your own Swarm
向 Docker Cloud 注册的群被添加到 Docker Cloud 群中,如图 14-43 所示。
图 14-43。
Docker Cloud Swarms , including the imported Swarm
摘要
本章介绍了 Docker Cloud Swarm 模式,这是一种托管服务,用于将 Docker Cloud 托管服务链接到 AWS 服务提供商帐户,并从 Docker Cloud 供应 Swarm。在命令行上创建的 Swarm 可以导入 Docker Cloud。在下一章,我们将讨论 Docker 服务栈。
十五、使用服务栈
Docker Swarm 模式是 Docker 1.12 版的 Docker 原生模式,用于为开发 Docker 应用创建分布式和可扩展的服务。
问题
虽然单个 Docker 映像应用也很常用,但绝大多数 Docker 企业应用都由多个映像组成,这些映像之间存在依赖关系。Docker Compose(在 v1 和 v2 中是独立的)可以通过使用links
和depends_on
选项来声明微服务之间的依赖关系,但是 Compose(独立的)是过时的,不同于在群模式服务的上下文中定义服务的格式。
解决方案
Docker Swarm mode 引入了服务栈来定义服务的集合(Swarm mode services ),这些服务彼此自动链接,以提供服务之间存在依赖关系的逻辑分组。栈使用的栈文件是 YAML 文件,其格式非常类似于docker-compose.yml
格式。有一些不同之处,比如缺少了用于定义 Docker Compose (standalone)中微服务之间依赖关系的links
和depends_on
选项。YAML ( http://www.yaml.org/
)是配置文件常用的一种数据序列化格式。
从 Docker v1.13 开始,已经引入了命令的docker stack
子集来创建 Docker 栈。使用一个定义多个服务的栈文件,包括服务的配置,如环境变量、标签、容器数量和卷,一个docker stack deploy
命令创建一个服务栈,如图 15-1 所示。这些服务会自动相互链接。
图 15-1。
Service stack created with the docker stack deploy command
Docker Compose v3.x 及更高版本完全兼容 Docker Swarm 模式,这意味着 Docker Compose v3.x docker-compose.yml
文件可以用作栈文件,但栈文件中不支持的几个子选项(包括build
、container_name
、external_links
和links
)除外。Docker Compose 3.x 仍然可以独立使用来开发非群模式服务,但这些微服务无法与 Docker 群模式docker service
命令组一起使用或扩展。
要使用栈来管理群模式服务,必须满足以下要求。
- Docker 版本必须是 1.13 或更高版本
- 必须启用群组模式
- 栈文件 YAML 格式必须基于 Docker Compose v3.x 文件格式
要使用服务栈,可以使用 Docker Compose 第 3 版 YAML 文件格式,但不需要安装 Docker Compose。
使用 Docker Swarm 模式时,对 Swarm 模式的 Docker 版本要求是 1.12 或更高版本。在开发管理群模式服务的栈之前,验证 Docker 版本至少是 1.13。本章使用的 Docker 版本是 17.0x。表 15-1 中列出的docker stack
组命令在 Docker v1.13 和更高版本中可用。
表 15-1。
The docker stack Commands
| 命令 | 描述 | | `deploy` | 部署服务栈或更新现有栈 | | `ls` | 列出栈 | | `ps` | 列出栈中的群组模式任务 | | `rm` | 移除栈 | | `services` | 列出栈中的群组模式服务 |运行docker --version
命令列出 Docker 版本。要列出栈使用的命令,请运行docker stack
命令。
[root@localhost ∼]# ssh -i "docker.pem" docker@34.205.43.53
Welcome to Docker!
∼ $ docker --version
Docker version 17.06.0-ce, build 02c1d87
∼ $ docker stack
Usage: docker stack COMMAND
Manage Docker stacks
Options:
--help Print usage
Commands:
deploy Deploy a new stack or update an existing stack
ls List stacks
ps List the tasks in the stack
rm Remove one or more stacks
services List the services in the stack
要使用栈,请使用以下过程。
- 安装 Docker 版本 1.13 或更高版本(不是 Docker 版本 1.12,它在前面的几章中使用)。
- 启用 Swarm 模式。
- 使用 Docker Compose(3 . x 版)YAML 格式创建一个栈文件。
- 使用
docker stack
组命令创建和管理栈。
本章创建了一个由两个服务组成的服务栈,一个用于 WordPress 博客,另一个用于 MySQL 数据库,以存储 WordPress 博客中的数据。
设置环境
我们使用位于 https://docs.docker.com/docker-for-aws/
的 Docker for AWS 来启动一个 Docker Swarm 模式的节点集群。Docker for AWS 使用 AWS CloudFormation 模板创建 Docker Swarm 模式集群。点击 Deploy Docker Community Edition(stable),如图 15-2 所示,启动 Create CloudFormation Stack 向导创建 Docker Swarm mode 集群。
图 15-2。
Deploying the Docker Community Edition for AWS (stable)
如第三章所述,使用创建栈向导配置一个群组。您可以指定群组管理器的数量为 1、3 或 5,群组工作节点的数量为 1-1000。我们使用了一个群管理器节点和两个群工作者节点,如图 15-3 所示。
图 15-3。
Configuring a CloudFormation stack
云形成栈被创建,如图 15-4 所示。
图 15-4。
CloudFormation Stack for Docker on AWS
启动三个 EC2 实例——一个用于 Docker Swarm manager 节点,两个用于 Swarm worker 节点,如图 15-5 所示。CloudFormation 栈使用的 Linux 发行版是莫比 Linux,如图 15-5 所示。
图 15-5。
The Moby Linux AMI used for Docker on AWS
在能够在 AWS 上使用 Docker 之前,在 EC2 实例使用的安全组中启用 EC2 实例之间的所有入站/出站流量。在图 15-6 中显示了群管理器节点实例入站规则的安全组。
图 15-6。
The security group inbound rules are enabled for all traffic
SSH 登录 Swarm manager EC2 实例,从 AWS 管理控制台获取公共 IP 地址,如图 15-7 所示。
图 15-7。
Public IP address
使用用于创建云形成栈的密钥对 SSH 登录到 Swarm manager 实例。
ssh -i "docker.pem" docker@54.205.48.154
将显示群管理器节点的命令提示符。
[root@localhost ∼]# ssh -i "docker.pem" docker@54.205.48.154
Welcome to Docker!
列出 Swarm 模式中的节点。
docker node ls
列出了三个节点,一个经理和两个工人。
∼ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
bf4ifhh86sivqp03ofzhk6c46 ip-172-31-21-175.ec2.internal Ready Active
ozdhl0jtnricny1y95xbnhwtq ip-172-31-37-108.ec2.internal Ready Active
ud2js50r4livrqf3f4l30fv9r * ip-172-31-19-138.ec2.internal Ready Active Leader
通过创建并列出 Hello World 服务来测试 Swarm 模式。
docker service create --replicas 2 --name helloworld alpine ping docker.com
docker service ls
docker service
命令输出表明 Docker Swarm 服务,因此它被创建并列出。
∼ $ docker service create --replicas 2 --name helloworld alpine ping docker.com
q05fef2a7cf98cv4r2ziyccnv
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
q05fef2a7cf9 helloworld replicated 2/2 alpine:latest
∼ $
配置服务栈
要创建由两个服务组成的服务栈,一个用于 WordPress 博客,另一个用于 MySQL 数据库,使用 Docker Compose 版本 3 YAML 格式( https://docs.docker.com/compose/compose-file/
)创建一个栈文件。创建一个docker-cloud.yml
栈文件(文件名是任意的)来分别使用 Docker 映像wordpress
和mysql
指定两个服务(web
和mysql
)。为 Docker 映像设置环境变量。唯一需要设置的环境变量是mysql
Docker 映像的MYSQL_ROOT_PASSWORD
。wordpress
Docker 映像的WORDPRESS_DB_PASSWORD
环境变量默认为MYSQL_ROOT_PASSWORD
,但也可以明确设置为与MYSQL_ROOT_PASSWORD
相同的值。表 15-2 中列出了wordpress
Docker 映像使用的其他一些环境变量。
表 15-2。
Environment Variables for the Docker Image WordPress
| 环境变量 | 描述 | 缺省值 | | --- | --- | --- | | `WORDPRESS_DB_HOST` | 链接的数据库主机,默认情况下假定为 MySQL 数据库。 | 链接的`mysql` Docker 容器的 IP 和端口 | | `WORDPRESS_DB_USER` | 数据库用户。 | `root` | | `WORDPRESS_DB_PASSWORD` | 数据库密码。 | `MYSQL_ROOT_PASSWORD` | | `WORDPRESS_DB_NAME` | 数据库名称。如果数据库尚不存在,则创建该数据库。 | `wordpress` | | `WORDPRESS_TABLE_PREFIX` | 表格前缀。 | `“”` |如果我们要用wordpress
和mysql
图片和docker run
命令创建一个 WordPress 博客,我们将为每个 Docker 图片分别创建 Docker 容器,并使用–link
选项链接这些容器。如果我们要使用 Docker Compose(独立的),我们需要在 Docker Compose 文件中添加一个links
或depends_on
子选项。
接下来,将 Docker 映像和环境变量指定到栈文件中,以创建服务栈。要将 Docker Compose YAML 文件格式用于群组模式栈,请将栈文件中的version
指定为3
或更高版本,如3.1
。列出了docker-cloud.yml
文件:
version: '3'
services:
web:
image: wordpress
links:
- mysql
environment:
- WORDPRESS_DB_PASSWORD="mysql"
ports:
- "8080:80"
mysql:
image: mysql:latest
environment:
- MYSQL_ROOT_PASSWORD="mysql"
- MYSQL_DATABASE="mysqldb"
8080:80
的ports
映射将 WordPress Docker 容器端口 80 映射到主机端口 8080。创建栈时,会忽略任何栈文件选项,例如前面列表中包含的docker stack deploy
不支持的链接。将前面的清单作为docker-cloud.yml
存储在 Swarm manager EC2 实例中。在 Swarm manager 中列出文件应该会列出docker-cloud.yml
文件。
∼ $ ls -l
total 4
-rwxr-x--- 1 docker docker 265 Jun 17 00:07 docker-cloud.yml
配置了包含两个服务的栈文件后,接下来我们将创建一个服务栈。
创建栈
docker stack deploy
命令用于创建和部署栈。它具有以下语法。
docker stack deploy [OPTIONS] STACK
表 15-3 中讨论了支持的选项。
表 15-3。
Options for the docker stack deploy Command
| [计]选项 | 描述 | 缺省值 | | --- | --- | --- | | `--bundle-file` | 分布式应用包文件的路径。从 Docker 合成文件创建应用包,就像从 Docker 文件创建 Docker 映像一样。应用包可用于创建栈。在开发本章时,应用包是一个试验性的特性,不在本章中讨论。 | | | `--compose-file, -c` | 栈文件的路径。 | | | `--with-registry-` `auth` | 是否向群代理发送注册认证信息。 | 错误的 |使用栈文件docker-cloud.yml
,用docker stack deploy
命令创建一个名为mysql
的 Docker 栈。
docker stack deploy --compose-file docker-cloud.yml mysql
会创建一个 Docker 栈,并忽略群组模式中不支持的links
选项。除了网络mysql_default
之外,还创建了两个群服务mysql_mysql
和mysql_web
。
∼ $ docker stack deploy --compose-file docker-cloud.yml mysql
Ignoring unsupported options: links
Creating network mysql_default
Creating service mysql_mysql
Creating service mysql_web
列表栈
使用以下命令列出栈。
docker stack ls
列出了mysql
栈。还列出了栈中的服务数量。
∼ $ docker stack ls
NAME SERVICES
mysql 2
列表服务
使用docker stack services
命令列出mysql
栈中的服务,其语法如下。
docker stack services [OPTIONS] STACK
表 15-4 中列出了支持的选项。
表 15-4。
Options for the docker stack services Command
| [计]选项 | 描述 | 缺省值 | | --- | --- | --- | | `--filter, -f` | 基于提供的过滤器(或条件)过滤输出 | | | `--quiet, -q` | 是否只显示服务的 id | `false` |若要列出所有服务,请运行以下命令。
docker stack services mysql
列出了两种服务— mysql_mysql
和mysql_web
。
∼ $ docker stack services mysql
ID NAME MODE REPLICAS IMAGE
ixv0ykhuo14c mysql_mysql replicated 1/1 mysql:latest
vl7ph81hfxan mysql_web replicated 1/1 wordpress:latest
要过滤服务,添加--filter
选项。要过滤多个服务,添加多个--filter
选项,如下面的命令所示。
docker stack services --filter name=mysql_web --filter name=mysql_mysql mysql
将列出过滤后的栈服务。因为两个服务都是使用–filter
指定的,所以两个服务都被列出。
∼ $ docker stack services --filter name=mysql_web --filter name=mysql_mysql mysql
l
ID NAME MODE REPLICAS IMAGE
ixv0ykhuo14c mysql_mysql replicated 1/1 mysql:latest
vl7ph81hfxan mysql_web replicated 1/1 wordpress:latest
栈创建的服务是群服务,也可以使用以下命令列出。
docker service ls
列出了相同的两种服务。
∼ $ docker service ls
ID NAME MODE REPLICAS IMAGE
ixv0ykhuo14c mysql_mysql replicated 1/1 mysql:latest
sl2jmsat30ex helloworld replicated 2/2 alpine:latest
vl7ph81hfxan mysql_web replicated 1/1 wordpress:latest
列出 Docker 容器
docker stack ps
命令用于列出栈中的 Docker 容器,其语法如下:使用--help
选项输出命令用法。
∼ $ docker stack ps --help
Usage: docker stack ps [OPTIONS] STACK
List the tasks in the stack
Options:
-f, --filter filter Filter output based on conditions provided
--help Print usage
--no-resolve Do not map IDs to Names
--no-trunc Do not truncate output
要列出mysql
栈中的所有 Docker 容器,运行以下命令。
docker stack ps mysql
默认情况下,为每个服务创建一个副本,因此为栈中的每个服务列出一个 Docker 容器。两个 Docker 容器都运行在一个 Swarm worker 节点上。
∼ $ docker stack ps mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
n9oqwaikd61g mysql_web.1 wordpress:latest ip-172-31-37-108.ec2.internal Running Running 3 minutes ago
infzi7kxg9g9 mysql_mysql.1 mysql:latest ip-172-31-37-108.ec2.internal Running Running 3 minutes ago
使用–f
选项过滤 Docker 容器,只列出mysql_web.1
容器。
∼ $ docker stack ps -f name=mysql_web.1 mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
n9oqwaikd61g mysql_web.1 wordpress:latest ip-172-31-37-108.ec2.internal Running Running 9 minutes ago
通过将desired-state
过滤器设置为正在运行,列出所有正在运行的容器。
∼ $ docker stack ps -f desired-state=running mysql
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
n9oqwaikd61g mysql_web.1 wordpress:latest ip-172-31-37-108.ec2.internal Running Running 10 minutes ago
infzi7kxg9g9 mysql_mysql.1 mysql:latest ip-172-31-37-108.ec2.internal Running Running 10 minutes ago
使用服务栈
接下来,我们使用这个栈来创建一个 WordPress 博客。可以在 Swarm manager 主机的端口8080
上访问名为web
的栈服务。获取群管理器节点 EC2 实例的公共 DNS,如图 15-8 所示。
图 15-8。
Public DNS of Swarm manager
在浏览器中打开<public dns>:8080
URL。显示<public dns>:8080/wp-admin/install.php
URL 来开始 WordPress 的安装。选择继续。指定副标题、用户名、密码、电子邮件以及是否阻止搜索引擎对网站进行索引。然后点击安装 WordPress,如图 15-9 所示。
图 15-9。
Installing WordPress
安装好 WordPress,如图 15-10 所示。点击登录。
图 15-10。
WordPress is installed
指定用户名和密码,点击登录,如图 15-11 所示。
图 15-11。
Logging in
显示 WordPress 博客仪表盘,如图 15-12 所示。
图 15-12。
The WordPress dashboard
要添加新帖子,请选择帖子并点击“添加新”,如图 15-13 所示。
图 15-13。
Adding a new post
在“添加新文章”对话框中,指定标题并添加博客条目。点击发布,如图 15-14 所示。
图 15-14。
Publishing a new post
新帖子已添加。点击查看帖子,如图 15-15 所示,显示帖子。
图 15-15。
Viewing the new post
显示博文,如图 15-16 所示。
图 15-16。
Displaying a blog post
向下滚动并添加注释,如图 15-17 所示。
图 15-17。
Adding a comment
添加注释,如图 15-18 所示。
图 15-18。
The comment has been added
移除栈
docker stack rm STACK
命令用于移除栈。使用以下命令移除mysql
栈。
docker stack rm mysql
删除了mysql
栈,并且docker stack service mysql
命令没有列出栈,如该命令的输出所示。
∼$ docker stack rm mysql
Removing service mysql_mysql
Removing service mysql_web
Removing network mysql_default
∼$ docker stack services mysql
Nothing found in stack: mysql
摘要
本章介绍了 Docker 1.13 中添加的 Docker 原生特性 stacks。栈是相关服务的集合,使用栈文件创建,栈文件以类似于 Docker Compose v3.x YAML 语法的 YAML 格式定义。本章总结了这本书关于 Docker 管理设计模式的内容。随着 Docker 中新特性的加入,其他设计模式也可以用于开发 Docker 原生应用。