15、Docker swarm-2-安装与存储
Docker swarm-2
本章要点:docker-swarm集群安装部署,docker swarm volume使用
参考文档:Docker Swarm 全面总结,Docker Swarm介绍,Docker之Swarm详细讲解,Swarm 集群管理,compose 多主机网络共享 overlay,纳米ai, 接上章
Swarm 集群部署
基础环境准备
-  环境准备 IP 主机名 系统 内核 架构 10.4.50.130 manager130 centos7 3.10.0-1127 x86_64 10.4.50.139 node139 centos7 3.10.0-1127 x86_64 10.4.50.161 node161 centos7 3.10.0-1127 x86_64 
-  修改主机名 # 10.4.50.130 执行 ]# hostnamectl set-hostname manager130 # 10.4.50.139 执行 ]# hostnamectl set-hostname node139 # 10.4.50.167 执行 ]# hostnamectl set-hostname node167
-  添加hosts # 三台都加一下 cat >> /etc/hosts << EOF 10.4.50.130 manager130 10.4.50.139 node139 10.4.50.167 node161 EOF
-  同步一下时间服务器 yum -y install ntpdate ntpdate ntp1.aliyun.com# 如果时间不一致,在加入节点时会提示以下异常 Error response from daemon: error while validating Root CA Certificate: x509: certificate has expired or is not yet valid: current time 2025-10-29T09:32:09+08:00 is before 2025-10-29T07:34:00Z
-  设置防火墙 端口 说明 2377/tcp 管理端口 7946/tcp/udp 节点间通信端口 4789/udp overlay 网络端口 # 全节点, 测试环境可以stop ~]# firewall-cmd --zone=public --add-port=2377/tcp --permanent ~]# firewall-cmd --zone=public --add-port=7946/tcp --permanent ~]# firewall-cmd --zone=public --add-port=7946/udp --permanent ~]# firewall-cmd --zone=public --add-port=4789/udp --permanent # 重新加载生效 ~]# firewall-cmd --reload # 查看 ~]# firewall-cmd --list-ports# selinux 关闭 ~]# setenforce 0
集群配置
-  创建swarm集群 # 创建集群 ~]# docker swarm init --advertise-addr=10.4.50.130 # 服务器初始会自动加入swarm集群, 同时也会给出一个唯一token值,后续节点加入会用到. # --advertise-addr 参数表示其它 swarm 中的 worker 节点使用此ip地址与 manager 联系 # 如果忘了token 也可以用提示的命令进行查看 ~]# docker swarm join-token manager-  如果想删除 # 这里的leave就是在集群中删除节点,-f参数强制删除,执行完在重新执行OK ~]# docker swarm leave -f
-  查看集群想关信息 # 集群主节点 ~]# docker info [root@manager130 ~]# docker info Client: Docker Engine - CommunityVersion: 26.1.4Context: defaultDebug Mode: falsePlugins:buildx: Docker Buildx (Docker Inc.)Version: v0.14.1Path: /usr/libexec/docker/cli-plugins/docker-buildxcompose: Docker Compose (Docker Inc.)Version: v2.27.0Path: /usr/libexec/docker/cli-plugins/docker-composeServer:Storage Driver: overlay2Swarm: activeNodeID: lptzlefacgceb16moa9c3ay3xIs Manager: trueClusterID: jthxgdgiuhc8j7j4az9zxfwyuManagers: 1Nodes: 2Data Path Port: 4789Orchestration:Task History Retention Limit: 5Raft:Snapshot Interval: 10000Number of Old Snapshots to Retain: 0Heartbeat Tick: 1Election Tick: 10Dispatcher:Heartbeat Period: 5 secondsCA Configuration:Expiry Duration: 3 monthsForce Rotate: 0Node Address: 10.4.50.130Manager Addresses:10.4.50.130:2377# 为了对比, 这里直接先拉出节点部分描述信息 # 139、docker infoSwarm: activeNodeID: ottqbgjip0wgp6md372klhn76Is Manager: falseNode Address: 10.4.50.139Manager Addresses:10.4.50.130:2377# 167、docker infoSwarm: activeNodeID: jggun3821zhi3odkv0a1z94k8Is Manager: falseNode Address: 10.4.50.167Manager Addresses:10.4.50.130:2377
 
-  
-  添加 node 节点主机到 Swarm 集群 -  加入节点 # 如果忘了token 也可以用提示的命令进行查看 ~]# docker swarm join-token manager <-- 主节点上查询# 139 跟 161 上都执行 ~]# docker swarm join --token SWMTKN-1-1k8zohbti5pynhwj1j3fqco9nlx61czwxjvbrab9ifxnnsm966-0yrfl9po2at7m6a32chuzmis2 10.4.50.130:2377 This node joined a swarm as a worker.
-  查看节点加入信息 ~]# docker node ls -  AVAILABILITY 的三种状态: - Active: 调度器能够安排任务到该节点。
- Pause: 调度器不能够安排任务到该节点,但是已经存在的任务会继续运行。
- Drain: 调度器不能够安排任务到该节点,而且会停止已存在的任务,并将这些任务分配到其他 Active 状态的节点。
 
-  改变节点的可用性(availability) ~]# docker node update --help 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")# master节点上执行 ~]# docker node update --availability drain 节点名 当node139的状态改为drain后,那么该节点就不会接受task任务分发,就算之前已经接受的任务也会转移到别的节点上 
-  MANAGER STATUS 的三种状态: - Leader: 为群体做出所有群管理和编排决策的主要管理者节点。
- Reachable: 如果 Leader 节点变为不可用,该节点有资格被选举为新的 Leader。
- Unavailable: 该节点不能和其他 Manager 节点产生任何联系,这种情况下,应该添加一个新的 Manager 节点到集群,或者将一个 Worker 节点提升为 Manager 节点。
 
-  升级/降级节点,示例 -  升级 ~]# docker node promote node139 # 升级为节点侯选者 Node node139 promoted to a manager in the swarm.# 此时我们再去查看 docker info --> 139上查看Swarm: activeNodeID: ottqbgjip0wgp6md372klhn76Is Manager: trueClusterID: xzv8uujn592j7hqhdwnzvy0zyManagers: 2Nodes: 3Data Path Port: 4789Orchestration:Task History Retention Limit: 5Raft:Snapshot Interval: 10000Number of Old Snapshots to Retain: 0Heartbeat Tick: 1Election Tick: 10Dispatcher:Heartbeat Period: 5 secondsCA Configuration:Expiry Duration: 3 monthsForce Rotate: 0Autolock Managers: falseRoot Rotation In Progress: falseNode Address: 10.4.50.139Manager Addresses:10.4.50.130:237710.4.50.139:2377# 查看 docker info --> 130上查看,除了NodeID、地址改变其它一样NodeID: md4wxe8qlgjg7ll6kwapyees3Is Manager: trueClusterID: xzv8uujn592j7hqhdwnzvy0zyNode Address: 10.4.50.130Manager Addresses:10.4.50.130:237710.4.50.139:2377 
-  降级 ~]# docker node demote node139 Manager node139 demoted in the swarm.
 
-  
 
-  
 
-  
服务部署
-  创建网络 ~]# docker network create -d overlay mytest1 5ofnc745p5zk98h74jprdx6gs~]# docker network ls | grep mytest 5ofnc745p5zk mytest1 overlay swarm
-  部署服务 # 创建一个名叫my1的服务 镜像nginx.1.28.0, 网络加入mytest1 映射端口9999 ~]# docker service create --network mytest1 --replicas=1 --name=my1 -p 9999:9999 nginx.1.28.0# 需要注意的是: 如果镜像是自己build的,需要手动将镜像发布到各节点,也可以用自建仓库直接指定 # 如果不能获取到该镜像则会提示异常。 mrajpx4c5evn \_ my1.1 nginx.1.28.0:latest node139 Shutdown Rejected 43 seconds ago "No such image: nginx.1.28.0:l…" ~]# docker service create --network=mytest1 --name=my1 --replicas=1 -p 9999:9999 nginx:1.28.0 ..... i71aexhnnuwuv526ncof2jj6d overall progress: 1 out of 1 tasks 1/1: running [==================================================>] verify: Service i71aexhnnuwuv526ncof2jj6d converged ~]# curl http://127.0.0.1:9999 <--- 访问正常 <div> hello world </div>
-  查询 Swarm 中服务的信息 - ⚠️: 都是在管理节点上操作 -  查看容器运行在哪个节点之上 ~]# docker service ps my1 
-  查看swarm运行的容器列表 ~]# docker service ls 
-  查询 Swarm 中服务的信息 # 以字典的形式打印出来,打印更为详细 ~]# docker service inspect my1# 格式化为可读的格式 ~]# docker service inspect --pretty my1 ID: i71aexhnnuwuv526ncof2jj6d Name: my1 Service Mode: ReplicatedReplicas: 1 Placement: UpdateConfig:Parallelism: 1On failure: pauseMonitoring Period: 5sMax failure ratio: 0Update order: stop-first RollbackConfig:Parallelism: 1On failure: pauseMonitoring Period: 5sMax failure ratio: 0Rollback order: stop-first ContainerSpec:Image: nginx:1.28.0Init: false Resources: Networks: mytest1 Endpoint Mode: vip Ports:PublishedPort = 9999Protocol = tcpTargetPort = 9999PublishMode = ingress
-  扩容容器 ~]# docker service scale my1=4 my1 scaled to 4 overall progress: 4 out of 4 tasks 1/4: running [==================================================>] 2/4: running [==================================================>] 3/4: running [==================================================>] 4/4: running [==================================================>] verify: Service my1 converged # 查看扩容容器~]# docker service ps my1# 访问容器,在哪台就直接用哪个ip访问即可 ~]# curl http://10.4.50.130:9999 <div> hello world </div> ~]# curl http://10.4.50.139:9999 <div> hello world </div> ~]# curl http://10.4.50.167:9999 <div> hello world </div> 
-  缩容容器 ~]# docker service scale my1=1# 缩减之后,哪怕容器不在 130之后也可以直接访问 以类vip的方式 
 
-  
-  将主节点降级测试一下 
-  测试 # 先将139设置为 侯选人 ~]# docker node promote node139 Node node139 promoted to a manager in the swarm. # 在将130降一级 ~]# docker node demote manager130 Manager manager130 demoted in the swarm.# 此时 130 就无法在查看node管理操作了 ~]# docker node ls Error response from daemon: This node is not a swarm manager~]# docker node ls ~]# docker service ps my1 ~]# curl http://ip:端口 
Swarm 中使用 Volume
存储卷-5-需要用到index.jsp, 先总结一下,主节点创建存储卷,其它节点在 service create 之后会自动创建volume,但文件不会共享, 建议用个存储给它串起来
-  参数说明 挂载类型 关键参数示例 适用场景 注意事项 volume(命名卷) type=volume,src=myvol,dst=/app,volume-driver=local服务间共享数据、持久化存储 需先创建卷( docker volume create)或自动创建bind(绑定挂载) type=bind,src=/host/path,dst=/app,readonly主机目录直接映射到容器 主机路径必须存在,否则报错 tmpfs(临时存储) type=tmpfs,dst=/tmp,tmpfs-size=100m临时数据(如缓存) 数据仅存于内存,容器销毁后丢失 
-  示例- volume挂载方式 - 本地文件,多容器间适合用 -  创建一个名为myv2的存储卷 # 创建一个名为myv2的存储卷, <---主节点上创建 ~]# docker volume create myv2# 此时记得查看 从节点即work节点 myv2是不存在的 [root@node139 ~]# docker volume inspect myv2 [{"CreatedAt": "2025-10-29T17:18:08+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/myv2/_data","Name": "myv2","Options": null,"Scope": "local"} ]
-  创建容器 ~]# docker service create --replicas=3 --mount type=volume,src=myv2,dst=/usr/local/tomcat/webapps/ -p 8889:8080 --name myt2 tomcat:8.5.100--replicas=3 # 搞三个容器 --mount,type=volume,src=创建的存储卷,dst=目录容器挂载的目录 -p 宿主机端口:容器端口# 默认是Spread模式,每台节点上会跑一个 
-  访问测试一下 # 我这台139创建了ROOT/index.jsp, 代码可以看第5节最下面 ~]# curl http://127.0.0.1:8889/index.jsp ... <div class="hostname">error: f1a394cff068: f1a394cff068: Name or service not known</div># 此时我们其它节点是没有创建volume的,此时在看一下 # 167、130上都自动生成了 ~]# docker volume ls DRIVER VOLUME NAME local myv2# 但文件它是不会同时复制的,直接访问 vip139,由于负载机制有一点成功性能打开,挂载点还是要串起来
 
-  
-  示例 - bind(绑定挂载),主机目录直接映射到容器 -  创建容器 ~]# mkdir /root/tomcat <-- 还是仅主节点创建, 用于测试 ~]# docker service create --replicas=3 --mount type=bind,src=/root/tomcat,dst=/usr/local/tomcat/webapps -p 9999:8080 --name=file3 tomcat:8.5.100--replicas=3 # 搞三个容器 --mount,type=bind,src=创建的存储卷,dst=目录容器挂载的目录 -p 宿主机端口:容器端口# 这里跟 craete 存储卷不同, 如果本地没有,它连建都建连不起来 # 可以通过 docker service ps file3 查看, 因为本地没有创建,只会有139创建成功
-  第二次继续,我们先手动将目录文件创建起来, # 三台都有, 假装它用了存储(nfs,ftp)之类的 ~]# ls /root/tomcat/ROOT/index.jsp~]# docker service create --replicas=3 --mount type=bind,src=/root/tomcat,dst=/usr/local/tomcat/webapps -p 10000:8080 --name=file4 tomcat:8.5.100 # 通过 docker service ps file4 查看
-  测试一下 # 访问三次,试试水。 ~]# curl http://10.4.50.139:10000/index.jsp hostname=23da9de8eeeb: 23da9de8eeeb~]# curl http://10.4.50.139:10000/index.jsp hostname=b988c025e438: b988c025e438~]# curl http://10.4.50.139:10000/index.jsp hostname=dd6f9876df30: dd6f9876df30
 
-  
-  示例- volume挂载方式 - nfs, 与bind一样, 只是本机生效,如果想使用得用 docker stack,待研究 -  创建nfs挂载点 # 需要先手动把全部节点创建一下. ~]# docker volume create --driver local --opt type=nfs --opt o=addr=10.4.50.139 --opt device=:/opt/volumetest/volume/ swv1~]# docker volume inspect swv1 --> worker节点上看 [{"CreatedAt": "2025-10-30T14:28:59+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/swv1/_data","Name": "swv1","Options": null,"Scope": "local"} ]
 
-  
