向 swarm 部署服务

Swarm 服务使用声明式模型,这意味着您定义服务的期望状态,并依赖 Docker 维护此状态。状态信息包括(但不限于):

  • 服务容器应运行的镜像名称和标签
  • 参与服务的容器数量
  • 是否向 Swarm 外部的客户端公开任何端口
  • 服务是否应在 Docker 启动时自动启动
  • 服务重启时发生的具体行为(例如是否使用滚动重启)
  • 可以运行服务的节点的特性(例如资源约束和放置偏好)

有关 Swarm 模式的概述,请参阅Swarm 模式关键概念。有关服务工作原理的概述,请参阅服务如何工作

创建服务

要创建一个没有任何额外配置的单副本服务,您只需提供镜像名称。此命令会启动一个 Nginx 服务,其名称随机生成且没有发布端口。这是一个简单的例子,因为您无法与 Nginx 服务交互。

$ docker service create nginx

该服务被调度到一个可用的节点上。要确认服务已成功创建并启动,请使用 docker service ls 命令。

$ docker service ls

ID                  NAME                MODE                REPLICAS            IMAGE                                                                                             PORTS
a3iixnklxuem        quizzical_lamarr    replicated          1/1                 docker.io/library/nginx@sha256:41ad9967ea448d7c2b203c699b429abe1ed5af331cd92533900c6d77490e0268

创建的服务并不总是立即运行。如果镜像不可用、没有节点满足您为服务配置的要求,或者由于其他原因,服务可能会处于待定状态。有关更多信息,请参阅待定服务

要为您的服务提供一个名称,请使用 --name 标志:

$ docker service create --name my_web nginx

就像独立容器一样,您可以在镜像名称后添加要运行的命令来指定服务容器应运行的命令。此示例启动一个名为 helloworld 的服务,它使用一个 alpine 镜像并运行 ping docker.com 命令:

$ docker service create --name helloworld alpine ping docker.com

您还可以为服务指定一个要使用的镜像标签。此示例修改了上一个示例,使用 alpine:3.6 标签:

$ docker service create --name helloworld alpine:3.6 ping docker.com

有关镜像标签解析的更多详细信息,请参阅指定服务应使用的镜像版本

用于 Swarm 的 gMSA

注意

此示例仅适用于 Windows 容器。

Swarm 现在允许使用 Docker 配置作为 gMSA 凭证规范——这是 Active Directory 认证应用程序的一个要求。这减轻了将凭证规范分发到使用它们的节点上的负担。

以下示例假定一个 gMSA 及其凭证规范(名为 credspec.json)已经存在,并且将要部署到的节点已为该 gMSA 正确配置。

要使用配置作为凭证规范,首先创建包含凭证规范的 Docker 配置:

$ docker config create credspec credspec.json

现在,您应该有一个名为 credspec 的 Docker 配置,并且您可以使用此凭证规范创建服务。为此,请将 --credential-spec 标志与配置名称一起使用,如下所示:

$ docker service create --credential-spec="config://credspec" <your image>

您的服务在启动时使用 gMSA 凭证规范,但与典型的 Docker 配置(通过传递 --config 标志使用)不同,凭证规范不会挂载到容器中。

使用私有仓库中的镜像创建服务

如果您的镜像在需要登录的私有仓库中可用,请在登录后将 --with-registry-auth 标志与 docker service create 一起使用。如果您的镜像存储在 registry.example.com(这是一个私有仓库)上,请使用类似以下的命令:

$ docker login registry.example.com

$ docker service  create \
  --with-registry-auth \
  --name my_service \
  registry.example.com/acme/my_image:latest

这会将登录令牌从您的本地客户端通过加密的 WAL 日志传递到部署服务的 swarm 节点。有了这些信息,节点就能够登录到仓库并拉取镜像。

为托管服务账户提供凭证规范

在企业版 3.0 中,通过使用 Docker 配置功能集中分发和管理组托管服务帐户(gMSA)凭证,安全性得到了提高。Swarm 现在允许使用 Docker 配置作为 gMSA 凭证规范,这减轻了将凭证规范分发到使用它们的节点上的负担。

注意

此选项仅适用于使用 Windows 容器的服务。

凭证规范文件在运行时应用,无需基于主机的凭证规范文件或注册表项——没有 gMSA 凭证被写入工作节点上的磁盘。您可以在容器启动之前,让运行 swarm kit 工作节点的 Docker Engine 可用凭证规范。当使用基于 gMSA 的配置部署服务时,凭证规范将直接传递给该服务中容器的运行时。

--credential-spec 必须是以下格式之一:

  • file://<filename>:引用的文件必须存在于 Docker 数据目录下的 CredentialSpecs 子目录中,在 Windows 上默认为 C:\ProgramData\Docker\。例如,指定 file://spec.json 会加载 C:\ProgramData\Docker\CredentialSpecs\spec.json
  • registry://<value-name>:凭证规范从守护程序主机上的 Windows 注册表中读取。
  • config://<config-name>:配置名称在 CLI 中会自动转换为配置 ID。将使用指定的 config 中包含的凭证规范。

以下简单示例从您的 Active Directory (AD) 实例中检索 gMSA 名称和 JSON 内容:

$ name="mygmsa"
$ contents="{...}"
$ echo $contents > contents.json

确保您要部署到的节点已为 gMSA 正确配置。

要使用配置作为凭证规范,请在名为 credpspec.json 的凭证规范文件中创建一个 Docker 配置。您可以为 config 的名称指定任何名称。

$ docker config create --label com.docker.gmsa.name=mygmsa credspec credspec.json

现在您可以使用此凭证规范创建服务。使用配置名称指定 --credential-spec 标志:

$ docker service create --credential-spec="config://credspec" <your image>

您的服务在启动时使用 gMSA 凭证规范,但与典型的 Docker 配置(通过传递 --config 标志使用)不同,凭证规范不会挂载到容器中。

更新服务

您可以使用 docker service update 命令更改现有服务的几乎所有内容。当您更新服务时,Docker 会停止其容器并使用新配置重新启动它们。

由于 Nginx 是一个 Web 服务,如果您将端口 80 发布给 swarm 外部的客户端,它会工作得更好。您可以在创建服务时使用 -p--publish 标志指定这一点。更新现有服务时,该标志是 --publish-add。还有一个 --publish-rm 标志用于移除之前发布的端口。

假设上一节中的 my_web 服务仍然存在,请使用以下命令更新它以发布端口 80。

$ docker service update --publish-add 80 my_web

要验证它是否有效,请使用 docker service ls

$ docker service ls

ID                  NAME                MODE                REPLICAS            IMAGE                                                                                             PORTS
4nhxl7oxw5vz        my_web              replicated          1/1                 docker.io/library/nginx@sha256:41ad9967ea448d7c2b203c699b429abe1ed5af331cd92533900c6d77490e0268   *:0->80/tcp

有关发布端口如何工作的更多信息,请参阅发布端口

您可以更新现有服务的几乎所有配置细节,包括它运行的镜像名称和标签。请参阅创建后更新服务的镜像

移除服务

要删除一个服务,请使用 docker service remove 命令。您可以按 ID 或名称删除服务,如 docker service ls 命令的输出所示。以下命令删除 my_web 服务。

$ docker service remove my_web

服务配置详情

以下部分提供了有关服务配置的详细信息。本主题不涵盖每个标志或场景。在几乎所有您可以在服务创建时定义配置的情况下,您也可以以类似的方式更新现有服务的配置。

请参阅 docker service createdocker service update 的命令行参考,或运行其中一个带有 --help 标志的命令。

配置运行时环境

您可以为容器中的运行时环境配置以下选项:

  • 使用 --env 标志设置环境变量
  • 使用 --workdir 标志设置容器内的工作目录
  • 使用 --user 标志设置用户名或 UID

以下服务的容器设置了环境变量 $MYVARmyvalue,从 /tmp/ 目录运行,并以 my_user 用户身份运行。

$ docker service create --name helloworld \
  --env MYVAR=myvalue \
  --workdir /tmp \
  --user my_user \
  alpine ping docker.com

更新现有服务运行的命令

要更新现有服务运行的命令,您可以使用 --args 标志。以下示例更新一个名为 helloworld 的现有服务,使其运行 ping docker.com 命令,而不是之前运行的任何命令:

$ docker service update --args "ping docker.com" helloworld

指定服务应使用的镜像版本

当您创建一个服务而未指定有关要使用的镜像版本的任何详细信息时,该服务将使用标记为 latest 标签的版本。您可以根据您期望的结果,通过几种不同的方式强制服务使用特定版本的镜像。

镜像版本可以通过多种方式表示:

  • 如果您指定一个标签,管理器(或者如果您使用内容信任,则是 Docker 客户端)会将该标签解析为一个摘要(digest)。当创建容器任务的请求在工作节点上接收时,工作节点只看到摘要,而不是标签。

    $ docker service create --name="myservice" ubuntu:16.04
    

    一些标签代表离散的发布版本,例如 ubuntu:16.04。这类标签随着时间的推移几乎总是解析为稳定的摘要。建议在可能的情况下使用这种类型的标签。

    其他类型的标签,如 latestnightly,可能会经常解析为新的摘要,这取决于镜像作者更新标签的频率。不建议使用更新频繁的标签来运行服务,以防止不同的服务副本任务使用不同的镜像版本。

  • 如果您根本不指定版本,按照惯例,镜像的 latest 标签将被解析为一个摘要。工作节点在创建服务任务时使用该摘要处的镜像。

    因此,以下两个命令是等效的:

    $ docker service create --name="myservice" ubuntu
    
    $ docker service create --name="myservice" ubuntu:latest
    
  • 如果您直接指定一个摘要,那么在创建服务任务时总是使用该镜像的确切版本。

    $ docker service create \
        --name="myservice" \
        ubuntu:16.04@sha256:35bc48a1ca97c3971611dc4662d08d131869daa692acb281c7e9e052924e38b1
    

当您创建一个服务时,镜像的标签会在**服务创建时**被解析为该标签指向的特定摘要。该服务的 Worker 节点将永远使用该特定摘要,除非服务被明确更新。如果您确实使用了像 latest 这样经常变化的标签,这个功能就特别重要,因为它确保了所有服务任务都使用相同版本的镜像。

注意

如果启用了内容信任,客户端在联系 swarm 管理器之前实际上会将镜像的标签解析为摘要,以验证镜像是否已签名。因此,如果您使用内容信任,swarm 管理器接收到的是预先解析的请求。在这种情况下,如果客户端无法将镜像解析为摘要,请求将失败。

如果管理器无法将标签解析为摘要,每个工作节点将负责将标签解析为摘要,不同的节点可能会使用不同版本的镜像。如果发生这种情况,将记录类似以下的警告,并将占位符替换为真实信息。

unable to pin image <IMAGE-NAME> to digest: <REASON>

要查看镜像当前的摘要,请执行命令 docker inspect <IMAGE>:<TAG> 并查找 RepoDigests 行。以下是编写此内容时 ubuntu:latest 的当前摘要。为清晰起见,输出已被截断。

$ docker inspect ubuntu:latest
"RepoDigests": [
    "ubuntu@sha256:35bc48a1ca97c3971611dc4662d08d131869daa692acb281c7e9e052924e38b1"
],

在您创建一个服务之后,它的镜像永远不会被更新,除非您明确地使用 --image 标志运行 docker service update,如下所述。其他更新操作,如扩展服务、添加或删除网络或卷、重命名服务,或任何其他类型的更新操作,都不会更新服务的镜像。

创建后更新服务的镜像

每个标签代表一个摘要,类似于 Git 哈希。一些标签,如 latest,会经常更新以指向新的摘要。其他的,如 ubuntu:16.04,代表一个已发布的软件版本,预计不会或者很少会更新指向新的摘要。当您创建一个服务时,它被限制为使用特定摘要的镜像创建任务,直到您使用 service update--image 标志更新服务。

当您使用 --image 标志运行 service update 时,swarm 管理器会查询 Docker Hub 或您的私有 Docker 仓库,获取标签当前指向的摘要,并更新服务任务以使用该摘要。

注意

如果您使用内容信任,Docker 客户端会解析镜像,而 swarm 管理器会收到镜像和摘要,而不是标签。

通常,管理器可以将标签解析为新的摘要,服务会更新,重新部署每个任务以使用新的镜像。如果管理器无法解析标签或出现其他问题,接下来的两节将概述预期的情况。

如果管理器解析标签

如果 swarm 管理器能够将镜像标签解析为一个摘要,它会指示工作节点重新部署任务并使用该摘要处的镜像。

  • 如果一个工作节点缓存了该摘要处的镜像,它将使用该镜像。

  • 否则,它会尝试从 Docker Hub 或私有仓库拉取该镜像。

    • 如果成功,任务将使用新镜像进行部署。

    • 如果工作节点拉取镜像失败,该服务在该工作节点上的部署将失败。Docker 会再次尝试部署该任务,可能会在不同的工作节点上进行。

如果管理器无法解析标签

如果 swarm 管理器无法将镜像解析为摘要,并非一切都完了:

  • 管理器指示工作节点使用该标签的镜像重新部署任务。

  • 如果工作节点有本地缓存的解析为该标签的镜像,它将使用该镜像。

  • 如果工作节点没有本地缓存的解析为该标签的镜像,该工作节点会尝试连接到 Docker Hub 或私有仓库,以拉取该标签的镜像。

    • 如果成功,工作节点将使用该镜像。

    • 如果失败,任务部署失败,管理器会再次尝试部署该任务,可能会在不同的工作节点上进行。

发布端口

当您创建一个 swarm 服务时,您可以通过两种方式将该服务的端口发布到 swarm 外部的主机:

  • 您可以依赖路由网格。当您发布一个服务端口时,swarm 会在每个节点的目标端口上使服务可访问,无论该节点上是否有该服务的任务在运行。这比较简单,对于许多类型的服务来说是正确的选择。

  • 您可以直接在运行该服务的 swarm 节点上发布服务任务的端口。这会绕过路由网格,并提供最大的灵活性,包括让您能够开发自己的路由框架。然而,您需要负责跟踪每个任务的运行位置,并将请求路由到这些任务,以及在节点之间进行负载均衡。

继续阅读以获取有关这两种方法的更多信息和用例。

使用路由网格发布服务端口

要将服务的端口发布到 swarm 外部,请使用 --publish <PUBLISHED-PORT>:<SERVICE-PORT> 标志。swarm 会在每个 swarm 节点的发布端口上使服务可访问。如果外部主机连接到任何 swarm 节点上的该端口,路由网格会将其路由到一个任务。外部主机不需要知道服务任务的 IP 地址或内部使用的端口就可以与服务交互。当用户或进程连接到一个服务时,任何运行服务任务的工作节点都可能响应。有关 swarm 服务网络的更多详细信息,请参阅管理 swarm 服务网络

示例:在 10 节点的 swarm 上运行一个三任务的 Nginx 服务

假设您有一个 10 节点的 swarm,并且您部署了一个在 10 节点 swarm 上运行三个任务的 Nginx 服务:

$ docker service create --name my_web \
                        --replicas 3 \
                        --publish published=8080,target=80 \
                        nginx

三个任务最多在三个节点上运行。您不需要知道哪些节点正在运行这些任务;连接到 10 个节点中任何一个的端口 8080 都会将您连接到三个 nginx 任务之一。您可以使用 curl 来测试这一点。以下示例假设 localhost 是 swarm 节点之一。如果不是这种情况,或者 localhost 在您的主机上无法解析为 IP 地址,请替换为主机的 IP 地址或可解析的主机名。

HTML 输出被截断:

$ curl localhost:8080

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...truncated...
</html>

后续连接可能会被路由到同一个 swarm 节点或不同的节点。

直接在 Swarm 节点上发布服务端口

如果您需要根据应用程序状态做出路由决策,或者需要完全控制将请求路由到服务任务的流程,使用路由网格可能不是您应用程序的正确选择。要直接在运行它的节点上发布服务的端口,请使用 --publish 标志的 mode=host 选项。

注意

如果您使用 mode=host 直接在 swarm 节点上发布服务端口,并且还设置了 published=<PORT>,这会产生一个隐含的限制,即您在给定的 swarm 节点上只能为该服务运行一个任务。您可以通过指定不带端口定义的 published 来解决这个问题,这会导致 Docker 为每个任务分配一个随机端口。

此外,如果您使用 mode=host 并且不在 docker service create 上使用 --mode=global 标志,那么很难知道哪些节点正在运行该服务,以便将工作路由给它们。

示例:在每个 swarm 节点上运行一个 nginx Web 服务器服务

nginx 是一个开源的反向代理、负载均衡器、HTTP 缓存和 Web 服务器。如果您使用路由网格将 nginx 作为服务运行,连接到任何 swarm 节点上的 nginx 端口都会向您显示(实际上是)运行该服务的随机 swarm 节点的网页。

以下示例将 nginx 作为服务在您的 swarm 中的每个节点上运行,并在每个 swarm 节点上本地公开 nginx 端口。

$ docker service create \
  --mode global \
  --publish mode=host,target=80,published=8080 \
  --name=nginx \
  nginx:latest

您可以在每个 swarm 节点的 8080 端口访问 nginx 服务器。如果您向 swarm 添加一个节点,将在该节点上启动一个 nginx 任务。您不能在任何 swarm 节点上启动另一个绑定到 8080 端口的服务或容器。

注意

这纯粹是一个说明性示例。为多层服务创建应用层路由框架是复杂的,超出了本主题的范围。

将服务连接到覆盖网络

您可以使用覆盖网络连接 swarm 内的一个或多个服务。

首先,在管理器节点上使用 docker network create 命令和 --driver overlay 标志创建一个覆盖网络。

$ docker network create --driver overlay my-network

在 swarm 模式下创建覆盖网络后,所有管理器节点都可以访问该网络。

您可以创建一个新服务,并传递 --network 标志将该服务附加到覆盖网络:

$ docker service create \
  --replicas 3 \
  --network my-network \
  --name my-web \
  nginx

swarm 将 my-network 扩展到运行该服务的每个节点。

您还可以使用 --network-add 标志将现有服务连接到覆盖网络。

$ docker service update --network-add my-network my-web

要将正在运行的服务从网络中断开,请使用 --network-rm 标志。

$ docker service update --network-rm my-network my-web

有关覆盖网络和服务发现的更多信息,请参阅将服务附加到覆盖网络Docker swarm 模式覆盖网络安全模型

授予服务访问密钥的权限

要创建一个有权访问 Docker 管理的密钥的服务,请使用 --secret 标志。更多信息,请参阅为 Docker 服务管理敏感字符串(密钥)

自定义服务的隔离模式

重要

此设置仅适用于 Windows 主机,对于 Linux 主机将被忽略。

Docker 允许您指定 swarm 服务的隔离模式。隔离模式可以是以下之一:

  • default:使用为 Docker 主机配置的默认隔离模式,该模式由 -exec-opt 标志或 daemon.json 中的 exec-opts 数组配置。如果守护进程未指定隔离技术,则对于 Windows Server,默认为 process,对于 Windows 10,默认为 hyperv(也是唯一的选择)。

  • process:将服务任务作为主机上的独立进程运行。

    注意

    process 隔离模式仅在 Windows Server 上受支持。Windows 10 仅支持 hyperv 隔离模式。

  • hyperv:将服务任务作为隔离的 hyperv 任务运行。这会增加开销,但提供更多的隔离。

您可以在创建或更新新服务时使用 --isolation 标志来指定隔离模式。

控制服务放置

Swarm 服务提供了几种不同的方法来控制服务在不同节点上的规模和放置。

  • 您可以指定服务是需要运行特定数量的副本,还是应该在每个工作节点上全局运行。请参阅复制服务或全局服务

  • 您可以配置服务的CPU 或内存需求,服务将只在能够满足这些需求的节点上运行。

  • 放置约束允许您配置服务只在设置了特定(任意)元数据的节点上运行,如果不存在合适的节点,则部署会失败。例如,您可以指定您的服务只应在任意标签 pci_compliant 设置为 true 的节点上运行。

  • 放置偏好允许您为每个节点应用一个具有一系列值的任意标签,并使用一种算法在这些节点之间分散您的服务任务。目前,唯一支持的算法是 spread,它会尝试将它们均匀放置。例如,如果您为每个节点标记一个值为 1-10 的标签 rack,然后指定一个基于 rack 的放置偏好,那么服务任务将在所有带有 rack 标签的节点上尽可能均匀地放置,同时考虑其他放置约束、放置偏好和其他特定于节点的限制。

    与约束不同,放置偏好是尽力而为的,如果没有任何节点能够满足偏好,服务也不会部署失败。如果您为服务指定了放置偏好,那么在 swarm 管理器决定哪些节点应该运行服务任务时,匹配该偏好的节点将被赋予更高的排名。其他因素,例如服务的高可用性,也会影响哪些节点被调度运行服务任务。例如,如果您有 N 个带有 rack 标签的节点(以及一些其他节点),并且您的服务被配置为运行 N+1 个副本,那么如果有可用的节点,+1 将被调度到一个尚未运行该服务的节点上,无论该节点是否具有 rack 标签。

复制服务或全局服务

Swarm 模式有两种类型的服务:复制服务和全局服务。对于复制服务,您需要指定 swarm 管理器要调度到可用节点上的副本任务数量。对于全局服务,调度器会在每个满足服务放置约束资源需求的可用节点上放置一个任务。

您可以使用 --mode 标志来控制服务的类型。如果您不指定模式,服务默认为 replicated。对于复制服务,您可以使用 --replicas 标志指定要启动的副本任务数量。例如,要启动一个有 3 个副本任务的复制 nginx 服务:

$ docker service create \
  --name my_web \
  --replicas 3 \
  nginx

要在每个可用节点上启动一个全局服务,请将 --mode global 传递给 docker service create。每当一个新节点变为可用时,调度器就会在新节点上放置一个全局服务的任务。例如,要启动一个在 swarm 中每个节点上运行 alpine 的服务:

$ docker service create \
  --name myservice \
  --mode global \
  alpine top

服务约束允许您设置节点在调度器将服务部署到该节点之前必须满足的标准。您可以根据节点属性和元数据或引擎元数据对服务应用约束。有关约束的更多信息,请参阅 docker service create CLI 参考

为服务预留内存或 CPU

要为服务预留一定数量的内存或 CPU,请使用 --reserve-memory--reserve-cpu 标志。如果没有可用的节点能够满足要求(例如,如果您请求 4 个 CPU,而 swarm 中没有节点有 4 个 CPU),服务将保持在待定状态,直到有合适的节点可以运行其任务。

内存不足异常 (OOME)

如果您的服务试图使用超过 swarm 节点可用内存的内存,您可能会遇到内存不足异常(OOME),并且容器或 Docker 守护进程可能会被内核 OOM 杀手杀死。为防止这种情况发生,请确保您的应用程序在具有足够内存的主机上运行,并参阅了解内存耗尽的风险

Swarm 服务允许您使用资源约束、放置偏好和标签,以确保您的服务部署到适当的 swarm 节点上。

放置约束

使用放置约束来控制服务可以分配到的节点。在以下示例中,服务只会在标签 region 设置为 east 的节点上运行。如果没有带适当标签的节点可用,任务将保持在 Pending 状态,直到有可用节点。--constraint 标志使用相等运算符(==!=)。对于复制服务,所有服务可能运行在同一个节点上,或者每个节点只运行一个副本,或者某些节点不运行任何副本。对于全局服务,该服务将在满足放置约束和任何资源需求的每个节点上运行。

$ docker service create \
  --name my-nginx \
  --replicas 5 \
  --constraint node.labels.region==east \
  nginx

您还可以在 compose.yaml 文件中使用服务级别的 `constraint` 键。

如果您指定多个放置约束,服务将只部署在同时满足所有这些约束的节点上。以下示例将服务限制在所有 region 设置为 easttype 未设置为 devel 的节点上运行:

$ docker service create \
  --name my-nginx \
  --mode global \
  --constraint node.labels.region==east \
  --constraint node.labels.type!=devel \
  nginx

您还可以将放置约束与放置偏好和 CPU/内存约束结合使用。请注意不要使用无法满足的设置。

有关约束的更多信息,请参阅 docker service create CLI 参考

放置偏好

虽然放置约束限制了服务可以运行的节点,但*放置偏好*则尝试以算法方式(目前仅支持均匀分布)将任务放置在适当的节点上。例如,如果您为每个节点分配一个 `rack` 标签,您可以设置一个放置偏好,以根据 `rack` 标签的值在节点间均匀分布服务。这样,如果您丢失一个机架,服务仍然在其他机架的节点上运行。

放置偏好不是严格执行的。如果没有任何节点具有您在偏好中指定的标签,服务将像未设置偏好一样部署。

注意

对于全局服务,放置偏好将被忽略。

以下示例设置了一个偏好,根据 datacenter 标签的值来分散部署。如果一些节点有 datacenter=us-east,而其他节点有 datacenter=us-west,那么服务将尽可能均匀地部署在这两组节点上。

$ docker service create \
  --replicas 9 \
  --name redis_2 \
  --placement-pref 'spread=node.labels.datacenter' \
  redis:7.4.0
注意

缺少用于分布的标签的节点仍然会接收任务分配。作为一个群体,这些节点接收到的任务比例与由特定标签值标识的任何其他群体相等。在某种意义上,缺少标签与附加了空值标签是相同的。如果服务只应在具有用于分布偏好的标签的节点上运行,则应将偏好与约束结合使用。

您可以指定多个放置偏好,它们将按照遇到的顺序进行处理。以下示例设置了一个具有多个放置偏好的服务。任务首先分布在不同的数据中心,然后分布在机架上(由相应的标签指示):

$ docker service create \
  --replicas 9 \
  --name redis_2 \
  --placement-pref 'spread=node.labels.datacenter' \
  --placement-pref 'spread=node.labels.rack' \
  redis:7.4.0

您还可以将放置偏好与放置约束或 CPU/内存约束结合使用。请注意不要使用无法满足的设置。

此图说明了放置偏好如何工作:

How placement preferences work

使用 docker service update 更新服务时,--placement-pref-add 会在所有现有的放置偏好之后追加一个新的放置偏好。--placement-pref-rm 会移除一个与参数匹配的现有放置偏好。

配置服务的更新行为

当您创建一个服务时,您可以指定一个滚动更新行为,用于在您运行 docker service update 时 swarm 应如何应用对该服务的更改。您也可以在更新时将这些标志作为 docker service update 的参数来指定。

--update-delay 标志配置了更新服务任务或任务集之间的时间延迟。您可以将时间 T 描述为秒数 Ts、分钟数 Tm 或小时数 Th 的组合。因此 10m30s 表示 10 分 30 秒的延迟。

默认情况下,调度器一次更新一个任务。您可以传递 --update-parallelism 标志来配置调度器同时更新的最大服务任务数量。

当对单个任务的更新返回状态为 RUNNING 时,调度器通过继续到另一个任务来继续更新,直到所有任务都更新完毕。如果在更新期间任何时候有任务返回 FAILED,调度器会暂停更新。您可以使用 docker service createdocker service update--update-failure-action 标志来控制此行为。

在下面的示例服务中,调度器一次最多对 2 个副本应用更新。当更新后的任务返回 RUNNINGFAILED 时,调度器会等待 10 秒,然后停止下一个要更新的任务:

$ docker service create \
  --replicas 10 \
  --name my_web \
  --update-delay 10s \
  --update-parallelism 2 \
  --update-failure-action continue \
  alpine

--update-max-failure-ratio 标志控制在更新过程中可以失败的任务比例,超过该比例则整个更新被视为失败。例如,使用 --update-max-failure-ratio 0.1 --update-failure-action pause,在 10% 的正在更新的任务失败后,更新将暂停。

如果任务未能启动,或者在用 --update-monitor 标志指定的监控期内停止运行,则单个任务更新被视为失败。--update-monitor 的默认值为 30 秒,这意味着在启动后的前 30 秒内失败的任务会计入服务更新失败阈值,而之后的失败则不计入。

回滚到服务的先前版本

如果更新后的服务版本未能按预期工作,可以使用 docker service update--rollback 标志手动回滚到服务的先前版本。这将使服务恢复到最近一次 docker service update 命令之前的配置。

其他选项可以与 --rollback 结合使用;例如,--update-delay 0s,可以在任务之间无延迟地执行回滚:

$ docker service update \
  --rollback \
  --update-delay 0s
  my_web

您可以配置服务在服务更新部署失败时自动回滚。请参阅如果更新失败则自动回滚

手动回滚在服务器端处理,这使得手动发起的回滚能够遵循新的回滚参数。请注意,--rollback 不能与 docker service update 的其他标志一起使用。

如果更新失败则自动回滚

您可以配置一个服务,使得如果服务更新导致重新部署失败,该服务可以自动回滚到先前的配置。这有助于保护服务的可用性。您可以在服务创建或更新时设置以下一个或多个标志。如果您不设置值,将使用默认值。

标志默认值描述
--rollback-delay0秒回滚一个任务后等待多长时间再回滚下一个任务。值为 0 表示在第一个回滚的任务部署后立即回滚第二个任务。
--rollback-failure-action暂停当任务回滚失败时,是 `pause`(暂停)还是 `continue`(继续)尝试回滚其他任务。
--rollback-max-failure-ratio0在回滚期间容忍的失败率,指定为 0 到 1 之间的浮点数。例如,对于 5 个任务,失败率为 .2 将容忍一个任务回滚失败。值为 0 表示不容忍任何失败,而值为 1 表示容忍任意数量的失败。
--rollback-monitor5秒每个任务回滚后监控失败的持续时间。如果任务在此时间段结束前停止,则认为回滚失败。
--rollback-parallelism1并行回滚的最大任务数。默认情况下,一次回滚一个任务。值为 0 会导致所有任务并行回滚。

以下示例配置一个 redis 服务,以便在 docker service update 部署失败时自动回滚。可以并行回滚两个任务。任务在回滚后被监控 20 秒,以确保它们不会退出,并且容忍的最大失败率为 20%。--rollback-delay--rollback-failure-action 使用默认值。

$ docker service create --name=my_redis \
                        --replicas=5 \
                        --rollback-parallelism=2 \
                        --rollback-monitor=20s \
                        --rollback-max-failure-ratio=.2 \
                        redis:latest

为服务提供卷或绑定挂载的访问权限

为了获得最佳性能和可移植性,您应避免将重要数据直接写入容器的可写层。您应该改用数据卷或绑定挂载。此原则也适用于服务。

您可以为 swarm 中的服务创建两种类型的挂载:volume 挂载或 bind 挂载。无论您使用哪种类型的挂载,都可以在创建服务时使用 --mount 标志,或在更新现有服务时使用 --mount-add--mount-rm 标志进行配置。如果您不指定类型,默认为数据卷。

数据卷

数据卷是独立于容器存在的存储。在 swarm 服务下,数据卷的生命周期与在容器下相似。卷的生命周期超过任务和服务,因此它们的移除必须单独管理。卷可以在部署服务之前创建,或者如果它们在任务被调度到特定主机时不存在,则会根据服务上的卷规范自动创建。

要将现有数据卷与服务一起使用,请使用 --mount 标志:

$ docker service create \
  --mount src=<VOLUME-NAME>,dst=<CONTAINER-PATH> \
  --name myservice \
  <IMAGE>

如果名为 <VOLUME-NAME> 的卷在任务被调度到特定主机时不存在,则会创建一个。默认的卷驱动是 local。要使用不同的卷驱动并采用这种按需创建的模式,请使用 --mount 标志指定驱动及其选项:

$ docker service create \
  --mount type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>,volume-driver=<DRIVER>,volume-opt=<KEY0>=<VALUE0>,volume-opt=<KEY1>=<VALUE1>
  --name myservice \
  <IMAGE>

有关如何创建数据卷以及卷驱动程序使用的更多信息,请参阅使用卷

绑定挂载

绑定挂载是调度器部署任务容器的主机上的文件系统路径。Docker 将该路径挂载到容器中。在 swarm 初始化任务容器之前,文件系统路径必须存在。

以下示例显示了绑定挂载语法:

  • 挂载一个读写绑定:

    $ docker service create \
      --mount type=bind,src=<HOST-PATH>,dst=<CONTAINER-PATH> \
      --name myservice \
      <IMAGE>
    
  • 挂载一个只读绑定:

    $ docker service create \
      --mount type=bind,src=<HOST-PATH>,dst=<CONTAINER-PATH>,readonly \
      --name myservice \
      <IMAGE>
    
重要

绑定挂载可能很有用,但也可能导致问题。在大多数情况下,建议您设计您的应用程序,使其不需要从主机挂载路径。主要风险包括以下几点:

  • 如果您将主机路径绑定挂载到服务的容器中,该路径必须在每个 swarm 节点上都存在。Docker swarm 模式调度器可以在任何满足资源可用性要求并满足您指定的所有约束和放置偏好的机器上调度容器。

  • 如果正在运行的服务容器变得不健康或无法访问,Docker Swarm 模式调度器可能随时重新调度它们。

  • 主机绑定挂载是不可移植的。当您使用绑定挂载时,无法保证您的应用程序在开发环境中的运行方式与在生产环境中相同。

使用模板创建服务

您可以使用 Go 的 text/template 包提供的语法,为 service create 的某些标志使用模板。

支持以下标志:

  • --hostname
  • --mount
  • --env

Go 模板的有效占位符是:

占位符描述
.Service.ID服务 ID
.Service.Name服务名称
.Service.Labels服务标签
.Node.ID节点 ID
.Node.Hostname节点主机名
.Task.Name任务名称
.Task.Slot任务插槽

模板示例

此示例根据服务名称和容器运行节点的 ID 设置创建容器的模板:

$ docker service create --name hosttempl \
                        --hostname="{{.Node.ID}}-{{.Service.Name}}"\
                         busybox top

要查看使用模板的结果,请使用 docker service psdocker inspect 命令。

$ docker service ps va8ew30grofhjoychbr6iot8c

ID            NAME         IMAGE                                                                                   NODE          DESIRED STATE  CURRENT STATE               ERROR  PORTS
wo41w8hg8qan  hosttempl.1  busybox:latest@sha256:29f5d56d12684887bdfa50dcd29fc31eea4aaf4ad3bec43daf19026a7ce69912  2e7a8a9c4da2  Running        Running about a minute ago
$ docker inspect --format="{{.Config.Hostname}}" hosttempl.1.wo41w8hg8qanxwjwsg4kxpprj

了解更多

© . This site is unofficial and not affiliated with Kubernetes or Docker Inc.