Docker-管理设计模式-全-

Docker 管理设计模式(全)

原文:Docker Management Design Patterns

协议:CC BY-NC-SA 4.0

一、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 向导所示,启动一个实例。

A454123_1_En_1_Fig1_HTML.jpg

图 1-1。

Selecting an AMI for CoreOS Linux

从选择实例类型中,选择 t2.micro 类型并单击下一步。在“配置实例详细信息”中,将实例数量指定为 1。选择一个网络或单击“创建新 VPC”来创建新的 VPC。选择一个子网或单击“创建新子网”来创建新子网。选择启用自动分配公共 IP。点击下一步。

从添加存储中,选择默认设置,然后单击下一步。在添加标签中,不需要添加任何标签。点击下一步。从配置安全组中,添加一个安全组,以允许来自任何来源(0.0.0.0/0)的所有端口范围内的任何协议的所有流量。点击查看和启动,然后点击启动。

选择一个密钥对,并在选择现有密钥对或创建新密钥对对话框中点击启动实例,如图 1-2 所示。

A454123_1_En_1_Fig2_HTML.jpg

图 1-2。

Launch instances

带有 CoreOS 的 EC2 实例被启动。从 EC2 控制台获取 EC2 实例的公共 DNS 或 IPv4 公共 IP 地址,如图 1-3 所示,SSH 登录实例。

A454123_1_En_1_Fig3_HTML.jpg

图 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 的网络浏览器中。

A454123_1_En_1_Fig4_HTML.jpg

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

    A454123_1_En_2_Fig1_HTML.gif

    图 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 节点本身仍然提取映像并启动容器。

A454123_1_En_2_Fig2_HTML.gif

图 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 一致性不适用于工作节点,因为群容量仅基于工作节点。即使三个工作者节点中的两个发生故障,一个工作者节点,即使管理器节点是仅管理器节点,也将保持群可用,尽管群容量减少,并且可以将一些运行任务转换到非运行状态。

A454123_1_En_2_Fig3_HTML.gif

图 2-3。

Fault tolerance for a Swarm

本节涵盖以下主题:

  • 设置环境
  • 初始化 Docker 群组模式
  • 将节点加入群集群
  • 测试群集群集
  • 将工作节点提升为管理器
  • 将管理员节点降级为工作节点
  • 使工作节点离开集群
  • 使工作节点重新加入集群
  • 使管理节点离开集群
  • 重新初始化蜂群
  • 修改节点可用性
  • 删除节点

设置环境

本章向您展示了如何创建一个由一个管理节点和两个工作节点组成的三节点集群。使用 CoreOS Stable AMI 创建三个 Amazon EC2 实例,如图 2-4 中的 EC2 控制台所示。为 EC2 实例配置安全组时,启用 EC2 实例之间的所有流量。获取为群组管理器启动的 EC2 实例的 IP 地址。

A454123_1_En_2_Fig4_HTML.jpg

图 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` | 格式为`[:port]`的广告地址。所通告的地址是其他节点可以访问群的 IP 地址。如果没有指定 IP 地址,Docker 确定系统是否有单个 IP 地址,如果有,则使用 IP 地址和端口 2337。如果系统有多个 IP 地址,必须为管理器间通信和覆盖网络指定`--advertise-addr`。 |   | | `--availability` | 节点的可用性。应该是`active` / `pause` / `drain`中的一个。 | `active` | | `--force-new-cluster` | 是否从当前状态强制创建新的群集。我们将在本章中讨论为什么可能需要强制创建和使用该选项。 | `false` | | `--listen-addr` | 格式为`[:port]`的监听地址。 | `0.0.0.0:2377` |

除了没有提供默认值的--advertise-addr,所有选项都使用默认值。使用私有地址作为广告地址,可以从 EC2 控制台获得,如图 2-5 所示。如果 AWS 上的 EC2 实例位于不同的区域,则应该使用外部公共 IP 地址来访问 manager 节点,该地址也可以从 EC2 控制台获得。

A454123_1_En_2_Fig5_HTML.jpg

图 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 后面的*表示这是当前节点。群中的节点也有一个STATUSAVAILABILITYMANAGER 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 and docker node rm `用`docker swarm join`添加另一个管理节点。或者用`docker node promote`将一个工作者节点提升为管理者节点 | | `Leader` | 执行所有群管理和协调的主管理器节点。 |

将节点加入群

额外的节点,管理者或工作者,可以根据需要被添加或加入到群中。默认情况下,管理器节点也是工作者节点,但反之亦然。添加管理节点的原因与添加工作节点的原因不同。添加管理节点以使群更加容错,添加工作者节点以增加群的容量。添加管理器和工作器节点的命令也不同。当初始化群时,输出添加工作者节点的命令。使用以下命令也可以找到添加工作节点的命令。

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` | 格式为`[:port]`的广告地址。 |   | | `--availability` | 节点的可用性。`active` / `pause` / `drain`之一。 | `active` | | `--listen-addr` | 格式为`[:port]`的监听地址。 | `0.0.0.0:2377` | | `--token` | 进入蜂群的令牌。 |   |

在 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命令将一个工作者节点加入群时,发生以下事件序列。

  1. 启用节点上 Docker 引擎的群组模式。
  2. 对 TLS 证书的请求被发送给管理者。
  3. 该节点以机器主机名命名。
  4. 当前节点在管理器监听地址加入群。基于该令牌,该节点作为工作者节点或管理者节点加入。
  5. 将当前节点设置为Active可用性。
  6. 入口覆盖网络扩展到当前节点。

当节点使用管理器令牌加入群时,该节点作为管理器节点加入。新的管理器节点应该是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 leavedocker node rm的区别在于docker node rm只能从管理节点运行。降级的节点只能用docker node rm命令从集群中移除。不使用--force选项删除管理器节点的顺序如下。

  1. 降级 manager 节点,使其成为 worker 节点。
  2. 清空工作节点。
  3. 让工作者节点离开群体。
  4. 移除节点。

摘要

本章讨论了在群组模式下使用 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 区域中,则该区域的故障将导致该区域不可用。单区域群不是高可用性群,并且没有容错能力。

A454123_1_En_3_Fig1_HTML.gif

图 3-1。

A single-zone Swarm

解决方案

Docker 和 AWS 合作创建了 Docker for AWS 部署平台,在 AWS 的多个区域提供 Docker 群。Docker for AWS 不需要用户在命令行上运行任何命令,并且是基于图形用户界面(GUI)的。对于多个区域中的管理者和工作者节点,单个 AWS 区域的故障不会使群不可用,如图 3-2 所示。Docker for AWS 为蜂群提供了容错能力。

A454123_1_En_3_Fig2_HTML.gif

图 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 机器上。

A454123_1_En_3_Fig3_HTML.jpg

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

A454123_1_En_3_Fig4_HTML.gif

图 3-4。

Deploy Docker for AWS

Create Stack 向导开始提供设计新模板或为 AWS 上的 Docker 选择默认 CloudFormation 模板。选择指定亚马逊 S3 模板 URL 选项,为其预先指定一个 URL,如图 3-5 所示。点击下一步。

A454123_1_En_3_Fig5_HTML.jpg

图 3-5。

Selecting a template

在指定详细信息中,指定栈名称(DockerSwarm)。群组参数部分包含表 3-1 中列出的字段。

表 3-1。

Swarm Parameters

| 参数 | 描述 | | --- | --- | | 群体管理者的数量? | 群管理器节点的数量。有效值为 1、3 和 5。 | | 群体工作者节点的数量? | 群中工作节点的数量(0-1000)。 |

群管理器数量保持默认设置 3,群工作器节点数量保持默认设置 5,如图 3-6 所示。

A454123_1_En_3_Fig6_HTML.jpg

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

A454123_1_En_3_Fig7_HTML.jpg

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

A454123_1_En_3_Fig8_HTML.jpg

图 3-8。

Swarm worker properties

群体工作者属性如图 3-8 所示。点击下一步。

接下来,指定栈的选项。可以为栈中的资源指定标签(键值对)。对于权限,可以选择云形成的 IAM 角色。这些选项都不需要设置,如图 3-9 所示。

A454123_1_En_3_Fig9_HTML.jpg

图 3-9。

Optional settings

对于高级选项,通知选项设置为无通知。将故障回退设置为是,如图 3-10 所示。点击下一步。

A454123_1_En_3_Fig10_HTML.jpg

图 3-10。

Setting rollback on failure

查看栈设置,如图 3-11 所示。

A454123_1_En_3_Fig11_HTML.jpg

图 3-11。

Reviewing the stack settings

选择确认复选框,然后点击创建,如图 3-12 所示。

A454123_1_En_3_Fig12_HTML.jpg

图 3-12。

Creating the stack

开始创建新的栈。点击刷新按钮刷新列表中的栈,如图 3-13 所示。

A454123_1_En_3_Fig13_HTML.jpg

图 3-13。

Refresh

如图 3-14 所示的状态CREATE_IN_PROGRESS所示,基于 Docker Swarm 的云形成模板的新栈开始创建。

A454123_1_En_3_Fig14_HTML.jpg

图 3-14。

CloudFormation stack status

为不同的栈详细信息提供了不同的选项卡。资源页签显示了 CloudFormation 模板创建的 AWS 资源,如图 3-15 所示。

A454123_1_En_3_Fig15_HTML.jpg

图 3-15。

CloudFormation stack resources

事件选项卡显示了创建云形成栈时发生的事件,如图 3-16 所示。

A454123_1_En_3_Fig16_HTML.jpg

图 3-16。

CloudFormation stack events

栈创建完成后,状态显示为CREATE_COMPLETE,如图 3-17 所示。

A454123_1_En_3_Fig17_HTML.jpg

图 3-17。

Stack status is CREATE_COMPLETE

所有需要的资源——包括自动扩展组、EC2 互联网网关、EC2 安全组、弹性负载均衡器、IAM 策略、日志组和 VPC 网关——都已创建,如图 3-18 所示。

A454123_1_En_3_Fig18_HTML.jpg

图 3-18。

Resources are created

输出选项卡列出了默认 DNS 目标、关于可用区域数量的区域可用性注释以及管理器节点,如图 3-19 所示。

A454123_1_En_3_Fig19_HTML.jpg

图 3-19。

Outputs

要列出群管理器的 EC2 实例,点击管理器中的链接,如图 3-20 所示。

A454123_1_En_3_Fig20_HTML.jpg

图 3-20。

The Managers link

这三个管理器实例都位于不同的可用性区域。每个 EC2 实例的公共/私有 IP 地址和公共 DNS 名称可以从 EC2 控制台获得,如图 3-21 所示。

A454123_1_En_3_Fig21_HTML.jpg

图 3-21。

Manager instances on EC2

使用 AMI ID 可以找到用于 EC2 实例的 AMI,如图 3-22 所示。一个莫比 Linux AMI 用于这个群,但是 AMI 对于不同的用户和在不同的 AWS 区域可能是不同的。

A454123_1_En_3_Fig22_HTML.jpg

图 3-22。

Moby Linux AMI

通过将实例状态设置为 Running,可以列出所有 EC2 实例。列出了 Docker Swarm manager 节点(三个)和 worker 节点(五个),如图 3-23 所示。管理节点和工作节点位于三个不同的可用性区域。

A454123_1_En_3_Fig23_HTML.jpg

图 3-23。

Swarm managers and workers in three different availability zones

在 EC2 仪表板中选择负载均衡器,将会列出提供的弹性负载均衡器,如图 3-24 所示。单击“实例”选项卡列出实例。所有实例的状态都应该设置为正在使用,如图 3-24 所示。

A454123_1_En_3_Fig24_HTML.jpg

图 3-24。

Elastic Load Balancer

从 EC2 仪表板中选择启动配置。将列出两个启动配置—一个用于管理器节点,一个用于工作者节点,如图 3-25 所示。

A454123_1_En_3_Fig25_HTML.jpg

图 3-25。

Launch configurations

在 EC2 仪表板中选择自动缩放组。将列出两个自动缩放组—一个用于管理器节点,一个用于工作者节点,如图 3-26 所示。

A454123_1_En_3_Fig26_HTML.jpg

图 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

列出了由管理者状态LeaderReachable指示的领导者节点和另外两个管理者节点。工作节点都是可用的,如可用性列中的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 所示。

A454123_1_En_3_Fig27_HTML.jpg

图 3-27。

Choosing Actions ➤ Delete Stack

在删除栈确认对话框中,点击是,删除,如图 3-28 所示。

A454123_1_En_3_Fig28_HTML.jpg

图 3-28。

Delete stack confirmation dialog

栈状态变为DELETE_IN_PROGRESS,如图 3-29 所示。

A454123_1_En_3_Fig29_HTML.jpg

图 3-29。

Delete in progress

随着每个栈的资源被删除,其状态变为DELETE_COMPLETE,如图 3-30 中事件选项卡上的一些资源所示。

A454123_1_En_3_Fig30_HTML.jpg

图 3-30。

Events list some of the resources with a status of DELETE_COMPLETE

当 EC2 实例被删除后,EC2 控制台将其状态列为terminated,如图 3-31 所示。

A454123_1_En_3_Fig31_HTML.jpg

图 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 引擎上运行。

A454123_1_En_4_Fig1_HTML.gif

图 4-1。

Docker container on a single host

解决方案

Docker Swarm 模式使 Docker 应用能够跨由覆盖网络连接的分布式 Docker 引擎集群运行,如图 4-2 所示。Docker 服务可以用特定数量的副本来创建,每个副本可能在集群中的不同主机上运行。群由一个或多个管理节点组成,由一个领导者负责群管理和协调。工作节点运行实际的服务任务,默认情况下,管理节点是工作节点。停靠服务只能从领导者节点启动。因此,在工作节点上调度的服务副本运行分布式应用。分布式应用提供了一些好处,例如容错、故障转移、增加容量和负载均衡等。

A454123_1_En_4_Fig2_HTML.gif

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

A454123_1_En_4_Fig3_HTML.jpg

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

A454123_1_En_4_Fig4_HTML.jpg

图 4-4。

Setting inbound rules on a security group to allow all traffic

与群组管理器相关联的安全组的出站规则如图 4-5 所示。

A454123_1_En_4_Fig5_HTML.jpg

图 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 格式:`[:]`。 | | `--workdir, -w` | 容器内的工作目录。 |

例如,创建一个名为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.nhello-world.1hello-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 所示。

A454123_1_En_4_Fig6_HTML.jpg

图 4-6。

Invoking a service in a browser

类似地,您可以获得一个托管有 Swarm worker 节点的 EC2 实例的公共 DNS,如图 4-7 所示。

A454123_1_En_4_Fig7_HTML.jpg

图 4-7。

Obtaining the public DNS for a EC2 instance on which a Swarm worker node is hosted

在浏览器中使用PublicDNS:8080 URL 调用服务,如图 4-8 所示。

A454123_1_En_4_Fig8_HTML.jpg

图 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=` `name=` `node=` `desired-state=(running | shutdown | accepted)` | | `--no-resolve` | 是否将 id 映射到名称。默认值为`false`。 | | `--no-trunc` | 是否截断输出。直到 Docker 1.13 才添加选项。默认值为`false`。 | | `--quiet, -q` | 是否只显示任务标识号。直到 Docker 1.13 才添加选项。默认值为`false`。 |

例如,您可以仅列出正在运行的服务任务。

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-worldmysql服务运行的任务少于三个时。如果副本的数量大于群中节点的数量,则一个节点可以运行多个服务任务。扩展到五个复制副本会在两个节点上启动多个复制副本。

∼ $ 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_PASSWORDdocker 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(格式:`[:]`)。 | | `--workdir, -w` | 更新容器内的工作目录。 |

接下来,我们更新已部署服务的一些参数。

更新副本

首先,创建一个要更新的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:latestDocker image。运行以下命令来更新 Docker 映像;输出服务名以表明更新成功。

∼ $ docker service update --image mysql:latest mysql
mysql

您可以使用docker service inspect命令列出服务的详细信息。ContainerSpec中列出的映像是mysql:latestPreviousSpec也在列。

∼ $ 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命令,并将过滤器设置为该节点。然后列出过滤后的任务,包括RunningShutdown

∼ $ 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_DATABASEMYSQL_PASSWORDMYSQL_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 原生群模式支持之前)。

A454123_1_En_5_Fig1_HTML.gif

图 5-1。

Docker engine without provision for scaling

解决方案

Docker 群模式提供了扩展 Docker 服务的功能。服务抽象与零个或多个副本(任务)相关联,每个任务为服务启动一个 Docker 容器。该服务可以根据需要扩大或缩小规模,以运行更多/更少的副本。只需一个docker service scale <svc>=<replicas>命令,服务就可以运行所需数量的副本,如图 5-2 所示。如果要跨分布式集群启动 10 个服务副本,那么一个命令就能够提供扩展。

A454123_1_En_5_Fig2_HTML.gif

图 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 updatedocker 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-worldnginx

∼ $ 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

创建了全局服务,并且列出服务应该指示设置为globalMode

∼ $ 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命令可以扩展多个服务。为了演示,创建两个服务:nginxmysql

∼ $ 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下的服务任务容器中。

A454123_1_En_6_Fig1_HTML.gif

图 6-1。

Volume mount

服务中的每个容器都可以访问运行该容器的主机上的相同命名卷,但是该主机命名卷可以存储相同或不同的数据。

使用卷装载时,内容不会跨群集复制。例如,如果您将某些内容放入正在使用的mysql-scripts目录,这些新文件将只能被运行在同一节点上的其他任务访问。在其他节点上运行的副本将无法访问这些文件。

绑定安装

绑定装载是要在其上计划服务任务的主机上的文件系统路径。主机文件系统路径被装载到服务任务容器的指定目录路径中。主机文件系统路径必须存在于群中的每个主机上,在创建服务之前可以在其上调度任务。如果使用节点约束将某些节点排除在服务部署之外,则绑定装载主机文件系统不必存在于这些节点上。使用绑定挂载时,请记住使用绑定挂载的服务本身是不可移植的。如果要在生产中部署服务,主机目录路径必须存在于生产集群中的每个主机上。

主机文件系统路径不必与任务容器中的目标目录路径相同。例如,在图 6-2 中,主机路径/db/mysql/data作为绑定挂载被挂载到目录路径/etc/mysql/data的服务容器中。默认情况下,绑定装载是读写的,但是在服务部署时可以设置为只读。服务中的每个容器都可以访问运行容器的主机上的相同目录路径,但是主机目录路径可以存储不同或相同的数据。

A454123_1_En_6_Fig2_HTML.gif

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

A454123_1_En_6_Fig3_HTML.jpg

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

A454123_1_En_6_Fig4_HTML.jpg

图 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

安装了类型为readonlymount活页夹。

访问调度任务的节点上的容器,并列出主机目录中的示例脚本。

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

A454123_1_En_7_Fig1_HTML.gif

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

A454123_1_En_7_Fig2_HTML.gif

图 7-2。

Unequal allocation of resources

可能导致的第二个问题是,节点的资源容量可能被完全用尽,而没有任何供应来调度任何更多的服务任务。例如,一个资源容量为 9GB 和 9 个 CPU 的节点运行着三个服务任务容器,每个容器使用 3GB 和 3 个 CPU,如图 7-3 所示。如果为同一个或另一个服务创建了新的服务任务,它在节点上没有任何可用的资源。

A454123_1_En_7_Fig3_HTML.gif

图 7-3。

Fully resource-utilized node

解决方案

Docker Swarm 模式具有设置资源保证(或储备)和资源限制的规定,如图 7-4 所示。资源储备是为服务任务保证或保留的最小资源量。资源限制是服务任务可以使用的最大资源量,而不管有多少资源可用。

A454123_1_En_7_Fig4_HTML.gif

图 7-4。

Managing Swarm resources with resource reserves and limits

有了资源储备,在前面讨论的问题中,每个服务任务容器可以保证 1 个 CPU 和 1GB,如图 7-5 所示。

A454123_1_En_7_Fig5_HTML.gif

图 7-5。

Resource allocation with resource reserves set

并且,如果对服务任务容器实施资源限制,则多余的资源将可用于启动新的服务任务容器。在前面讨论的示例中,每个服务任务 2GB 和 2 个 CPU 的限制将使 3GB 和 3 个 CPU 的额外资源可用于新的服务任务容器,如图 7-6 所示。

A454123_1_En_7_Fig6_HTML.gif

图 7-6。

Resource allocation with resource limits set

本章涵盖以下主题:

  • 设置环境
  • 创建没有资源规范的服务
  • 保留资源
  • 设置资源限制
  • 使用资源规范创建服务
  • 扩展和资源
  • 保留资源必须小于资源限制
  • 设置资源限制和储备的滚动更新
  • 资源使用和节点容量

设置环境

在 Docker 上为 AWS 创建一个三节点 Swarm,其中有一个管理节点和两个工作节点。在 Docker 上为 AWS 创建一个 Swarm 将在第三章中讨论。我们在本章中也使用了在第六章中创建的三节点蜂群。获取 Swarm manager 实例的公共 IP 地址,如图 7-7 所示。

A454123_1_En_7_Fig7_HTML.jpg

图 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 createdocker service update命令中提供了两个资源预留选项,如表 7-1 所示。

表 7-1。

Options for Resource Reserves

| [计]选项 | 描述 | 缺省值 | | --- | --- | --- | | `--reserve-cpu` | 储备 CPU。值`0.000`意味着没有设置储备。 | `0.000` | | `--reserve-memory` | 保留记忆。值`0`意味着没有设置储备。 | `0` |

设置资源限制

群模式在docker service createdocker 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/3Replicas列值所示,没有服务副本被调度,因为请求的容量大于单个节点的节点容量。

∼ $ 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/1Replicas列值所示。

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

A454123_1_En_7_Fig8_HTML.jpg

图 7-8。

Choosing Actions ➤ Update Stack

“更新 Docker 栈向导”启动。它类似于创建栈向导。在选择模板中,单击下一步,不修改任何设置。具体来说,增加集群工作节点的数量?至 10,如图 7-9 所示。点击下一步。

A454123_1_En_7_Fig9_HTML.jpg

图 7-9。

Increasing the number of worker nodes to 10

在预览你的修改时,点击更新,如图 7-10 所示。

A454123_1_En_7_Fig10_HTML.jpg

图 7-10。

Click Update to preview your changes

更新完成后,栈状态变为UPDATE_COMPLETE,如图 7-11 所示。

A454123_1_En_7_Fig11_HTML.jpg

图 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 所示。只能在工作节点上计划服务任务。默认情况下,管理器节点也是工作者节点。

A454123_1_En_8_Fig1_HTML.gif

图 8-1。

Scheduling

问题

如果没有调度策略,服务任务可能会被调度到群中的一个节点子集上。例如,一个服务中的所有三个任务都可以在一个群中的同一个节点上被调度,如图 8-2 所示。

A454123_1_En_8_Fig2_HTML.gif

图 8-2。

Avoid scheduling all tasks on one node

不使用调度策略可能会导致以下问题:

  • 群中资源利用不足—如果所有任务都在单个节点或节点子集上调度,则其他节点的资源容量没有得到利用。
  • 资源利用不均衡—如果所有任务都安排在单个节点或节点子集上,则安排任务的节点上的资源会被过度利用,任务甚至会用尽所有资源容量,而没有扩展副本的余地。
  • 缺乏局部性—客户端基于节点位置访问服务的任务。如果所有服务任务都被安排在单个节点上,则在其他节点上访问服务的外部客户端不能本地访问服务,从而在访问相对远程的任务时导致网络开销。
  • 单点故障—如果所有服务都在一个节点上运行,而该节点出现问题,就会导致停机。增加节点间的冗余可以避免这个问题。

解决方案

为了克服前面讨论的问题,Docker 群中的服务任务调度是基于一个内置的调度策略。Docker Swarm 模式使用分散调度策略对节点进行排序,以放置服务任务(副本)。为每个任务的调度计算节点排序,并且在具有最高计算排序的节点上调度任务。分布调度策略根据节点的可用 CPU、RAM 和已经在节点上运行的容器数量来计算节点等级。传播策略针对容器数量最少的节点进行优化。负载共享是分布策略的目标,它导致任务(容器)稀疏而均匀地分布在集群中的几台机器上。传播策略的预期结果是,如果单个节点或节点的一个小子集宕机或变得可用,则只有少数任务丢失,而群中的大多数任务继续可用。

Note

因为容器在所有状态下都会消耗资源,包括退出时,所以扩展策略不会考虑容器的状态。建议用户删除已停止的容器,因为如果某个节点有几个已停止的容器,那么该节点将变得不适合调度新任务。

扩展调度策略不考虑为哪个服务调度任务。只有可用的和请求的资源被用于调度新的任务。图 8-3 显示了使用扩展调度策略的调度。

A454123_1_En_8_Fig3_HTML.gif

图 8-3。

Using the spread scheduling policy

举个假设的例子:

  1. 从三个节点开始,每个节点有 3GB 的容量和 3 个 CPU,并且没有容器运行。
  2. 创建一个带有一个副本的mysql服务,它需要 1GB 和 1 个 CPU 的资源。第一个副本被随机安排在群中三个节点中的一个上,因为所有节点都具有相同的排名。如果所有的节点都有相同的等级,一个新的任务会被随机安排在其中一个节点上。
  3. mysql服务扩展为三个任务。由于其中一个节点已经加载,这两个新任务被安排在另外两个节点上,将一个任务分配给每个节点。
  4. mysql服务扩展到五个任务。必须启动两个新任务,并且所有节点都具有相同的等级,因为它们具有相同的可用资源容量和相同数量的正在运行的容器。这两个新任务随机安排在两个节点上。因此,两个节点各有两个任务,一个节点有一个任务。
  5. nginx服务器创建另一个服务,需要两个任务,每个任务需要 0.5GB 和 0.5 CPU。这两个任务都被安排在只有mysql服务任务的节点上,因为它负载最小。因此,两个节点有两个mysql服务任务和 1GB 和 1 个 CPU 的可用容量,一个节点有两个nginx服务任务和一个mysql服务任务以及 1GB 和 1 个 CPU 的可用资源容量。
  6. nginx服务扩展到三个。即使所有节点都有相同的可用 CPU 和 RAM,新任务也不会随机安排在三个节点中的一个上,而是安排在容器数量最少的节点上。结果,新的nginx任务被随机安排在其中一个节点上,每个节点有两个mysql任务。如果节点具有相同的可用 CPU 和 RAM,则选择具有较少容器(运行或停止)的节点来调度新任务。

本章涵盖以下主题:

  • 设置环境
  • 创建和计划服务—扩展计划
  • 期望的国家和解
  • 受节点资源容量限制的任务调度
  • 添加服务调度约束
  • 在特定节点上调度
  • 添加多个日程排定限制
  • 为调度添加节点标签
  • 添加、更新和删除服务调度约束
  • 扩展调度和全球服务

设置环境

使用 Docker for AWS 创建一个由一个管理节点和两个工作节点组成的 CloudFormation 栈。AWS 的 Docker 在第三章中介绍。栈如图 8-4 所示。

A454123_1_En_8_Fig4_HTML.jpg

图 8-4。

CloudFormation stack

栈中的三个 EC2 实例如图 8-5 所示。

A454123_1_En_8_Fig5_HTML.jpg

图 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                       

期望的国家和解

当一个服务被创建或者被放大或缩小时,该服务最初在当前状态和期望状态之间存在差异。期望状态的不同值是readyrunningshutdownaccepted。Docker 服务是为期望的状态协调而设计的,这意味着群管理器持续监视集群状态,以协调服务的期望状态和当前状态之间的任何差异。任务的当前状态可以是assignedpreparingreadyrunningshutdownpending。已经分配给节点但当前未运行的任务处于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 所示。

A454123_1_En_8_Fig6_HTML.jpg

图 8-6。

Updating a stack

将工作节点的数量从两个减少到一个,如图 8-7 所示。

A454123_1_En_8_Fig7_HTML.jpg

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

A454123_1_En_8_Fig8_HTML.jpg

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

A454123_1_En_8_Fig9_HTML.jpg

图 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。这不会降低已经运行的任务的资源使用率,也不会使pendingshutdown任务运行。例如,当一些任务由于缺乏资源而处于pendingshutdown时,降低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 支持放置或调度约束来调度新任务。服务放置约束是放置服务任务的附加标准,可以基于节点属性、元数据和引擎元数据。群调度器使用以下序列来调度服务任务。

  1. 节点是否满足所有放置约束?
  2. 一个节点是否满足均匀分布的调度策略要求?
  3. 节点是否有足够的资源来调度任务?

可通过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 所示。因此,该服务在滚动更新期间继续可用。在滚动更新期间,提供给客户端的服务任务可以来自旧的和新的服务定义。例如,如果滚动更新对更近的映像标签执行更新,则在滚动更新期间提供给外部客户端的一些任务可能来自旧映像标签和新映像标签的混合。

A454123_1_En_9_Fig1_HTML.gif

图 9-1。

Rolling update

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

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

  1. 第一个任务停止。
  2. 已计划对停止的任务进行更新。
  3. 启动更新任务的 Docker 容器。
  4. 如果任务更新返回RUNNING,等待--update-delay中指定的持续时间,并开始下一个任务的更新。
  5. 如果在更新过程中,任务返回FAILED,执行--update-failure-action,默认暂停更新。
  6. docker service update <SERVICE-ID>重启暂停的更新。
  7. 如果更新失败重复出现,请找到失败的原因,并通过向 docker 服务更新提供其他选项来重新配置服务。

设置环境

使用 Docker for AWS 创建一个由一个管理节点和两个工作节点组成的 Docker 群,如第三章所述。从 EC2 控制台获取 manager 实例的公共 IP 地址,然后 SSH 登录到该实例。

[root@localhost ∼]# ssh -i "docker.pem" docker@54.84.133.157

Welcome to Docker!

列出群体节点。

∼ $ docker node ls
ID                          HOSTNAME                      STATUS  AVAILABILITY MANAGER STATUS
81h6uvu8uq0emnovzkg6v7mzg   ip-172-31-2-177.ec2.internal  Ready   Active              
e7vigin0luuo1kynjnl33v9pa   ip-172-31-29-67.ec2.internal  Ready   Active              
ptm7e0p346zwypos7wnpcm72d * ip-172-31-25-121.ec2.internal Ready   Active       Leader

使用滚动更新策略创建服务

滚动更新策略或更新配置由表 9-1 中讨论的服务定义选项组成。

表 9-1。

Rolling Update Options

| [计]选项 | 描述 | 缺省值 | | --- | --- | --- | | `--update-delay` | 更新之间的延迟(ns|us|ms|s|m|h)。 | 0 秒 | | `--update-failure-action` | 更新失败时的操作。值可以是`pause`或`continue`。 | 中止 | | `--update-max-failure-ratio` |   |   | | `--update-monitor` | 每次任务更新后监视失败的持续时间(ns|us|ms|s|m|h)。 | 0 秒 | | `--update-` `parallelism` | 同时更新的最大任务数。值为 0 会一次更新所有内容。 | one |

要在服务部署时配置滚动更新策略,必须在创建服务时提供要配置的选项。例如,为 MySQL 数据库创建一个服务,并指定更新策略选项--update-delay--update-parallelism

∼ $ docker service create \
>   --env MYSQL_ROOT_PASSWORD='mysql'\
>   --replicas 1 \
>   --name mysql \
>   --update-delay 10s \
>   --update-parallelism 1  \
>  mysql:5.6

wr0z48v1uguk1c40pa42ywrpn

服务已创建。列出服务可能不会列出最初运行的所有副本,如REPLICAS列中的0/1所示。

∼ $ docker service ls
ID              NAME         MODE             REPLICAS            IMAGE              PORTS
wr0z48v1uguk    mysql        replicated       0/1                 mysql:5.6    

过一会儿运行相同的命令应该会将所有副本列为正在运行,如REPLICAS列中的1/1所示。

∼ $ docker service ls
ID             NAME           MODE              REPLICAS            IMAGE             PORTS
wr0z48v1uguk   mysql          replicated        1/1                 mysql:5.6    

单个服务副本被调度在管理器节点本身上,并且副本的 Docker 容器被启动。

∼ $ docker service ps mysql
ID                  NAME                IMAGE               NODE                      DESIRED STATE       CURRENT STATE               ERROR               PORTS
38dm9gm6cmvk        mysql.1             mysql:5.6           ip-172-31-25-121.ec2.internal   Running             Running 13 seconds ago                       

使用滚动更新选项创建服务本身并不演示滚动更新。它仅定义服务的UpdateConfig设置。在下一节中,我们将执行滚动更新。

滚动更新以增加副本数量

滚动更新可用于通过对docker service update命令使用--replicas选项来更新副本的数量。滚动更新更新首次部署服务时应用的UpdateConfig策略。接下来,我们从上一节中创建的一个副本更新基于mysql:5.6映像的服务的副本数量。运行以下命令,将服务定义从一个副本更新到五个副本。--update-delay--update-parallelism选项修改服务定义的UpdateConfig。如果更新成功,则docker service update命令输出服务名。

∼ $ docker service update \
>   --replicas 5 \
>   --update-delay 20s \
>   --update-parallelism 1  \
>  mysql

mysql

随后,服务列表可能会在docker service ls命令的输出中列出一些尚未启动的副本。但是,过一会儿再次运行该命令应该会将所有复制副本列为正在运行。

∼ $ docker service ls
ID                NAME                MODE                REPLICAS       IMAGE         PORTS
wr0z48v1uguk      mysql               replicated          5/5            mysql:5.6        

在滚动更新期间,所有正在运行的任务都将关闭,并启动新的任务。mysql.1任务的期望状态被更新为shutdown,当前状态被设置为failed。新任务mysql.1开始。

∼ $ docker service ps mysql
ID                  NAME                IMAGE               NODE                   DESIRED STATE       CURRENT STATE             ERROR                        PORTS
ydqj6vf9rsgw        mysql.1             mysql:5.6           ip-172-31-25-121.ec2.internal   Running             Running 26 seconds ago                                 
38dm9gm6cmvk         \_ mysql.1         mysql:5.6           ip-172-31-25-121.ec2.internal   Shutdown            Failed 31 seconds ago     "task: non-zero exit (137)"   
7bns96iu8ygz        mysql.2             mysql:5.6           ip-172-31-29-67.ec2.internal    Running             Running 32 seconds ago                                 
62wfdbcv3cr4        mysql.3             mysql:5.6           ip-172-31-2-177.ec2.internal    Running             Running 33 seconds ago                                 
ql66z5x0a2lf        mysql.4             mysql:5.6           ip-172-31-25-121.ec2.internal   Running             Running 14 seconds ago                                 
3n3b1j7ey732         \_ mysql.4         mysql:5.6           ip-172-31-25-121.ec2.internal   Shutdown            Failed 19 seconds ago     "task: non-zero exit (137)"   
bl1365y60vuu        mysql.5             mysql:5.6           ip-172-31-2-177.ec2.internal    Running               Running 33 seconds ago                                 

当从一个复制副本扩展到五个复制副本时,首先启动几个新任务,然后关闭最初运行的任务,以便在滚动更新期间服务继续可用。如果服务中的唯一任务在开始任何新任务之前被首先关闭,那么服务在短时间内不会有任何正在运行的任务。

在滚动更新期间,运行五个副本的理想状态不会立即协调。滚动更新正在进行时,运行的任务可能少于五个。列出正在运行的服务任务只列出三个任务作为running

∼ $ docker service ps -f desired-state=running mysql
ID               NAME                IMAGE               NODE                      DESIRED STATE       CURRENT STATE              ERROR               PORTS
ydqj6vf9rsgw     mysql.1             mysql:5.6           ip-172-31-25-121.ec2.internal   Running             Running 35 seconds ago                       
7bns96iu8ygz     mysql.2             mysql:5.6           ip-172-31-29-67.ec2.internal    Running             Running 40 seconds ago                       
ql66z5x0a2lf     mysql.4             mysql:5.6           ip-172-31-25-121.ec2.internal   Running             Running 22 seconds ago                       

当滚动更新完成时,五个任务正在运行。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                   DESIRED STATE       CURRENT STATE              ERROR               PORTS
u8falo7q95cq        mysql.1             mysql:5.6           ip-172-31-25-121.ec2.internal   Running             Running 20 seconds ago                       
luabknwzwqoj        mysql.2             mysql:5.6           ip-172-31-29-67.ec2.internal    Running             Running 13 seconds ago                       
ce4l2qvtcanv        mysql.3             mysql:5.6           ip-172-31-2-177.ec2.internal    Running             Running 25 seconds ago                       
iw8vwsxq3tjz        mysql.4             mysql:5.6           ip-172-31-25-121.ec2.internal   Running             Running 6 seconds ago                        
qfi5fionjt2v        mysql.5             mysql:5.6           ip-172-31-29-67.ec2.internal    Running             Running 25 seconds ago                       

检查服务应该会列出更新后的副本数量。UpdateConfig也与docker service inspect命令一起列出。

∼ $ docker service inspect mysql
[
     ...
        "Spec": {
            "Name": "mysql",    
...
            },
            "Mode": {
                "Replicated": {
                    "Replicas": 5
                }
            },
            "UpdateConfig": {
                "Parallelism": 1,
                "Delay": 20000000000,
                "FailureAction": "pause",
                "Monitor": 5000000000,
                "MaxFailureRatio": 0,
                "Order": "stop-first"
            },
            "RollbackConfig": {
                "Parallelism": 1,
                "FailureAction": "pause",
                "Monitor": 5000000000,
                "MaxFailureRatio": 0,
                "Order": "stop-first"
            },
...
]

滚动更新到不同的映像标签

滚动更新的一个用例是更新到较新的映像标签。例如,为mysql服务执行滚动更新,从mysql:5.6更新到 Docker 映像mysql:latestUpdate parallelism设置为 2,一次更新两个副本。

∼ $ docker service update --image mysql:latest --update-parallelism 2  mysql

mysql

服务滚动更新开始。列出服务复制副本时,会将基于映像的复制副本列为正在关闭,如shutdown期望状态所示,并将基于映像的复制副本列为正在启动,如running期望状态所示。

∼ $ docker service ps mysql
ID                  NAME                IMAGE               NODE                    DESIRED STATE       CURRENT STATE               ERROR                             PORTS
vqc6rhzw5uxz        mysql.1             mysql:latest        ip-172-31-2-177.ec2.internal    Ready               Ready 7 seconds ago                                       
80kswuu4d5gc         \_ mysql.1         mysql:5.6           ip-172-31-2-177.ec2.internal    Shutdown            Running 7 seconds ago                                     
u8falo7q95cq         \_ mysql.1         mysql:5.6           ip-172-31-25-121.ec2.internal   Shutdown            Failed 12 seconds ago       "task: non-zero exit (1)"     
ydqj6vf9rsgw         \_ mysql.1         mysql:5.6           ip-172-31-25-121.ec2.internal   Shutdown            Failed 56 seconds ago       "task: non-zero exit (1)"     
38dm9gm6cmvk         \_ mysql.1         mysql:5.6           ip-172-31-25-121.ec2.internal   Shutdown            Failed about a minute ago   "task: non-zero exit (137)"   
tvxjmahy08uh        mysql.2             mysql:5.6           ip-172-31-29-67.ec2.internal    Running             Running 2 seconds ago                                     
luabknwzwqoj         \_ mysql.2         mysql:5.6           ip-172-31-29-67.ec2.internal    Shutdown            Failed 8 seconds ago        "task: non-zero exit (137)"   
7bns96iu8ygz         \_ mysql.2         mysql:5.6           ip-172-31-29-67.ec2.internal    Shutdown              Failed 50 seconds ago       "task: non-zero exit (137)"   
u2ea4xq4yx6t        mysql.3             mysql:latest        ip-172-31-2-177.ec2.internal    Running             Running 4 seconds ago                                     
ce4l2qvtcanv         \_ mysql.3         mysql:5.6           ip-172-31-2-177.ec2.internal    Shutdown            Shutdown 4 seconds ago                                    
62wfdbcv3cr4         \_ mysql.3         mysql:5.6           ip-172-31-2-177.ec2.internal    Shutdown            Failed about a minute ago   "task: non-zero exit (1)"     
iw8vwsxq3tjz         mysql.4            mysql:5.6           ip-172-31-25-121.ec2.internal   Running             Running 37 seconds ago                                    
ql66z5x0a2lf         \_ mysql.4         mysql:5.6           ip-172-31-25-121.ec2.internal   Shutdown            Failed 43 seconds ago       "task: non-zero exit (137)"   
3n3b1j7ey732         \_ mysql.4         mysql:5.6           ip-172-31-25-121.ec2.internal   Shutdown            Failed about a minute ago   "task: non-zero exit (137)"   
f5vcf9mgluqe        mysql.5             mysql:5.6           ip-172-31-29-67.ec2.internal    Running             Running 14 seconds ago                                    
qfi5fionjt2v         \_ mysql.5         mysql:5.6           ip-172-31-29-67.ec2.internal    Shutdown            Failed 19 seconds ago       "task: non-zero exit (1)"     
bl1365y60vuu         \_ mysql.5         mysql:5.6           ip-172-31-2-177.ec2.internal    Shutdown            Failed about a minute ago   "task: non-zero exit (1)"     

当滚动更新正在进行时,一些正在运行的任务可以基于先前的服务规范(mysql:5.6),而其他的基于新的服务规范(mysql:latest)。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                        DESIRED STATE       CURRENT STATE                ERROR               PORTS
vqc6rhzw5uxz        mysql.1             mysql:latest        ip-172-31-2-177.ec2.internal    Running             Running 4 seconds ago                        
tvxjmahy08uh        mysql.2             mysql:5.6           ip-172-31-29-67.ec2.internal    Running             Running 11 seconds ago                       
u2ea4xq4yx6t        mysql.3             mysql:latest        ip-172-31-2-177.ec2.internal    Running             Running 13 seconds ago                       
iw8vwsxq3tjz        mysql.4             mysql:5.6           ip-172-31-25-121.ec2.internal   Running             Running 46 seconds ago                       
f5vcf9mgluqe        mysql.5             mysql:5.6           ip-172-31-29-67.ec2.internal    Running             Running 23 seconds ago                       

当滚动更新完成时,所有正在运行的任务都基于新的服务规范。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                        DESIRED STATE       CURRENT STATE                    ERROR               PORTS
vqc6rhzw5uxz        mysql.1             mysql:latest        ip-172-31-2-177.ec2.internal   Running             Running 45 seconds ago                               
53choz0dd967        mysql.2             mysql:latest        ip-172-31-29-67.ec2.internal   Running             Running less than a second ago                       
u2ea4xq4yx6t        mysql.3             mysql:latest        ip-172-31-2-177.ec2.internal   Running             Running 53 seconds ago                               
tyo6v0yen7ev        mysql.4             mysql:latest        ip-172-31-29-67.ec2.internal   Running             Running 21 seconds ago                               
upt212osx7au        mysql.5             mysql:latest        ip-172-31-29-67.ec2.internal   Running             Running 25 seconds ago                               

添加和删除环境变量的滚动更新

Docker 镜像mysql需要一个强制的环境变量MYSQL_ROOT_PASSWORD作为根密码,并支持其他一些可能被指定的环境变量。其他环境变量是 MySQL 数据库的MYSQL_DATABASEMYSQL用户的MYSQL_USER、MySQL 密码的MYSQL_PASSWORD以及是否允许 root 密码为空的MYSQL_ALLOW_EMPTY_PASSWORD。创建mysql服务时已经设置了MYSQL_ROOT_PASSWORD。使用docker service update命令的--env-add选项,我们可以添加其他环境变量。

∼ $ docker service update --env-add MYSQL_DATABASE='mysqldb' --env-add MYSQL_USER='mysql'  --env-add MYSQL_PASSWORD='mysql'  --env-add MYSQL_ALLOW_EMPTY_PASSWORD='no' --update-parallelism 1 mysql

mysql

mysql的输出意味着命令成功运行。

滚动更新状态是通过docker service inspect命令找到的,该命令除了列出添加到Env JSON 对象中的env变量之外,还列出了UpdateStatus。更新状态的Stateupdating,消息为“update in progress”

∼ $ docker service inspect mysql
[
    {...
        "Spec": {
            "Name": "mysql",
                "ContainerSpec": {
...
                    "Env": [
                        "MYSQL_ROOT_PASSWORD=mysql",
                        "MYSQL_DATABASE=mysqldb",
                        "MYSQL_USER=mysql",
                        "MYSQL_PASSWORD=mysql",
                        "MYSQL_ALLOW_EMPTY_PASSWORD=no"
                    ],
...        },
        "UpdateStatus": {
            "State": "updating",
            "StartedAt": "2017-07-25T19:18:11.44139778Z",
            "Message": "update in progress"
        }
    }
]

当更新完成后,UpdateStatus状态变为"completed"Message变为"update completed"

∼ $ docker service inspect mysql
[
...        },
        "UpdateStatus": {
            "State": "completed",
            "StartedAt": "2017-07-25T19:18:11.44139778Z",
            "CompletedAt": "2017-07-25T19:20:37.912993431Z",
            "Message": "update completed"
        }
    }
]

StartedAtCompletedAt时间戳所示,滚动更新大约需要两分钟。只列出期望状态为running的任务表明一个任务已经running了 21 秒,另一个任务已经运行了两分钟。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                      DESIRED STATE       CURRENT STATE                ERROR               PORTS
3zhf94kklu6r        mysql.1             mysql:latest        ip-172-31-29-67.ec2.internal   Running             Running 21 seconds ago                           
ta16ch5kjlr9        mysql.2             mysql:latest        ip-172-31-29-67.ec2.internal   Running             Running 2 minutes ago                            
fc7uxvwvcmk3        mysql.3             mysql:latest        ip-172-31-2-177.ec2.internal   Running             Running about a minute ago                       
jir97p344kol        mysql.4             mysql:latest        ip-172-31-29-67.ec2.internal   Running             Running about a minute ago                       
5rly53mcc8yq        mysql.5             mysql:latest        ip-172-31-2-177.ec2.internal   Running             Running 45 seconds ago                           

添加的环境变量可以用另一个docker service update命令和每个要删除的环境变量的--env-rm选项删除。在--env-rm中只指定了env变量名,而不是env值。

∼ $ docker service update --env-rm MYSQL_DATABASE --env-rm MYSQL_USER  --env-rm
MYSQL_PASSWORD  --env-rm MYSQL_ALLOW_EMPTY_PASSWORD mysql

mysql

执行另一个滚动更新。关闭所有服务任务,并启动基于新服务规范的新服务任务。服务定义只列出了强制的环境变量MYSQL_ROOT_PASSWORD

∼ $ docker service inspect mysql
[...
                    "Env": [
                        "MYSQL_ROOT_PASSWORD=mysql"
                    ],
        },
        "UpdateStatus": {
            "State": "completed",
            "StartedAt": "2017-07-25T19:20:57.968668604Z",
            "CompletedAt": "2017-07-25T19:22:59.18517919Z",
            "Message": "update completed"
        }
    }
]

滚动更新以设置 CPU 和内存限制和预留

滚动更新可用于设置新的资源限制和储备。

∼ $ docker service update --reserve-cpu 1 --limit-cpu 2 --reserve-memory 256mb -
-limit-memory 512mb mysql

mysql

配置新的资源限制和预留,如服务规范中所列。PreviousSpec表示没有Resources LimitsReservations被配置开始。

∼ $ docker service inspect mysql
[
 ...
        "Spec": {
            "Name": "mysql",
...
                "ContainerSpec": {
...                },
                "Resources": {
                    "Limits": {
                        "NanoCPUs": 2000000000,
                        "MemoryBytes": 536870912
                    },
                    "Reservations": {
                        "NanoCPUs": 1000000000,
                        "MemoryBytes": 268435456
                    }
                },
...        },
        "PreviousSpec": {
...
            "Name": "mysql",
                "Resources": {
                    "Limits": {},
                    "Reservations": {}
                },
        "UpdateStatus": {
            "State": "updating",
            "StartedAt": "2017-07-25T19:23:44.004458295Z",
            "Message": "update in progress"
        }
    }
]

设置新的资源限制和保留受节点容量限制的约束。如果请求的资源超过了节点容量,滚动更新可能会继续运行而无法完成,一些任务处于pending当前状态。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                      DESIRED STATE       CURRENT STATE               ERROR               PORTS
5u7zifw15n7t        mysql.1             mysql:latest        ip-172-31-25-121.ec2.internal   Running             Running about an hour ago                       
2kgsb16c8m8u        mysql.2             mysql:latest                                        Running             Pending about an hour ago                       
mu08iu9qzqlh        mysql.3             mysql:latest        ip-172-31-29-67.ec2.internal    Running             Running about an hour ago                       
aakxr8dw5s15        mysql.4             mysql:latest        ip-172-31-2-177.ec2.internal    Running             Running about an hour ago                       
z6045639f20p        mysql.5             mysql:latest        ip-172-31-25-121.ec2.internal   Running             Running about an hour ago                       

如果一些任务是pending,向集群添加资源可以使pending任务运行。我们可以更新 CloudFormation 栈,将工作节点的数量从 2 个增加到 3 个,如图 9-2 所示。

A454123_1_En_9_Fig2_HTML.jpg

图 9-2。

Increasing the number of worker nodes in the Swarm

随后,Swarm 应该列出四个节点。

∼ $ docker node ls
ID                           HOSTNAME                       STATUS AVAILABILITY  MANAGER STATUS
81h6uvu8uq0emnovzkg6v7mzg   ip-172-31-2-177.ec2.internal  Ready  Active              
e7vigin0luuo1kynjnl33v9pa   ip-172-31-29-67.ec2.internal  Ready  Active              
ptm7e0p346zwypos7wnpcm72d * ip-172-31-25-121.ec2.internal Ready  Active       Leader
t4d0aq9w2a6avjx94zgkwc557   ip-172-31-42-198.ec2.internal Ready  Active              

随着群中资源的增加,pending任务也开始运行。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                         DESIRED STATE       CURRENT STATE            ERROR            PORTS
5u7zifw15n7t        mysql.1             mysql:latest        ip-172-31-25-121.ec2.internal   Running             Running about an hour ago                       
2kgsb16c8m8u        mysql.2             mysql:latest        ip-172-31-2-177.ec2.internal    Running             Running 7 minutes ago                           
mu08iu9qzqlh        mysql.3             mysql:latest        ip-172-31-29-67.ec2.internal    Running             Running about an hour ago                       
i5j2drlcm75f        mysql.4             mysql:latest        ip-172-31-42-198.ec2.internal   Running             Running 4 seconds ago                           
z6045639f20p        mysql.5             mysql:latest        ip-172-31-25-121.ec2.internal   Running             Running about an hour ago                       

滚动更新到不同的映像

滚动更新也可用于更新到完全不同的 Docker 映像。例如,对mysql服务执行滚动更新,以使用 Docker 映像postgres而不是它正在使用的mysql映像。也可以设置其他选项,如--update-parallelism

∼ $ docker service update --image postgres --update-parallelism 1  mysql

mysql

mysql:latest基于映像的任务开始关闭,postgres基于映像的替换任务开始一次启动一个任务。滚动更新不会立即完成,将具有所需状态的服务任务列为running会列出一些基于postgres:latest映像的任务,而其他任务仍然使用mysql:latest映像。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                   DESIRED STATE       CURRENT STATE                ERROR               PORTS
9tzm5pa6pcyx        mysql.1             postgres:latest     ip-172-31-2-177.ec2.internal    Running             Running 39 seconds ago                           
xj23fu5svv9d        mysql.2             postgres:latest     ip-172-31-42-198.ec2.internal   Running             Running about a minute ago                       
mu08iu9qzqlh        mysql.3             mysql:latest        ip-172-31-29-67.ec2.internal    Running             Running about an hour ago                        
skzxi33c606o        mysql.4             postgres:latest     ip-172-31-2-177.ec2.internal    Running             Running 13 seconds ago                           
z6045639f20p        mysql.5             mysql:latest        ip-172-31-25-121.ec2.internal   Running             Running about an hour ago                        

一次一个复制副本,关闭mysql基于映像的复制副本,启动postgres基于映像的复制副本。大约两分钟后,所有任务都更新到了postgres:latest的映像上。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                  DESIRED STATE       CURRENT STATE                ERROR               PORTS
9tzm5pa6pcyx        mysql.1             postgres:latest     ip-172-31-2-177.ec2.internal    Running             Running about a minute ago                       
xj23fu5svv9d        mysql.2             postgres:latest     ip-172-31-42-198.ec2.internal   Running             Running about a minute ago                       
kd9pk31vpof2        mysql.3             postgres:latest     ip-172-31-42-198.ec2.internal   Running             Running 35 seconds ago                           
skzxi33c606o        mysql.4             postgres:latest     ip-172-31-2-177.ec2.internal    Running             Running 59 seconds ago                           
umtitiuvt5gg        mysql.5             postgres:latest     ip-172-31-25-121.ec2.internal   Running             Running 8 seconds ago                            

服务名称保持不变,副本名称也包含前缀mysqlmysql服务定义ContainerSpec将映像列为postgres。将映像更新为postgres并不意味着为新映像更新所有其他服务定义设置。postgres映像不使用MYSQL_ROOT_PASSWORD,但是环境变量仍然在服务规范中。

∼ $ docker service inspect mysql
[
        "Spec": {
            "Name": "mysql",
                "ContainerSpec": {
                    "Image": "postgres:latest@sha256:e92fe21f695d27be7050284229a1c8c63ac10d88cba58d779c243566e125aa34",
                    "Env": [
                        "MYSQL_ROOT_PASSWORD=mysql"
                    ],
        "PreviousSpec": {
            "Name": "mysql",
                "ContainerSpec": {
                    "Image": "mysql:latest@sha256:75c563c474f1adc149978011fedfe2e6670483d133b22b07ee32789b626f8de3",
                    "Env": [
                        "MYSQL_ROOT_PASSWORD=mysql"
...        },
        "UpdateStatus": {
            "State": "completed",
            "StartedAt": "2017-07-25T20:39:45.230997671Z",
            "CompletedAt": "2017-07-25T20:42:04.186537673Z",
            "Message": "update completed"
        }
    }
]

可以用另一个update命令删除MYSQL_ROOT_PASSWORD环境变量。

∼ $ docker service update   --env-rm MYSQL_ROOT_PASSWORD    mysql
mysql

随后,ContainerSpec不包括MYSQL_ROOT_PASSWORD环境变量。

∼ $ docker service inspect mysql
[
...
        "Spec": {
            "Name": "mysql",
            ...
                "ContainerSpec": {
                    "Image": "postgres:latest@sha256:e92fe21f695d27be7050284229a1c8c63ac10d88cba58d779c243566e125aa34",
                    "StopGracePeriod": 10000000000,
                    "DNSConfig": {}
                },
...        },
        "PreviousSpec": {
                "ContainerSpec": {
                    "Image": "postgres:latest@sha256:e92fe21f695d27be7050284229a1c8c63ac10d88cba58d779c243566e125aa34",
                    "Env": [
                        "MYSQL_ROOT_PASSWORD=mysql"
                    ],
...        },
        "UpdateStatus": {
            "State": "updating",
            "StartedAt": "2017-07-25T20:42:56.651025816Z",
            "Message": "update in progress"
        }
    }
]

删除环境变量的滚动更新涉及关闭所有服务任务并启动所有新任务。更新大约需要两分钟完成。

∼ $ docker service inspect mysql
[
        },
        "UpdateStatus": {
            "State": "completed",
            "StartedAt": "2017-07-25T20:42:56.651025816Z",
            "CompletedAt": "2017-07-25T20:44:55.078906359Z",
            "Message": "update completed"
        }
    }
]

列出正在运行的任务表明任务最多只运行了两分钟。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                     DESIRED STATE       CURRENT STATE         ERROR        PORTS
menpo2zgit5u        mysql.1             postgres:latest     ip-172-31-2-177.ec2.internal    Running             Running about a minute ago                       
adnid3t69sue        mysql.2             postgres:latest     ip-172-31-25-121.ec2.internal   Running             Running about a minute ago                       
we92apfuivil        mysql.3             postgres:latest     ip-172-31-42-198.ec2.internal   Running             Running 46 seconds ago                           
ed7vh4ozefm5        mysql.4             postgres:latest     ip-172-31-29-67.ec2.internal    Running             Running 2 minutes ago                            
i2x2377ad7u0        mysql.5             postgres:latest     ip-172-31-25-121.ec2.internal   Running             Running about a minute ago                       

通过移除env变量MYSQL_ROOT_PASSWORD,mysql服务被更新以使用 Docker 映像postgres。服务名称本身无法更新。该服务可以被更新回mysql映像,并且强制环境变量MYSQL_ROOT_PASSWORD被添加另一个滚动更新。

∼ $ docker service update --image mysql --env-add MYSQL_ROOT_PASSWORD='mysql'  mysql

mysql

同样,将具有所需状态的副本列为running会列出由mysql基于映像的副本替换的postgres基于映像的副本。一次一个副本,postgres基于映像的副本被mysql基于映像的副本替换。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                   DESIRED STATE       CURRENT STATE                ERROR               PORTS
menpo2zgit5u        mysql.1             postgres:latest     ip-172-31-2-177.ec2.internal    Running             Running 2 minutes ago                            
adnid3t69sue        mysql.2             postgres:latest     ip-172-31-25-121.ec2.internal   Running             Running 2 minutes ago                            
we92apfuivil        mysql.3             postgres:latest     ip-172-31-42-198.ec2.internal   Running             Running about a minute ago                       
pjvj50j822xr        mysql.4             mysql:latest        ip-172-31-29-67.ec2.internal    Running             Running 12 seconds ago                           
i2x2377ad7u0        mysql.5             postgres:latest     ip-172-31-25-121.ec2.internal   Running             Running 2 minutes ago                            

在一两分钟内,所有的postgres映像副本都被基于mysql映像的副本所取代。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                    DESIRED STATE       CURRENT STATE                ERROR               PORTS
sobd90v7gbmz        mysql.1             mysql:latest        ip-172-31-25-121.ec2.internal   Running             Running about a minute ago                       
st5t7y8rdgg1        mysql.2             mysql:latest        ip-172-31-29-67.ec2.internal    Running             Running 57 seconds ago                           
upekevrlbmgo        mysql.3             mysql:latest        ip-172-31-42-198.ec2.internal   Running             Running about a minute ago                       
pjvj50j822xr        mysql.4             mysql:latest        ip-172-31-29-67.ec2.internal    Running             Running 2 minutes ago                            
nmrmdug87cy0        mysql.5             mysql:latest        ip-172-31-2-177.ec2.internal    Running             Running 2 minutes ago                  

服务规范被更新为mysql映像,并添加了强制环境变量MYSQL_ROOT_PASSWORD。更新完成后,UpdateStatus State变为completed

∼ $ docker service inspect mysql
[
        "Spec": {
            "Name": "mysql",
 ...
                    "Image": "mysql:latest@sha256:75c563c474f1adc149978011fedfe2e6670483d133b22b07ee32789b626f8de3",
                    "Env": [
                        "MYSQL_ROOT_PASSWORD=mysql"
                    ],

...        },
        "PreviousSpec": {
            "Name": "mysql",
                "ContainerSpec": {
                    "Image": "postgres:latest@sha256:e92fe21f695d27be7050284229a1c8c63ac10d88cba58d779c243566e125aa34",
...        },
        "UpdateStatus": {
            "State": "completed",
            "StartedAt": "2017-07-25T20:45:54.104241339Z",
            "CompletedAt": "2017-07-25T20:47:47.996420791Z",
            "Message": "update completed"
        }
    }
]

滚动重启

Docker 1.13 增加了一个新选项,即使根据更新选项不需要更新,也可以执行滚动重启。例如,从更新配置为--update-parallelism 1 和--update-delay 20s 的mysql服务开始,下面的update命令不会执行任何滚动更新,因为没有对服务进行任何更改。

∼ $ docker service update  --update-parallelism 1 --update-delay 20s mysql

mysql

要强制滚动重启,包括--force选项。

∼ $ docker service update --force --update-parallelism 1 --update-delay 20s mysql
mysql

服务任务开始被关闭,并且新的服务任务开始,即使没有对服务规范进行更新。一些任务被列为几秒钟前已经开始。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                            DESIRED STATE       CURRENT STATE                    ERROR               PORTS
sobd90v7gbmz        mysql.1             mysql:latest        ip-172-31-25-121.ec2.internal   Running             Running 3 minutes ago                                
trye9chir91l        mysql.2             mysql:latest        ip-172-31-25-121.ec2.internal   Running             Running 23 seconds ago                               
uu7sfp147xnu        mysql.3             mysql:latest        ip-172-31-42-198.ec2.internal   Running             Running less than a second ago                       
pjvj50j822xr        mysql.4             mysql:latest        ip-172-31-29-67.ec2.internal    Running             Running 4 minutes ago                                
nmrmdug87cy0        mysql.5             mysql:latest        ip-172-31-2-177.ec2.internal    Running             Running 3 minutes ago                                

滚动重启可能需要 1-2 分钟才能完成。

∼ $ docker service inspect mysql
[
    ...
        },
        "UpdateStatus": {
            "State": "completed",
            "StartedAt": "2017-07-25T20:49:34.716535081Z",
            "CompletedAt": "2017-07-25T20:51:36.880045931Z",
            "Message": "update completed"
        }
    }
]

滚动重启完成后,该服务具有所有新的服务任务,如下所示。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                    DESIRED STATE       CURRENT STATE                ERROR               PORTS
z2n2qcgfsbke        mysql.1             mysql:latest        ip-172-31-29-67.ec2.internal    Running             Running 6 seconds ago                            
trye9chir91l        mysql.2             mysql:latest        ip-172-31-25-121.ec2.internal   Running             Running about a minute ago                       
uu7sfp147xnu        mysql.3             mysql:latest        ip-172-31-42-198.ec2.internal   Running             Running about a minute ago                       
1aovurxkteq1        mysql.4             mysql:latest        ip-172-31-29-67.ec2.internal    Running             Running 29 seconds ago                           
r0lslq6jibvp        mysql.5             mysql:latest        ip-172-31-2-177.ec2.internal    Running             Running 52 seconds ago                           

添加和删除装载的滚动更新

滚动更新还可以用于添加和删除挂载。例如,我们添加一个类型为volume的挂载,其中源卷由src指定,目标目录由dst指定。

∼ $ docker service update \
>     --mount-add type=volume,src=mysql-scripts,dst=/etc/mysql/scripts \
>     mysql

mysql

装载将添加到服务中,并在服务定义中列出。添加装载涉及关闭所有服务任务并启动新任务。滚动更新可能需要 1-2 分钟。

∼ $ docker service inspect mysql
[
        "Spec": {
                "ContainerSpec": {
...
                    "Mounts": [
                        {
                            "Type": "volume",
                            "Source": "mysql-scripts",
                            "Target": "/etc/mysql/scripts"
                        }
                    ],
...
        "UpdateStatus": {
            "State": "completed",
            "StartedAt": "2017-07-25T20:51:55.205456644Z",
            "CompletedAt": "2017-07-25T20:53:56.451313826Z",
            "Message": "update completed"
        }
    }
]

添加的挂载可以用docker service update命令的--mount-rm选项删除,只需提供挂载目标目录作为参数。

∼ $ docker service update \
>     --mount-rm /etc/mysql/scripts \
>     mysql

mysql

执行另一次滚动更新,并删除挂载。它不会在服务定义中列出。PreviousSpec列出了挂载。UpdateStatus表示滚动更新的状态。

∼ $ docker service inspect mysql
[
        "Spec": {
            "Name": "mysql",
                "ContainerSpec": {
...
        "PreviousSpec": {
            "Name": "mysql",
...
                    "Mounts": [
                        {
                            "Type": "volume",
                            "Source": "mysql-scripts",
                            "Target": "/etc/mysql/scripts"
                        }
        "UpdateStatus": {
            "State": "completed",
            "StartedAt": "2017-07-25T20:55:56.30844324Z",
            "CompletedAt": "2017-07-25T20:57:58.489349432Z",
            "Message": "update completed"
        }
    }
]

滚动更新失败操作

docker service createdocker service update命令的--update-failure-action选项指定如果任务更新失败并返回FAILED时要采取的后续动作。我们将mysql服务的UpdateConfig设置为包含一个pause--update-failure-action(默认)。另一个选项设置是continue,它不会暂停滚动更新,而是继续下一个任务的更新。为了演示一个更新失败动作,指定一个不存在的 Docker 映像,比如mysql:5.9

∼ $ docker service update \
>   --replicas 10 \
>   --image mysql:5.9  \
>   --update-delay 10s \
>   --update-failure-action pause \
>  mysql

image mysql:5.9 could not be accessed on a registry to record

its digest. Each node will access mysql:5.9 independently,

possibly leading to different nodes running different

versions of the image.

mysql

滚动更新仍在开始,更新状态显示更新为paused。更新状态消息指示“由于失败或任务提前终止,更新暂停”。

∼ $ docker service inspect mysql
[
        "Spec": {
            "Name": "mysql",
            },
            "UpdateConfig": {
                "Parallelism": 1,
                "Delay": 10000000000,
                "FailureAction": "pause",
                "Monitor": 5000000000,
                "MaxFailureRatio": 0,
                "Order": "stop-first"
            },
            "RollbackConfig": {
                "Parallelism": 1,
                "FailureAction": "pause",
                "Monitor": 5000000000,
                "MaxFailureRatio": 0,
                "Order": "stop-first"
            },
...        },
        "UpdateStatus": {
            "State": "paused",
            "StartedAt": "2017-07-25T20:58:51.695333064Z",
            "Message": "update paused due to failure or early termination of task s1p1n0x3k67uwpoj7qxg13747"
        }
    }
]

如果滚动更新因任务更新失败而暂停,则有两个选项可用。

  • 使用docker service update <SERVICE-ID>重启暂停的更新。
  • 如果更新失败重复出现,找到失败的原因,并通过向docker service update <SERVICE-ID>命令提供其他选项来重新配置服务。

回滚到以前的规格

Docker 1.13 Swarm mode 增加了回滚到之前服务定义的特性。例如,执行滚动更新,将mysql服务的映像更新为postgres。基于mysql的副本开始关闭,基于postgres的副本开始启动。在从mysql映像到postgres映像的滚动更新过程中的任何时候,或者在对postgres映像的更新完成之后,如果确定滚动更新不应该开始或执行,可以使用以下命令回滚滚动更新。为了演示回滚,我们首先启动一个mysql服务。

∼ $ docker service rm mysql
mysql
∼ $ docker service create \
>   --env MYSQL_ROOT_PASSWORD='mysql'\
>   --replicas 5 \
>   --name mysql \
>   --update-delay 10s \
>   --update-parallelism 1  \
>  mysql:5.6

xkmrhnk0a444zambp9yh1mk9h

我们从mysql映像开始对postgres映像进行滚动更新。

∼ $ docker service update --image postgres  mysql

mysql

随后,一些任务基于postgres映像,一些基于mysql映像。

∼ $ docker service ps mysql
ID                  NAME                IMAGE               NODE                    DESIRED STATE       CURRENT STATE             ERROR               PORTS
mnm5pg9ha61u        mysql.1             mysql:5.6           ip-172-31-25-121.ec2.internal   Running             Running 58 seconds ago                        
9y0fzn4sgiv0        mysql.2             postgres:latest     ip-172-31-2-177.ec2.internal    Ready               Ready 2 seconds ago                           
ewl7zxwi07gc         \_ mysql.2         mysql:5.6           ip-172-31-2-177.ec2.internal    Shutdown            Running 2 seconds ago                         
l3ock28cmtzx        mysql.3             mysql:5.6           ip-172-31-42-198.ec2.internal   Running             Running 22 seconds ago                        
1vqs3lcqvbt5        mysql.4             postgres:latest     ip-172-31-29-67.ec2.internal    Running             Running 12 seconds ago                        
wu11jjbszesy         \_ mysql.4         mysql:5.6           ip-172-31-29-67.ec2.internal    Shutdown            Shutdown 13 seconds ago                       
g3tr6z9l5vzx        mysql.5             mysql:5.6           ip-172-31-42-198.ec2.internal   Running             Running 22 seconds ago                        

开始回滚以恢复到mysql映像。

∼ $ docker service update --rollback mysql

mysql

postgres基于映像的任务开始关闭,而mysql基于映像的任务开始。

∼ $ docker service ps mysql
ID                  NAME                IMAGE               NODE                     DESIRED STATE       CURRENT STATE                ERROR                       PORTS
mnm5pg9ha61u        mysql.1             mysql:5.6           ip-172-31-25-121.ec2.internal   Running             Running about a minute ago                               
gyqgtoc4ix3y        mysql.2             mysql:5.6           ip-172-31-2-177.ec2.internal    Running             Running 14 seconds ago                                   
9y0fzn4sgiv0         \_ mysql.2         postgres:latest     ip-172-31-2-177.ec2.internal    Shutdown            Shutdown 15 seconds ago                                  
ewl7zxwi07gc         \_ mysql.2         mysql:5.6           ip-172-31-2-177.ec2.internal    Shutdown            Shutdown 23 seconds ago                                  
l3ock28cmtzx        mysql.3             mysql:5.6           ip-172-31-42-198.ec2.internal   Running             Running 46 seconds ago                                   
ecvh8fd5308k        mysql.4             mysql:5.6           ip-172-31-29-67.ec2.internal    Running             Running 16 seconds ago                                   
1vqs3lcqvbt5         \_ mysql.4         postgres:latest     ip-172-31-29-67.ec2.internal    Shutdown            Shutdown 16 seconds ago                                  
wu11jjbszesy         \_ mysql.4         mysql:5.6           ip-172-31-29-67.ec2.internal    Shutdown            Shutdown 37 seconds ago                                  
m27d3gz4g6dy        mysql.5             mysql:5.6           ip-172-31-25-121.ec2.internal   Running             Running 1 second ago                                     
g3tr6z9l5vzx         \_ mysql.5         mysql:5.6           ip-172-31-42-198.ec2.internal   Shutdown            Failed 6 seconds ago         "task: non-zero exit (1)"   

mysqlpostgres的滚动更新被回滚。回滚完成后,所有副本都是基于mysql映像的,这是服务开始时的理想状态。

∼ $ docker service ps -f desired-state=running mysql
ID                  NAME                IMAGE               NODE                    DESIRED STATE       CURRENT STATE                ERROR               PORTS
xamxi29okj74        mysql.1             mysql:5.6           ip-172-31-25-121.ec2.internal   Running             Running 30 seconds ago                           
gyqgtoc4ix3y        mysql.2             mysql:5.6           ip-172-31-2-177.ec2.internal    Running             Running 56 seconds ago                           
l3ock28cmtzx        mysql.3             mysql:5.6           ip-172-31-42-198.ec2.internal   Running             Running about a minute ago                       
ecvh8fd5308k        mysql.4             mysql:5.6           ip-172-31-29-67.ec2.internal    Running             Running 58 seconds ago                           

全球服务的滚动更新

也可以在全局服务上执行滚动更新。为了演示,我们为mysql:latest映像创建了一个全局服务。

∼ $ docker service rm mysql

mysql

∼ $ docker service create \
>   --mode global \
>   --env MYSQL_ROOT_PASSWORD='mysql'\
>   --name mysql \
>  mysql

7nokncnti3izud08gfdovwxwa

Start a rolling update to Docker image mysql:5.6\. ∼ $ docker service update \
>   --image mysql:5.6 \
>   --update-delay 10s \
>  mysql

mysql

服务已更新。Spec>ContainerSpec>Imagemysql:latestPreviousSpec>ContainerSpec>Image更新为mysql:5.6

∼ $ docker service inspect mysql
[
        "Spec": {
            "Name": "mysql",
                "ContainerSpec": {
                    "Image": "mysql:5.6@sha256:6ad5bd392c9190fa92e65fd21f6debc8b2a76fc54f13949f9b5bc6a0096a5285",
            },
        "PreviousSpec": {
            "Name": "mysql",
                "ContainerSpec": {
                    "Image": "mysql:latest@sha256:75c563c474f1adc149978011fedfe2e6670483d133b22b07ee32789b626f8de3",
        "UpdateStatus": {
            "State": "completed",
            "StartedAt": "2017-07-25T21:06:46.973666693Z",
            "CompletedAt": "2017-07-25T21:07:46.656023733Z",
            "Message": "update completed"
        }
    }
]

一分钟之内,所有基于mysql:5.6的新服务任务开始。

∼ $ docker service ps -f desired-state=running mysql
ID               NAME                             IMAGE        NODE                 DESIRED STATE    CURRENT STATE                 ERROR       PORTS
ybf4xpofte8l     mysql.81h6uvu8uq0emnovzkg6v7mzg  mysql:5.6    ip-172-31-2-177.ec2.internal    Running          Running 46 seconds ago                           
7nq99jeil9n0     mysql.t4d0aq9w2a6avjx94zgkwc557   mysql:5.6    ip-172-31-42-198.ec2.internal   Running          Running about a minute ago                       
wcng24mq7e8m     mysql.e7vigin0luuo1kynjnl33v9pa  mysql:5.6    ip-172-31-29-67.ec2.internal    Running          Running about a minute ago                       
q14t2pyhra3w     mysql.ptm7e0p346zwypos7wnpcm72d  mysql:5.6    ip-172-31-25-121.ec2.internal   Running          Running about a minute ago          

不能在全局服务上执行滚动更新以使用--replicas选项设置副本,如下面的docker service update命令中的消息所示。

∼ $ docker service update \
>   --image mysql \
>   --replicas 1 \
>  mysql

replicas can only be used with replicated mode

如输出所示,虽然副本是在复制的服务mysql上设置的,但副本不是在全局服务上设置的。

摘要

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

十、网络连接

Docker 引擎上的网络是由桥接网络提供的,即docker0桥。docker0桥在 Docker 主机的范围内是本地的,并且在安装 Docker 时默认安装。所有 Docker 容器都运行在 Docker 主机上,并连接到docker0桥网络。他们通过网络互相交流。

问题

默认的docker0桥接网络有以下限制:

  • 桥接网络的范围限于本地 Docker 主机,以提供容器到容器的联网,而不是多主机联网。

  • The bridge network isolates the Docker containers on the host from external access. A Docker container may expose a port or multiple ports and the ports may be published on the host for an external client host access, as illustrated in Figure 10-1, but by default the docker0 bridge does not provide any external client access outside the network.

    A454123_1_En_10_Fig1_HTML.gif

    图 10-1。

    The default docker0 bridge network

解决方案

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

A454123_1_En_10_Fig2_HTML.gif

图 10-2。

The Swarm overlay networks

以下 Docker 网络用于或可用于群组模式。

入口网络

初始化群组模式时,会自动创建ingress网络。在 Docker for AWS 上,ingress网络是现成可用的,因为托管服务默认启用了群组模式。名为ingress的默认覆盖网络扩展到群中的所有节点,无论该节点是否有预定的服务任务。ingress提供服务任务之间的负载均衡。所有发布端口的服务都被添加到ingress网络中。如果服务发布一个端口,甚至在内部网络中创建的服务也被添加到ingress。如果服务没有发布端口,它不会被添加到ingress网络中。服务使用下面的docker service create命令语法发布带有--publish–p选项的端口。

docker service create \
  --name <SERVICE-NAME> \
  --publish <PUBLISHED-PORT>:<TARGET-PORT> \
  <IMAGE>

如果省略了<PUBLISHED-PORT>,群组管理器选择 30000-32767 范围内的一个端口来发布服务。

要使用ingress网络,群组节点之间必须开放以下端口。

  • 端口 7946 TCP/UDP 用于容器网络发现
  • 端口 4789 UDP 用于容器ingress网络

自定义覆盖网络

使用覆盖驱动器创建定制覆盖网络,并且可以在覆盖网络中创建服务。使用docker service create命令的--network选项在覆盖网络中创建服务。覆盖网络提供服务对服务的通信。覆盖网络中的一个对接容器可以直接与网络中的另一个对接容器通信,无论该容器是在同一节点上还是在不同的节点上。只有用于群服务任务的 Docker 容器可以使用覆盖网络相互连接,而不仅仅是群中主机上运行的任何 Docker 容器。例如,用docker run <img>命令启动的 Docker 容器不能连接到 Swarm 覆盖网络,例如使用docker network connect <overlay network> <container>。不在群中的对接主机上的对接容器也不能与群中的对接容器直接连接和通信。不同群覆盖网络中的 Docker 容器不能彼此直接通信,因为每个群覆盖网络与其他网络隔离。

尽管群中的默认覆盖网络ingress扩展到群中的所有节点,而不管服务任务是否在其上运行,但是其范围也是群的定制覆盖网络默认不扩展到群中的所有节点。定制的群覆盖网络仅扩展到群中运行由定制的群覆盖网络创建的服务任务的那些节点。

“覆盖”网络覆盖主机的底层网络,覆盖网络的范围是群。覆盖网络中的服务容器具有不同的 IP 地址,并且每个覆盖网络分配有不同范围的 IP 地址。在现代内核中,覆盖网络可以与底层网络重叠,因此,多个网络可以拥有相同的 IP 地址。

docker_gwbridge 网络

初始化群组模式时自动创建的另一个网络(除了ingress网络)是docker_gwbridge网络。docker_gwbr idge 网络是一个桥接网络,它将包括ingress网络在内的所有覆盖网络连接到 Docker 守护进程的主机网络。每个服务容器都连接到本地 Docker 守护进程主机的docker_gwbridge网络。

桥接网络

网桥网络是由 Docker 管理的主机上的网络。主机上的 Docker 容器通过桥接网络相互通信。不公布端口的群模式服务也在桥接网络中创建。以docker run命令开始的 Docker 容器也是如此。这意味着不发布端口的群模式 Docker 服务与用docker run命令启动的 Docker 容器在同一个网络中。

本章涵盖以下主题:

  • 设置环境
  • Swarm 模式下的联网
  • 使用默认覆盖网络ingress创建服务
  • 创建自定义叠加网络
  • 使用自定义覆盖网络创建服务
  • 连接到同一覆盖网络中的另一个 Docker 容器
  • 创建内部网络
  • 删除网络

设置环境

在 Docker 上为 AWS 创建一个三节点 Docker 群,如第三章所述。如图 10-3 所示,一个自动气象站云形成栈被用来创建一个蜂群。

A454123_1_En_10_Fig3_HTML.jpg

图 10-3。

AWS CloudFormation stack

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

A454123_1_En_10_Fig4_HTML.jpg

图 10-4。

Obtaining the public IP address of a Swarm manager node instance

SSH 登录到 Swarm manager 实例。

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

列出群节点——一个管理者和两个工作者节点。

∼ $ docker node ls
ID                          HOSTNAME                       STATUS  AVAILABILITY  MANAGER STATUS
npz2akark8etv4ib9biob5yyk   ip-172-31-47-123.ec2.internal  Ready   Active            
p6wat4lxq6a1o3h4fp2ikgw6r   ip-172-31-3-168.ec2.internal   Ready   Active              
tb5agvzbi0rupq7b83tk00cx3 * ip-172-31-47-15.ec2.internal   Ready   Active        Leader

Swarm 模式下的联网

群组模式提供了一些默认网络,可以用docker network ls命令列出。这些网络不仅可以在 Docker for AWS 上使用,还可以在任何平台(如 CoreOS)上以 Swarm 模式使用。

∼ $ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
34a5f77de8cf        bridge              bridge              local
0e06b811a613        docker_gwbridge     bridge              local
6763ebad69cf        host                host                local
e41an60iwval        ingress             overlay             swarm
eb7399d3ffdd        none                null                local

我们在上一节中讨论了这些网络中的大多数。“host”网络是主机的网络栈。“none”网络不提供 Docker 容器和主机网络栈之间的网络,并且创建没有网络访问的容器。

即使在调度任何服务任务之前,默认网络在群管理器节点和群工作者节点上也是可用的。

可使用设置为overlay的驱动过滤器过滤列出的网络。

docker network ls --filter driver=overlay

仅列出了ingress网络。默认情况下,不会提供其他覆盖网络。

∼ $ docker network ls --filter driver=overlay
NETWORK ID          NAME                DRIVER              SCOPE
e41an60iwval        ingress             overlay             swarm

感兴趣的网络是称为ingress的覆盖网络,但是除了在章节介绍中讨论之外,所有默认网络都在表 10-1 中讨论。

表 10-1。

Docker Networks

| 网络 | 描述 | | `bridge` | `bridge`网络是在所有 Docker 主机上创建的`docker0`网络。默认情况下,Docker 守护进程将容器连接到`docker0`网络。任何以`docker run command`开头的 Docker 容器,甚至在一个群节点上,都连接到`docker0`桥网络。 | | `docker_` `gwbridge` | 用于不同主机上的群节点之间的通信。该网络用于向缺少用于连接外部网络和其他群节点的替代网络的容器提供外部连接。当一个容器连接到多个网络时,它的外部连接是通过第一个非内部网络按词汇顺序提供的。 | | `host` | 将容器添加到主机的网络栈中。容器内部的网络配置与主机的配置相同。 | | `ingress` | Swarm 用于`ingress`的覆盖网络,是外部接入。`ingress`网络仅用于路由网状网/ `ingress`流量。 | | `none` | 将容器添加到特定于容器的网络栈中,该容器缺少网络接口。 |

默认网络不能删除,除了ingress网络,用户不需要直接连接或使用其他网络。要查找关于ingress网络的详细信息,请运行以下命令。

docker network inspect ingress

ingress网络的范围是 Swarm,使用的驱动程序是overlay。子网和网关分别是10.255.0.0/1610.255.0.1false的内部设置显示ingress网络不是内部网络,这意味着该网络连接到外部网络。ingress网络有一个 IPv4 地址,但该网络不支持 IPv6。

∼ $ docker network inspect ingress
[
    {
        "Name": "ingress",
        "Id": "e41an60iwvalbeq5y3stdfem9",
        "Created": "2017-07-26T18:38:29.753424199Z",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.255.0.0/16",
                    "Gateway": "10.255.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": true,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "ingress-sbox": {
                "Name": "ingress-endpoint",
                "EndpointID": "f646b5cc4316994b8f9e5041ae7c82550bc7ce733db70df3f66b8d771d0f53c4",
                "MacAddress": "02:42:0a:ff:00:02",
                "IPv4Address": "10.255.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "4096"
        },
        "Labels": {},
        "Peers": [
            {
                "Name": "ip-172-31-47-15.ec2.internal-17c7f752fb1a",
                "IP": "172.31.47.15"
            },
            {
                "Name": "ip-172-31-47-123.ec2.internal-d6ebe8111adf",
                "IP": "172.31.47.123"
            },
            {
                "Name": "ip-172-31-3-168.ec2.internal-99510f4855ce",
                "IP": "172.31.3.168"
            }
        ]
    }
]

使用默认桥接网络创建服务

要使用默认桥接网络在群组模式下创建服务,不需要指定任何特殊选项。不得指定--publish–p选项。为mysql数据库创建一个服务。

∼ $ docker service create \
>   --env MYSQL_ROOT_PASSWORD='mysql'\
>   --replicas 1 \
>   --name mysql \
>  mysql

likujs72e46ti5go1xjtksnky

创建服务,并在其中一个节点上安排服务任务。

∼ $ docker service ls
ID              NAME   MODE           REPLICAS    IMAGE         PORTS
likujs72e46t    mysql  replicated     1/1         mysql:latest       

该服务可以扩展到在整个群体中运行任务。

∼ $ docker service scale mysql=3
mysql scaled to 3
∼ $ docker service ps mysql
ID                  NAME                IMAGE               NODE                            DESIRED STATE       CURRENT STATE           ERROR               PORTS
v4bn24seygc6        mysql.1             mysql:latest        ip-172-31-47-15.ec2.internal    Running             Running 2 minutes ago                       
29702ebj52gs        mysql.2             mysql:latest        ip-172-31-47-123.ec2.internal   Running             Running 3 seconds ago                       
c7b8v16msudl        mysql.3             mysql:latest        ip-172-31-3-168.ec2.internal    Running             Running 3 seconds ago                       

创建的mysql服务没有添加到ingress网络,因为它没有发布端口。

在入口网络中创建服务

在本节中,我们将在ingress网络中创建一个 Docker 服务。不使用docker service create--network选项指定ingress网络。服务必须发布一个要在ingress网络中创建的端口。创建一个在端口8080上发布的 Hello World 服务。

∼ $ docker service rm hello-world
hello-world
∼ $ docker service create \
>   --name hello-world \
>   -p 8080:80\
>   --replicas 3 \
>   tutum/hello-world

l76ukzrctq22mn97dmg0oatup

该服务创建三个任务,群中的每个节点一个。

∼ $ docker service ls
ID            NAME         MODE        REPLICAS    IMAGE                     PORTS
l76ukzrctq22  hello-world  replicated  3/3         tutum/hello-world:latest  *:8080->80/tcp
∼ $ docker service ps hello-world
ID             NAME            IMAGE                       NODE                          DESIRED STATE  CURRENT STATE                                   ERROR            PORTS
5ownzdjdt1yu   hello-world.1   tutum/hello-world: latest   ip-172-31-14-234.ec2.internal    Running        Running 33 seconds ago                       
csgofrbrznhq   hello-world.2   tutum/hello-world:latest    ip-172-31-47-203.ec2.internal   Running        Running 33 seconds ago                       
sctlt9rvn571   hello-world.3   tutum/hello-world:latest    ip-172-31-35-44.ec2.internal    Running        Running 32 seconds ago                       

可以使用<Public DNS>:<8080> URL 在端口 8080 上的 Swarm 中的任何节点实例上访问该服务。如果创建了弹性负载均衡器,对于 AWS 的 Docker,可以在<LoadBalancer DNS>:<8080>访问服务,如图 10-5 所示。

A454123_1_En_10_Fig5_HTML.jpg

图 10-5。

Invoking a Docker service in the ingress network using EC2 elastic load balancer public DNS

docker service create命令中可以省略<PublishedPort> 8080

∼ $ docker service create \
>   --name hello-world \
>   -p 80\
>   --replicas 3 \
>   tutum/hello-world

pbjcjhx163wm37d5cc5au2fog

在整个蜂群中启动三个服务任务。

∼ $ docker service ls
ID             NAME            MODE        REPLICAS IMAGE                      PORTS
pbjcjhx163wm   hello-world    replicated   3/3      tutum/hello-world:latest   *:0->80/tcp
∼ $ docker service ps hello-world
ID             NAME            IMAGE                      NODE      DESIRED STATE   CURRENT STATE            ERROR   PORTS
xotbpvl0508n   hello-world.1   tutum/hello-world:latest   ip-172-31-37-130.ec2.internal   Running         Running 13 seconds ago   
nvdn3j5pzuqi   hello-world.2   tutum/hello-world:latest   ip-172-31-44-205.ec2.internal   Running         Running 13 seconds ago
uuveltc5izpl   hello-world.3   tutum/hello-world:latest   ip-172-31-15-233.ec2.internal   Running         Running 14 seconds ago                       

群组管理器自动分配一个发布端口(30000),如docker service inspect命令中所列。

∼ $ docker service inspect hello-world
[
        "Spec": {
            "Name": "hello-world",
...
            "EndpointSpec": {
                "Mode": "vip",
                "Ports": [
                    {
                        "Protocol": "tcp",
                        "TargetPort": 80,
                        "PublishMode": "ingress"
                    }
                ]
            }
        },
        "Endpoint": {
            "Spec": {
                "Mode": "vip",
                "Ports": [
                    {
                        "Protocol": "tcp",
                        "TargetPort": 80,
                        "PublishMode": "ingress"
                    }
                ]
            },
            "Ports": [
                {
                    "Protocol": "tcp",
                    "TargetPort": 80,
                    "PublishedPort": 30000,
                    "PublishMode": "ingress"
                }
            ],
            "VirtualIPs": [
                {
                    "NetworkID": "bllwwocjw5xejffmy6n8nhgm8",
                    "Addr": "10.255.0.5/16"
                }
            ]
        }
    }
]

即使服务发布了一个端口(30000 或 30000-32767 范围内的其他可用端口),AWS 群组 Docker 的 AWS 弹性负载均衡器也不会为发布的端口(30000 或 30000-32767 范围内的其他可用端口)添加侦听器。我们添加一个<Load Balancer Port:Instance Port>映射为 30000:30000 的监听器,如图 10-6 所示。

A454123_1_En_10_Fig6_HTML.jpg

图 10-6。

Adding a load balancer listener

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

A454123_1_En_10_Fig7_HTML.jpg

图 10-7。

Invoking a Hello World service on port 30000

创建自定义叠加网络

我们使用默认的覆盖网络ingress在 Swarm 模式下提供。ingress网络仅用于包含所有节点的群模式路由网格。提供群路由网格,使得群中的每个节点可以接受群中服务的公布端口上的连接,即使服务没有在节点上运行任务。ingress网络不是用于服务对服务的通信。

定制的覆盖网络可以在群组模式中用于服务到服务的通信。接下来,使用一些高级选项创建一个覆盖网络,包括使用--subnet选项设置子网,使用--gateway选项设置默认网关,以及使用--ip-range选项设置 IP 范围。--driver选项必须设置为overlay,网络必须以群组模式创建。指定 IP 范围的匹配子网必须可用。子网是 IP 网络的逻辑细分。网关是将主机的子网连接到其他网络的路由器。以下命令必须从管理器节点运行。

∼ $ docker network create \
>   --subnet=192.168.0.0/16 \
>   --subnet=192.170.0.0/16 \
>   --gateway=192.168.0.100 \
>   --gateway=192.170.0.100 \
>   --ip-range=192.168.1.0/24 \
>   --driver overlay \
>   mysql-network

mkileuo6ve329jx5xbd1m6r1o

自定义覆盖网络被创建并在网络中作为具有群组范围的覆盖网络列出。

∼ $ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
34a5f77de8cf        bridge              bridge              local
0e06b811a613        docker_gwbridge     bridge              local
6763ebad69cf        host                host                local
e41an60iwval        ingress             overlay             swarm
mkileuo6ve32        mysql-network       overlay             swarm
eb7399d3ffdd        none                null                local

仅列出覆盖网络应列出ingress网络和自定义mysql-network

∼ $ docker network ls --filter driver=overlay
NETWORK ID          NAME                DRIVER              SCOPE
e41an60iwval        ingress             overlay             swarm
mkileuo6ve32        mysql-network       overlay             swarm

关于自定义覆盖网络的详细信息mysql-network列出了子网和网关。

∼ $ docker network inspect mysql-network
[
    {
        "Name": "mysql-network",
        "Id": "mkileuo6ve329jx5xbd1m6r1o",
        "Created": "0001-01-01T00:00:00Z",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "192.168.0.0/16",
                    "IPRange": "192.168.1.0/24",
                    "Gateway": "192.168.0.100"
                },
                {
                    "Subnet": "192.170.0.0/16",
                    "Gateway": "192.170.0.100"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": null,
        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "4097,4098"
        },
        "Labels": null
    }
]

只能为特定的子网、网关和 IP 范围创建一个覆盖网络。使用不同的子网、网关或 IP 范围,可以创建不同的覆盖网络。

∼ $ docker network create \
>   --subnet=10.0.0.0/16 \
>   --gateway=10.0.0.100 \
>   --ip-range=10.0.1.0/24 \
>   --driver overlay \
>   mysql-network-2

qwgb1lwycgvogoq9t62ea4ny1

创建mysql-network-2并将其添加到网络列表中。

∼ $ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
34a5f77de8cf        bridge              bridge              local
0e06b811a613        docker_gwbridge     bridge              local
6763ebad69cf        host                host                local
e41an60iwval        ingress             overlay             swarm
mkileuo6ve32        mysql-network       overlay             swarm
qwgb1lwycgvo        mysql-network-2     overlay             swarm
eb7399d3ffdd        none                null                local

新的覆盖网络仅对具有使用覆盖的容器的工作者节点可用。虽然新的覆盖网络mysql-networkmysql-network-2在管理节点上可用,但是网络没有扩展到两个工作节点。SSH 登录到一个工作节点。

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

工作节点上没有列出mysql-networkmysql-network-2网络。

∼ $ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
255542d86c1b        bridge              bridge              local
3a4436c0fb00        docker_gwbridge     bridge              local
bdd0be4885e9        host                host                local
e41an60iwval        ingress             overlay             swarm
5c5f44ec3933        none                null                local

要将自定义覆盖网络扩展到工作节点,请在网络中创建一个在工作节点上运行任务的服务,这将在下一节中讨论。

默认情况下,群组模式覆盖网络是安全的。gossip协议用于在群节点之间交换覆盖网络信息。节点在 GCM 模式下使用 AES 算法对交换的信息进行加密和认证。默认情况下,管理节点每 12 小时轮换一次八卦数据的加密密钥。覆盖网络上不同节点上的容器之间交换的数据也可以使用--opt encrypted选项来加密,这在调度任务的所有节点之间创建 IPSEC 隧道。IPSEC 隧道还在 GCM 模式下使用 AES 算法,并每 12 小时轮换一次 gossip 数据的加密密钥。以下命令创建一个加密网络。

∼ $ docker network create \
>   --driver overlay \
>   --opt encrypted \
>   overlay-network-2

aqppoe3qpy6mzln46g5tunecr

创建加密的群范围网络。

∼ $ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
34a5f77de8cf        bridge              bridge              local
0e06b811a613        docker_gwbridge     bridge              local
6763ebad69cf        host                host                local
e41an60iwval        ingress             overlay             swarm
mkileuo6ve32        mysql-network       overlay             swarm
qwgb1lwycgvo        mysql-network-2     overlay             swarm
eb7399d3ffdd        none                null                local
aqppoe3qpy6m        overlay-network-2   overlay             swarm

使用自定义覆盖网络创建服务

如果使用自定义覆盖网络来创建服务,则必须指定--network。下面的命令使用自定义 Swarm 作用域覆盖网络mysql-network在 Swarm 模式下创建一个 MySQL 数据库服务。

∼ $ docker service create \
>   --env MYSQL_ROOT_PASSWORD='mysql'\
>   --replicas 1 \
>   --network mysql-network \
>   --name mysql-2\
>  mysql

ocd9sz8qqp2becf0ww2rj5p5n

创建了mysql-2服务。将mysql-2服务扩展到三个副本,并列出该服务的服务任务。

∼ $ docker service scale mysql-2=3

mysql-2 scaled to 3

两种服务的两个不同网络中的 Docker 容器— mysql(桥接网络)和mysql-2(MySQL-网络覆盖网络)—同时运行在同一个节点上。

定制覆盖网络不扩展到群中的所有节点,直到节点具有使用定制网络的服务任务。直到在节点上为mysql-2安排了服务任务之后,mysql-network才被扩展到工作节点并在其上列出。

由默认对接引擎桥接网络docker0管理的对接容器不能与群范围覆盖网络中的对接容器连接。不支持在docker run命令中使用群组覆盖网络,使用docker network connect命令连接群组覆盖网络,或者使用docker network connect命令的--link选项链接 Docker 容器和群组覆盖网络。群范围内的覆盖网络只能由群中的 Docker 服务使用。

对于服务容器之间的连接:

  • 同一群范围覆盖网络中的相同或不同服务的对接容器能够彼此连接。
  • 不同群范围覆盖网络中相同或不同服务的对接容器不能相互连接。

在下一节中,我们将讨论内部网络,但在此之前,应该先介绍一下外部网络。我们已经创建的 Docker 容器是外部网络容器。ingress网络和自定义覆盖网络mysql- network是外部网络。外部网络提供通往网关的默认路由。主机和更广泛的互联网网络可以连接到ingress或定制覆盖网络中的 Docker 容器。例如,运行以下命令从 Docker 容器的 bash shell pinggoogle.com;Docker 容器应该在ingress覆盖网络或定制的 Swarm 覆盖网络中。

docker exec –it  <containerid> ping –c 1 google.com

建立连接并交换数据。命令输出以斜体显示。

∼ $ docker exec -it  3762d7c4ea68  ping -c 1 google.com

PING google.com (172.217.7.142): 56 data bytes

64 bytes from 172.217.7.142: icmp_seq=0 ttl=47 time=0.703 ms

--- google.com ping statistics ---

1 packets transmitted, 1 packets received, 0% packet loss

round-trip min/avg/max/stddev = 0.703/0.703/0.703/0.000 ms

创建内部覆盖网络

在本节中,我们将讨论如何创建和使用内部覆盖网络。内部网络不提供外部连接。使网络内部化的原因是,没有为来自主机或更广泛的互联网的外部连接提供通往网关的默认路由。

首先,使用docker network create命令的--internal选项创建一个内部覆盖网络。添加一些其他选项,如--label,,这些选项与内部网络无关。它是用docker network create命令的--internal选项配置的。

∼ $ docker network create \
>    --subnet=10.0.0.0/16 \
>    --gateway=10.0.0.100 \
>    --internal  \
>    --label HelloWorldService \
>    --ip-range=10.0.1.0/24 \
>   --driver overlay \
>   hello-world-network

pfwsrjeakomplo5zm6t4p19a9

内部网络的创建和列出方式与外部网络完全相同。

∼ $ docker network ls
NETWORK ID          NAME                  DRIVER              SCOPE
194d51d460e6        bridge                bridge              local
a0674c5f1a4d        docker_gwbridge       bridge              local
pfwsrjeakomp        hello-world-network   overlay             swarm
03a68475552f        host                  host                local
tozyadp06rxr        ingress               overlay             swarm
3dbd3c3ef439        none                  null                local

在网络描述中,internal被设置为true

core@ip-172-30-2-7 ∼ $ docker network inspect hello-world-network
[
    {
        "Name": "hello-world-network",
        "Id": "58fzvj4arudk2053q6k2t8rrk",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.0.0.0/16",
                    "IPRange": "10.0.1.0/24",
                    "Gateway": "10.0.0.100"
                }
            ]
        },
        "Internal": true,
        "Containers": null,
        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "257"
        },
        "Labels": {
            "HelloWorldService": ""
        }
    }
]

使用--network选项创建一个使用内部网络的服务。

∼ $ docker service create \
>   --name hello-world \
>   --network  hello-world-network \
>   --replicas 3 \
>   tutum/hello-world

hm5pf6ftcvphdrd2zm3pp4lpj

创建服务并安排副本。

获取服务任务之一的容器 ID,d365d4a5ff4c

∼ $ docker ps
CONTAINER ID   IMAGE                      COMMAND                  CREATED   STATUS              PORTS   NAMES
d365d4a5ff4c   tutum/hello-world:latest   "/bin/sh -c 'php-f..."   About a minute ago   Up About a minute           hello-world.3.r759ddnl1de11spo0zdi7xj4z

和以前一样,从 Docker 容器中 ping google.com

docker exec –it  <containerid> ping –c 1 google.com

未建立连接,这是因为容器位于内部覆盖网络中。

∼ $ docker exec -it  d365d4a5ff4c ping -c 1 google.com
ping: bad address 'google.com'

在同一内部网络中的容器之间建立连接,因为限制仅在外部连接性上。为了进行演示,获取同一内部网络中另一个容器的容器 ID。

∼ $ docker ps
CONTAINER ID   IMAGE                      COMMAND                  CREATED    STATUS          PORTS      NAMES
b7b505f5eb8d   tutum/hello-world:latest   "/bin/sh -c 'php-f..."   3 seconds ago       Up 2 seconds               hello-world.6.i60ezt6da2t1odwdjvecb75fx
57e612f35a38   tutum/hello-world:latest   "/bin/sh -c 'php-f..."   3 seconds ago       Up 2 seconds               hello-world.7.6ltqnybn8twhtblpqjtvulkup
d365d4a5ff4c   tutum/hello-world:latest   "/bin/sh -c 'php-f..."   7 minutes ago       Up 7 minutes               hello-world.3.r759ddnl1de11spo0zdi7xj4z

连接同一个内部网络中的两个容器。建立了连接。

∼ $ docker exec -it  d365d4a5ff4c ping -c 1 57e612f35a38
PING 57e612f35a38 (10.0.1.7): 56 data bytes
64 bytes from 10.0.1.7: seq=0 ttl=64 time=0.288 ms

--- 57e612f35a38 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.288/0.288/0.288 ms

如果在内部网络中创建的服务发布(公开)了一个端口,该服务将被添加到ingress网络中,即使该服务位于内部网络中,也会提供外部连接。例如,我们添加了docker service create命令的--publish选项,以在端口8080上发布服务。

∼ $ docker service create \
>   --name hello-world \
>   --network  hello-world-network \
>   --publish 8080:80 \
>   --replicas 3 \
>   tutum/hello-world

mqgek4umisgycagy4qa206f9c

查找服务任务的 Docker 容器 ID。

∼ $ docker ps
CONTAINER ID   IMAGE                      COMMAND                  CREATED      STATUS          PORTS    NAMES
1c52804dc256   tutum/hello-world:latest   "/bin/sh -c 'php-f..."   28 seconds ago      Up 27 seconds   80/tcp   hello-world.1.20152n01ng3t6uaiahpex9n4f

例如,在google.com从内部网络中的容器连接到更大的外部网络。建立了连接。命令输出以斜体显示。

∼ $ docker exec -it  1c52804dc256  ping -c 1 google.com

PING google.com (172.217.7.238): 56 data bytes

64 bytes from 172.217.7.238: seq=0 ttl=47 time=1.076 ms

--- google.com ping statistics ---

1 packets transmitted, 1 packets received, 0% packet loss

round-trip min/avg/max = 1.076/1.076/1.076 ms

删除网络

可以使用 docker network rm <networkid>命令删除未使用的网络。可以在同一个命令中删除多个网络。例如,我们可以列出和删除多个网络。

∼ $ docker network ls
NETWORK ID          NAME                  DRIVER              SCOPE
34a5f77de8cf        bridge                bridge              local
0e06b811a613        docker_gwbridge       bridge              local
wozpfgo8vbmh        hello-world-network                       swarm
6763ebad69cf        host                  host                local
e41an60iwval        ingress               overlay             swarm
mkileuo6ve32        mysql-network         overlay             swarm
qwgb1lwycgvo        mysql-network-2       overlay             swarm
eb7399d3ffdd        none                  null                local
aqppoe3qpy6m        overlay-network-2     overlay             swarm

服务正在使用的网络不会被删除。命令输出以斜体显示。

∼ $ docker network rm hello-world-network mkileuo6ve32 qwgb1lwycgvo overlay-network-2

hello-world-network

Error response from daemon: rpc error: code = 9 desc = network mkileuo6ve329jx5xbd1m6r1o is in use by service ocd9sz8qqp2becf0ww2rj5p5nqwgb1lwycgvo

overlay-network-2

摘要

本章讨论了 Docker 群组模式使用的网络。Swarm 模式中使用的默认网络是覆盖网络ingress,这是一个跨同一 Swarm 中所有 Docker 节点的多主机网络,为每个节点提供路由网格,以便能够接受发布端口上服务的ingress连接。自定义覆盖网络可用于创建 Docker 服务,不同之处在于,自定义覆盖网络提供服务对服务的通信,而不是ingress通信,并且仅当使用网络的服务任务被调度在节点上时,才扩展到群工作器节点。本章还讨论了内部网络和外部网络的区别。在下一章中,我们将讨论 Docker 群组模式下的日志记录和监控。

十一、日志和监控

Docker 包括几个内置的容器日志驱动,比如json-file、syslog、journald、gelf、fluentd 和 awslogs。Docker 还提供了docker logs命令来获取容器的日志。Docker 1.13 包括一个实验性的特性,使用docker service logs命令获取 Docker 服务日志。

问题

Docker Swarm 模式不包括针对 Docker 服务和容器的本地监控服务。此外,获取服务日志的实验性特性是一个命令行特性,并且每个服务都需要运行该特性。缺少一个日志记录服务,通过它可以收集所有服务的日志和指标,并在仪表板中查看。

解决方案

Sematext 是一个集成的数据分析平台,为指标和事件收集提供 SPM 性能监控,为日志收集提供 Logsene,包括性能指标、日志和事件之间的关联。Logsene 是一个托管的 ELK (Elasticsearch,Logtash,Kibana)栈。需要在群中的每个群节点上安装 Sematext Docker 代理,用于持续收集日志、指标和事件,如图 11-1 所示。

A454123_1_En_11_Fig1_HTML.gif

图 11-1。

Sematext Docker agent on each Swarm node

本章涵盖以下主题:

  • 设置环境
  • 创建 SPM 应用
  • 创建 Logsene 应用
  • 将 Sematext Docker 代理部署为服务
  • 在 Docker Swarm 上创建 MySQL 数据库部署
  • 监控 Docker 群体指标
  • 获取 Logsene 中的 Docker 群集日志

设置环境

使用 Docker for AWS 启动一个由一个管理器和两个工作者节点组成的三节点集群。(这将在第三章中讨论。)从 EC2 控制台获取 manager 节点实例的公共 IP 地址,并通过 SSH 登录到该实例。

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

使用 Sematext SPM 和 Logsene 通过 Docker Swarm 进行日志记录和监控的过程如下。

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

创建 SPM 应用

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

A454123_1_En_11_Fig2_HTML.jpg

图 11-2。

Adding a SPM Docker app

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

A454123_1_En_11_Fig3_HTML.jpg

图 11-3。

SPM app is created

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

A454123_1_En_11_Fig4_HTML.jpg

图 11-4。

Docker Swarm configuration

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

A454123_1_En_11_Fig5_HTML.jpg

图 11-5。

DockerSwarmSPM app on the dashboard

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

A454123_1_En_11_Fig6_HTML.jpg

图 11-6。

The DockerSwarmSPM app has not received any data

创建 Logsene 应用

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

A454123_1_En_11_Fig7_HTML.jpg

图 11-7。

Selecting the Logs app

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

A454123_1_En_11_Fig8_HTML.jpg

图 11-8。

Adding the Logsene app

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

A454123_1_En_11_Fig9_HTML.jpg

图 11-9。

The Logsene app is added and LOGSENE_TOKEN is generated

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

A454123_1_En_11_Fig10_HTML.jpg

图 11-10。

The DockerSwarmLogsene app

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

A454123_1_En_11_Fig11_HTML.jpg

图 11-11。

The app does not receive any data at first

连接 SPM 和 Logsene 应用

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

A454123_1_En_11_Fig12_HTML.jpg

图 11-12。

Choosing Integrations ➤ Connected Apps

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

A454123_1_En_11_Fig13_HTML.jpg

图 11-13。

DockerSwarmLogsene

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

A454123_1_En_11_Fig14_HTML.jpg

图 11-14。

The connected apps

将 Sematext Docker 代理部署为服务

之前复制的docker service create命令只包含了SPM_TOKEN令牌。添加从 Logsene 应用获得的–e LOGSENE_TOKEN。在群管理器节点上运行docker service create命令。

∼ $ docker service create --mode global \
> --restart-condition any \
> --name sematext-agent-docker \
> --mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
> --mount type=bind,src=/,dst=/rootfs,readonly=true \
> -e SPM_TOKEN=9b5552fd-001d-44f0-9452-76046d4a3413 \
> -e LOGSENE_TOKEN=81ac5395-fe8f-47d9-93b2-dc00c649116a \
>   sematext/sematext-agent-docker

oubjk53mpdnjgak5dgfdxs4ft

为语义对接代理创建服务;使用docker service ls列出。

∼ $ docker service ls
ID            NAME                   MODE    REPLICAS  IMAGE      PORTS
oubjk53mpdnj  sematext-agent-docker  global  3/3       sematext/sematext-agent-docker:latest   

列出服务任务。因为这是一个全局服务,所以在每个节点上启动一个任务。

∼ $ docker service ps sematext-agent-docker
ID             NAME                                                 IMAGENODE                            DESIRED STATE   CURRENT STATE           ERROR   PORTS
5jvl7gnvl0te   sematext-agent-docker.8d0qv1epqu8xop4o2f94i8j40      sematext/sematext-agent-docker:latest
ip-172-31-8-4.ec2.internal      Running         Running 2 minutes ago
y53f20d3kknh   sematext-agent-docker.xks3sw6qgwbcuacyypemfbxyj      sematext/sematext-agent-docker:latest   
ip-172-31-31-117.ec2.internal   Running         Running 2 minutes ago                      
t5w2pxy4fc9l       sematext-agent-docker.r02ftwtp3n4m0cl7v2llw4gi8   sematext/sematext-agent-docker:latest   
ip-172-31-44-8.ec2.internal     Running         Running 2 minutes ago                      

如果额外的节点被添加到群中,则 Sematext Docker 代理在新节点上启动服务任务。例如,更新 CloudFormation 栈,将管理节点的数量增加到 3 个,将工作节点的数量增加到 5 个,如图 11-15 所示。

A454123_1_En_11_Fig15_HTML.jpg

图 11-15。

Increasing the number of worker nodes

当栈更新完成时,群节点增加到三个管理节点和五个工作节点。

∼ $ docker node ls
ID                          HOSTNAME                      STATUS AVAILABILITY MANAGER STATUS
8d0qv1epqu8xop4o2f94i8j40   ip-172-31-8-4.ec2.internal    Ready  Active              
9rvieyqnndgecagbuf73r9gs5   ip-172-31-35-125.ec2.internal Ready  Active       Reachable
j4mg3fyzjtsdcnmr7rkiytltj   ip-172-31-18-156.ec2.internal Ready  Active              
mhbbunhl358chah1dmr0y6i71   ip-172-31-7-78.ec2.internal   Ready  Active       Reachable
r02ftwtp3n4m0cl7v2llw4gi8   ip-172-31-44-8.ec2.internal   Ready  Active              
vdamjjjrz7a3ri3prv9fjngvy   ip-172-31-6-92.ec2.internal   Ready  Active              
xks3sw6qgwbcuacyypemfbxyj * ip-172-31-31-117.ec2.internal Ready  Active       Leader
xxyy4ys4oo30bb4l5daoicsr2   ip-172-31-21-138.ec2.internal Ready  Active      

向群组添加节点会在添加的节点上启动一个语义代理。

∼ $ docker service ps sematext-agent-docker
ID                 NAME                                            IMAGE                                    NODE                             DESIRED STATE CURRENT STATE         ERROR              PORTS
cgaturw05p59       sematext-agent-docker.xxyy4ys4oo30bb4l5daoicsr2 sematext/sematext-agent-docker:latest    ip-172-31-21-138.ec2.internal    RunningRunning 2 minutes ago                        
lj4f46q3ydv1        sematext-agent-docker.j4mg3fyzjtsdcnmr7rkiytltj   sematext/sematext-agent-docker:latest    ip-172-31-18-156.ec2.internal    RunningRunning 2 minutes ago     
v54bjs3c8u5r        sematext-agent-docker.vdamjjjrz7a3ri3prv9fjngvy   sematext/sematext-agent-docker:latest    ip-172-31-6-92.ec2.internal      Running   Running 2 minutes ago      
s7arohbeoake       sematext-agent-docker.9rvieyqnndgecagbuf73r9gs5   sematext/sematext-agent-docker:latest    ip-172-31-35-125.ec2.internal    Running    Running 3 minutes ago                        
ixpri65xwpds       sematext-agent-docker.mhbbunhl358chah1dmr0y6i71   sematext/sematext-agent-docker:latest    ip-172-31-7-78.ec2.internal      Running    Running 4 minutes ago                        
5jvl7gnvl0te       sematext-agent-docker.8d0qv1epqu8xop4o2f94i8j40   sematext/sematext-agent-docker:latest    ip-172-31-8-4.ec2.internal       Running    Running 15 minutes ago                       
y53f20d3kknh       sematext-agent-docker.xks3sw6qgwbcuacyypemfbxyj   sematext/sematext-agent-docker:latest    ip-172-31-31-117.ec2.internal    Running    Running 15 minutes ago                       
t5w2pxy4fc9l       sematext-agent-docker.r02ftwtp3n4m0cl7v2llw4gi8   sematext/sematext-agent-docker:latest    ip-172-31-44-8.ec2.internal      Running    Running 15 minutes ago                       

在 Docker Swarm 上创建 MySQL 数据库服务

在本节中,我们将创建一个 MySQL 数据库服务,使用我们安装的 Sematext Docker 代理,通过 Sematext SCM 和 Logsene 从该服务中收集指标、日志和事件。首先,运行下面的命令来创建一个有 10 个副本的mysql服务。

∼ $ docker service create \
>   --env MYSQL_ROOT_PASSWORD='mysql'\
>   --replicas 10 \
>   --name mysql \
>  mysql

rmy45fpa31twkyb3dowzpc74a

除了 Sematext Docker 代理服务之外,还会创建并列出该服务。

∼ $ docker service ls
ID           NAME                  MODE       REPLICAS IMAGE                     PORTS
oubjk53mpdnj sematext-agent-docker global     8/8      sematext/sematext-agent-docker:latest
rmy45fpa31tw mysql                 replicated 10/10    mysql:latest

还列出了mysql服务的服务任务。

∼ $ docker service ps mysql
ID               NAME      IMAGE           NODE                               DESIRED STATECURRENT STATE          ERROR      PORTS
x8j221ws4kx2     mysql.1   mysql:latest    ip-172-31-21-138.ec2.internal      Running     Running 13 seconds ago                       
98rbd6nwspqz     mysql.2   mysql:latest    ip-172-31-44-8.ec2.internal        Running     Running 11 seconds ago                       
vmq0lylni8or     mysql.3   mysql:latest    ip-172-31-8-4.ec2.internal         Running    Running 24 seconds ago                       
0vb6oda3yh3d     mysql.4   mysql:latest    ip-172-31-7-78.ec2.internal        Running   Running 23 seconds ago                       
vdpplkyxy1uy     mysql.5   mysql:latest    ip-172-31-6-92.ec2.internal        Running   Running 23 seconds ago                       
9ser7fwz6998     mysql.6   mysql:latest    ip-172-31-18-156.ec2.internal      Running   Running 17 seconds ago                       
vfsfvanghns0     mysql.7   mysql:latest    ip-172-31-18-156.ec2.internal      Running   Running 17 seconds ago                       
v71qwpvjhhzn     mysql.8   mysql:latest    ip-172-31-6-92.ec2.internal        Running   Running 23 seconds ago                       
j7172i5ml43d     mysql.9   mysql:latest    ip-172-31-31-117.ec2.internal      Running   Running 24 seconds ago                       
5p5mg2wnbb0o     mysql.10  mysql:latest    ip-172-31-35-125.ec2.internal      Running  Running 20 seconds ago                       

在 Swarm 上启动 Sematext Docker 代理服务和 MySQL 数据库服务后,SPM 和 Logsene 应用都开始接收数据,如仪表板中的Data Received列所示。参见图 11-16 。

A454123_1_En_11_Fig16_HTML.jpg

图 11-16。

DockerSwarmSPM overview

监控 Docker 群体指标

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

A454123_1_En_11_Fig17_HTML.jpg

图 11-17。

Docker Swarm SPM overview

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

A454123_1_En_11_Fig18_HTML.jpg

图 11-18。

Docker metrics

Docker ➤容器网络选项显示接收和传输的网络流量、接收速率和传输速率。通过选择操作系统➤磁盘,可以显示已使用的操作系统磁盘空间。指标收集粒度可以设置为自动粒度(默认)、按月、按周、按天、按小时、按 5 分钟或按 1 分钟。可以使用日志按钮显示日志概述。

如果图表未设置为默认的自动刷新,请单击刷新图表按钮刷新图表。

使用 Logsene UI 或 Kibana 4 显示详细的日志,我们将在下一节中讨论。

获取 Logsene 中的 Docker 群集日志

在利润导航中选择日志 dockerswarmLogsene 以显示 logsene 收集的日志。显示日志计数、日志事件和过滤字段,如图 11-19 所示。要搜索由mysql服务生成的日志,请将“mysql”添加到搜索字段,然后单击搜索按钮。显示由mysql Docker 服务生成的日志,包括状态消息,如"mysqld ready for connections"。单击刷新按钮刷新日志。

A454123_1_En_11_Fig19_HTML.jpg

图 11-19。

Logs generated by the mysql Docker Service

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

A454123_1_En_11_Fig20_HTML.jpg

图 11-20。

Logs for Docker event for mysql image pull

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

A454123_1_En_11_Fig21a_HTML.jpg A454123_1_En_11_Fig21b_HTML.jpg

图 11-21。

Logs for Docker event volume mount

摘要

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

十二、负载均衡

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 显示了多节点集群的入口负载均衡。客户端可以访问群中的任何节点,无论该节点是否调度了服务任务,并且使用入口负载均衡将客户端请求转发到服务任务之一。

A454123_1_En_12_Fig1_HTML.gif

图 12-1。

Ingress load balancing

单个客户端访问单个节点,结果,在跨群节点分布外部客户端负载方面,群未被充分利用。客户端负载在群节点之间并不均衡。单个节点不提供任何容错。如果该节点出现故障,访问该节点服务的外部客户端将无法使用该服务。

解决方案

AWS 弹性负载均衡器(ELB)用于在多个 EC2 实例之间分配客户端负载。当用于 Docker 群模式时,AWS 弹性负载均衡器将客户端负载分布在托管群节点的不同 EC2 实例上。外部负载均衡器使用 LB 监听器访问(监听)群中运行的服务的发布端口处的每个 EC2 实例上的群。每个 LB 侦听器都有一个 LB 端口映射到每个 EC2 实例上的一个实例端口(服务的发布端口)。蜂群中的 ELB 如图 12-2 所示。

A454123_1_En_12_Fig2_HTML.gif

图 12-2。

External load balancer

由于即使单个节点停机或变得不可用,客户端也不访问单个主机上的服务,所以群不会变得不可用,因为外部负载均衡器将客户端请求定向到群中的不同节点。即使所有节点都可用,客户端流量也会分布在不同的节点上。例如,客户端可能在特定时间从一个节点接受服务,此后不久从另一个节点接受服务。因此,外部负载均衡器提供两种功能:负载均衡和容错。此外,托管群的云提供商可以提供额外的功能,如安全和灵活的外部负载均衡。AWS 弹性负载均衡器提供的弹性负载均衡根据客户端流量来调整请求处理能力。

本章讨论 CoreOS 上用户创建的 Swarm 的负载均衡。它还讨论了 Docker 上为 AWS 托管服务自动提供的弹性负载均衡器。

设置环境

启动三个 CoreOS 实例——一个用于管理节点,两个用于工作节点——如图 12-3 所示。从 EC2 仪表板中获取 manager 实例的公共 IP 地址,如图 12-3 所示。

A454123_1_En_12_Fig3_HTML.jpg

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

A454123_1_En_12_Fig4_HTML.jpg

图 12-4。

Invoking the service in a browser

类似地,要在 worker 节点调用服务,从 EC2 控制台获取 worker 实例的公共 DNS,并在 web 浏览器的<PublicDNS>:<PublishedPort> URL 调用服务,如图 12-5 所示。

A454123_1_En_12_Fig5_HTML.jpg

图 12-5。

Invoking the service at a worker node

类似地,要在另一个 worker 节点调用服务,从 EC2 控制台获取 worker 实例的公共 DNS,并在 web 浏览器中的<PublicDNS>:<PublishedPort> URL 调用服务,如图 12-6 所示。

A454123_1_En_12_Fig6_HTML.jpg

图 12-6。

Invoking the service at the other worker node

外部 AWS 弹性负载均衡器在 EC2 实例之间分配负载,而入口负载均衡器在服务任务之间分配负载。在前面的例子中,当在群管理器实例和群工作器实例调用服务时,调用相同的服务任务,如相同的主机名所示(图 12-4 和 12-6 )。这演示了入口负载均衡。

如果在同一主机上调用服务,则可能会调用不同的服务任务。例如,再次调用 Swarm manager 实例上的服务。服务于不同的服务任务,如图 12-7 中不同的主机名所示。这与之前在图 12-4 中提供的主机名进行了比较,再次证明了入口负载均衡。

A454123_1_En_12_Fig7_HTML.jpg

图 12-7。

Different hostname served when invoking the service at the manager node again

创建外部弹性负载均衡器

在本节中,我们将在 AWS 云上创建一个外部弹性负载均衡器。单击 EC2 仪表板中的负载均衡器。然后点击 Create Load Balancer 创建一个新的负载均衡器,如图 12-8 所示。

A454123_1_En_12_Fig8_HTML.jpg

图 12-8。

Creating a new load balancer

AWS 弹性负载均衡提供两种类型的负载均衡器——经典负载均衡器和应用负载均衡器。传统负载均衡器基于应用或网络级别的信息路由流量,而应用负载均衡器基于高级应用级别的信息路由流量。经典的负载均衡器应该能够满足对多个 EC2 实例的流量进行最简单的负载均衡,并且是我们用于 Docker Swarm 实例的负载均衡器。选择经典负载均衡器,然后点击继续,如图 12-9 所示。

A454123_1_En_12_Fig9_HTML.jpg

图 12-9。

Selecting the classic load balancer option

在定义负载均衡器对话框中,指定一个负载均衡器名称(HelloWorldLoadBalancer),并选择一个 VPC 在其中创建负载均衡器,如图 12-10 所示。VPC 必须在创建负载均衡器之前存在,并且必须是创建要进行负载均衡的 EC2 实例的位置。默认情况下,负载均衡器协议是 HTTP,实例协议也是。保留 HTTP 协议的默认设置,将负载均衡器端口和实例端口指定为 8080,因为 Hello World 服务在端口 8080 公开。

A454123_1_En_12_Fig10_HTML.jpg

图 12-10。

Selecting the load balancer protocol

在选择子网选项卡中,单击可用子网表中列出的一个或多个子网。子网被添加到选中的子网中,如图 12-11 所示。点击下一步。要提供高可用性,请在不同的可用性区域中至少选择两个子网。

A454123_1_En_12_Fig11_HTML.jpg

图 12-11。

Selecting subnets

在分配安全组选项卡中,选择创建新的安全组,如图 12-12 所示。在类型中,选择自定义 TCP 规则。选择 TCP 协议和端口范围 8080。选择任意位置作为源,其值为 0.0.0.0/0。点击下一步。

A454123_1_En_12_Fig12_HTML.jpg

图 12-12。

Assigning security groups

单击“配置安全设置”中的“下一步”,因为我们没有使用 HTTPS 或 SSL 协议。在配置运行状况检查选项卡中,为 ping 协议选择 HTTP,为 ping 端口选择 8080。指定 ping 路径为/,如图 12-13 所示。在“高级详细信息”区域保留默认值,然后单击“下一步”。

A454123_1_En_12_Fig13_HTML.jpg

图 12-13。

Configuring a health check

选择列出的三个 Swarm 实例,如图 12-14 所示。另外,选择启用跨区域负载均衡,这将在所有可用性区域中的所有后端实例之间平均分配流量。点击下一步。

A454123_1_En_12_Fig14_HTML.jpg

图 12-14。

Adding EC2 instances

在“添加标签”选项卡中,不需要添加任何标签。在审核选项卡中,点击创建,如图 12-15 所示。如上所述,负载均衡器是面向互联网的类型。

A454123_1_En_12_Fig15_HTML.jpg

图 12-15。

Review your settings then create the load balancer

负载均衡器创建完成,如图 12-16 所示。

A454123_1_En_12_Fig16_HTML.jpg

图 12-16。

The load balancer has been created

从 EC2 控制台获取负载均衡器的 DNS 名称,如图 12-17 所示。最初,状态将是“3 个实例中的 0 个在服务中”,因为注册仍在进行中。

A454123_1_En_12_Fig17_HTML.jpg

图 12-17。

Obtaining the DNS name of the load balancer

过一会儿,状态应该变成“3 个实例中的 3 个在服务中”,所有实例都应该在服务中,如图 12-18 所示。

A454123_1_En_12_Fig18_HTML.jpg

图 12-18。

Status indicates three of three instances InService

Hello World 服务可以从 web 浏览器中的<DNSname>:<LoadBalancerPort> URL 调用,如图 12-19 所示。

A454123_1_En_12_Fig19_HTML.jpg

图 12-19。

Invoking the Hello World service

外部弹性负载均衡器平衡集群中 EC2 实例之间的负载。因为入口负载均衡器在不同的服务任务之间平衡负载,如果在 ELB DNS 名称上再次调用服务,则可能会调用不同的服务任务,如图 12-20 所示。

A454123_1_En_12_Fig20_HTML.jpg

图 12-20。

Different service task served

AWS Docker 中的负载均衡

虽然在使用命令行创建 Docker 群组时必须创建外部弹性负载均衡器(首先启动群组模式,然后将工作节点加入群组),但在第三章中介绍的 AWS 托管服务 Docker 会自动创建弹性负载均衡器。

使用 Docker for AWS 创建一个有三个管理器节点和五个工作者节点的 Swarm(之前创建的 Swarm 可能会被更新),如图 12-21 所示。如图 12-21 中的资源选项卡所示,创建一个外部弹性负载均衡器作为群资源之一。

A454123_1_En_12_Fig21_HTML.jpg

图 12-21。

CloudFormation stack for a Docker Swarm

创建一个面向互联网的弹性负载均衡器,如图 12-22 所示。负载均衡器的公共 DNS 可以用于访问群,如后面所讨论的。

A454123_1_En_12_Fig22_HTML.jpg

图 12-22。

Load balancer for the Swarm created with Docker for AWS

选择“实例”选项卡。列出了群中的所有实例,经理或工人。所有的实例都应处于使用状态,如图 12-23 所示。

A454123_1_En_12_Fig23_HTML.jpg

图 12-23。

Instances status is InService

更新 listeners 选项卡中的负载均衡器侦听器,添加/修改一个侦听器,其负载均衡器端口设置为 8080,实例端口设置为 8080,这是我们创建的 Hello World 服务的发布端口,如图 12-24 所示。

A454123_1_En_12_Fig24_HTML.jpg

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

A454123_1_En_12_Fig25_HTML.jpg

图 12-25。

Obtaining the public DNS of the ELB

在 web 浏览器中访问<PublicDNS>:<PublishedPort>处的服务,如图 12-26 所示。该请求被转发到群中的一个实例上的入口负载均衡器。外部请求转发到的实例不一定要承载服务任务。寻找服务任务是入口负载均衡器的工作。

A454123_1_En_12_Fig26_HTML.jpg

图 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 区域的故障会导致网站不可用。

A454123_1_En_13_Fig1_HTML.gif

图 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 的主-从架构模式。

A454123_1_En_13_Fig2_HTML.gif

图 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命令将密钥对的权限设置为只读。

A454123_1_En_13_Fig3_HTML.jpg

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

A454123_1_En_13_Fig4_HTML.jpg

图 13-4。

CloudFormation stack for Docker Swarm

每个 Docker 群都有分布在 AWS 区域中的 AWS 可用性区域的管理器和工作者节点。管理节点的公共 IP 可以从 EC2 控制台获取,如图 13-5 所示。

A454123_1_En_13_Fig5_HTML.jpg

图 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 所示。对于不同的用户,区域可能是不同的。

A454123_1_En_13_Fig6_HTML.jpg

图 13-6。

CloudFormation stack for the Docker Swarm in one region

第二停靠站群的群节点 EC2 实例也分布在第二 AWS 区域中的 AWS 可用性区域,如图 13-7 所示。获取管理器节点的公共 IP。

A454123_1_En_13_Fig7_HTML.jpg

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

A454123_1_En_13_Fig8_HTML.jpg

图 13-8。

Service tasks scheduled across the Swarm nodes

从 EC2 仪表板获取第一个 Docker 群组的负载均衡器 DNS,如图 13-9 所示。

A454123_1_En_13_Fig9_HTML.jpg

图 13-9。

Docker Swarm load balancer

在 web 浏览器中访问<DNS>:<LoadBalancerPort>处的服务,如图 13-10 所示;负载均衡器端口设置为 8080,该端口用于公开服务。

A454123_1_En_13_Fig10_HTML.jpg

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

A454123_1_En_13_Fig11_HTML.jpg

图 13-11。

Service replicas distributed across the Swarm

获取第二个集群的弹性负载均衡器的 DNS,如图 13-12 所示。

A454123_1_En_13_Fig12_HTML.jpg

图 13-12。

Obtaining the DNS name for the Swarm ELB

在 web 浏览器中访问<DNS>:<LoadBalancerPort>处的服务,如图 13-13 所示。

A454123_1_En_13_Fig13_HTML.jpg

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

A454123_1_En_13_Fig14_HTML.jpg

图 13-14。

Selecting the Amazon Route 53 service

创建托管区域

托管区域是一种配置,用于确定如何路由互联网上某个域的流量。要创建托管区域,在网页浏览器中打开 https://console.aws.amazon.com/route53/ ,在 DNS 管理中点击创建托管区域,如图 13-15 所示。

A454123_1_En_13_Fig15_HTML.jpg

图 13-15。

Creating the hosted zone

或者,选择托管区域或在浏览器中打开 https://console.aws.amazon.com/route53/home#hosted-zones ,点击创建托管区域,如图 13-16 所示。

A454123_1_En_13_Fig16_HTML.jpg

图 13-16。

Creating a hosted zone

再次点击创建托管区域,如图 13-17 所示。

A454123_1_En_13_Fig17_HTML.jpg

图 13-17。

Creating a hosted zone

在创建托管区域对话框中,指定域名(nosqlsearch.com)。域名必须向用户注册。类型选择公共托管区域,如图 13-18 所示。

A454123_1_En_13_Fig18_HTML.jpg

图 13-18。

Configuring the hosted zone

创建一个新的公共托管区域,如图 13-19 所示。托管区域的名称服务器(默认情况下有四个)已分配。

A454123_1_En_13_Fig19_HTML.jpg

图 13-19。

The new public hosted zone

配置名称服务器

接下来,我们需要通过域名注册机构来配置域名服务器。不同的域名注册商配置域名服务器的程序是不同的,但是应该提供一个为域名添加区域记录的选项。

将记录类型指定为名称服务器,如图 13-20 所示。将主机指定为@。每个区域记录应该指向一个名称服务器,该服务器可以从我们之前创建的公共托管区域获得。

A454123_1_En_13_Fig20_HTML.jpg

图 13-20。

Adding a name server record

为要创建托管区域的域添加四台名称服务器(统称为委托集),如图 13-21 所示。

A454123_1_En_13_Fig21_HTML.jpg

图 13-21。

Name servers configured on a domain

创建资源记录集

创建和配置托管区域后,创建一个或多个资源记录集。资源记录集是用于将流量路由到域的域名系统(DNS)配置。点击创建记录集,创建一个资源记录集,如图 13-22 所示。

A454123_1_En_13_Fig22_HTML.jpg

图 13-22。

Creating a record set

在创建记录集选项卡中,类型应设置为–IP v4 地址,如图 13-23 所示。每个记录集的名称都以域名结尾。为别名选择是。

A454123_1_En_13_Fig23_HTML.jpg

图 13-23。

Configuring a record set

接下来,选择别名目标作为其中一个 Docker 集群的 AWS 弹性负载均衡器 DNS,如图 13-24 所示。

A454123_1_En_13_Fig24_HTML.jpg

图 13-24。

Selecting an alias target

接下来,选择路由策略,如图 13-25 所示。

A454123_1_En_13_Fig25_HTML.jpg

图 13-25。

Selecting a routing policy

为路由策略选择故障转移。这将配置 DNS 故障转移,如图 13-26 所示。选择故障转移记录类型为主记录类型。

A454123_1_En_13_Fig26_HTML.jpg

图 13-26。

Selecting failover record type

对于评估目标健康,选择是,如图 13-27 所示。

A454123_1_En_13_Fig27_HTML.jpg

图 13-27。

Selecting the Evaluate Target Health option

对于关联健康检查,选择否,点击创建,如图 13-28 所示。

A454123_1_En_13_Fig28_HTML.jpg

图 13-28。

Creating a record set

创建一个主记录集,如图 13-29 所示;“主要”意味着网站流量将首先被路由到记录集。

A454123_1_En_13_Fig29_HTML.jpg

图 13-29。

Primary record set

要创建辅助记录集,再次点击创建记录集,如图 13-30 所示。

A454123_1_En_13_Fig30_HTML.jpg

图 13-30。

Creating another record set

选择类型为–IP v4 地址,并为别名选择是。选择别名目标作为第二个 ELB DNS,如图 13-31 所示。

A454123_1_En_13_Fig31_HTML.jpg

图 13-31。

Selecting an alias target

选择故障转移路由策略和辅助故障转移记录类型,如图 13-32 所示。

A454123_1_En_13_Fig32_HTML.jpg

图 13-32。

Selecting failover record type as secondary

为评估目标健康状况选择是,为与健康状况检查关联选择否。点击创建,如图 13-33 所示。

A454123_1_En_13_Fig33_HTML.jpg

图 13-33。

Creating a secondary record set

创建辅助记录集;“次要”意味着如果主要记录集出现故障,流量将被路由到该记录集,如图 13-34 所示。点击返回托管区域。

A454123_1_En_13_Fig34_HTML.jpg

图 13-34。

Secondary record set is created

域(nosqlsearch.com)配置了四个记录集,如图 13-35 所示。

A454123_1_En_13_Fig35_HTML.jpg

图 13-35。

Hosted zone created

测试高可用性

接下来,我们测试我们配置的高可用性。在 web 浏览器中打开域,包括服务发布端口(nosqlsearch.com:8080),如图 13-36 所示。应该会显示 Docker 服务输出。

A454123_1_En_13_Fig36_HTML.jpg

图 13-36。

Invoking a service in a browser

为了测试高可用性,删除与主记录集关联的 Docker Swarm 的 CloudFormation 栈,如图 13-37 所示。

A454123_1_En_13_Fig37_HTML.jpg

图 13-37。

Deleting a stack

在删除栈对话框中单击是,删除。栈应该开始被删除,如图 13-38 中的DELETE_IN_PROGRESS状态所示。

A454123_1_En_13_Fig38_HTML.jpg

图 13-38。

The delete is in progress

DNS 故障转移到二级资源记录集,域继续服务 Docker 服务,如图 13-39 所示。

A454123_1_En_13_Fig39_HTML.jpg

图 13-39。

Domain continues to serve

如果请求被转发到不同的服务任务副本,浏览器中的主机名可能会变得不同,如图 13-40 所示。但是无论是否启动了故障转移,主机名也可能变得不同,因为入口负载均衡器在不同的服务副本之间分配流量。

A454123_1_En_13_Fig40_HTML.jpg

图 13-40。

Different hostname

删除托管区域

在删除托管区域之前,必须删除与托管区域关联的所有资源记录集。选择要删除的资源记录集,点击【删除记录集】,如图 13-41 所示。

A454123_1_En_13_Fig41_HTML.jpg

图 13-41。

Deleting the record sets

在确认对话框中点击确认,如图 13-42 所示。

A454123_1_En_13_Fig42_HTML.jpg

图 13-42。

Confirmation dialog

点击返回托管区域,如图 13-43 所示。

A454123_1_En_13_Fig43_HTML.jpg

图 13-43。

Going back to the hosted zones

选择要删除的托管区域,点击删除托管区域,如图 13-44 所示。

A454123_1_En_13_Fig44_HTML.jpg

图 13-44。

Deleting a hosted zone

在确认对话框中点击确认,如图 13-45 所示。

A454123_1_En_13_Fig45_HTML.jpg

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

A454123_1_En_14_Fig1_HTML.jpg

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

A454123_1_En_14_Fig2_HTML.jpg

图 14-2。

Creating a new role

指定一个角色名称(dockercloud-swarm-role),如图 14-3 ,点击下一步。

A454123_1_En_14_Fig3_HTML.jpg

图 14-3。

Specifying a role name

显示选择角色类型页面,如图 14-4 所示。由于我们链接了两个服务——Docker Cloud 和 Docker for AWS——我们不需要选择 AWS 服务角色。

A454123_1_En_14_Fig4_HTML.jpg

图 14-4。

Select the role type

选择跨账户访问的角色,如图 14-5 所示,并使用选择按钮选择名为“在您的 AWS 账户和第三方 AWS 账户之间提供访问”的子选项。

A454123_1_En_14_Fig5_HTML.jpg

图 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 复选框未选中。点击下一步。

A454123_1_En_14_Fig6_HTML.jpg

图 14-6。

Specifying account and external IDs

由于我们正在嵌入自定义策略,因此不要从“Attach Policy”中列出的任何策略中进行选择。点击下一步,如图 14-7 所示。

A454123_1_En_14_Fig7_HTML.jpg

图 14-7。

Do not select a policy

在审核页面,点击创建角色,如图 14-8 所示。

A454123_1_En_14_Fig8_HTML.jpg

图 14-8。

Creating a role

一个名为dockercloud-swarm-role的新 AWS IAM 角色被创建,如图 14-9 所示。单击 dockercloud-swarm-role 角色名称。

A454123_1_En_14_Fig9_HTML.jpg

图 14-9。

New role

接下来,我们将添加一个嵌入式(也称为内联)策略。默认情况下,应选择“权限”选项卡。点击 v 图标,展开内嵌策略部分,如图 14-10 所示。

A454123_1_En_14_Fig10_HTML.jpg

图 14-10。

Expanding the inline policies

首先,没有列出内联策略。点击点击此处链接,添加一个内嵌策略,如图 14-11 所示。

A454123_1_En_14_Fig11_HTML.jpg

图 14-11。

Click on the Click Here link to add an inline policy

在设置权限界面,使用选择按钮选择自定义策略,如图 14-12 所示。

A454123_1_En_14_Fig12_HTML.jpg

图 14-12。

Selecting a custom policy

策略文档列出了一些权限,IAM 角色使用 Docker for AWS 的策略文档可从 https://docs.docker.com/docker-for-aws/iam-permissions/ 获得。点击【验证策略】对策略进行验证,如图 14-13 所示。

A454123_1_En_14_Fig13_HTML.jpg

图 14-13。

Validating the policy

点击应用策略,如图 14-14 所示。

A454123_1_En_14_Fig14_HTML.jpg

图 14-14。

Applying the policy

dockercloud-swarm-role角色添加了一个新的内联策略,如图 14-15 所示。

A454123_1_En_14_Fig15_HTML.jpg

图 14-15。

The new inline policy is added

复制图 14-16 中列出的角色 ARN 字符串,因为我们需要 ARN 字符串从 Docker Cloud 连接到 AWS 云提供商。

A454123_1_En_14_Fig16_HTML.jpg

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

A454123_1_En_14_Fig17_HTML.jpg

图 14-17。

The Swarm Mode slider

点击 Swarm 模式滑块;应启用群组模式,如图 14-18 所示。

A454123_1_En_14_Fig18_HTML.jpg

图 14-18。

Switching to Swarm mode

增加一个群组工具栏选项,如图 14-19 所示。

A454123_1_En_14_Fig19_HTML.jpg

图 14-19。

Swarms toolbar option

有两种选择——带上你自己的蜂群或者创建一个新的蜂群。点击创建创建一个新的蜂群,如图 14-20 所示。

A454123_1_En_14_Fig20_HTML.jpg

图 14-20。

Creating a new Swarm

接下来,我们将配置群组,包括指定群组名称、选择云提供商和选择云提供商选项。支持两个云服务提供商:Amazon Web Services (AWS)和 Microsoft Azure(尚不可用)。我们在本章中使用 AWS。我们需要用之前复制的 ARN 字符串来配置 AWS 的云设置。云设置可以通过以下两个选项之一进行配置。一种选择是从账户中选择云设置,如图 14-21 所示。

A454123_1_En_14_Fig21_HTML.jpg

图 14-21。

Cloud settings

在云设置页面,点击表示亚马逊网络服务提供商 Connect Provider 的插件图标,如图 14-22 所示。

A454123_1_En_14_Fig22_HTML.jpg

图 14-22。

Connecting the provider

显示添加 AWS 凭证对话框,如图 14-23 所示。

A454123_1_En_14_Fig23_HTML.jpg

图 14-23。

Adding AWS credentials

配置云设置的另一个选项是单击 Amazon Web Service 服务提供商图标,如图 14-24 所示,这也会显示添加 AWS 凭证对话框。

A454123_1_En_14_Fig24_HTML.jpg

图 14-24。

Connecting to an Amazon web services provider

指定之前从添加 AWS 凭证对话框中复制的 ARN 字符串并点击保存,如图 14-25 所示。

A454123_1_En_14_Fig25_HTML.jpg

图 14-25。

Saving the AWS credentials

无论选择哪种方式,服务提供商 Amazon Web Services 都应该连接,如连接提供商图标变为已连接所示,如图 14-26 所示。

A454123_1_En_14_Fig26_HTML.jpg

图 14-26。

Amazon Web Services provider in connected mode

亚马逊网络服务选项应该显示connected,如图 14-27 所示。

A454123_1_En_14_Fig27_HTML.jpg

图 14-27。

Amazon Web Services provider connected

指定群组名称。该名称不得包含除“,”、,-_之外的任何空格、大写字母或特殊字符,如图 14-28 所示。

A454123_1_En_14_Fig28_HTML.jpg

图 14-28。

Specifying a Swarm name

指定一个有效的群名称(docker-cloud-swarm),选择已经连接好的 Amazon Web Services 服务提供商,点击创建,如图 14-29 所示。

A454123_1_En_14_Fig29_HTML.jpg

图 14-29。

Creating a Docker Swarm using the AWS service provider

在区域中,选择一个区域(us-east-2)、群管理器数量(3)、群工作器数量(5)、群管理器实例类型(t2.micro)、代理工作器实例类型(t2.micro)和 SSH 密钥。点击创建,如图 14-30 所示。

A454123_1_En_14_Fig30_HTML.jpg

图 14-30。

Configuring and creating a Swarm

蜂群应该开始部署,如图 14-31 中的DEPLOYING消息所示。

A454123_1_En_14_Fig31_HTML.jpg

图 14-31。

Deploying a Swarm

当蜂群部署完毕后,消息变成Deployed,如图 14-32 所示。

A454123_1_En_14_Fig32_HTML.jpg

图 14-32。

The Swarm is now deployed

为 Swarm 创建和配置 AWS 基础设施。生成一个云状栈,如图 14-33 所示。

A454123_1_En_14_Fig33_HTML.jpg

图 14-33。

CloudFormation stack for the created Swarm

为群组添加了一个新的代理 AWS IAM 角色,如图 14-34 所示。

A454123_1_En_14_Fig34_HTML.jpg

图 14-34。

Proxy role and Docker Cloud Swarm AWS role

启动群管理器和工作节点的 EC2 实例。每个 EC2 实例都以自动创建的代理 IAM 角色启动,如图 14-35 中的管理节点所示。

A454123_1_En_14_Fig35_HTML.jpg

图 14-35。

IAM role for EC2 instances

每个 Docker 云帐户名称空间必须只与一个 AWS IAM 角色相关联。如果多个 Docker 云帐户要访问同一个 AWS 帐户,则必须为每个 Docker 云帐户或 Docker 云帐户名称空间创建多个角色。Docker Cloud 访问 AWS 的每个 AWS IAM 角色都与一个 ARN 字符串相关联。可以使用编辑端点链接编辑已部署群组的 ARN 字符串,如图 14-36 所示。

A454123_1_En_14_Fig36_HTML.jpg

图 14-36。

Edit Endpoint link

如果要修改群组端点,请在“编辑端点”对话框中指定新的 ARN 字符串(用于与不同 Docker 云名称空间关联的不同 IAM 角色)。点击保存,如图 14-37 所示。

A454123_1_En_14_Fig37_HTML.jpg

图 14-37。

Editing the endpoint

接下来,我们连接到 Docker 群。有两种方法可以做到:

  • 从任何 Docker 主机直接连接
  • 从 EC2 仪表板获取群管理器的公共 IP 地址,并通过 SSH 登录群管理器

我们讨论这些选项中的每一个。

从 Docker 主机连接到 Docker 群组

点击 Docker 云仪表盘中的 Docker Swarm。应显示带有docker run命令的连接对话框,如图 14-38 所示。复制docker run命令。

A454123_1_En_14_Fig38_HTML.jpg

图 14-38。

Listing and copying the docker run command to connect to the Swarm

用预装 Docker 的 CoreOS AMI 启动一个 EC2 实例,如图 14-39 所示。

A454123_1_En_14_Fig39_HTML.jpg

图 14-39。

Creating an EC2 instance with CoreOS AMI

从 EC2 控制台获取 CoreOS 实例的公共 IP 地址,如图 14-40 所示。

A454123_1_En_14_Fig40_HTML.jpg

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

A454123_1_En_14_Fig41_HTML.jpg

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

A454123_1_En_14_Fig42_HTML.jpg

图 14-42。

Bring your own Swarm

向 Docker Cloud 注册的群被添加到 Docker Cloud 群中,如图 14-43 所示。

A454123_1_En_14_Fig43_HTML.jpg

图 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 中是独立的)可以通过使用linksdepends_on选项来声明微服务之间的依赖关系,但是 Compose(独立的)是过时的,不同于在群模式服务的上下文中定义服务的格式。

解决方案

Docker Swarm mode 引入了服务栈来定义服务的集合(Swarm mode services ),这些服务彼此自动链接,以提供服务之间存在依赖关系的逻辑分组。栈使用的栈文件是 YAML 文件,其格式非常类似于docker-compose.yml格式。有一些不同之处,比如缺少了用于定义 Docker Compose (standalone)中微服务之间依赖关系的linksdepends_on选项。YAML ( http://www.yaml.org/ )是配置文件常用的一种数据序列化格式。

从 Docker v1.13 开始,已经引入了命令的docker stack子集来创建 Docker 栈。使用一个定义多个服务的栈文件,包括服务的配置,如环境变量、标签、容器数量和卷,一个docker stack deploy命令创建一个服务栈,如图 15-1 所示。这些服务会自动相互链接。

A454123_1_En_15_Fig1_HTML.gif

图 15-1。

Service stack created with the docker stack deploy command

Docker Compose v3.x 及更高版本完全兼容 Docker Swarm 模式,这意味着 Docker Compose v3.x docker-compose.yml文件可以用作栈文件,但栈文件中不支持的几个子选项(包括buildcontainer_nameexternal_linkslinks)除外。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

要使用栈,请使用以下过程。

  1. 安装 Docker 版本 1.13 或更高版本(不是 Docker 版本 1.12,它在前面的几章中使用)。
  2. 启用 Swarm 模式。
  3. 使用 Docker Compose(3 . x 版)YAML 格式创建一个栈文件。
  4. 使用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 集群。

A454123_1_En_15_Fig2_HTML.gif

图 15-2。

Deploying the Docker Community Edition for AWS (stable)

如第三章所述,使用创建栈向导配置一个群组。您可以指定群组管理器的数量为 1、3 或 5,群组工作节点的数量为 1-1000。我们使用了一个群管理器节点和两个群工作者节点,如图 15-3 所示。

A454123_1_En_15_Fig3_HTML.jpg

图 15-3。

Configuring a CloudFormation stack

云形成栈被创建,如图 15-4 所示。

A454123_1_En_15_Fig4_HTML.jpg

图 15-4。

CloudFormation Stack for Docker on AWS

启动三个 EC2 实例——一个用于 Docker Swarm manager 节点,两个用于 Swarm worker 节点,如图 15-5 所示。CloudFormation 栈使用的 Linux 发行版是莫比 Linux,如图 15-5 所示。

A454123_1_En_15_Fig5_HTML.jpg

图 15-5。

The Moby Linux AMI used for Docker on AWS

在能够在 AWS 上使用 Docker 之前,在 EC2 实例使用的安全组中启用 EC2 实例之间的所有入站/出站流量。在图 15-6 中显示了群管理器节点实例入站规则的安全组。

A454123_1_En_15_Fig6_HTML.jpg

图 15-6。

The security group inbound rules are enabled for all traffic

SSH 登录 Swarm manager EC2 实例,从 AWS 管理控制台获取公共 IP 地址,如图 15-7 所示。

A454123_1_En_15_Fig7_HTML.jpg

图 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 映像wordpressmysql指定两个服务(webmysql)。为 Docker 映像设置环境变量。唯一需要设置的环境变量是mysql Docker 映像的MYSQL_ROOT_PASSWORDwordpress 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` | 表格前缀。 | `“”` |

如果我们要用wordpressmysql图片和docker run命令创建一个 WordPress 博客,我们将为每个 Docker 图片分别创建 Docker 容器,并使用–link选项链接这些容器。如果我们要使用 Docker Compose(独立的),我们需要在 Docker Compose 文件中添加一个linksdepends_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:80ports映射将 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_mysqlmysql_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_mysqlmysql_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 所示。

A454123_1_En_15_Fig8_HTML.jpg

图 15-8。

Public DNS of Swarm manager

在浏览器中打开<public dns>:8080 URL。显示<public dns>:8080/wp-admin/install.php URL 来开始 WordPress 的安装。选择继续。指定副标题、用户名、密码、电子邮件以及是否阻止搜索引擎对网站进行索引。然后点击安装 WordPress,如图 15-9 所示。

A454123_1_En_15_Fig9_HTML.jpg

图 15-9。

Installing WordPress

安装好 WordPress,如图 15-10 所示。点击登录。

A454123_1_En_15_Fig10_HTML.jpg

图 15-10。

WordPress is installed

指定用户名和密码,点击登录,如图 15-11 所示。

A454123_1_En_15_Fig11_HTML.jpg

图 15-11。

Logging in

显示 WordPress 博客仪表盘,如图 15-12 所示。

A454123_1_En_15_Fig12_HTML.jpg

图 15-12。

The WordPress dashboard

要添加新帖子,请选择帖子并点击“添加新”,如图 15-13 所示。

A454123_1_En_15_Fig13_HTML.jpg

图 15-13。

Adding a new post

在“添加新文章”对话框中,指定标题并添加博客条目。点击发布,如图 15-14 所示。

A454123_1_En_15_Fig14_HTML.jpg

图 15-14。

Publishing a new post

新帖子已添加。点击查看帖子,如图 15-15 所示,显示帖子。

A454123_1_En_15_Fig15_HTML.jpg

图 15-15。

Viewing the new post

显示博文,如图 15-16 所示。

A454123_1_En_15_Fig16_HTML.jpg

图 15-16。

Displaying a blog post

向下滚动并添加注释,如图 15-17 所示。

A454123_1_En_15_Fig17_HTML.jpg

图 15-17。

Adding a comment

添加注释,如图 15-18 所示。

A454123_1_En_15_Fig18_HTML.jpg

图 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 原生应用。

posted @ 2024-08-12 11:18  绝不原创的飞龙  阅读(1)  评论(0编辑  收藏  举报