基于 Swarm 的 Docker 集群管理
一、Swarm简介
Swarm是Docker的一个编排工具,参考官网:https://docs.docker.com/engine/swarm/
1、Swarm 模式简介
要在Swarm模式下运行docker,需要先安装docker,参考安装教程
当前版本的docker包含了swarm模式,用于管理docker集群。可以使用命令行来创建swarm集群,部署应用,管理swarm的行为。
如果你使用低于1.12.0版本的docker,可以使用独立模式的是swarm,但是建议使用最新版本
2、Swarm 特性
- 与docker集成的集群管理工具
- 去中心化设计,只使用docker引擎即可创建各类节点
- 声明式服务模型。可以声明的方式来定义应用。
- 动态伸缩。管理节点自动调整服务数量。
- 高可用,对于服务期望状态做到动态调整,swarm的管理节点会持续监控集群状态,集群中有没有达到期望状态的服务,管理节点会自动调度来达到期望状态。
- 自定义网络。可以为你的服务指定一个网络,容器创建的时候分配一个IP
- 服务发现。管理节点给集群中每个服务一个特定的DNS名字,并给运行的容器提供负载均衡。
- 负载均衡。你可以暴露服务端口给外部的负载均衡。内部swarm提供可配置的容器分配到节点的策略。
- 默认的安全机制。swarm集群中各个节点强制TLS协议验证。连接加密,你可以自定义根证书。
- 滚动更新。增量跟新,可以自定义更新下个节点的时间间隔,如果有问题,可以会滚到上个版本。
3、Swarm 主要概念
(1)开始使用Swarm模式
后续文档按如下流程进行:
- 在swarm模式下初始化一个基于docker引擎的swarm集群
- 在swarm集群中添加节点
- 部署应用服务到swarm集群中
- 管理swarm集群
- 本教程使用docker命令行的方式交互
(2)安装环境要求
- 准备3台主机,3台可以网络通信的linux主机,可以是物理机,虚拟机,云主机,甚至是docker machine创建的主机,并且3台主机都要安装docker(docker版本要大于1.12.0,安装步骤参考:在linux上安装docker),本例3台主机名称分别为:cnkanon-1、cnkanon-2、cnkanon-3
- 安装1.12.0以上的docker,本例中 docker 版本为:v18.09.6, build 481bc77156
- 管理节点的IP地址
- 主机之间开放端口
(3)管理节点的IP地址
所有swarm集群中的节点都能连接到管理节点的IP地址。
(4)端口开放
- 以下端口需要开放
2377/tcp 为集群管理通信
7946/tcp、7946/udp 为节点间通信
4789/udp 为网络间流量
- 如果你想使用加密网络(–opt encrypted)也需要确保ip protocol 50 (ESP)是可用的
二、创建一个 Swarm 集群
完成上面的开始过程后,可以开始创建一个swarm集群,确保docker的后台应用已经在主机上运行了。
- 登陆到 cnkanon-1 上,如果使用 docker-machine 创建的主机,要求可以 docker-machine ssh cnkanon-1
- 运行以下命令来创建一个新的swarm集群:
docker swarm init --advertise-addr <MANAGER1-IP>
- 使用如下命令在 cnkanon-1 上创建swarm集群:
[root@cnkanon-1 ~]# docker swarm init --advertise-addr 192.168.56.3
Swarm initialized: current node (82a19cjqf5gvorp2p5jxwhnn6) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-2if9pe4h2bvnv2izdaqckxsbhxferygh9h9enh7uqpalyi284o-3hp093o5gyb9oimfn4v64zqz1 192.168.56.3:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
- –advertise-addr 选项表示管理节点公布它的IP是多少。其它节点必须能通过这个IP找到管理节点。
- 命令输出了加入swarm 集群的命令。通过 --token 选项来判断是加入为管理节点还是工作节点
3、运行 docker info
来查看当前 swarm 集群的状态:
[root@cnkanon-1 ~]# docker info
Containers: 8
Running: 1
Paused: 0
Stopped: 7
Images: 5
Server Version: 18.09.6
Storage Driver: overlay2
Backing Filesystem: xfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: active
NodeID: 82a19cjqf5gvorp2p5jxwhnn6
Is Manager: true
ClusterID: 7jmo6jykf14u1lpd90lwinxso
Managers: 1
Nodes: 1
Default Address Pool: 10.0.0.0/8
SubnetSize: 24
Orchestration:
Task History Retention Limit: 5
Raft:
Snapshot Interval: 10000
Number of Old Snapshots to Retain: 0
Heartbeat Tick: 1
Election Tick: 10
Dispatcher:
Heartbeat Period: 5 seconds
CA Configuration:
Expiry Duration: 3 months
Force Rotate: 0
Autolock Managers: false
Root Rotation In Progress: false
Node Address: 192.168.56.3
Manager Addresses:
192.168.56.3:2377
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: bb71b10fd8f58240ca47fbb579b9d1028eea7c84
runc version: 2b18fe1d885ee5083ef9f0838fee39b62d653e30
init version: fec3683
Security Options:
seccomp
Profile: default
Kernel Version: 3.10.0-957.21.2.el7.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 1
Total Memory: 991.2MiB
Name: cnkanon-1
ID: RZDA:2RQT:Q5M5:HHTW:VTTD:E4GW:ZNHK:E7GI:D6HV:PHFD:NGDZ:TOUE
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Registry Mirrors:
https://registry.docker-cn.com/
http://hub-mirror.c.163.com/
https://docker.mirrors.ustc.edu.cn/
http://ef017c13.m.daocloud.io/
Live Restore Enabled: false
Product License: Community Engine
WARNING: API is accessible on http://0.0.0.0:2375 without encryption.
Access to the remote API is equivalent to root access on the host. Refer
to the 'Docker daemon attack surface' section in the documentation for
more information: https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface
4、运行 docker node ls
来查看节点信息:
[root@cnkanon-1 ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
82a19cjqf5gvorp2p5jxwhnn6 * cnkanon-1 Ready Active Leader 18.09.6
- nodeId 旁边的 * 号表示你当前连接到的节点
- docker 引擎的swarm模式自动使用宿主机的主机名作为节点名
三、将节点加入到 Swarm 集群
1、找回指令
如果你找不到加入命令了,可以在管理节点运行下列命令找回加入命令:
[root@cnkanon-1 ~]# docker swarm join-token worker
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-2if9pe4h2bvnv2izdaqckxsbhxferygh9h9enh7uqpalyi284o-3hp093o5gyb9oimfn4v64zqz1 192.168.56.3:2377
2、加入节点到集群
一旦前面的创建swarm集群完成,你就可以加入工作节点了。
登录到服务器 cnkanon-2、cnkanon-3,将这两个服务器加入到集群 cnkanon-1
运行如下指令加入到集群中:
# 服务器 cnkanon-2
[root@cnkanon-2 ~]# docker swarm join --token SWMTKN-1-2if9pe4h2bvnv2izdaqckxsbhxferygh9h9enh7uqpalyi284o-3hp093o5gyb9oimfn4v64zqz1 192.168.56.3:2377
This node joined a swarm as a worker.
# 服务器 cnkanon-3
[root@cnkanon-3 ~]# docker swarm join --token SWMTKN-1-2if9pe4h2bvnv2izdaqckxsbhxferygh9h9enh7uqpalyi284o-3hp093o5gyb9oimfn4v64zqz1 192.168.56.3:2377
This node joined a swarm as a worker.
返回服务器 cnkanon-1 管理节点查看集群中节点列表:
[root@cnkanon-1 ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
82a19cjqf5gvorp2p5jxwhnn6 * cnkanon-1 Ready Active Leader 18.09.6
a9durycfoospewwyf7gvyoo35 cnkanon-2 Ready Active 18.09.6
s879hzzvfqc9l2ixmj130ved4 cnkanon-3 Ready Active 18.09.6
3、部署服务
创建好 swarm 集群后,就可以部署服务了(也可以不加入工作节点,直接在管理节点单节点上部署服务),回到管理节点 cnkanon-1,基于 portainer/portainer 镜像创建服务:
[root@cnkanon-1 ~]# docker service create --replicas=1 --name=dev-portainer --publish=9000:9000 portainer/portainer
3k2ptlq025amaevlb2zmebsj4
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
–replicas:表示期望1个服务实例
–name:表示创建服务的名称
–publish:表示映射的端口
如果指定的镜像未下载,则系统会自动下载,本例中的 portainer/portainer 镜像是提前下载好的
在管理节点 cnkanon-1 上查看运行的服务:
[root@cnkanon-1 ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
3k2ptlq025am dev-portainer replicated 1/1 portainer/portainer:latest *:9000->9000/tcp
4、查看服务详情
在管理节点 cnkanon-1 上运行如下命令查看服务详情
[root@cnkanon-1 ~]# docker service inspect dev-portainer --pretty
ID: 3k2ptlq025amaevlb2zmebsj4
Name: dev-portainer
Service Mode: Replicated
Replicas: 1
Placement:
UpdateConfig:
Parallelism: 1
On failure: pause
Monitoring Period: 5s
Max failure ratio: 0
Update order: stop-first
RollbackConfig:
Parallelism: 1
On failure: pause
Monitoring Period: 5s
Max failure ratio: 0
Rollback order: stop-first
ContainerSpec:
Image: portainer/portainer:latest@sha256:cc226d8a06b6d5e24b44a4f10d0d1fd701741e84a852adc6d40bef9424a000ec
Init: false
Resources:
Endpoint Mode: vip
Ports:
PublishedPort = 9000
Protocol = tcp
TargetPort = 9000
PublishMode = ingress
–pretty:表示以 yaml 格式显示,不加此参数将以 json 格式显示
5、查看服务运行在哪个节点
在管理节点 cnkanon-1 上运行指令查看 dev-portainer 服务运行在 swarm 集群中的哪个节点:
[root@cnkanon-1 ~]# docker service ps dev-portainer
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
ei18defdqie6 dev-portainer.1 portainer/portainer:latest cnkanon-1 Running Running 4 minutes ago
in1rwx6g63kd \_ dev-portainer.1 portainer/portainer:latest cnkanon-1 Shutdown Failed 4 minutes ago "task: non-zero exit (1)"
服务可能运行在管理或工作节点上,默认的管理节点可以像工作节点一样运行任务
该命令也显示服务期望的状态 DESIRED STATE,和实际的状态 CURRENT STATE
6、在swarm集群中动态伸缩服务实例数
一旦你在swarm集群中创建一个服务后,你就可以使用命令来改变服务的实例个数。在服务中运行的容器称为“任务”。
在管理节点 cnkanon-1 中执行如下指令将 dev-portainer 的实例个数变为3个:
[root@cnkanon-1 ~]# docker service scale dev-portainer=3
dev-portainer scaled to 3
overall progress: 3 out of 3 tasks
1/3: running [==================================================>]
2/3: running [==================================================>]
3/3: running [==================================================>]
verify: Service converged
scale S E R V I C E N A M E / {SERVICE_NAME}/ SERVICENAME/{SERVICE_ID} = n:伸缩服务的实例为指定个数
在管理节点 cnkanon-1 上查看服务 dev-portainer 的运行情况及实例个数:
# 查看服务运行情况
[root@cnkanon-1 ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
3k2ptlq025am dev-portainer replicated 3/3 portainer/portainer:latest *:9000->9000/tcp
# 查看服务运行的实例个数
[root@cnkanon-1 ~]# docker service ps dev-portainer
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
rugy9x8je284 dev-portainer.1 portainer/portainer:latest cnkanon-1 Running Running about a minute ago
1a5hyn0g23c8 dev-portainer.2 portainer/portainer:latest cnkanon-3 Running Running 4 minutes ago
tjgi9ptunfg8 dev-portainer.3 portainer/portainer:latest cnkanon-2 Running Running 4 minutes ago
可以看出,dev-portainer 服务运行情况中,REPLICAS=3 表示服务运行实例已扩展为3个
dev-portainer 服务的运行节点 NODE 有 cnkanon-1、cnkanon-2、cnkanon-3,共3个节点
现在分别登录工作节点 cnkanon-2、cnkanon-3 查看服务 dev-portainer 的运行情况:
# 工作节点 cnkanon-2
[root@cnkanon-2 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a91f1c529425 portainer/portainer:latest "/portainer" 3 minutes ago Up 3 minutes 9000/tcp dev-portainer.3.va0p1mllzqnte3cghnbp19mk6
# 工作节点 cnkanon-3
[root@cnkanon-3 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e69063554d71 portainer/portainer:latest "/portainer" 3 minutes ago Up 3 minutes 9000/tcp dev-portainer.2.ene51pt4kpqtq7czpduqrmn9w
可以看到,dev-portainer.3 运行在工作节点 cnkanon-2 上,dev-portainer.2 运行在工作节点 cnkanon-3 上。
在管理节点 cnkanon-1 中尝试将 dev-portainer 的实例个数变为2个,会发现并不会动态删除第3个节点上的容器,只是停止第3个容器:
# 在管理节点 cnkanon-1 上将 dev-portainer 的运行实例缩放到2个节点
[root@cnkanon-1 ~]# docker service scale dev-portainer=2
dev-portainer scaled to 2
overall progress: 2 out of 2 tasks
1/2: running [==================================================>]
2/2: running [==================================================>]
verify: Service converged
# 在管理节点 cnkanon-1 上查看 dev-portainer 运行情况
[root@cnkanon-1 ~]# docker service ps dev-portainer
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
rugy9x8je284 dev-portainer.1 portainer/portainer:latest cnkanon-1 Running Running about a minute ago
1a5hyn0g23c8 dev-portainer.2 portainer/portainer:latest cnkanon-3 Running Running 4 minutes ago
tjgi9ptunfg8 dev-portainer.3 portainer/portainer:latest cnkanon-2 Shutdown Failed 3 minutes ago "task: non-zero exit (1)"
# 登录工作节点 cnkanon-2 查看 dev-portainer 已不在运行
[root@cnkanon-2 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAME
7、从swarm集群中删除服务
在管理节点 cnkanon-1 中删除 dev-portainer:
[root@cnkanon-1 ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
3k2ptlq025am dev-portainer replicated 2/2 portainer/portainer:latest *:9000->9000/tcp
[root@cnkanon-1 ~]# docker service rm dev-portainer
dev-portainer
[root@cnkanon-1 ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
[root@cnkanon-1 ~]# docker service ps dev-portainer
no such service: dev-portainer
[root@cnkanon-1 ~]# docker service inspect dev-portainer
[]
Status: Error: no such service: dev-portainer, Code: 1
尽管服务不存在了,任务容器还需要几秒钟来清理,你可以在工作节点 cnkanon-2、cnkanon-3 上 docker ps 查看任务什么时候被移除。
8、更新、回滚服务
swarm 集群有一项非常重要的功能:对服务进行滚动更新、滚动回滚以及对服务资源配置进行更新。
(1)更新服务资源配置
(2)滚动更新服务
在swarm集群中可以对服务的版本进行升级,此例中以 nginx:1.16.0 版本为例,先基于 nginx:1.16.0 创建服务,设置 replicas=3,创建3个实例运行来运行,再将服务升级到 nginx:1.17 版本:
# 先下载 nginx:1.16.0 镜像
[root@cnkanon-1 ~]# docker pull nginx:1.16.0
# 创建服务,并指定实例数为3
[root@cnkanon-1 ~]# docker service create --replicas=3 --name=dev-nginx --publish=80:80 nginx:1.16.0
# 查看服务版本为 nginx:1.16.0
[root@cnkanon-1 ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
uyqnphtca0lx dev-nginx replicated 3/3 nginx:1.16.0 *:80->80/tcp
现在将 nginx:1.16.0 版本升级到 nginx:1.17.0 版本:
# 更新版本到 nginx:1.17.0
[root@cnkanon-1 ~]# docker service update --image nginx:1.17 dev-nginx
dev-nginx
overall progress: 3 out of 3 tasks
1/3: running [==================================================>]
2/3: running [==================================================>]
3/3: running [==================================================>]
verify: Service converged
# 查看服务版本为 nginx:1.17.0
[root@cnkanon-1 ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
uyqnphtca0lx dev-nginx replicated 3/3 nginx:1.17 *:80->80/tcp
# 查看服务运行情况,版本为 nginx:1.17.0
[root@cnkanon-1 ~]# docker service ps dev-nginx
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
3zstw4g8a3vg dev-nginx.1 nginx:1.17 cnkanon-2 Running Running about a minute ago
hug1u1seuzwg \_ dev-nginx.1 nginx:1.16.0 cnkanon-2 Shutdown Shutdown 2 minutes ago
9av4gh54tk3o dev-nginx.2 nginx:1.17 cnkanon-3 Running Running 54 seconds ago
63ajbftz8dp3 \_ dev-nginx.2 nginx:1.16.0 cnkanon-3 Shutdown Shutdown about a minute ago
spzalsgvt7w8 dev-nginx.3 nginx:1.17 cnkanon-1 Running Running about a minute ago
y3wyuqq06ldy \_ dev-nginx.3 nginx:1.16.0 cnkanon-1 Shutdown Shutdown about a minute ago
调度器 scheduler 依照以下步骤来滚动更新:
- 停止第一个任务
- 对停止的任务进行更新
- 对更新的任务进行启动
- 如果更新的任务返回RUNNING,等待特定间隔后启动下一个任务
- 如果在任何更新的时间,任务返回了FAILED,则停止更新。
可以看出执行更新指令后,并未将 nginx:1.16.0 版本删除,而是停止了并且新创建了 nginx:1.17 版本的服务
同时可以指定相应参数来制定滚动更新策略:
- –update-delay:配置更新服务的时间间隔,可以指定时间T为秒是Ts,分是Tm,或时是Th,所以10m30s就是10分30秒的延迟
- 默认的调度器 scheduler 一次更新一个任务,使用参数 --update-parallelism 来配置调度器同时更新的最大任务数量
- 默认的当一个更新任务返回RUNNING状态后,调度器才调度另一个更新任务,直到所有任务都更新了。如果更新过程中任何任务返回了FAILED,调度器就会停止更新。可以给命令
docker service create or docker service update
配置配置 --update-failure-action,来设置这个行为。
(3)回滚更新服务
将上述更新到 nginx:1.17.0 版本的服务回滚到 nginx:1.16.0 版本,其滚动回滚的原理和滚动更新类似,并不会删除 nginx:1.17.0 服务,只是停止,也不会重启之前的 nginx:1.16.0 版本的服务,而是直接再创建一个 nginx:1.16.0 版本的服务:
# 执行回滚
[root@cnkanon-1 ~]# docker service update --rollback dev-nginx
dev-nginx
rollback: manually requested rollback
overall progress: rolling back update: 3 out of 3 tasks
1/3: running [> ]
2/3: running [> ]
3/3: running [> ]
verify: Service converged
# 查看服务运行情况,running状态的版本为 nginx:1.16.0,停止状态的版本为 nginx:1.16.0、nginx:1.17.0
[root@cnkanon-1 ~]# docker service ps dev-nginx
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
0jlkh0ci9r42 dev-nginx.1 nginx:1.16.0 cnkanon-2 Running Running about a minute ago
3zstw4g8a3vg \_ dev-nginx.1 nginx:1.17 cnkanon-2 Shutdown Shutdown about a minute ago
hug1u1seuzwg \_ dev-nginx.1 nginx:1.16.0 cnkanon-2 Shutdown Shutdown 13 minutes ago
54sciwitz0he dev-nginx.2 nginx:1.16.0 cnkanon-3 Running Running about a minute ago
9av4gh54tk3o \_ dev-nginx.2 nginx:1.17 cnkanon-3 Shutdown Shutdown about a minute ago
63ajbftz8dp3 \_ dev-nginx.2 nginx:1.16.0 cnkanon-3 Shutdown Shutdown 12 minutes ago
spzalsgvt7w8 dev-nginx.3 nginx:1.17 cnkanon-1 Shutdown Shutdown about a minute ago
y3wyuqq06ldy \_ dev-nginx.3 nginx:1.16.0 cnkanon-1 Shutdown Shutdown 13 minutes ago
9、从swarm集群中下线一个节点
- 前面的教程中管理节点会把任务分配给 ACTIVE 的节点,所有 ACTIVE 的节点都能接到任务
- 有时候,例如特定的维护时间,我们就需要从集群中下线一个节点。下线节点使节点不会接受新任务,管理节点会停止该节点上的任务,分配到别的 ACTIVE 的节点上。
注意:下线一个节点不移除节点中的独立容器,如
docker run,docker-compose up、docker api
启动的容器都不会删除。节点的状态仅影响集群服务的负载是否分到该节点。
# swarm 集群中3个节点都牌 ACTIVE 状态
[root@cnkanon-1 ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
82a19cjqf5gvorp2p5jxwhnn6 * cnkanon-1 Ready Active Leader 18.09.6
a9durycfoospewwyf7gvyoo35 cnkanon-2 Ready Active 18.09.6
s879hzzvfqc9l2ixmj130ved4 cnkanon-3 Ready Active 18.09.6
# swarm 集群中有一个服务 nginx:1.16.0 在2个节点运行
[root@cnkanon-1 ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
uyqnphtca0lx dev-nginx replicated 2/2 nginx:1.16.0 *:80->80/tcp
# nginx:1.16.0 服务分别运行在节点 cnkanon-2、cnkanon-3 上
[root@cnkanon-1 ~]# docker service ps dev-nginx
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
0jlkh0ci9r42 dev-nginx.1 nginx:1.16.0 cnkanon-2 Running Running about a minute ago
3zstw4g8a3vg \_ dev-nginx.1 nginx:1.17 cnkanon-2 Shutdown Shutdown about a minute ago
hug1u1seuzwg \_ dev-nginx.1 nginx:1.16.0 cnkanon-2 Shutdown Shutdown 13 minutes ago
54sciwitz0he dev-nginx.2 nginx:1.16.0 cnkanon-3 Running Running about a minute ago
9av4gh54tk3o \_ dev-nginx.2 nginx:1.17 cnkanon-3 Shutdown Shutdown about a minute ago
63ajbftz8dp3 \_ dev-nginx.2 nginx:1.16.0 cnkanon-3 Shutdown Shutdown 12 minutes ago
spzalsgvt7w8 dev-nginx.3 nginx:1.17 cnkanon-1 Shutdown Shutdown about a minute ago
y3wyuqq06ldy \_ dev-nginx.3 nginx:1.16.0 cnkanon-1 Shutdown Shutdown 13 minutes ago
将节点 cnkanon-3 下线:
# 更新节点 cnkanon-3 的状态为 DRAIN
[root@cnkanon-1 ~]# docker node update --availability drain cnkanon-3
cnkanon-3
# swarm 集群中节点 cnkanon-3 状态已变为 DRAIN
[root@cnkanon-1 ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
82a19cjqf5gvorp2p5jxwhnn6 * cnkanon-1 Ready Active Leader 18.09.6
a9durycfoospewwyf7gvyoo35 cnkanon-2 Ready Active 18.09.6
s879hzzvfqc9l2ixmj130ved4 cnkanon-3 Ready Drain 18.09.6
# 服务 nginx:1.16.0 依旧在2个节点运行,但已经从 cnkanon-3 切换到了 cnkanon-1
[root@cnkanon-1 ~]# docker service ps dev-nginx
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
0jlkh0ci9r42 dev-nginx.1 nginx:1.16.0 cnkanon-2 Running Running 6 hours ago
3zstw4g8a3vg \_ dev-nginx.1 nginx:1.17 cnkanon-2 Shutdown Shutdown 6 hours ago
hug1u1seuzwg \_ dev-nginx.1 nginx:1.16.0 cnkanon-2 Shutdown Shutdown 6 hours ago
9esjmlrua9hv dev-nginx.2 nginx:1.16.0 cnkanon-1 Running Running about a minute ago
54sciwitz0he \_ dev-nginx.2 nginx:1.16.0 cnkanon-3 Shutdown Shutdown about a minute ago
9av4gh54tk3o \_ dev-nginx.2 nginx:1.17 cnkanon-3 Shutdown Shutdown 6 hours ago
63ajbftz8dp3 \_ dev-nginx.2 nginx:1.16.0 cnkanon-3 Shutdown Shutdown 6 hours ago
spzalsgvt7w8 dev-nginx.3 nginx:1.17 cnkanon-1 Shutdown Shutdown 6 hours ago
y3wyuqq06ldy \_ dev-nginx.3 nginx:1.16.0 cnkanon-1 Shutdown Shutdown 6 hours ago
- 当节点重新active的时候,在以下情况下它会重新接受任务:
- 当一个服务缩容扩容时
- 在滚动更新的时候
- 当另一个节点Drain下线的时候
- 当一个任务在另一个active节点上运行失败的时候
10、swarm 集群中的路由网络
- docker的swarm模式使服务暴露给外部端口更加方便。所有的节点都在一个路由网络里。这个路由网络使得集群内的所有节点都能在开放的端口上接受请求。即使节点上没有任务运行,这个服务的端口也暴露的。路由网络路由所有的请求到暴露端口的节点上。
- 前提是需要暴露以下端口来使节点间能通信
- 7946/tcp、7946/udp 用于容器间网络发现
- 4789/udp 用于容器进入网络
- 你也必须开放节点之间的公开端口,和任何外部资源端口,例如一个外部的负载均衡
- 你也可以使特定服务绕过路由网络
(1)创建服务时暴露端口
- 使用
--publish
来在创建一个服务的时候暴露端口。target指明容器内暴露的端口。published 指明绑定到路由网络上的端口。如果不写published,就会为每个服务绑定一个随机的高数字端口。你需要检查任务才能确定端口
docker service create --name=dev-nginx --publish=published=8080,target=80 --replicas=1 nginx:1.16.0
注意旧版的语法是冒号分开的published:target,例如 -p 8080:80。新语法更易读且灵活。
当你在任何节点访问8080端口时,路由网络将把请求分发到一个active的容器中。在各个节点,8080端口可能并没有绑定,但是路由网络知道如何路由流量,并防止任何端口冲突。
路由网络监听各个节点的IP上的 published port 。从外面看,这些端口是各个节点暴露的。对于别的IP地址,只在该主机内可以访问。
(2)对已存在服务暴露端口
- 也可以用以下命令给一个已经存在的服务暴露端口
docker service update --publish-add published=8080,target=80 dev-nginx
# 查看暴露端口情况
docker service inspect --format="{{json .Endpoint.Spec.Ports}}" dev-nginx
[{"Protocol":"tcp","TargetPort":80,"PublishedPort":8080,"PublishMode":"ingress"}]
(3)仅暴露一个tcp/udp端口
- 默认你暴露的端口都是tcp的。如果你使用长语法(Docker 1.13 and higher),设置protocol为tcp或udp即可暴露相应协议的端口
# 仅暴露 tcp 端口
# 长语法
docker service create --name=dev-nginx dns-cache --publish=published=8080,target=80 dns-cache
# 短语法
docker service create --name=dev-nginx dns-cache -p 53:53 dns-cache
# 暴露 tcp和udp 端口
# 长语法
docker service create --name=dev-nginx dns-cache --publish=published=8080,target=80 --publish=published=6379,target=6379,protocol=udp dns-cache
# 短语法
docker service create --name=dev-nginx dns-cache -p 53:53 -p 53:53/udp dns-cache
# 仅暴露 udp 端口
# 长语法
docker service create --name=dev-nginx dns-cache --publish=published=8080,target=80,protocol=udp dns-cache
#短语法
docker service create --name=dev-nginx dns-cache -p 53:53/udp dns-cache
(4)绕过路由网络
- 你可以绕过路由网络,直接和一个节点上的端口通信,来访问服务。这叫做Host模式:
如果该节点上没有服务运行,服务也没有监听端口,则可能无法通信。
你不能在一个节点上运行多个服务实例他们绑定同一个静态target端口。或者你让docker分配随机高数字端口(通过空配置target),或者确保该节点上只运行一个服务实例(通过配置全局服务global service 而不是副本服务,或者使用配置限制)。
- 为了绕过路由网络,必须使用长格式–publish,设置模式mode为host模式。如果你忽略了mode设置或者设置为内网ingress,则路由网络将启动。下面的命令创建了全局应用使用host模式绕过路由网络:
docker service create --name=dev-nginx dns-cache --publish=published=8080,target=80,protocol=udp,mode=host --mode global dns-cache
(5)配置外部负载均衡
可以为swarm集群配置外部的负载均衡,或者结合路由网络使用或者完全不使用
- 使用路由网络
使用一个外部的HAProxy来负载均衡,服务是8080端口上的nginx服务
上图中负载均衡和集群节点之间的8080端口必须是开放的。swarm集群节点在一个外部不可访问的内网中,节点可以与HAProxy通信。
可以配置负载均衡分流请求到不同的集群节点,即使节点上没有服务运行
当请求HAProxy的80端口的时候,它会转发请求到后端节点。swarm的路由网络会路由到相应的服务节点。这样无论任何原因swarm的调度器调度服务到不同节点,都不需要重新配置负载均衡。
可以配置任何类型的负载均衡来分流请求。
- 不使用路由网络
如果不使用路由网络,配置--endpoint-mode
的值为dnsrr,而不是vip。在本例子中没有一个固定的虚拟IP。Docker为服务做了DNS注册,这样一个服务的DNS查询会返回一系列IP地址。客户端就可以直接连接其中一个节点。你负责提供这一系列的IP地址,开放端口给你的负载均衡器。参考Configure service discovery.