Docker-管理设计模式-一-

62 阅读45分钟

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 服务。