在 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 节点。有了这些信息,节点就可以登录到注册表并拉取镜像。

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

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

注意

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

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

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

  • file://<文件名>:引用的文件必须存在于docker数据目录下的CredentialSpecs子目录中,Windows系统默认路径为C:\ProgramData\Docker\。例如,指定file://spec.json将加载C:\ProgramData\Docker\CredentialSpecs\spec.json
  • registry://<值名称>:凭据规范从守护进程主机上的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服务,因此如果您将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

以下服务的容器设置了一个环境变量$MYVAR,其值为myvalue,从/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
    

创建服务时,镜像的标签将解析为**服务创建时**标签指向的特定摘要。该服务的worker节点将永远使用该特定摘要,除非显式更新服务。如果您确实使用了经常更改的标签(例如latest),此功能尤其重要,因为它确保所有服务任务都使用相同版本的镜像。

注意

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

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

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

要查看镜像的当前摘要,请发出命令docker inspect <镜像>:<标签>并查找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时,群集管理器会查询Docker Hub或您的私有Docker注册表以获取标签当前指向的摘要,并更新服务任务以使用该摘要。

注意

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

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

如果管理器解析了标签

如果群集管理器可以将镜像标签解析为摘要,它会指示工作节点重新部署任务并使用该摘要中的镜像。

  • 如果工作节点已缓存该摘要中的镜像,则它会使用该镜像。

  • 如果不是,它会尝试从Docker Hub或私有注册表中提取镜像。

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

    • 如果工作节点未能提取镜像,则服务无法在该工作节点上部署。Docker会再次尝试部署任务,可能在不同的工作节点上。

如果管理器无法解析标签

如果群集管理器无法将镜像解析为摘要,则并非一切都丢失了

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

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

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

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

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

发布端口

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

  • 您可以依赖路由网格。发布服务端口时,群集会在每个节点上的目标端口使服务可访问,无论该节点上是否正在运行该服务的任务。这不太复杂,并且对于许多类型的服务都是正确的选择。

  • 您可以直接在运行该服务的群集节点上发布服务任务的端口。这绕过了路由网格并提供了最大的灵活性,包括允许您开发自己的路由框架。但是,您有责任跟踪每个任务的运行位置以及将请求路由到任务以及跨节点的负载平衡。

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

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

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

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

假设您有一个包含 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=,则会创建一个隐式限制,即您只能在一个给定的 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 节点上启动另一个服务或容器。

注意

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

将服务连接到覆盖网络

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

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

$ 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 主机配置的默认隔离模式,如daemon.json中的-exec-opt标志或exec-opts数组所配置。如果守护程序未指定隔离技术,则 Windows Server 的默认值为process,而 Windows 10 的默认值(也是唯一选择)为hyperv

  • 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 模式有两种类型的服务:复制的服务和全局服务。对于复制的服务,您可以为 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 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.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 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

注意

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

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

$ 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 模式调度程序可能会随时重新调度这些容器。

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

使用模板创建服务

您可以使用 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 ps` 和 `docker 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

了解更多