Docker Swarm

学习来源:

Swarm 简介

Docker Swarm 是 Docker 的集群管理工具。它将 Docker 主机池转变为单个虚拟 Docker 主机。 Docker Swarm 提供了标准的 Docker API,所有任何已经与 Docker 守护程序通信的工具都可以使用 Swarm 轻松地扩展到多个主机。

Swarm 架构

docker swarm:管理集群

# 初始化一个swarm
docker swarm init [OPTIONS]
# 选项:
--advertise-addr string     # 发布的地址(格式:<ip|interface>[:port])
--force-new-cluster         # 强制从当前状态创建一个新的集群(去除本地之外的所有管理器身份)
--cert-expiry duration      # 节点证书有效期(ns|us|ms|s|m|h)(默认为2160h0m0s)
--data-path-addr string     # 用于数据路径通信的地址或接口(格式: <ip|interface>)
--data-path-port uint32     # 用于数据路径流量的端口号(1024 - 49151)。如果没有值,则默认端口号4789
--dispatcher-heartbeat duration     # 调度程序的心跳周期(ns|us|ms|s|m|h)(默认为5s)
--listen-addr node-addr     # 监听地址(格式: <ip|interface>[:port]) (默认 0.0.0.0:2377)

# 查看加入节点到集群的命令及令牌(token)
docker swarm join-token [OPTIONS] (worker|manager)
# 选项:
-q, --quiet    # 只显示令牌
    --rotate   # 使旧令牌无效并生成新令牌
# 查看加入工作节点到集群的命令及令牌
docker swarm join-token worker
# 查看加入管理节点到集群的命令及令牌
docker swarm join-token manager

# 将节点加入swarm集群,作为一个manager或worker
docker swarm join [OPTIONS] HOST:PORT
# 选项:
--advertise-addr string   # 发布的地址 (格式: <ip|interface>[:port])
--availability string     # 节点的可用性 ("active"|"pause"|"drain") (default "active")
--data-path-addr string   # 用于数据路径通信的地址或接口 (格式: <ip|interface>)
--listen-addr node-addr   # 监听地址 (格式: <ip|interface>[:port]) (default 0.0.0.0:2377)
--token string            # 进入的swarm集群的令牌     

# 主动退出集群,让节点处于down状态(在需要退出Swarm集群的节点主机上执行命令)
docker swarm leave [OPTIONS]
# 选项:
-f, --force     # 强制。Manager若要退出 Swarm 集群,需要加上强制选项
## 移除一个work-node节点主机的完整步骤:
# 1.在管理节点上操作,清空work-node节点的容器。id 可以使用命令 docker node ls 查看
docker node update --availability drain [id]
# 2.在work-node节点主机上操作,退出集群
docker swarm leave
# 3,在管理节点上操作,删除work-node节点
docker node rm [id]
# 若想解散整个集群,则需先移除所有work-node节点主机,然后所有管理节点也退出集群

# 更新 swarm 集群的配置
docker swarm update [OPTIONS]
# 选项:
--autolock                          # 更改管理器自动锁定设置(true|false)
--cert-expiry duration              # 节点证书有效期(ns|us|ms|s|m|h)(默认为2160h0m0s)
--dispatcher-heartbeat duration     # 调度程序心跳周期(ns|us|ms|s|m|h)(默认为5s)

docker node:管理swarm集群节点

# 查看集群中的节点
docker node ls
#选项:
-f, --filter filter     # 根据所提供的条件过滤输出。(格式:key=value)
                            # 目前支持的过滤器是:id, label, name, membership[=accepted|pending]
                                               # , role[manager|worker]
-q, --quiet             # 只显示id

# 查看运行的一个或多个及节点任务数,默认当前节点
docker node ps [OPTIONS] [NODE...]
#选项:
-f, --filter filter     # 根据所提供的条件过滤输出
-q, --quiet             # 只显示id

# 将worker角色升级为manager
docker node promote NODE [NODE...]
# 将manager角色降级为worker
docker node demote NODE [NODE...]

# 查看节点的详细信息,默认json格式
docker node inspect 主机名
# 查看节点信息平铺格式
docker node inspect --pretty 主机名

# 从swarm中删除一个节点
docker node rm 主机名
# 从swarm中强制删除一个节点
docker node rm -f 主机名

# 更新一个节点
docker node update [options] 主机名
# 选项
--label-add list        # 添加节点标签(key=value)
--label-rm list         # 删除节点标签
--role string           # 更改节点角色 ("worker"|"manager")
--availability active/pause/drain   # 设置节点的状态
                # active  正常
                # pause   暂停。调度程序不向节点分配新任务,但是现有任务仍然保持运行
                # drain   排除自身work任务。调度程序不向节点分配新任务,且会关闭任何现有任务并在可用节点上安排它们

docker service:服务管理

# 列出服务列表
docker service ls

# 列出服务任务信息
docker service ps [OPTIONS] SERVICE [SERVICE...]
# 选项:
    --no-trunc          # 显示完整的信息
-f, --filter filter     # 根据所提供的条件过滤输出。过滤只运行的任务信息:"desired-state=running"
-q, --quiet             # 只显示任务id

# 查看服务内输出
docker service logs [OPTIONS] SERVICE|TASK
# 选项:
    --details        # 显示提供给日志的额外细节
-f, --follow         # 跟踪日志输出
    --since string   # 显示自时间戳 (2013-01-02T13:23:37Z) 或相对时间戳 (42m for 42 minutes) 以来的日志
-n, --tail string    # 从日志末尾显示的行数(默认为“all”)
-t, --timestamps     # 显示时间戳


# 更新服务的相关配置
docker service update [options] 服务名
# 选项
--args "指令"         # 容器加入指令
--image IMAGE        # 更新服务容器镜像
--rollback              # 回滚服务容器版本
--network-add 网络名     # 添加容器网络
--network-rm 网络名      # 删除容器网络
--reserve-cpu int           # 更新分配的cpu
--reserve-memory bytes      # 更新分配的内存(示例:512m)
--publish-add 暴露端口:容器端口     # 映射容器端口到主机
--publish-rm 暴露端口:容器端口      # 移除暴露端口
--endpoint-mode dnsrr        # 修改负载均衡模式为dnsrr
--force                      # 强制重启服务
--config-rm 配置文件名称       # 删除配置文件
--constraint-add list        # 新增一个约束 
--constraint-rm list         # 移除一个约束 
--placement-pref-add pref    # 新增一个偏好 
--placement-pref-rm pref     # 移除一个偏好
--config-add 配置文件名,target=/../容器内配置文件名      # 添加新的配置文件到容器内


# 查看服务详细信息,默认json格式
docker service inspect [OPTIONS] 服务名 [SERVICE...]
# 查看服务信息平铺形式
docker service inspect --pretty 服务名

# 删除服务
docker service rm [OPTIONS] 服务名 [SERVICE...]

# 缩容扩容服务容器副本数量
docker service scale 服务名=副本数 [SERVICE=REPLICAS...]

# 创建一个服务。一般搭建好 Swarm 集群后,使用 docker stack 部署应用,此处仅作了解
docker service create [OPTIONS] IMAGE [COMMAND] [ARG...]
# 选项:
--name string           # 指定容器名称
--replicas int          # 指定副本数
--network 网络名         # 添加网络组
--mode string           # 服务模式(复制或全局)(replicated | global)
--reserve-cpu int       # 预留的cpu
--reserve-memory bytes  # 预留的内存(512m)
--limit-cpu int         # 限制CPU
--limit-memory bytes    # 限制内存(512m)
-l, --label list            # 服务的标签(key=value)
--container-label list      # 容器标签(key=value)
-p, --publish 暴露端口:容器端口   # 映射容器端口到主机
-e, --env MYVAR=myvalue         # 配置环境变量
-w, --workdir string            # 指定工作目录(示例:/tmp)
-restart-condition string       # 满足条件时重新启动(no | always | on-failure | unless-stopped)
--restart-delay duration        # 重新启动尝试之间的延迟 (ns/us/ms/s/m/h)
--restart-max-attempts int      # 放弃前的最大重启次数
--restart-window duration           # 用于评估重启策略的窗口 (ns/us/ms/s/m/h)
--stop-grace-period duration        # 强制杀死容器前的等待时间 (ns/us/ms/s/m/h)
--update-delay duration             # 更新之间的延迟(ns/us/ms/s/m/h)(默认 0s)
--update-failure-action string      # 更新失败的操作("pause"停止|"continue"继续)(默认pause)
--update-max-failure-ratio float    # 更新期间容忍的失败率
--update-monitor duration           # 每次任务更新后监控失败的持续时间(ns/us/ms/s/m/h)(默认 0s)
--update-parallelism int            # 同时更新的最大任务数(0表示一次更新全部任务)(默认为1)  
--endpoint-mode string              # 负载均衡模式(vip or dnsrr) (默认 "vip")
--rollback-monitor 20s              # 每次容器与容器之间的回滚时间间隔
--rollback-max-failure-ratio .数值    # 回滚故障率如果小于百分比允许运行(“.2”为%20)
--mount type=volume,src=volume名称,dst=容器目录    # 创建volume类型数据卷
--mount type=bind,src=宿主目录,dst=容器目录         # 创建bind读写目录挂载
--mount type=bind,src=宿主目录,dst=容器目录,readonly     # 创建bind只读目录挂载
--config source=docker配置文件,target=配置文件路径        # 创建docker配置文件到容器本地目录

docker secret:管理敏感数据存储

# 查看敏感数据卷列表
$ docker secret ls

# 显示一个或多个敏感数据卷的详细信息
$ docker secret inspect [OPTIONS] SECRET [SECRET...]
# 选项
    --pretty        # 易读的格式打印信息

# 从文件或标准输入创建一个敏感数据卷作为内容
$ docker secret create [OPTIONS] SECRET [file|-]
# 选项
  -d, --driver string            # 指定驱动
  -l, --label list               # 指定标签
      --template-driver string   # 指定模板驱动程序

# 移除一个或多个敏感数据卷
$ docker secret rm SECRET [SECRET...]
# 别名:rm, remove

docker config:管理配置文件

# 查看已创建配置文件
docker config ls [OPTIONS]
# 选项:
-f, --filter filter     # 根据所提供的条件过滤输出
-q, --quiet             # 只显示id

# 查看配置详细信息
docker config inspect 配置文件名

# 删除配置
docker config rm CONFIG [CONFIG...]

# 创建配置文件
docker config create 配置文件名 本地配置文件
# 示例:新建配置文件并添加新配置文件到服务
# 1.创建配置文件
docker config create nginx2_config nginx2.conf 
# 2.删除旧配置文件
docker service update --config-rm ce_nginx_config 服务名
# 3.添加新配置文件到服务
docker service update --config-add src=nginx2_config,target=/etc/nginx/nginx.conf ce_nginx

docker network: 网络信息

# 查看集群网络列表
docker network ls

# 将容器连接到集群网络中
$ docker network connect [OPTIONS] NETWORK CONTAINER
# 选项
    --alias strings       # 为容器添加网络范围的别名
    --driver-opt string   · # 指定网络驱动程序
    --ip string         # 指定IPv4地址(如172.30.100.104)
    --ip6 string        # 指定IPv6地址(例如,2001:db8::33)
    --link list         # 添加到另一个容器的链接
    --link-local-ip string    # 为容器添加一个链接本地地址
# 示例
docker network connect mynet nginx

# 断开一个容器与集群网络的连接
$ docker network disconnect [OPTIONS] NETWORK CONTAINER
# 选项
  -f, --force   # 强制容器从网络断开连接  

# 显示一个或多个集群网络的详细信息
$ docker network inspect [OPTIONS] NETWORK [NETWORK...]
# 选项
  -f, --format string   # 使用给定的Go模板格式化输出
  -v, --verbose         # 输出详细的诊断信息

# 创建一个集群网络
$ docker network create [OPTIONS] NETWORK
# 选项
      --attachable           # 准许手动容器连接
      --aux-address map      # 网络驱动使用的辅助IPv4或IPv6地址(默认映射[])
      --config-from string   # 要从其中复制配置的网络
      --config-only          # 创建仅配置网络
  -d, --driver string        # 管理网络的驱动程序(默认为“"bridge”)。选项:bridge、overlay、macvlan
      --gateway strings      # 指定IPv4或IPv6主子网网关。示例:172.20.0.1
      --ingress              # 创建群路由-网格网络
      --internal             # 限制外部访问网络
      --ip-range strings     # 从子范围分配容器ip
      --ipam-driver string   # IP管理驱动(默认为“default”)
      --ipam-opt map         # 设置IPAM驱动程序的特定选项(默认map[])
      --ipv6                 # 启用IPv6网络
      --label list           # 在网络中设置元数据
  -o, --opt map              # 设置驱动程序特定选项(默认map[])
      --scope string         # 控制网络的范围
      --subnet strings       # 指定一个CIDR格式的网段。示例:172.20.0.0/24
# 示例:
docker network create -d overlay --attachable apps_net

# 移除所有未使用的集群网络
$ docker network prune [OPTIONS]
# 选项
      --filter filter   # 提供过滤值(e.g. 'until=<timestamp>') 
  -f, --force           # 强制,没有提示确认

# 删除一个或多个集群网络
$ docker network rm NETWORK [NETWORK...]
# 别名:rm, remove

开始配置

环境准备

1、修改主机名 2、配置IP地址 3、关闭防火墙和SELINUX安全模式 4、配置系统YUM源和Docker镜像源 5、更新系统(yum update -y) 6、安装好docker环境

初始化集群

docker swarm init --advertise-addr 192.168.65.148

root@chenjx12-0:~# docker swarm init --advertise-addr 192.168.65.148
Swarm initialized: current node (1e0chr8bpn27vlq9g056zy7a2) is now a manager.

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

    docker swarm join --token SWMTKN-1-10u0nu575ou6thien8kscddxebzsnatljdytcas3n9sgin5jog-9cu9j19pfitelu1oap7rgdlub 192.168.65.148:2377

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


root@chenjx12-0:~# docker swarm join-token manager
To add a manager to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-10u0nu575ou6thien8kscddxebzsnatljdytcas3n9sgin5jog-ejpgz6tdq0xsm3c6qjrstq299 192.168.65.148:2377

让其他节点加入集群

root@chenjx12-0:~# docker node ls
ID                            HOSTNAME     STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
1e0chr8bpn27vlq9g056zy7a2 *   chenjx12-0   Ready     Active         Leader           27.3.1
jb55cgd88n5tl2ybdokhznq1t     chenjx12-1   Ready     Active                          27.3.1
oav94gisk9wm4mfuqvzpfhqzh     chenjx12-2   Ready     Active                          27.3.1
g6vxdk2myltc9i9lwx1zr3s4i     chenjx12-3   Ready     Active                          27.3.1

查看详细情况

root@chenjx12-0:~# docker info
Client: Docker Engine - Community
 Version:    27.3.1
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.17.1
    Path:     /usr/libexec/docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  v2.29.7
    Path:     /usr/libexec/docker/cli-plugins/docker-compose

Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 0
 Server Version: 27.3.1
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: systemd
 Cgroup Version: 2
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
 Swarm: active
  NodeID: 1e0chr8bpn27vlq9g056zy7a2
  Is Manager: true
  ClusterID: ye2nnvev8mzdyv0g56ee0ka6w
  Managers: 1
  Nodes: 4
  Data Path Port: 4789
  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.65.148
  Manager Addresses:
   192.168.65.148:2377
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 7f7fdf5fed64eb6a7caf99b3e12efcf9d60e311c
 runc version: v1.1.14-0-g2c9f560
 init version: de40ad0
 Security Options:
  apparmor
  seccomp
   Profile: builtin
  cgroupns
 Kernel Version: 6.1.0-26-amd64
 Operating System: Debian GNU/Linux 12 (bookworm)
 OSType: linux
 Architecture: x86_64
 CPUs: 4
 Total Memory: 1.887GiB
 Name: chenjx12-0
 ID: 8d684424-29b4-4611-8a59-d4ce19b0f9da
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false

WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled

查看网络信息

root@chenjx12-0:~# docker network ls
NETWORK ID     NAME              DRIVER    SCOPE
a162d4d5a77e   bridge            bridge    local
1f622764003c   docker_gwbridge   bridge    local
8f11bd442416   host              host      local
d8isx3sccunf   ingress           overlay   swarm
3b8eea1a0902   none              null      local

root@chenjx12-0:~# docker network inspect d8isx3sccunf
[
    {
        "Name": "ingress",
        "Id": "d8isx3sccunf33xeb63c1v21x",
        "Created": "2024-10-28T19:50:32.014998441+08:00",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.0.0.0/24",
                    "Gateway": "10.0.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": true,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "ingress-sbox": {
                "Name": "ingress-endpoint",
                "EndpointID": "f2dd609d5bc4ba0cb7206b7e4bdc3cd1ba232d727841b693805eecd1b591f7b9",
                "MacAddress": "02:42:0a:00:00:02",
                "IPv4Address": "10.0.0.2/24",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "4096"
        },
        "Labels": {},
        "Peers": [
            {
                "Name": "b4947f7ebf00",
                "IP": "192.168.65.148"
            },
            {
                "Name": "bac21fa67653",
                "IP": "192.168.65.154"
            },
            {
                "Name": "d6c037335dd0",
                "IP": "192.168.65.155"
            },
            {
                "Name": "3b5f5c73c94c",
                "IP": "192.168.65.152"
            }
        ]
    }
]

更换节点的角色(manager – worker)

root@chenjx12-0:~# docker node update -h
Flag shorthand -h has been deprecated, use --help

Usage:  docker node update [OPTIONS] NODE

Update a node

Options:
      --availability string   Availability of the node ("active", "pause", "drain")
      --label-add list        Add or update a node label ("key=value")
      --label-rm list         Remove a node label if exists
      --role string           Role of the node ("worker", "manager")


root@chenjx12-0:~# docker node update --role manager chenjx12-1
chenjx12-1
root@chenjx12-0:~# docker node ls
ID                            HOSTNAME     STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
1e0chr8bpn27vlq9g056zy7a2 *   chenjx12-0   Ready     Active         Leader           27.3.1
jb55cgd88n5tl2ybdokhznq1t     chenjx12-1   Ready     Active         Reachable        27.3.1
oav94gisk9wm4mfuqvzpfhqzh     chenjx12-2   Ready     Active                          27.3.1
g6vxdk2myltc9i9lwx1zr3s4i     chenjx12-3   Ready     Active                          27.3.1

root@chenjx12-0:~# docker node update --role worker chenjx12-1
chenjx12-1
root@chenjx12-0:~# docker node ls
ID                            HOSTNAME     STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
1e0chr8bpn27vlq9g056zy7a2 *   chenjx12-0   Ready     Active         Leader           27.3.1
jb55cgd88n5tl2ybdokhznq1t     chenjx12-1   Ready     Active                          27.3.1
oav94gisk9wm4mfuqvzpfhqzh     chenjx12-2   Ready     Active                          27.3.1
g6vxdk2myltc9i9lwx1zr3s4i     chenjx12-3   Ready     Active                          27.3.1

节点退出集群

在3号节点上使用命令,回到0号节点发现3号节点下线了(down)

root@chenjx12-0:~# docker swarm leave -h
Flag shorthand -h has been deprecated, use --help

Usage:  docker swarm leave [OPTIONS]

Leave the swarm

Options:
  -f, --force   Force this node to leave the swarm, ignoring warnings

root@chenjx12-3:~# docker swarm leave -f
Node left the swarm.

root@chenjx12-0:~# docker node ls
ID                            HOSTNAME     STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
1e0chr8bpn27vlq9g056zy7a2 *   chenjx12-0   Ready     Active         Leader           27.3.1
jb55cgd88n5tl2ybdokhznq1t     chenjx12-1   Ready     Active                          27.3.1
oav94gisk9wm4mfuqvzpfhqzh     chenjx12-2   Ready     Active                          27.3.1
g6vxdk2myltc9i9lwx1zr3s4i     chenjx12-3   Down      Active                          27.3.1

删除节点

docker node rm [id]

注意,删除节点无法删除manager管理节点,需要先--role worker,才能rm

服务部署

查看help

root@chenjx12-0:~# docker service

Usage:  docker service COMMAND

Manage Swarm services

Commands:
  create      Create a new service
  inspect     Display detailed information on one or more services
  logs        Fetch the logs of a service or task
  ls          List services
  ps          List the tasks of one or more services
  rm          Remove one or more services
  rollback    Revert changes to a service's configuration
  scale       Scale one or multiple replicated services
  update      Update a service

Run 'docker service COMMAND --help' for more information on a command.

先在leader上pull一个nginx

root@chenjx12-0:/etc/docker# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
a480a496ba95: Pull complete 
f3ace1b8ce45: Pull complete 
11d6fdd0e8a7: Pull complete 
f1091da6fd5c: Pull complete 
40eea07b53d8: Pull complete 
6476794e50f4: Pull complete 
70850b3ec6b2: Pull complete 
Digest: sha256:28402db69fec7c17e179ea87882667f1e054391138f77ffaf0c3eb388efc3ffb
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest

使用service命令启动Nginx

root@chenjx12-0:~# docker service create -p 8888:80 --name xybdiy-nginx nginx
qvx9t2qskhr8g0r1i7y5f9ub9
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service qvx9t2qskhr8g0r1i7y5f9ub9 converged 

使用docker services创建服务,具有扩缩容,滚动更新

查看服务

root@chenjx12-0:~# docker service ls
ID             NAME           MODE         REPLICAS   IMAGE          PORTS
qvx9t2qskhr8   xybdiy-nginx   replicated   1/1        nginx:latest   *:8888->80/tcp
root@chenjx12-0:~# docker service ps xybdiy-nginx
ID             NAME             IMAGE          NODE         DESIRED STATE   CURRENT STATE                ERROR     PORTS
7jzyf300ygqh   xybdiy-nginx.1   nginx:latest   chenjx12-1   Running         Running about a minute ago             
root@chenjx12-0:~# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

root@chenjx12-1:~# docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS     NAMES
1efdebcefcd0   nginx:latest   "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes   80/tcp    xybdiy-nginx.1.7jzyf300ygqhnj8zfukkjmrtp


root@chenjx12-0:~# docker service inspect xybdiy-nginx
[
    {
        "ID": "qvx9t2qskhr8g0r1i7y5f9ub9",
        "Version": {
            "Index": 76
        },
        "CreatedAt": "2024-10-28T13:46:32.118348646Z",
        "UpdatedAt": "2024-10-28T13:46:32.127340473Z",
        "Spec": {
            "Name": "xybdiy-nginx",
            "Labels": {},
            "TaskTemplate": {
                "ContainerSpec": {
                    "Image": "nginx:latest@sha256:28402db69fec7c17e179ea87882667f1e054391138f77ffaf0c3eb388efc3ffb",
                    "Init": false,
                    "StopGracePeriod": 10000000000,
                    "DNSConfig": {},
                    "Isolation": "default"
                },
                "Resources": {
                    "Limits": {},
                    "Reservations": {}
                },
                "RestartPolicy": {
                    "Condition": "any",
                    "Delay": 5000000000,
                    "MaxAttempts": 0
                },
                "Placement": {
                    "Platforms": [
                        {
                            "Architecture": "amd64",
                            "OS": "linux"
                        },
                        {
                            "Architecture": "unknown",
                            "OS": "unknown"
                        },
                        {
                            "OS": "linux"
                        },
                        {
                            "Architecture": "unknown",
                            "OS": "unknown"
                        },
                        {
                            "OS": "linux"
                        },
                        {
                            "Architecture": "unknown",
                            "OS": "unknown"
                        },
                        {
                            "Architecture": "arm64",
                            "OS": "linux"
                        },
                        {
                            "Architecture": "unknown",
                            "OS": "unknown"
                        },
                        {
                            "Architecture": "386",
                            "OS": "linux"
                        },
                        {
                            "Architecture": "unknown",
                            "OS": "unknown"
                        },
                        {
                            "Architecture": "mips64le",
                            "OS": "linux"
                        },
                        {
                            "Architecture": "unknown",
                            "OS": "unknown"
                        },
                        {
                            "Architecture": "ppc64le",
                            "OS": "linux"
                        },
                        {
                            "Architecture": "unknown",
                            "OS": "unknown"
                        },
                        {
                            "Architecture": "s390x",
                            "OS": "linux"
                        },
                        {
                            "Architecture": "unknown",
                            "OS": "unknown"
                        }
                    ]
                },
                "ForceUpdate": 0,
                "Runtime": "container"
            },
            "Mode": {
                "Replicated": {
                    "Replicas": 1
                }
            },
            "UpdateConfig": {
                "Parallelism": 1,
                "FailureAction": "pause",
                "Monitor": 5000000000,
                "MaxFailureRatio": 0,
                "Order": "stop-first"
            },
            "RollbackConfig": {
                "Parallelism": 1,
                "FailureAction": "pause",
                "Monitor": 5000000000,
                "MaxFailureRatio": 0,
                "Order": "stop-first"
            },
            "EndpointSpec": {
                "Mode": "vip",
                "Ports": [
                    {
                        "Protocol": "tcp",
                        "TargetPort": 80,
                        "PublishedPort": 8888,
                        "PublishMode": "ingress"
                    }
                ]
            }
        },
        "Endpoint": {
            "Spec": {
                "Mode": "vip",
                "Ports": [
                    {
                        "Protocol": "tcp",
                        "TargetPort": 80,
                        "PublishedPort": 8888,
                        "PublishMode": "ingress"
                    }
                ]
            },
            "Ports": [
                {
                    "Protocol": "tcp",
                    "TargetPort": 80,
                    "PublishedPort": 8888,
                    "PublishMode": "ingress"
                }
            ],
            "VirtualIPs": [
                {
                    "NetworkID": "d8isx3sccunf33xeb63c1v21x",
                    "Addr": "10.0.0.5/24"
                }
            ]
        }
    }
]

可以看到该服务被部署到1号节点上,0节点本地docker ps是没有的,而1节点下出现了该服务

多个部署,动态扩容

root@chenjx12-0:~# docker service update --replicas 2 xybdiy-nginx
xybdiy-nginx
overall progress: 2 out of 2 tasks 
1/2: running   [==================================================>] 
2/2: running   [==================================================>] 
verify: Service xybdiy-nginx converged 

由于第一个服务前面已经部署在1号节点上了,所以这次只需要再部署一个

root@chenjx12-0:~# docker service ls
ID             NAME           MODE         REPLICAS   IMAGE          PORTS
qvx9t2qskhr8   xybdiy-nginx   replicated   2/2        nginx:latest   *:8888->80/tcp
root@chenjx12-0:~# docker service ps xybdiy-nginx
ID             NAME             IMAGE          NODE         DESIRED STATE   CURRENT STATE           ERROR     PORTS
7jzyf300ygqh   xybdiy-nginx.1   nginx:latest   chenjx12-1   Running         Running 9 minutes ago             
i5m1yshi8p8o   xybdiy-nginx.2   nginx:latest   chenjx12-0   Running         Running 2 minutes ago  

可以看到这次部署到了0号和1号两个节点上,但是注意啊:

1号节点本身是worker,用来运行和部署服务是没问题的

但是0号节点原来是manager呀,甚至还是leader

所以,再次查看节点信息会发现:

root@chenjx12-0:~# docker node ls
ID                            HOSTNAME     STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
1e0chr8bpn27vlq9g056zy7a2 *   chenjx12-0   Ready     Active         Reachable        27.3.1
jb55cgd88n5tl2ybdokhznq1t     chenjx12-1   Ready     Active                          27.3.1
oav94gisk9wm4mfuqvzpfhqzh     chenjx12-2   Ready     Active                          27.3.1
u6x1nn2xjqsfchbtlegne7swf     chenjx12-3   Ready     Active         Leader           27.3.1

leader被转移到另一个manager节点(3号)上了

在Docker Swarm集群中,如果当前的leader节点变得不可用或者其资源不足以运行新的任务,leadership(领导权)会自动转移到另一个manager节点上。这是Swarm集群的高可用特性之一。

在您描述的场景中,如果有一个manager节点被部署了nginx服务,并且这个manager节点恰好是当前的leader,以下情况可能会发生:

  1. 如果该manager节点只是被分配了nginx服务,并且它仍然能够处理manager节点的职责,那么它仍然可以保持leader的角色。
  2. 如果部署nginx服务导致该manager节点过载,或者由于某些原因它变得不可用(比如网络问题、硬件故障等),Swarm集群会检测到这个问题,并进行以下操作:
    • 集群中的其他manager节点会通过raft协议选举出一个新的leader。
    • leadership将从当前不可用的manager节点转移到新的leader节点上。

为了确保Swarm集群的稳定性和高可用性,通常建议将manager节点专用于管理任务,而不要在它们上面运行worker任务。这样可以确保manager节点有足够的资源来处理集群管理和决策任务,即使在节点故障的情况下也能保持集群的稳定运行。

如果您确实需要在manager节点上运行服务,可以通过以下策略来降低风险:

  • 限制在manager节点上运行的服务数量,确保它们不会消耗过多资源。
  • 使用资源限制和预留,确保manager节点有足够的资源来执行其管理职责。

如果我们需要保持manager节点不要运行服务,专心管理worker,并且避免leader转移,我们可以使用下面的命令

docker service create \
  --name my_nginx \
  --constraint 'node.role != manager' \
  nginx

如果我们要更具体一点,限制它不会部署在某个节点上:

使用节点ip限制
docker service create \
  --name my_nginx \
  --constraint 'node.id != node_id' \
  nginx

使用节点名称限制
docker service create \
  --name my_nginx \
  --constraint 'node.hostname != node_name' \
  nginx

虽然看上去是只部署在了0号和1号节点,但是实际上你访问集群中任意一个ip的8888端口都能够看到nginx页面

这就是swarm集群的负载均衡

故障

前面可以知道,虽然我部署只在两个服务器上但是我访问集群中任意一个ip都能得到服务

那如果我某个,或者多个服务器挂了呢

  • 首先,我把所有的(两个)manager挂了

    得到的结果是:访问剩下两个worker节点的ip均能得到服务,但是manager不行

  • 我把一个manager和一个woker挂了

    得到的结果是:还是能访问到,但是挂了的不行

    并且最后的manager报错:

    root@chenjx12-3:~# docker node ls Error response from daemon: rpc error: code = Unknown desc = The swarm does not have a leader. It’s possible that too few managers are online. Make sure more than half of the managers are online.

  • 把两个worker都挂掉

    结果是:也是仅存活的两个才能访问,但是使用docker node ls指令不会报错了

所有就引出了我们swarm集群的节点管理规则:

Docker Swarm集群节点规划

为了利用 swarm 模式的容错功能,可以根据组织的高可用性要求实现奇数个节点。 当有多个管理中心时,可以从一个管理中心节点的故障中恢复,而无需停机。

  • 三个管理器群可以容忍最多损失一个管理器。
  • 五个管理器群最多可以同时丢失两个管理器节点。
  • 七个管理器群最多可以同时丢失三个管理器节点。
  • 九个管理器群最多可以同时丢失四个管理器节点。
  • Docker 建议一个群最多有七个管理器节点。 (添加更多管理器并不意味着可伸缩性或性能的提高。一般来说,情况恰恰相反。)