docker service create

描述创建新服务
用法docker service create [OPTIONS] IMAGE [COMMAND] [ARG...]

Swarm 此命令适用于 Swarm 调度器。

描述

根据指定的参数创建服务。

注意

这是一个集群管理命令,必须在 Swarm 管理节点上执行。要了解管理节点和工作节点,请参阅文档中的Swarm 模式部分

选项

选项默认描述
--cap-addAPI 1.41+ 添加 Linux 权能
--cap-dropAPI 1.41+ 移除 Linux 权能
--configAPI 1.30+ 指定要向服务公开的配置
--constraint部署约束条件
--container-label容器标签
--credential-specAPI 1.29+ 托管服务帐户的凭据规范(仅限 Windows)
-d, --detachAPI 1.29+ 立即退出而不是等待服务收敛
--dnsAPI 1.25+ 设置自定义 DNS 服务器
--dns-optionAPI 1.25+ 设置 DNS 选项
--dns-searchAPI 1.25+ 设置自定义 DNS 搜索域
--endpoint-modevip端点模式 (vip 或 dnsrr)
--entrypoint覆盖镜像的默认 ENTRYPOINT
-e, --env设置环境变量
--env-file读取环境变量文件
--generic-resource用户定义资源
--groupAPI 1.25+ 为容器设置一个或多个附加用户组
--health-cmdAPI 1.25+ 用于检查健康状况的命令
--health-intervalAPI 1.25+ 执行检查的时间间隔 (ms|s|m|h)
--health-retriesAPI 1.25+ 报告不健康所需的连续失败次数
--health-start-intervalAPI 1.44+ 启动期间执行检查的时间间隔 (ms|s|m|h)
--health-start-periodAPI 1.29+容器在计入不稳定重试次数之前的初始化启动时间 (ms|s|m|h)
--health-timeoutAPI 1.25+ 允许单次检查运行的最长时间 (ms|s|m|h)
--hostAPI 1.25+ 设置一个或多个自定义主机到 IP 的映射 (host:ip)
--hostnameAPI 1.25+ 容器主机名
--initAPI 1.37+在每个服务容器中使用 init 来转发信号并回收进程
--isolationAPI 1.35+ 服务容器隔离模式
-l, --label服务标签
--limit-cpu限制 CPU
--limit-memory限制内存
--limit-pidsAPI 1.41+ 限制最大进程数(默认 0 = 无限制)
--log-driver服务的日志驱动
--log-opt日志驱动选项
--max-concurrentAPI 1.41+同时运行的任务数量(默认等于 --replicas)
--modereplicated服务模式 (replicated, global, replicated-job, global-job)
--mount将文件系统挂载附加到服务
--name服务名称
--network网络连接
--no-healthcheckAPI 1.25+ 禁用任何容器指定的 HEALTHCHECK
--no-resolve-imageAPI 1.30+不要查询注册表以解析镜像摘要和支持的平台
--oom-score-adjAPI 1.46+ 调整宿主机的 OOM 优先级 (-1000 到 1000)
--placement-prefAPI 1.28+ 添加放置偏好
-p, --publish将端口发布为节点端口
-q, --quiet抑制进度输出
--read-onlyAPI 1.28+ 将容器的根文件系统挂载为只读
--replicas任务数量
--replicas-max-per-nodeAPI 1.40+ 每个节点的最大任务数(默认 0 = 无限制)
--reserve-cpu预留 CPU
--reserve-memory保留内存
--restart-condition满足条件时重启 (none, on-failure, any) (默认any)
--restart-delay重启尝试之间的延迟 (ns|us|ms|s|m|h) (默认 5s)
--restart-max-attempts放弃前的最大重启次数
--restart-window用于评估重启策略的时间窗口 (ns|us|ms|s|m|h)
--rollback-delayAPI 1.28+ 任务回滚之间的延迟 (ns|us|ms|s|m|h) (默认为 0s)
--rollback-failure-actionAPI 1.28+ 回滚失败时的操作 (pause, continue) (默认 pause)
--rollback-max-failure-ratioAPI 1.28+ 回滚期间允许的失败率(默认为 0)
--rollback-monitorAPI 1.28+每次任务回滚后监控失败情况的持续时间 (ns|us|ms|s|m|h) (默认 5s)
--rollback-orderAPI 1.29+回滚订单 (start-first, stop-first) (默认stop-first)
--rollback-parallelism1API 1.28+同时回滚的最大任务数(0 表示一次性全部回滚)
--secretAPI 1.25+ 指定要向服务公开的机密信息
--stop-grace-period强制终止容器前等待的时间 (ns|us|ms|s|m|h) (默认 10s)
--stop-signalAPI 1.28+ 停止容器的信号
--sysctlAPI 1.40+ Sysctl 选项
-t, --ttyAPI 1.25+ 分配一个伪TTY
--ulimitAPI 1.41+ Ulimit 选项
--update-delay更新间隔 (ns|us|ms|s|m|h) (默认 0s)
--update-failure-action更新失败时的操作 (pause, continue, rollback) (默认pause)
--update-max-failure-ratioAPI 1.25+ 更新期间允许的失败率(默认为 0)
--update-monitorAPI 1.25+每次任务更新后监控失败的时间长度 (ns|us|ms|s|m|h) (默认 5s)
--update-orderAPI 1.29+ 更新顺序 (start-first, stop-first) (默认 stop-first)
--update-parallelism1同时更新的最大任务数(0 表示一次性全部更新)
-u, --user用户名或 UID (格式: <name|uid>[:<group|gid>])
--with-registry-auth将镜像仓库认证详情发送给 Swarm 代理
-w, --workdir容器内的工作目录

示例

创建一个服务

$ docker service create --name redis redis:7.4.1

dmu1ept4cxcfe8k8lhtux3ro3

$ docker service create --mode global --name redis2 redis:7.4.1

a8q9dasaafudfs8q8w32udass

$ docker service ls

ID            NAME    MODE        REPLICAS  IMAGE
dmu1ept4cxcf  redis   replicated  1/1       redis:7.4.1
a8q9dasaafud  redis2  global      1/1       redis:7.4.1

使用私有注册表上的镜像创建服务 (--with-registry-auth)

如果您的镜像托管在需要登录的私有仓库中,请在登录后使用带 --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 节点。凭借这些信息,节点能够登录镜像仓库并拉取镜像。

创建一个具有 5 个副本任务的服务 (--replicas)

使用 --replicas 标志设置副本服务的副本任务数量。以下命令创建一个包含 redis 个服务的 5 个副本任务:

$ docker service create --name redis --replicas=5 redis:7.4.1

4cdgfyky7ozwh3htjfw0d12qv

上述命令为服务设置了期望的任务数量。尽管该命令会立即返回,但服务的实际扩缩容可能需要一些时间。REPLICAS 列显示了服务的副本任务的实际数量和期望数量。

在下面的例子中,期望状态是 5 个副本,但是当前 RUNNING 个任务的数量是 3

$ docker service ls

ID            NAME   MODE        REPLICAS  IMAGE
4cdgfyky7ozw  redis  replicated  3/5       redis:7.4.1

一旦所有任务创建完成并变为 RUNNING,实际任务数就等于期望任务数:

$ docker service ls

ID            NAME   MODE        REPLICAS  IMAGE
4cdgfyky7ozw  redis  replicated  5/5       redis:7.4.1

创建带有密钥的服务 (--secret)

使用 --secret 标志赋予容器访问机密信息的权限。

创建一个指定密钥的服务:

$ docker service create --name redis --secret secret.json redis:7.4.1

4cdgfyky7ozwh3htjfw0d12qv

创建一个指定密钥、目标、用户/组 ID 和模式的服务:

$ docker service create --name redis \
    --secret source=ssh-key,target=ssh \
    --secret source=app-key,target=app,uid=1000,gid=1001,mode=0400 \
    redis:7.4.1

4cdgfyky7ozwh3htjfw0d12qv

要授予服务访问多个机密的权限,请使用多个 --secret 标志。

如果没有指定目标,Secrets 位于容器中的 /run/secrets。 如果没有指定目标,Secret 的名称将用作容器中内存文件的名称。如果指定了目标,则将其用作文件名。在上面的示例中,创建了两个文件:/run/secrets/ssh/run/secrets/app,分别对应指定的每个 Secret 目标。

使用配置创建服务 (--config)

使用 --config 标志赋予容器访问 配置的权限。

创建一个带有配置的服务。该配置将被挂载到 redis-config, 归容器内运行命令的用户所有(通常是 root), 并且文件模式为 0444 或全局可读。您可以指定 uidgid 为数字 ID 或名称。使用名称时,提供的组名/用户名必须 预先存在于容器中。mode 被指定为一个 4 位数字序列,例如 0755

$ docker service create --name=redis --config redis-conf redis:7.4.1

创建一个带有配置的服务,并指定目标位置和文件模式:

$ docker service create --name redis \
  --config source=redis-conf,target=/etc/redis/redis.conf,mode=0400 redis:7.4.1

若要授予服务对多个配置的访问权限,请使用多个 --config 标志。

如果未指定目标,配置位于容器中的 /。如果未指定目标,配置的名称将用作容器中文件的名称。如果指定了目标,则将其用作文件名。

使用滚动更新策略创建服务

$ docker service create \
  --replicas 10 \
  --name redis \
  --update-delay 10s \
  --update-parallelism 2 \
  redis:7.4.1

当您运行服务更新时,调度器每次最多更新 2 个任务,更新间隔为 10s。欲了解更多信息,请参阅滚动更新教程

设置环境变量 (-e, --env)

这将为服务中的所有任务设置环境变量。例如:

$ docker service create \
  --name redis_2 \
  --replicas 5 \
  --env MYVAR=foo \
  redis:7.4.1

要指定多个环境变量,请指定多个 --env 标志,每个标志具有单独的键值对。

$ docker service create \
  --name redis_2 \
  --replicas 5 \
  --env MYVAR=foo \
  --env MYVAR2=bar \
  redis:7.4.1

创建具有特定主机名的服务 (--hostname)

此选项将 docker 服务容器的主机名设置为特定字符串。 例如:

$ docker service create --name redis --hostname myredis redis:7.4.1

设置服务的元数据 (-l, --label)

标签是一个应用于服务的 key=value 对。要为一个服务添加两个标签:

$ docker service create \
  --name redis_2 \
  --label com.example.foo="bar" \
  --label bar=baz \
  redis:7.4.1

有关标签的更多信息,请参阅 应用自定义元数据

添加绑定挂载、卷或内存文件系统 (--mount)

Docker 支持三种不同类型的挂载,允许容器在宿主操作系统或内存文件系统上读取或写入文件或目录。这些类型包括数据卷(通常简称为卷)、绑定挂载、tmpfs 和命名管道。

绑定挂载 使主机上的文件或目录可供其挂载的容器使用。绑定挂载可以是只读的或读写的。例如,容器可以通过绑定挂载主机的 /etc/resolv.conf 来共享其主机的 DNS 信息,或者容器可以将日志写入其主机的 /var/log/myContainerLogs 目录。如果您使用绑定挂载,并且您的主机和容器具有不同的权限、访问控制或其他此类细节的概念,您将遇到可移植性问题。

命名卷是一种将容器所需的持久化数据与用于创建容器的镜像以及主机解耦的机制。命名卷由 Docker 创建和管理,即使当前没有容器在使用它,命名卷也会持久存在。命名卷中的数据可以在容器和主机之间共享,也可以在多个容器之间共享。Docker 使用卷驱动程序来创建、管理和挂载卷。您可以使用 Docker 命令备份或恢复卷。

tmpfs 在容器内挂载一个 tmpfs,用于存储易失性数据。

npipe 将主机中的命名管道挂载到容器中。

考虑这样一种情况,您的镜像启动了一个轻量级 Web 服务器。您可以 将该镜像作为基础镜像,复制您网站的 HTML 文件,并打包 成另一个镜像。每次网站更改时,您都需要更新 新镜像并重新部署所有服务于您网站的容器。一个更好的 解决方案是将网站存储在命名卷中,该卷在启动时附加到每个 Web 服务器容器。要更新网站,您只需 更新该命名卷。

有关命名卷的更多信息,请参阅 数据卷

下表描述了适用于服务中绑定挂载和命名卷的选项:

选项必需描述
类型

挂载类型,可以是 volumebindtmpfsnpipe。如果未指定类型,则默认为 volume

  • volume: 将 托管卷 挂载到容器中。
  • bind: 将主机上的目录或文件绑定挂载到容器中。
  • tmpfs: 在容器中挂载 tmpfs
  • npipe: 将主机上的命名管道挂载到容器中(仅限 Windows 容器)。
srcsource对于 type=bindtype=npipe
  • type=volume: src 是指定卷名称的可选方式(例如,src=my-volume)。 如果指定的卷不存在,则会自动创建。如果未指定 src,则会为该卷 分配一个随机名称,该名称在主机上保证是唯一的,但在整个集群范围内可能不唯一。 随机命名的卷与其容器具有相同的生命周期,并且当容器 被销毁时也会被销毁(这发生在 service update 时,或者在服务扩缩容或重新平衡时)
  • type=bind: src 是必需的,它指定要绑定挂载的文件或目录的绝对路径 (例如,src=/path/on/host/)。如果文件或目录不存在,则会产生错误。
  • type=tmpfs: src 不受支持。

dstdestinationtarget

容器内的挂载路径,例如 /some/path/in/container/。 如果容器文件系统中不存在该路径,引擎会在挂载卷或绑定挂载之前在指定位置创建一个目录。

只读ro

引擎以读写方式挂载绑定和卷,除非在挂载绑定或卷时指定了只读选项。请注意,根据内核版本的不同,为绑定挂载设置只读可能不会使其子挂载变为只读。另请参阅bind-recursive

  • true1 或无值:以只读方式挂载绑定或卷。
  • false0:以读写模式挂载绑定或卷。

绑定挂载的选项

以下选项仅可用于绑定挂载 (type=bind):

选项描述
bind-propagation

参见 绑定传播部分

一致性

挂载的一致性要求;其中之一

  • default: 等同于 consistent
  • consistent: 完全一致性。容器运行时和主机在所有时间都维护对挂载点的相同视图。
  • cached: 主机的挂载视图具有权威性。在主机上进行的更新可能在容器内可见之前会有延迟。
  • delegated: 容器运行时对挂载的视图具有权威性。在容器中进行的更新可能需要一段时间才能在主机上可见。
bind-recursive默认情况下,子挂载点也会递归地绑定挂载。然而,当绑定挂载配置为只读选项,因为子挂载点可能无法以只读方式挂载, 这取决于内核版本。 设置bind-recursive用于控制递归绑定挂载的行为。

一个值可以是以下之一:

  • <enabled: 启用递归绑定挂载。 如果内核版本为 v5.12 或更高,只读挂载将递归设置为只读。 否则,它们不会被递归设置为只读。
  • <disabled: 禁用递归绑定挂载。
  • <writable: 启用递归绑定挂载。 只读挂载不会递归设置为只读。
  • <readonly: 启用递归绑定挂载。 如果内核版本为 v5.12 或更高,则只读挂载将递归设置为只读。 否则,引擎将报错。
当未指定该选项时,默认行为对应于设置已启用.
bind-nonrecursivebind-nonrecursive自 Docker Engine v25.0 起已弃用。 使用bind-recursiveinstead.

一个值是可选的:

  • true or 1: 等同于 bind-recursive=disabled
  • false0:等同于 bind-recursive=enabled
绑定传播

绑定传播是指在一个给定的绑定挂载或命名卷内创建的挂载是否可以传播到该挂载的副本。考虑一个挂载点 /mnt,它也被挂载在 /tmp 上。传播设置控制 /tmp/a 上的挂载是否也会出现在 /mnt/a 上。每个传播设置都有一个递归对应项。在递归的情况下,假设 /tmp/a 也被挂载为 /foo。传播设置控制 /mnt/a 和/或 /tmp/a 是否存在。

bind-propagation 选项对于绑定挂载和卷挂载默认值均为 rprivate,且仅对绑定挂载可配置。换句话说,命名卷不支持绑定传播。

  • shared: 原始挂载的子挂载会暴露给副本挂载, 且副本挂载的子挂载也会传播到 原始挂载。
  • slave: 类似于共享挂载,但仅单向生效。如果原始挂载暴露了子挂载,则副本挂载可以看到它。然而,如果副本挂载暴露了子挂载,原始挂载则无法看到它。
  • private: 该挂载点是私有的。其内部的子挂载点不会暴露给副本挂载点,且副本挂载点的子挂载点也不会暴露给原始挂载点。
  • rshared: 与 shared 相同,但传播范围也扩展到原始或副本挂载点内嵌套的任何其他挂载点。
  • rslave: 与 slave 相同,但传播范围还扩展到任何原始或副本挂载点内嵌套的挂载点。
  • rprivate: 默认值。与private相同,表示原始挂载点或副本挂载点范围内的任何位置都不会向任一方向传播挂载点。

有关绑定传播的更多信息,请参阅 Linux 内核关于共享子树的文档

命名卷的选项

以下选项仅可用于命名卷(type=volume):

选项描述
volume-driver

要用于卷的卷驱动插件的名称。默认为 "local",如果卷不存在,则使用本地卷驱动来创建该卷。

volume-label一个或多个自定义元数据("标签"),用于在创建卷时应用。例如, volume-label=mylabel=hello-world,my-other-label=hello-mars。有关标签的更多信息,请参阅 应用自定义元数据
volume-nocopy默认情况下,如果您将一个空卷附加到容器,且挂载路径在容器中已存在文件或目录(Docker 容器平台), 引擎将这些文件和目录复制到卷中,使主机能够访问它们。设置volume-nocopy要禁用将文件从容器的文件系统复制到卷,并挂载空卷。

一个值是可选的:

  • true or 1: 如果您不提供值,则为默认值。禁用复制功能。
  • false0:启用复制功能。
volume-opt特定于给定卷驱动程序的选项,在创建卷时将传递给该驱动程序。选项以逗号分隔的键/值对列表形式提供,例如, volume-opt=some-option=some-value,volume-opt=some-other-option=some-other-value。 对于给定驱动程序的可用选项,请参阅该驱动程序的文档。

tmpfs 选项

以下选项仅可用于 tmpfs 挂载(type=tmpfs);

选项描述
tmpfs-sizetmpfs 挂载的字节大小。在 Linux 上默认无限制。
tmpfs-modetmpfs 的八进制文件模式。(例如 "700""0700"。)在 Linux 上默认为 "1777"

"--mount" 和 "--volume" 之间的区别

--mount 标志支持大多数 docker run-v--volume 标志所支持的选项,但有一些重要例外:

  • --mount 标志允许你为每个卷指定卷驱动器和卷驱动器选项,而无需预先创建这些卷。相比之下,使用--volume-driver标志时,docker run允许你指定一个由所有卷共享的单一卷驱动器。

  • --mount 标志允许你在卷创建之前为其指定自定义元数据("标签")。

  • 当您使用 --mounttype=bind 时,主机路径必须指向主机上已存在的路径。该路径不会为您创建,如果路径不存在,服务将失败并报错。

  • --mount 标志不允许您使用 Zz 标志重新标记卷, 这些标志用于 selinux 标记。

使用命名卷创建服务

以下示例创建一个使用命名卷的服务:

$ docker service create \
  --name my-service \
  --replicas 3 \
  --mount type=volume,source=my-volume,destination=/path/in/container,volume-label="color=red",volume-label="shape=round" \
  nginx:alpine

对于服务的每个副本,引擎会从默认("local")卷驱动程序请求一个名为 "my-volume" 的卷,该驱动程序部署在任务所在处。如果卷不存在,引擎将创建一个新卷并应用 "color" 和 "shape" 标签。

当任务启动时,卷会被挂载到容器内的 /path/in/container/ 位置。

请注意,默认的("local")卷是一个本地作用域的卷驱动程序。 这意味着根据任务部署的位置,该任务要么获得一个名为"my-volume"的卷,要么与其他同服务的任务共享同一个"my-volume"。多个容器写入单个共享卷可能导致数据损坏,如果容器内运行的软件未设计为处理并发进程写入同一位置的情况。此外,还需考虑到容器可能被 Swarm 编排器重新调度并部署到不同的节点上。

创建一个使用匿名卷的服务

以下命令创建一个具有三个副本并在 /path/in/container 上挂载匿名卷的服务:

$ docker service create \
  --name my-service \
  --replicas 3 \
  --mount type=volume,destination=/path/in/container \
  nginx:alpine

在此示例中,未为卷指定名称(source),因此每个任务都会创建一个新卷。这保证了每个任务都有自己独立的卷,且卷不会在任务之间共享。匿名卷在使用它们的任务完成后会被移除。

创建一个使用绑定挂载主机目录的服务

以下示例将主机目录绑定挂载到容器 backing the service 中的/path/in/container位置:

$ docker service create \
  --name my-service \
  --mount type=bind,source=/path/on/host,destination=/path/in/container \
  nginx:alpine

设置服务模式 (--mode)

服务模式决定了这是一个复制服务还是一个全局服务。复制服务会运行指定数量的任务,而全局服务会在集群中的每个活动节点上运行。

以下命令会创建一个全局服务:

$ docker service create \
 --name redis_2 \
 --mode global \
 redis:7.4.1

指定服务约束条件 (--constraint)

您可以通过定义约束表达式来限制任务可以调度的节点集合。约束表达式可以使用匹配==)或排除!=)规则。多个约束会查找满足所有表达式的节点(AND 匹配)。约束可如下匹配节点或 Docker Engine 标签:

节点属性比赛示例
node.id节点 IDnode.id==2ivku8v2gvtg4
node.hostname节点主机名node.hostname!=node-2
node.role节点角色 (manager/worker)node.role==manager
node.platform.os节点操作系统node.platform.os==windows
node.platform.arch节点架构node.platform.arch==x86_64
node.labels用户定义的节点标签node.labels.security==high
engine.labelsDocker 引擎的标签engine.labels.operatingsystem==ubuntu-24.04

engine.labels 应用于 Docker Engine 标签,例如操作系统、驱动程序等。Swarm 管理员使用docker node update命令为操作目的添加node.labels

例如,以下限制将 redis 服务的任务调度到节点类型标签等于 queue 的节点上:

$ docker service create \
  --name redis_2 \
  --constraint node.platform.os==linux \
  --constraint node.labels.type==queue \
  redis:7.4.1

如果服务约束排除了集群中的所有节点,则会打印一条消息提示未找到合适的节点,但调度器将启动一个重新协调循环,并在有合适节点可用时部署该服务。

在下面的示例中,未找到满足约束的节点,导致服务无法与期望状态进行协调:

$ docker service create \
  --name web \
  --constraint node.labels.region==east \
  nginx:alpine

lx1wrhhpmbbu0wuk0ybws30bc
overall progress: 0 out of 1 tasks
1/1: no suitable node (scheduling constraints not satisfied on 5 nodes)

$ docker service ls
ID                  NAME     MODE         REPLICAS   IMAGE               PORTS
b6lww17hrr4e        web      replicated   0/1        nginx:alpine

在将region=east标签添加到集群中的节点后,服务会进行协调,并部署所需数量的副本:

$ docker node update --label-add region=east yswe2dm4c5fdgtsrli1e8ya5l
yswe2dm4c5fdgtsrli1e8ya5l

$ docker service ls
ID                  NAME     MODE         REPLICAS   IMAGE               PORTS
b6lww17hrr4e        web      replicated   1/1        nginx:alpine

指定服务放置偏好 (--placement-pref)

您可以配置服务,以便在不同类别的节点上均匀分配任务。这种配置的一个典型应用场景是在一组数据中心或可用区之间平衡任务。下面的示例展示了这一点:

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

此功能使用 --placement-pref 配合 spread 策略(当前唯一受支持的策略),将任务均匀分布到 datacenter 节点标签的各个值上。在此示例中,我们假设每个节点都附加了一个 datacenter 节点标签。如果群集中的节点存在三个不同的该标签值,则三分之一的任务将被放置在与每个值关联的节点上。即使具有某一值的节点数量多于其他值,这一规则依然成立。例如,考虑以下一组节点:

  • 三个节点,node.labels.datacenter=east
  • 两个节点,node.labels.datacenter=south
  • 一个节点包含 node.labels.datacenter=west

由于我们将值datacenter的标签进行分布,且该服务有9个副本,因此每个数据中心将各有3个副本。与值east相关联的节点有三个,因此每个节点将获得为该值预留的三个副本中的一个。有两个节点具有值south,这三个副本将在这两个节点之间分配,其中一个接收两个副本,另一个接收一个副本。最后,west只有一个节点,它将获得为west预留的所有三个副本。

如果某一类别的节点(例如,那些带有 node.labels.datacenter=south)由于约束或资源限制而无法处理其应分配的任务份额,在可能的情况下,多余的任务将被分配给其他节点。

放置偏好同时支持引擎标签和节点标签。上述示例使用的是节点标签,因为该标签是通过node.labels.datacenter引用的。若要按引擎标签的值进行分散,请使用--placement-pref spread=engine.labels.<labelname>

可以向服务添加多个放置偏好。这会建立一个偏好层次结构,使得任务首先按一个类别进行划分,然后进一步按其他类别进行划分。使用此功能的一个示例是在数据中心之间公平分配任务,然后在每个数据中心内将任务在机架选择之间进行拆分。要添加多个放置偏好,请多次指定 --placement-pref 标志。顺序很重要,在进行调度决策时,将按照给定的顺序应用这些放置偏好。

以下示例设置了一个具有多个放置优先级的服务。 任务首先在各个数据中心之间分散,然后在机架之间分散 (如相应标签所示):

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

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

为服务指定内存需求和约束(--reserve-memory 和 --limit-memory)

如果您的服务需要最低限度的内存才能正常运行, 您可以使用--reserve-memory来指定该服务仅应在具有此可用内存以预留的节点上调度。如果没有满足条件的节点可用,则任务不会被调度,但会保持在待定状态。

以下示例要求在使用该节点调度服务运行之前,该节点上必须有可用且可保留的 4GB 内存。

$ docker service create --reserve-memory=4GB --name=too-big nginx:alpine

管理器不会将一组容器调度到单个节点上,如果这些容器的预留内存总和超过该节点可用的内存。

任务调度并运行后,--reserve-memory 不会强制实施内存限制。使用 --limit-memory 可确保任务在节点上使用的内存不超过指定数量。此示例将任务使用的内存限制为 4GB。即使每个节点的内存仅有 2GB,任务仍会被调度,因为 --limit-memory 是一个上限值。

$ docker service create --limit-memory=4GB --name=too-big nginx:alpine

使用 --reserve-memory--limit-memory 并不能保证 Docker 不会在你的主机上使用超过你预期的内存。例如,你可以 创建许多服务,这些服务的内存使用总和可能会耗尽可用 内存。

您可以通过同时考虑主机上运行的其他(非容器化)软件,来防止此场景耗尽可用内存。如果 --reserve-memory 大于或等于 --limit-memory,Docker 将不会在内存不足的主机上调度服务。--limit-memory 会将服务的内存限制在该范围内,因此如果每个服务都设置了内存预留和限制,Docker 服务就不太可能使主机内存饱和。在 Docker 主机上直接运行的其他非服务容器或应用程序仍可能导致内存耗尽。

这种方法有一个缺点。保留内存也意味着您可能无法充分利用节点上可用的内存。考虑一个在正常情况下使用100MB内存的服务,但根据负载情况可能会"峰值"达到500MB。为该服务保留500MB(以确保持续拥有500MB应对这些"峰值")会导致大部分时间有400MB的内存被浪费。

简而言之,您可以采取更保守或更灵活的方法:

  • 保守模式:保留 500MB,并限制为 500MB。本质上,您现在是将服务容器当作虚拟机来对待,这可能会让您失去容器的一个巨大优势,即每台主机上可部署更多服务的高密度特性。

  • 灵活:限制为 500MB,假设如果服务需要超过 500MB,则表明其运行异常。在 100MB 的“正常”需求和 500MB 的“峰值”需求之间预留资源。这假设当此服务处于“峰值”时,其他服务或非容器工作负载可能不会同时达到峰值。

您采取的方法很大程度上取决于您的工作负载的内存使用模式。在确定方法之前,您应在正常和峰值条件下进行测试。

在 Linux 上,您还可以使用 cgroups 或其他相关的操作系统工具,在主机操作系统级别限制服务在给定主机上的总体内存占用。

指定每个节点的最大副本数 (--replicas-max-per-node)

使用 --replicas-max-per-node 标志来设置每个节点上可以运行的副本任务的最大数量。 以下命令创建一个具有 2 个副本任务的 nginx 服务,但每个节点仅运行一个副本任务。

其中一个有用的场景是在多个数据中心之间平衡任务,并结合--placement-pref设置,同时让--replicas-max-per-node设置确保在维护或数据中心故障期间副本不会被迁移到其他数据中心。

下面的示例说明了这一点:

$ docker service create \
  --name nginx \
  --replicas 2 \
  --replicas-max-per-node 1 \
  --placement-pref 'spread=node.labels.datacenter' \
  nginx

将服务附加到现有网络 (--network)

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

首先,在 Docker 管理器节点上创建一个覆盖网络,使用 docker network create 命令:

$ docker network create --driver overlay my-network

etjpu59cykrptrgw0z0hk5snf

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

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

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

716thylsndqma81j6kkkb5aus

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

同一网络中的容器可以使用 服务发现互相访问。

--network的长形式语法允许指定别名列表和驱动程序选项: --network name=my-network,alias=web1,driver-opt=field1=value1

将服务端口发布到外部以接入集群 (-p, --publish)

您可以使用--publish标志发布服务端口,使其在集群外部可用。--publish标志可以接受两种不同风格的参数。简写版本是位置参数,允许您指定发布的端口和目标端口,两者用冒号分隔(:)。

$ docker service create --name my_web --replicas 3 --publish 8080:80 nginx

还有一种长格式,更易阅读并允许指定更多选项。推荐使用长格式。使用短格式时无法指定服务的模式。以下是针对上述相同服务使用长格式的示例:

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

您可以指定的选项是:

选项简短语法长语法描述
发布和目标端口--publish 8080:80--publish published=8080,target=80

容器内的目标端口及其在节点上映射的端口,可使用路由网格(ingress)或主机级网络。 本表后文将介绍更多选项。推荐使用键值语法,因其具有自文档特性。

模式无法使用简写语法进行设置。--publish published=8080,target=80,mode=host

用于绑定端口的模式,可选值为 ingresshost。 默认值为 ingress,以使用路由网格。

协议--publish 8080:80/tcp--publish published=8080,target=80,protocol=tcp

要使用的协议,tcpudpsctp。默认为tcp。若需为两种协议绑定端口,请分别指定两次-p--publish标志。

当您使用ingress模式发布服务端口时,集群路由网格会使该服务在所有节点上通过发布的端口可访问,无论该节点上是否有运行中的服务任务。如果您使用host模式,则端口仅绑定在运行该服务的节点上,且一个节点上的给定端口只能被绑定一次。您只能使用长语法设置发布模式。更多信息请参阅 使用集群模式路由网格

为托管服务账户提供凭据规范(--credentials-spec)

此选项仅用于使用 Windows 容器的服务。--credential-spec 必须以 file://<filename>registry://<value-name> 的格式指定。

当使用file://<filename>格式时,所引用的文件必须存在于 Docker 数据目录下的CredentialSpecs子目录中,该目录在 Windows 上默认为C:\ProgramData\Docker\。例如,指定file://spec.json将加载C:\ProgramData\Docker\CredentialSpecs\spec.json

当使用registry://<value-name>格式时,凭据规范将从守护程序的宿主机的Windows注册表中读取。指定的注册表值必须位于:

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs

使用模板创建服务

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

支持的标志如下:

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

Go 模板的有效占位符列表如下:

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

模板示例

在此示例中,我们将根据服务的名称、节点 ID 及其所在的主机名来设置所创建容器的模板。

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

va8ew30grofhjoychbr6iot8c

$ 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}}" 2e7a8a9c4da2-wo41w8hg8qanxwjwsg4kxpprj-hosttempl

x3ti0erg11rjpg64m75kej2mz-hosttempl

指定 Windows 上的隔离模式 (--isolation)

默认情况下,在 Windows 节点上调度的任务将使用为该特定节点配置的默认隔离模式运行。若要强制使用特定的隔离模式,可使用 --isolation 标志:

$ docker service create --name myservice --isolation=process microsoft/nanoserver

Windows 上支持的隔离模式有:

  • default: 使用在运行任务的节点上指定的默认设置
  • process: 使用进程隔离(仅限 Windows Server)
  • hyperv: 使用 Hyper-V 隔离

创建请求通用资源的服务(--generic-resources)

您可以通过使用 --generic-resource标志(如果节点宣传这些资源)来缩小任务可以落地的节点类型:

$ docker service create \
    --name cuda \
    --generic-resource "NVIDIA-GPU=2" \
    --generic-resource "SSD=1" \
    nvidia/cuda

作为作业运行

作业是一种特殊的服务类型,旨在运行某项操作直至完成然后停止,与长期运行的守护进程不同。当属于作业的 Task 成功退出(返回值为 0)时,该 Task 会被标记为“已完成”,且不会再次运行。

作业通过使用两种模式之一启动,replicated-jobglobal-job

$ docker service create --name myjob \
                        --mode replicated-job \
                        bash "true"

此命令将运行一个任务,该任务将使用 bash 镜像执行命令 true,该命令返回 0 然后退出。

尽管作业最终是一种不同类型的服务,但与其他服务相比,它们有几个注意事项:

  • 没有任何更新或回滚配置选项是有效的。作业可以更新,但无法部署或回滚,因此这些配置选项无关紧要。
  • 作业在达到 Complete 状态时永远不会重启。这意味着对于作业,将值从 --restart-condition 设置为 any 与将其设置为 on-failure 是相同的。

作业在复制模式和全局模式下均可用。

复制任务

复制任务类似于复制服务。设置--replicas标志将指定要执行的任务迭代总次数。

默认情况下,所有副本化的作业副本将同时启动。若要控制任意时刻同时执行的副本总数,可使用--max-concurrent标志:

$ docker service create \
    --name mythrottledjob \
    --mode replicated-job \
    --replicas 10 \
    --max-concurrent 2 \
    bash "true"

上述命令将总共执行 10 个任务,但任意时刻仅运行其中的 2 个。

全球职位

全局任务类似于全局服务,即每个匹配放置约束的节点上执行一次任务。全局任务由模式 global-job 表示。

请注意,在全局任务创建后,任何添加到集群的新节点都将启动该任务的作业。全局任务本身没有整体的“完成”状态,除非所有满足该任务约束的节点上的任务均已完成任务。