将服务部署到集群

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

  • 服务容器应运行的镜像名称和标签
  • 有多少个容器参与服务
  • 是否将任何端口暴露给集群外的客户端
  • 是否应在 Docker 启动时自动启动服务
  • 服务重启时发生的具体行为(例如是否使用滚动重启)
  • 服务可运行节点的特征(例如资源限制和放置偏好)

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

创建一个服务

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

$ docker service create nginx

The service is scheduled on an available node. To confirm that the service was created and started successfully, use the docker service ls command:

$ 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(Write-Ahead Logging)日志,将本地客户端的登录令牌传递给部署了服务的 Swarm 节点。凭借这些信息,节点能够登录镜像仓库并拉取镜像。

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

在 Docker Enterprise Edition 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>: 配置名称在命令行界面中会自动转换为配置 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 端口发布给集群外的客户端效果会更好。您可以在创建服务时指定此操作,使用 -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 命令。您可以像 docker service ls 命令输出中所示,通过 ID 或名称删除服务。以下命令将删除 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 客户端,如果您使用 内容信任)会将该标签解析为摘要。 当在 worker 节点上收到创建容器任务请求时, worker 节点仅能看到摘要,而看不到标签。

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

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

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

  • 如果您完全不指定版本,按照惯例,镜像的 latest 标签 将被解析为摘要(digest)。工人在创建服务任务时将使用该摘要对应的镜像。

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

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

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

创建服务时,镜像的标签会被解析为该标签在服务创建时刻所指向的具体摘要(digest)。该服务的 Worker 节点将永久使用该特定摘要,除非显式更新服务。如果您经常使用频繁变化的标签(例如 latest),此功能尤为重要,因为它可确保所有服务任务均使用相同版本的镜像。

注意

如果启用了 内容信任,客户端会在联系 Swarm 管理器之前,先将镜像的标签解析为摘要(digest),以验证该镜像是否已签名。 因此,如果您使用内容信任,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"
],

创建服务后,除非您显式运行 docker service update 并设置 --image 标志(如下所述),否则其镜像不会被更新。其他更新操作,例如扩展服务、添加或移除网络或卷、重命名服务,或任何其他类型的更新操作,都不会更新服务的镜像。

创建后更新服务的镜像

每个标签代表一个摘要(digest),类似于 Git hash。某些标签,例如 latest,会频繁更新以指向新的摘要。另一些标签,例如 ubuntu:16.04,代表已发布的软件版本,通常不会频繁(甚至从不)更新为指向新摘要。创建服务时,它被限制为使用特定镜像的某个固定摘要来创建任务,除非您使用带有 --image 标志的 service update 命令更新该服务。

当使用 --image 标志运行 service update 时,Swarm Manager 会查询 Docker Hub 或您的私有 Docker 注册表,以获取当前标签所指向的摘要(digest),并更新服务任务以使用该摘要。

注意

如果您使用 内容信任,Docker 客户端将解析镜像,且 Swarm 管理器将接收镜像和摘要(digest), 而不是标签(tag)。

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

如果管理器解析了标签

如果 Swarm 管理器能够将镜像标签解析为摘要(digest),它将指示工作节点重新部署任务并使用该摘要对应的镜像。

  • 如果工作节点已缓存该摘要对应的镜像,则直接使用它。

  • 如果未找到,它将尝试从 Docker Hub 或私有镜像仓库拉取该镜像。

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

    • 如果工作节点无法拉取镜像,则该服务将无法在该 工作节点上部署。Docker 会尝试重新部署任务,可能会在不同的 工作节点上进行。

如果管理器无法解析标签

如果 Swarm 管理器无法将镜像解析为摘要,也并非毫无办法:

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

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

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

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

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

发布端口

当创建群集服务时,您可以通过以下两种方式将该服务的端口发布到群集之外的主机:

  • 您可以依赖路由网. 当您发布一个服务端口时,无论该节点上是否有运行该服务的任务,Swarm 都会使该服务在每个目标端口的每个节点上可访问。这复杂度较低,是许多类型服务的正确选择。

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

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

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

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

示例:在 10 节点集群上运行三任务 Nginx 服务

假设您有一个包含 10 个节点的集群,并在该 10 节点集群上部署了一个运行三个任务的 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 作为服务运行, 并在每个集群节点上本地暴露 nginx 端口。

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

您可以在群集的每个节点上的 8080 端口访问 Nginx 服务器。如果向群集添加新节点,将自动在该节点上启动一个 Nginx 任务。您无法在任何绑定到 8080 端口的群集节点上启动其他服务或容器。

注意

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

将服务连接到覆盖网络

您可以使用覆盖网络来连接群集中的一个或多个服务。

First, create overlay network on a manager node using the docker network create command with the --driver=overlay flag.

$ docker network create --driver overlay my-network

在您于Swarm模式下创建覆盖网络(overlay network)后,所有管理节点均可访问该网络。

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

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

The swarm extends my-network to each node running the service.

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

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

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

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

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

授予服务访问密钥的权限

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

自定义服务的隔离模式

重要

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

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

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

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

    注意

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

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

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

控制服务部署

Swarm 服务提供了多种方式来控制不同节点上服务的扩展和部署。

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

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

  • 部署约束 使您可以配置服务,使其仅在设置了特定(任意)元数据的节点上运行,并且如果不存在适当的节点则导致部署失败。例如,您可以指定您的服务仅应在任意标签 pci_compliant 设置为 true 的节点上运行。

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

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

复制服务或全局服务

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

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

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

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

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

服务限制允许您在调度器将服务部署到节点之前,为节点设置必须满足的条件。您可以根据节点属性、元数据或引擎元数据向服务应用这些限制。有关限制的详细信息,请参阅 docker service create

为服务预留内存或 CPU

要为服务预留一定量的内存或 CPU,请使用 --reserve-memory--reserve-cpu 标志。如果没有可用的节点能满足要求(例如,如果请求了 4 个 CPU,但集群中没有任何节点拥有 4 个 CPU),该服务将保持在待处理状态,直到有合适的节点可供运行为止。

内存溢出异常 (OOME)

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

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

部署约束条件

使用放置约束来控制服务可以分配到的节点。在 下面的示例中,服务仅运行在设置了 标签 regioneast 的节点上。如果没有可用且标签匹配的节点,任务将等待在 Pending 状态直到节点可用。--constraint 标志使用相等运算符(==!=)。对于副本服务,可能会出现所有服务运行在同一节点上、每个节点仅运行一个副本,或者某些节点不运行任何副本的情况。对于全局服务,服务将在满足放置约束和任何 资源要求 的每个节点上运行。

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

您也可以在 compose.yml 文件中使用 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秒。

默认情况下,调度器一次只更新 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

如果更新失败则自动回滚

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

Flag默认描述
--rollback-delay0s在回滚一个任务后,等待多久再回滚下一个任务。值为 0 表示在第一个被回滚的任务部署完成后立即回滚第二个任务。
--rollback-failure-actionpause当任务回滚失败时,是否尝试回滚其他任务(pausecontinue)。
--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

授予服务对卷或绑定挂载的访问权限

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

您可以在群集的服务中创建两种类型的挂载: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>
    

重要

绑定挂载(Bind mounts)可能很有用,但也可能引发问题。在大多数情况下,建议设计应用程序架构,使其无需从宿主机挂载路径。主要风险包括以下几点:

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

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

  • 主机绑定挂载(bind mounts)不具备可移植性。当您使用绑定挂载时,无法保证您的应用程序在开发环境与生产环境中的运行方式一致。

使用模板创建服务

您可以为某些 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

了解更多