Docker 三剑客之 Docker Swarm

docker

Docker Swarm是管理跨节点容器的编排工具,相较于Docker Compose而言,Compose只能编排单节点上的容器,Swarm将一群Docker节点虚拟化为一个主机,使得用户只要在单一主机上操作就能完成对整个容器集群的管理工作。

准备工作

准备三个节点,要求实现奇数个节点

How nodes work | Docker Documentation

1
2
3
192.168.2.1 ————作为manager
192.168.2.2 ————作为node1
192.168.2.3 ————作为node2

三个节点在同一个网段,确保相互之间可以ping通

安装Docker-Engine

Install Docker Engine on CentOS | Docker Documentation

查看是否安装成功

1
2
3
4
[root@manager ~]# docker --version
Docker version 20.10.17, build 100c701
[root@manager ~]# docker compose version
Docker Compose version v2.6.0

节点管理

节点的工作原理|Docker 文档

如下图所示,swarm 集群由管理节点(manager)和工作节点(work node)构成。

  • swarm mananger:负责整个集群的管理工作包括集群配置、服务管理等所有跟集群有关的工作。
  • work node:即图中的 available node,主要负责运行相应的服务来执行任务(task)

群模式集群

在创建集群之前,使用docker node ls想查看下集群中节点的信息,反馈目前没有节点信息,并且当前节点并不是manager

1
2
[root@manager ~]# docker node ls
Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again.

创建一个Swarm

在创建集群之前,使用docker node ls想查看下集群中节点的信息,反馈目前没有节点信息,并且当前节点并不是manager

1
2
[root@manager ~]# docker node ls
Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again.

创建新的集群

1
docker swarm init --advertise-addr 192.168.2.1 

示例:

1
2
3
4
5
6
7
8
[root@manager ~]# docker swarm init --advertise-addr 192.168.2.81
Swarm initialized: current node (44qw5hbq836fryftoc51pnlbn) is now a manager.

To add a worker to this swarm, run the following command:

docker swarm join --token SWMTKN-1-xxxxxxxxxxx-cyl00cwc71rp0jfp59eyfhp4x 192.168.2.81:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

docker swarm join-token manager 命令用于获取添加新的 Manager Node 的命令参数

docker swarm join-token worker 命令用于获取添加新的 Worker Node 的命令参数

加入Swarm

可以在其它节点上执行docker swarm join --token......来将该节点设置为工作node,并加入到这个swarm集群中

目前演示的是一个manager,两个工作node的模式,所以在node1node2上执行第一个命令即可:

1
docker swarm join --token SWMTKN-1-xxxxxxxx-cyl00cwc71rp0jfp59eyfhp4x 192.168.2.81:2377

常用命令

命令 说明
docker swarm init 初始化集群
docker swarm join-token worker 查看工作节点的 token
docker swarm join-token manager 查看管理节点的 token
docker swarm join 加入集群

参考命令:docker swarm | Docker Documentation

查看节点信息

1
docker info
image-20220704145815755

查看节点列表

1
docker node ls

节点 ID 旁边的*表示当前已在此节点上连接

1
2
3
4
5
[root@manager ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
44qw5hbq836fryftoc51pnlbn * manager Ready Active Leader 20.10.17
q3yd5vno8t4c9hgi47mqztzbr node1 Ready Active 20.10.17
svwqj9t6g4izhsbnh0oneq4lw node2 Ready Active 20.10.17

注意,manager是管理集群的入口,docker命令都是在manager上执行,node节点上是不能执行docker命令的

1
2
[root@node1 ~]# docker node ls
Error response from daemon: This node is not a swarm manager. Worker nodes can't be used to view or modify cluster state. Please run this command on a manager node or promote the current node to a manager.

查看节点详细信息

使用以下命令查看节点的详情:

1
docker node inspect --pretty 节点名称

添加新的 Manager Node 到集群

在另一台机器运行 docker swarm join 命令加入已存在的集群中

1
docker swarm join --token SWMTKN-1-5d47eim8blk5jh37ww2eua7ve0r3u4w106j7oaoxuf4ilva0tw-cyl00cwc71rp0jfp59eyfhp4x 192.168.2.81:2377

Manager Node 必须是单数(Raft),生产环境推荐3台或5台作为 Manager Node

多数管理节点的分区继续对集群进行管理

docker swarm join-token manager 命令用于获取添加新的 Manager Node 的命令参数

global vs replicated services

添加新的 Worker Node 到集群

在另一台机器运行

docker swarm join 命令加入已存在的集群中

1
docker swarm join --token SWMTKN-1-5qv7t73fvawvh795ckh3nxl9vnyo2hwwsqnnjwqyav3spj7ufu-1i7wir7oc3g9fh7yidg19i8p5 192.168.1.80:2377

docker swarm join-token worker 命令用于获取添加新的 Worker Node 的命令参数

禁用节点

在生产环境 Manager Node 不推荐运行任何容器实例,但是 Swarm 调度器会分配给Manager Node,

可以通过 禁用节点 告诉 Swarm 调度器不要分配给 Manager Node 任何容器实例。

1
docker node update --availability drain 节点名称

启用节点

禁用节点后使用以下命令即可启用节点

1
docker node update --availability active 节点名称

更新节点

1
docker node update --label-add foo --label-add bar=baz 节点名称

–label-add:给节点添加标签,可以用来控制服务放置

常用命令

命令 说明
docker node ls 查看所有集群节点
docker node rm 删除某个节点(-f强制删除)
docker node inspect 查看节点详情
docker node demote 节点降级,由管理节点降级为工作节点
docker node promote 节点升级,由工作节点升级为管理节点
docker node update 节点升级,由工作节点升级为管理节点
docker node ps 查看节点中的 Task 任务

参考命令:docker node | Docker Documentation

服务管理

参考文档:将服务部署到群|Docker 文档

How services work | Docker Documentation

将服务部署到 swarm 时,swarm 管理器接收服务定义作为服务的所需状态。然后,它将群中的节点上的服务调度为一个或多个副本任务。这些任务在群中的节点上彼此独立运行。

例如,假设在 HTTP 侦听器的三个实例之间进行负载平衡。下图显示了具有三个副本的 HTTP 侦听器服务。侦听器的三个实例中的每一个都是群中的一个任务。

服务图

运行服务

连接到 Manager Node,使用

docker service create 命令创建服务.

例:

1
docker service create --replicas 1 --name helloworld alpine ping docker.com
  • --name 指定服务名称为 helloworld

  • --replicas 指定服务运行实例数量为 1

  • 参数 alpine 表示运行的镜像为 Alpine Linux

  • 参数 ping docker.com 表示在容器中执行的命令

查看运行的服务

在 Manager Node 运行此命令查看正在运行的服务列表:

1
docker service ls

例:

1
2
3
4
5
[root@manager ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
82bpay4gdvd0 deploy-demo_nginx replicated 3/3 nginx:latest *:8088->80/tcp
7cad5hto20ql deploy-demo_portainer replicated 1/1 portainer/portainer:latest *:9000->9000/tcp
520x2l3lcwyp redis replicated 2/2 redis:3.0.6 *:6379->6379/tcp

查看服务的详细信息

在 Manager Node 运行此命令查看服务的运行详情:

1
docker service inspect --pretty 服务名称

参数 --pretty 表示返回格式化后的详细信息,不加这个参数则打印 JSON 格式的信息

查看服务运行在哪些节点

在 Manager Node 使用此命令查看服务都在那些节点运行:

1
docker service ps 服务名称

docker-swarm 中的服务实例由 swarm 调度。因此有部分服务的实例运行在 Manager Node 是正常表现。

伸缩服务

docker-swarm 支持对服务实例进行动态伸缩,使用以下命令即可实现:

1
docker service scale 服务名称=实例数量(最少为1)

例:

1
2
3
4
5
6
[root@manager ~]# docker service scale redis=2
redis scaled to 2
overall progress: 2 out of 2 tasks
1/2: running [==================================================>]
2/2: running [==================================================>]
verify: Service converged

删除服务

在 Manager Node 使用以下命令删除服务:

1
docker service rm 服务名称

注意,因为是集群的原因,集群中的Node将会存在延迟的情况,想确认服务是否被删除成功请使用 docker service ls 查看

滚动更新服务

创建服务

进入 Manager Node 创建一个redis服务用于演示滚动更新:

1
2
3
4
5
docker service create \
--replicas 3 \
--name redis \
--update-delay 10s \
redis:3.0.6

--update-dely 表示更新服务或服务集之间的时间延迟:1h10m3s,表示延迟1小时10分钟3秒。

调度器默认一次更新一个任务,可以通过 --update-parallelism 参数配置调度器同时更新服务数量。

默认情况下,当单个服务更新返回状态为 RUNNING,调度器会让另一个服务更新,直到所有服务都更新完成。

如果在更新期间某个服务返回 FAILED ,调度器会暂停更新,可以通过 --update-failure-action 参数配置控制当服务更新发生错误时的行为。

检查服务状态

1
docker service inspect --pretty redis

更新服务

1
docker service update --image redis:3.0.7 redis

默认情况下,调度器将按以下方式更新服务:

  • 停止一个服务

  • 更新已停止的服务

  • 启动已更新的服务

如果更新的服务返回 RUNNING ,等待指定的延迟时间后开始更新下一个服务

如果更新期间某个服务返回 FAILED ,则暂停服务更新

重新启动暂停的服务更新

1
docker service update redis

为了避免重复某些失败的更新,可以重新指定更新参数

查看服务的滚动更新

1
docker service ps redis

在swarm更新完成所有服务之前,可以看到一些服务的镜像为 redis:3.0.6,另一些为 redis:3.0.7

指定服务约束

泊坞窗服务创建|Docker 文档

将服务部署到群|Docker 文档

placement_prefs

常用命令

命令 说明
docker service create 部署服务
docker service inspect 查看服务详情
docker service logs 查看某个服务日志
docker service ls 查看所有服务详情
docker service rm 删除某个服务(-f强制删除)
docker service scale 设置某个服务个数
docker service update 更新某个服务

命令参考:docker service | Docker Documentation

路由网格

docker swarm支持路由网格。路由网格让处于swarm集群中的任意一个节点都可以作为被访问的入口,即使此节点没有运行任何服务。

要在 swarm 集群中使用使用路由网格,首先需要开启加入swarm集群的节点的以下端口:

  • 7946 :容器网络发现

  • 4789 :容器网络入口

其次需要将节点服务实例的端口公开,使服务可以被外部访问(例如使用nginx做负载均衡)

服务原理

使用群模式路由网格|Docker 文档

服务入口映像

创建服务时公开端口

1
2
3
4
docker service create \
--name <SERVICE-NAME> \
--publish published=<PUBLISHED-PORT>,target=<CONTAINER-PORT> \
<IMAGE>

--publish-p 效果相同,其中 --published 值为公布的端口,target 值为容器内部监听的端口。--publish 的写法

更新现有服务的公开端口

1
2
3
docker service update \
--publish-add published=<PUBLISHED-PORT>,target=<CONTAINER-PORT> \
<SERVICE>

查看服务发布的端口

1
docker service inspect --format="{{json .Endpoint.Spec.Ports}}" 服务名称

只公开TCP或UDP端口

默认情况下公开端口都是 TCP 端口,你可以通过参数配置公开端口的类型:

仅TCP

1
2
3
docker service create --name dns-cache \
--publish published=53,target=53 \
dns-cache

1
2
3
docker service create --name dns-cache \
-p 53:53 \
dns-cache

仅UDP

1
2
3
docker service create --name dns-cache \
--publish published=53,target=53,protocol=udp \
dns-cache

1
2
3
docker service create --name dns-cache \
-p 53:53/udp \
dns-cache

TCP+UDP

1
2
3
4
docker service create --name dns-cache \
--publish published=53,target=53 \
--publish published=53,target=53,protocol=udp \
dns-cache

1
2
3
4
docker service create --name dns-cache \
-p 53:53 \
-p 53:53/udp \
dns-cache

绕过路由网格

要绕过 swarm 集群的路由网格,需要使用

--publish 参数设置 mode 值为host

下面的命令使用

host 模式创建全局服务并绕过路由网格:

1
2
3
4
docker service create --name dns-cache \
--publish published=53,target=53,protocol=udp,mode=host \
--mode global \
dns-cache

绕过路由网格后的注意事项:

如果你访问未运行服务的节点,则无法访问此服务

如果你希望在每个节点运行多个服务,就不能指定静态的端口。要么就允许docker随机分配一个公开端口(通过置空 published 参数的值实现)

Stack-Deploy

Deploy a stack to a swarm | Docker Documentation

正式部署集群服务,使用nginx镜像做为示例

1
2
3
4
5
6
7
[root@manager ~]# docker service create --replicas 3 -p 8088:80 --name nginx nginx:latest
ap8h8srb8yh3mni0h2nz61njz
overall progress: 3 out of 3 tasks
1/3: running [==================================================>]
2/3: running [==================================================>]
3/3: running [==================================================>]
verify: Service converged

--replicas 3表示创建服务的实例个数(默认1个),在个Docker节点上,分别创建一个nginx服务,REPLICAS会有进度显示,并且执行是异步的

查看服务

1
2
3
4
[root@manager ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
82bpay4gdvd0 deploy-demo_nginx replicated 3/3 nginx:latest *:8088->80/tcp
520x2l3lcwyp redis replicated 2/2 redis:3.0.6 *:6379->6379/tcp

访问任一节点8088端口:

image-20220706214254711

docker service部署的是单体服务,我使用docker stack进行多服务编排部署,使用的同样是docker-compose.yml配置文件,示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
version: "3"

services:
nginx:
image: nginx:latest
ports:
- 8088:80
deploy:
mode: replicated
replicas: 3

portainer:
image: portainer/portainer:latest
ports:
- "9000:9000"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
replicas: 1
placement:
constraints: [node.role == manager] ## 约束服务 仅在manager node 分配

部署命令:

1
2
3
[root@manager ~]# docker stack deploy -c docker-compose.yml deploy-demo
Creating service deploy-demo_nginx
Creating service deploy-demo_portainer

查看部署详情:

1
2
3
[root@manager ~]# docker stack ls
NAME SERVICES ORCHESTRATOR
deploy-demo 2 Swarm

访问portainer,可以方便的查看和管理所有的服务和堆栈

swarm-portainer

编写支持docker stackdocker-compose.yml

Compose file version 3 reference | Docker Documentation

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
version: "3.9"
services:
db:
image: redis:3.0.5
deploy:
replicas: 6 # 启动实例数量
placement: # 配置容器实例位置------------
max_replicas_per_node: 2 # 每个节点最多运行容器实例数量
constraints: # 将容器分配到匹配标签的节点运行
- "node.role==manager"
- "engine.labels.operatingsystem==ubuntu 18.04"
preferences: # 将任务平均分配到不同类别的节点上
- spread: node.labels.zone
resources: # 资源限制------------
limits: # 占用上限
cpus: '0.50'
memory: 50M
reservations: # 启动占用
cpus: '0.25'
memory: 20M
restart_policy: # 重启策略------------
condition: on-failure # 发生失败时
delay: 5s # 重启时间间隔
max_attempts: 3 #最大尝试次数
window: 120s # 判断是否重启成功的等待时长
update_config: # 滚动更新的配置------------
parallelism: 2 # 同时更新的数量
delay: 10s # 每次更新间隔时间
monitor: 5s # 每次更新监控失败持续的时长
failure_action: 'pause' # 滚动更新出现错误时执行的操作: continue/rollback/pause
max_failure_ratio: # 允许的更新失败率
order: stop-first # 更新顺序 stop-first(旧任务在启动新任务之前停止)或start-first(新任务首先启动,运行中的任务会出现短暂重叠)
rollback_config: # 更新失败如何回滚------------
parallelism: 2 # 每次回滚的数量,如果为0则全部回滚
delay: 10s # 每次回滚间隔时间
monitor: 5s # 每次更新监控失败持续的时长
failure_action: 'pause' # 回滚出现错误时执行的操作: continue/rollback/pause
max_failure_ratio: # 允许的回滚失败率,默认为0
order: stop-first # 回滚顺序 stop-first(旧任务在启动新任务之前停止)或start-first(新任务首先启动,运行中的任务会出现短暂重叠)

docker-stack 不支持的docker-compose配置

常用命令

命令 说明
docker stack deploy 部署新的堆栈或更新现有堆栈
docker stack ls 列出现有堆栈
docker stack ps 列出堆栈中的任务
docker stack rm 删除堆栈
docker stack services 列出堆栈中的服务
docker stack down 移除某个堆栈(不会删除数据)

参考命令:docker stack | Docker Documentation

其他文档

配置管理

普通配置

使用 Docker 配置|存储配置数据Docker 文档

加密配置

锁定您的集群以保护其加密密钥|Docker 文档

锁定集群

锁定您的集群以保护其加密密钥|Docker 文档

管理指南

管理和维护一组 Docker 引擎|Docker 文档

遇到的坑

问题描述

使用swarm搭建集群时出现如下错误:

1
Error response from daemon: rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: Error while dialing dial tcp 192.168.0.108:2377: connect: no route to host"

这个错误是因为将node节点加入swarm中导致的,原因就是manager节点这台机器上的防火墙没有关闭。

问题解决

把manager这台机器上的防火墙关闭

1
2
3
4
5
6
## 查看防火墙状态
systemctl status firewalld.service
## 停止防火墙
systemctl stop firewalld.service
## 禁用防火墙
systemctl disable firewalld.service

Docker Swarm 错误 :

1
error creating external connectivity network: Failed to Setup IP tables: Unable to enable SKIP DNAT rule: (iptables failed: iptables --wait -t nat -I DOCKER -i docker_gwbridge -j RETURN: iptables: No chain/target/match by that name. (exit status 1))

关闭防火墙后需要重启Docker

1
2
service docker restart
systemctl restart docker