将服务部署到 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 标志使用)不同的是,凭据规范不会挂载到容器中。

使用私有注册表中的镜像创建服务

如果您的镜像在需要登录的私有注册表上可用,请在登录后使用 docker service create 命令的 --with-registry-auth 标志。如果您的镜像存储在私有注册表 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 节点。有了这些信息,节点就能登录注册表并拉取镜像。

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

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

注意

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

凭据规范文件在运行时应用,无需基于宿主的凭据规范文件或注册表条目 - 没有 gMSA 凭据写入 worker 节点上的磁盘。您可以在容器启动前使凭据规范可用于运行 swarm kit worker 节点的 Docker Engine。使用基于 gMSA 的配置部署服务时,凭据规范将直接传递给该服务中容器的运行时。

--credential-spec 必须采用以下格式之一

  • file://<文件名>:引用的文件必须存在于 docker 数据目录(Windows 上默认为 C:\ProgramData\Docker\)中的 CredentialSpecs 子目录中。例如,指定 file://spec.json 会加载 C:\ProgramData\Docker\CredentialSpecs\spec.json
  • registry://<值名称>:凭据规范从 daemon 宿主上的 Windows 注册表读取。
  • config://<配置名称>:配置名称在 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 服务,如果向 Swarm 外部的客户端发布端口 80,效果会更好。您可以在创建服务时使用 -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 客户端)会将该标签解析为摘要。当工作节点收到创建容器任务的请求时,工作节点只能看到摘要,而不是标签。

    $ 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
    

当您创建服务时,镜像的标签会解析为该标签在 服务创建时 指向的特定摘要。该服务的工作节点将永久使用该特定摘要,除非服务被明确更新。如果您确实使用像 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,代表已发布的软件版本,通常不会(或根本不)更新以指向新的摘要。创建服务时,它被约束为使用镜像的特定摘要创建任务,直到您使用带有 --image 标志的 service update 命令更新服务。

当您使用 --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 上运行一个包含 3 个任务的 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 节点的网页。

以下示例在您的 Swarm 中的每个节点上运行 nginx 作为服务,并在每个 Swarm 节点上本地暴露 nginx 端口。

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

您可以通过每个 Swarm 节点的端口 8080 访问 nginx 服务器。如果您向 Swarm 添加节点,会在其上启动一个 nginx 任务。您不能在绑定到端口 8080 的任何 Swarm 节点上启动其他服务或容器。

注意

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

将服务连接到 Overlay 网络

您可以使用 Overlay 网络连接 Swarm 中的一个或多个服务。

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

$ docker network create --driver overlay my-network

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

您可以创建一个新服务并传递 --network 标志,将服务连接到 Overlay 网络

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

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

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

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

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

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

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

授予服务访问 secrets 的权限

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

自定义服务的隔离模式

重要

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

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

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

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

    注意

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

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

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

控制服务放置

Swarm 服务提供了几种不同的方式来控制服务在不同节点上的扩缩和放置。

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

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

  • 放置约束 允许您配置服务仅在设置了特定(任意)元数据的节点上运行,并在不存在合适的节点时导致部署失败。例如,您可以指定服务仅在设置了任意标签 pci_complianttrue 的节点上运行。

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

    与约束不同,放置偏好是尽力而为的,即使没有节点可以满足偏好,服务也不会部署失败。如果您为服务指定放置偏好,当 Swarm 管理器决定哪些节点应该运行服务任务时,匹配该偏好的节点会获得更高的优先级。其他因素,例如服务的高可用性,也会影响哪些节点被调度运行服务任务。例如,如果您有 N 个带有 rack 标签的节点(以及一些其他节点),并且您的服务配置为运行 N+1 个副本,如果存在尚未运行该服务的节点,则该 +1 副本会被调度到该节点上,无论该节点是否带有 rack 标签。

复制型服务或全局服务

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

您可以使用 --mode 标志控制服务类型。如果您未指定模式,服务默认为 replicated。对于副本服务,您可以使用 --replicas 标志指定要启动的副本任务数量。例如,要启动一个包含 3 个副本任务的 replicated 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

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

为服务预留内存或 CPU

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

内存不足异常 (OOME)

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

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 createCLI 参考

放置偏好

虽然 放置约束 限制了服务可以运行的节点,但 放置偏好 尝试以算法方式将任务放置到合适的节点上(当前仅支持均匀分散)。例如,如果您为每个节点分配一个 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

注意

缺少用于分散的标签的节点仍然会收到任务分配。作为一个组,这些节点接收任务的比例与其他按特定标签值识别的组相同。从某种意义上说,缺少标签与带有 null 值标签相同。如果服务只应在带有用于分散偏好的标签的节点上运行,应将该偏好与约束结合使用。

您可以指定多个放置偏好,它们会按遇到的顺序处理。以下示例设置了一个具有多个放置偏好的服务。任务首先分散到各个数据中心,然后(如相应标签所示)分散到机架上

$ 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 秒的延迟。

默认情况下,调度器一次更新 1 个任务。您可以传递 --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-delay0s回滚一个任务后,在回滚下一个任务之前等待的时间。值 0 表示在第一个已回滚任务部署后立即回滚第二个任务。
--rollback-failure-actionpause当任务回滚失败时,是 pause (暂停) 还是 continue (继续) 尝试回滚其他任务。
--rollback-max-failure-ratio0回滚期间可容忍的失败率,指定为一个介于 0 和 1 之间的浮点数。例如,给定 5 个任务,失败率 .2 可以容忍一个任务回滚失败。值 0 表示不容忍任何失败,而值 1 表示容忍任意数量的失败。
--rollback-monitor5s每个任务回滚后用于监控失败的持续时间。如果任务在此时间段之前停止,则回滚被视为失败。
--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 模式调度器可能会随时重新调度它们。

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

使用模板创建服务

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

支持以下标志

  • --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

了解更多

页面选项