介绍
在这个实验中,你将通过使用 docker service create 命令,亲身体验在 Docker Swarm 中部署和管理服务。你将学习如何创建和检查不同类型的服务,包括用于实现高可用性和可扩展性的简单副本服务,以及在每个节点上运行的全局服务。
此外,你还将探索高级服务配置,例如实施滚动更新策略以实现无缝部署、利用绑定挂载和环境变量实现数据持久化和配置,以及应用放置约束和偏好设置来控制服务任务在 Swarm 中的运行位置。通过完成这些步骤,你将深入理解如何在 Docker Swarm 环境中有效管理容器化应用程序。
创建一个简单的副本服务
在这一步中,你将学习如何在 Docker Swarm 中创建一个简单的副本服务。副本服务是一种在 Swarm 中运行多个相同任务的服务,它能提供高可用性和可扩展性。
首先,确保 Docker 正在运行,并且你处于正确的目录中。
docker version
pwd
你应该会看到显示 Docker 版本和当前目录(为 /home/labex/project)的输出。
在创建服务之前,我们需要拉取必要的 Docker 镜像。在这个示例中,我们将使用 nginx 镜像。
docker pull nginx:latest
此命令会从 Docker Hub 下载最新版本的 nginx 镜像。
现在,让我们创建一个名为 my-nginx-service 的简单副本服务,设置副本数量为 3。
docker service create --name my-nginx-service --replicas 3 nginx:latest
这个命令使用 nginx:latest 镜像创建了一个名为 my-nginx-service 的新服务,并将所需的副本数量设置为 3。然后,Docker Swarm 会将这 3 个任务分布到 Swarm 中的可用节点上。
要检查服务及其任务的状态,你可以使用 docker service ls 和 docker service ps 命令。
docker service ls
docker service ps my-nginx-service
docker service ls 命令会列出 Swarm 中运行的所有服务,显示它们的 ID、名称、模式、副本数量和镜像。docker service ps my-nginx-service 命令会显示与 my-nginx-service 关联的任务,包括它们的状态(例如,运行中、已关闭)、期望状态以及它们运行所在的节点。你应该会看到 3 个任务处于“运行中”状态。
创建一个全局服务并检查其任务
在这一步中,你将学习如何在 Docker Swarm 中创建全局服务。与运行指定数量任务的副本服务不同,全局服务会在 Swarm 中每个满足服务约束条件的节点上恰好运行一个任务。这对于像监控代理或日志收集器这样需要在每个节点上运行的服务非常有用。
在这个示例中,我们将使用 busybox 镜像。首先,让我们拉取该镜像。
docker pull busybox:latest
此命令会下载最新版本的 busybox 镜像。
现在,让我们创建一个名为 my-global-service 的全局服务。
docker service create --name my-global-service --mode global busybox:latest sleep infinity
这个命令使用 busybox:latest 镜像创建了一个名为 my-global-service 的新服务。--mode global 标志指定这是一个全局服务。sleep infinity 命令用于让容器无限期运行。
要检查全局服务的任务,你可以使用 docker service ps 命令。
docker service ps my-global-service
这个命令会显示与 my-global-service 关联的任务。由于这是一个全局服务,你应该会看到 Swarm 中的每个节点都运行着一个任务。在这个实验环境中,由于只有一个节点,你可能只会看到一个任务。输出会显示任务的状态、期望状态以及它运行所在的节点。
你还可以使用 docker service inspect 命令检查服务配置。
docker service inspect my-global-service
这个命令会提供关于服务的详细信息,包括其模式、副本数量(在服务列表中,全局服务的副本数量为 0,但 docker service ps 会显示每个节点上的任务)以及其他配置细节。
创建一个带有副本和滚动更新策略的服务
在这一步中,你将学习如何创建一个副本服务并配置其滚动更新策略。滚动更新允许你在不中断服务的情况下,逐步将任务替换为新版本,从而将服务更新到新版本。
我们将继续使用 nginx 镜像。让我们创建一个名为 my-nginx-update-service 的副本服务,设置 5 个副本,并配置滚动更新策略。
docker service create \
--name my-nginx-update-service \
--replicas 5 \
--update-delay 10s \
--update-parallelism 2 \
nginx:latest
这个命令创建了一个具有以下配置的服务:
--name my-nginx-update-service:设置服务名称。--replicas 5:将所需的副本数量设置为 5。--update-delay 10s:将每组任务更新之间的延迟设置为 10 秒。--update-parallelism 2:将同时更新的任务数量设置为 2。
这些更新参数定义了当你部署镜像的新版本时,服务将如何进行更新。Docker Swarm 会一次更新 2 个任务,在更新下一批任务之前等待 10 秒。
你可以使用 docker service ls 和 docker service ps 命令检查服务状态及其任务。
docker service ls
docker service ps my-nginx-update-service
你应该会看到 my-nginx-update-service 被列出,副本数量为 5/5,并且任务处于“运行中”状态。
现在,让我们通过将镜像更改为不同版本来模拟一次更新。我们将使用 nginx:1.21 镜像。首先,拉取该镜像。
docker pull nginx:1.21
现在,更新服务以使用 nginx:1.21 镜像。
docker service update --image nginx:1.21 my-nginx-update-service
这个命令会启动滚动更新。Docker Swarm 将根据你之前配置的 --update-parallelism 和 --update-delay 设置,开始用 nginx:1.21 任务替换 nginx:latest 任务。
你可以通过反复运行 docker service ps my-nginx-update-service 来观察滚动更新过程。
docker service ps my-nginx-update-service
你会看到任务从使用旧镜像的“运行中”状态转变为“已关闭”状态,然后新的任务使用新镜像启动并进入“运行中”状态。更新将以每批 2 个任务的方式进行,每批之间有 10 秒的延迟。
创建一个带有绑定挂载和环境变量的服务
在这一步中,你将学习如何创建一个使用绑定挂载(bind mounts)来持久化数据,并使用环境变量来配置容器内应用程序的服务。绑定挂载允许你将主机上的文件或目录挂载到容器中,使容器可以访问这些数据,并且即使容器被移除,数据仍然得以保留。环境变量是向应用程序传递配置信息的常用方式。
首先,让我们在主机上创建一个目录,稍后会将其绑定挂载到容器中。
mkdir -p ~/project/html
这个命令会在你的 ~/project 目录下创建一个名为 html 的目录。
现在,让我们在这个目录中创建一个简单的 HTML 文件。
echo "<h1>Hello from Bind Mount!</h1>" > ~/project/html/index.html
这个命令会在 ~/project/html 目录下创建一个名为 index.html 的文件,其内容为 "
Hello from Bind Mount!
"。我们将再次使用 nginx 镜像。让我们创建一个名为 my-nginx-volume-service 的副本服务,设置 1 个副本,将 ~/project/html 目录绑定到容器内 Nginx 的默认网站根目录(/usr/share/nginx/html),并设置一个环境变量。
docker service create \
--name my-nginx-volume-service \
--replicas 1 \
--publish published=8080,target=80 \
--mount type=bind,source=/home/labex/project/html,target=/usr/share/nginx/html \
--env MY_VARIABLE=hello \
nginx:latest
让我们来详细解释一下这些新选项:
--publish published=8080,target=80:这将主机上的 8080 端口映射到容器内的 80 端口,这样你就可以从主机访问 Nginx 服务器。--mount type=bind,source=/home/labex/project/html,target=/usr/share/nginx/html:这创建了一个绑定挂载。type=bind指定了挂载类型。source=/home/labex/project/html是主机上的路径。target=/usr/share/nginx/html是容器内挂载主机目录的路径。--env MY_VARIABLE=hello:这在容器内设置了一个名为MY_VARIABLE的环境变量,其值为hello。
检查服务状态和任务:
docker service ls
docker service ps my-nginx-volume-service
等待任务进入“运行中”状态。
现在,你可以通过在浏览器中访问 http://localhost:8080 或使用 curl 命令来访问容器中运行的 Nginx 服务器。
curl http://localhost:8080
你应该会看到你创建的 index.html 文件的内容:<h1>Hello from Bind Mount!</h1>。这证实了绑定挂载正常工作。
为了验证环境变量,你可以在运行的容器内执行一个命令。首先,找到正在运行的服务的任务 ID。
docker service ps my-nginx-volume-service
记录输出中的任务 ID。然后,使用 docker exec 在容器内运行一个命令。将 <task_id> 替换为实际的任务 ID。
docker exec < task_id > env | grep MY_VARIABLE
这个命令会在与任务 ID 关联的容器内执行 env 命令,并将输出通过管道传递给 grep MY_VARIABLE 以查找环境变量。你应该会在输出中看到 MY_VARIABLE=hello。
创建一个带有放置约束和偏好设置的服务
在这一步中,你将学习如何使用放置约束(placement constraints)和放置偏好(placement preferences)来控制 Docker Swarm 中服务任务的部署位置。放置约束是节点必须满足的硬性要求,只有满足这些要求,任务才能被调度到该节点上。放置偏好是软性要求,它会影响调度,但如果没有节点满足该偏好,也不会阻止任务被调度。
首先,让我们检查当前节点,查看其标签。标签是可以附加到节点上的键值对,用于提供元数据。
docker node inspect self --format '{{ .Spec.Labels }}'
这个命令会检查当前节点(由 self 标识),并格式化输出以显示其标签。默认情况下,可能没有任何自定义标签。
让我们为当前节点添加一个标签。我们将添加一个标签 node_type=app。
docker node update --label-add node_type=app self
这个命令会更新当前节点,并添加标签 node_type=app。
现在,让我们验证标签是否已添加。
docker node inspect self --format '{{ .Spec.Labels }}'
你应该会在输出中看到 map[node_type:app],这表明标签已成功添加。
现在,让我们创建一个名为 my-constrained-service 的副本服务,并设置一个放置约束,要求节点必须具有标签 node_type=app。我们将使用 nginx 镜像。
docker service create \
--name my-constrained-service \
--replicas 1 \
--constraint 'node.labels.node_type == app' \
nginx:latest
这个命令创建了一个带有约束的服务。--constraint 'node.labels.node_type == app' 指定该服务的任务只能调度到标签 node_type 等于 app 的节点上。由于我们已将此标签添加到当前节点,任务应该会被调度到这里。
检查服务状态和任务:
docker service ls
docker service ps my-constrained-service
你应该会看到 my-constrained-service 被列出,并且其任务正在当前节点上运行。
现在,让我们创建另一个带有放置偏好的服务。放置偏好用于指导调度器,但不是严格强制执行的。我们将使用 busybox 镜像,并偏好具有标签 node_type=database 的节点。由于我们的当前节点没有这个标签,任务仍会被调度到当前节点,但如果有其他具有该标签的节点,调度器会优先选择它们。
首先,拉取 busybox 镜像。
docker pull busybox:latest
现在,创建带有放置偏好的服务。
docker service create \
--name my-preferred-service \
--replicas 1 \
--placement-pref 'spread=node.labels.node_type' \
busybox:latest sleep infinity
--placement-pref 'spread=node.labels.node_type' 选项告诉调度器根据 node_type 标签的值在节点之间分散任务。在具有不同 node_type 标签的多节点 Swarm 中,这将更均匀地分布任务。在这个单节点环境中,任务将简单地调度到可用节点上。
检查服务状态和任务:
docker service ls
docker service ps my-preferred-service
你应该会看到 my-preferred-service 被列出,并且其任务正在当前节点上运行。
总结
在本次实验中,我们学习了如何使用 docker service create 命令在 Docker Swarm 中部署和管理服务。首先,我们使用 nginx 镜像创建了一个简单的副本服务,并指定了所需的副本数量,以实现高可用性和可扩展性。然后,我们使用 docker service ls 和 docker service ps 命令来验证服务的状态并检查其正在运行的任务。
随后,我们探索了全局服务(global service)的创建,了解了它会在 Swarm 中每个符合条件的节点上运行一个任务的特性。这展示了 Docker Swarm 根据不同需求部署服务的灵活性。



