Docker主机集群化方案 Docker Swarm
docker swarm介绍
Docker Swarm是Docker官方提供的一款集群管理工具,其主要作用是把若干台Docker主机抽象为一个整体,并且通过一个入口统一管理这些Docker主机上的各种Docker资源。Swarm和Kubernetes比较类似,但是更加轻,具有的功能也较kubernetes更少一些。
- 是docker host集群管理工具
- docker官方提供的
- docker 1.12版本以后
- 用来统一集群管理的,把整个集群资源做统一调度
- 比kubernetes要轻量化
- 实现scaling 规模扩大或缩小
- 实现rolling update 滚动更新或版本回退
- 实现service discovery 服务发现
- 实现load balance 负载均衡
- 实现route mesh 路由网格,服务治理
docker swarm概念与架构
架构
概念
节点 (node): 就是一台docker host上面运行了docker engine 节点分为两类:
- 管理节点(manager node) 负责管理集群中的节点并向工作节点分配任务
- 工作节点(worker node) 接收管理节点分配的任务,运行任务
可以通过docker node ls命令查看
服务(services): 在工作节点运行的,由多个任务共同组成
可以通过docker service ls查看
任务(task): 运行在工作节点上容器或容器中包含应用,是集群中调度最小管理单元
docker swarm集群部署
部署3主2从节点集群,另需提前准备1台本地容器镜像仓库服务器(Harbor)
容器镜像仓库 Harbor准备
见上面的harbor章节
主机准备
准备5个节点,并设置主机名称和ip
sm1 管理节点1 192.168.91.140 sm2 管理节点2 192.168.91.141 sm3 管理节点3 192.168.91.142 sw1 工作节点1 192.168.91.143 sw2 工作节点2 192.168.91.144
编辑每个节点/etc/hosts文件,添加主机名解析
192.168.91.140 sm1 192.168.91.141 sm2 192.168.91.142 sm3 192.168.91.143 sw1 192.168.91.144 sw2
在每个节点上添加时间同步
使用crontab -e添加计划任务,实现时间同步,该命令会弹出一个vi编辑页面,添加0 */1 * * * ntpdate time1.aliyun.com
# 查看添加后计划任务
crontab -l
0 */1 * * * ntpdate time1.aliyun.com
主机安全设置
# 在每个节点上执行下面的操作
# 关闭防火墙并查看其运行状态
systemctl stop firewalld && systemctl disable firewalld
firewall-cmd --state
not running
# 使用非交互式修改selinux配置文件
sed -ri 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
# 重启节点
reboot
# 重启后验证selinux是否关闭
sestatus
SELinux status: disabled
docker安装
参考前面的Docker部署章节,在每个节点上执行
配置docker daemon
在每个节点上执行,下面配置了镜像加速器和harbor
// /etc/docker/daemon.json
{
"registry-mirrors": ["https://zwyx2n3v.mirror.aliyuncs.com"],
"insecure-registries": ["http://172.25.10.67"]
}
# 重启docker
systemctl daemon-reload
systemctl restart docker
# 登录harbor,输入用户名和密码登陆
docker login 172.25.10.67
# 最后在harbor上创建test项目
docker swarm集群初始化
获取docker swarm命令帮助
# 获取docker swarm命令使用帮助
docker swarm --help
Usage: docker swarm COMMAND
Manage Swarm
Commands:
init Initialize a swarm
join Join a swarm as a node and/or manager
Run 'docker swarm COMMAND --help' for more information on a command.
管理节点初始化
# 本次在sm1上初始化
# 初始化集群,--advertise-addr 当节点有多块网卡时使用其中一块用于广播,用于其它节点连接管理节点使用;--listen-addr 监听地址,用于承载集群流量使用
docker swarm init --advertise-addr 192.168.91.140 --listen-addr 192.168.91.140:2377
Swarm initialized: current node (62eoz80ca1mvli7du8iabuw18) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-4n0y4345yg8gk0ph94w3nyt2pyb3b4tduaf51njo4tbrocia3k-8lny6ydmgvl9tv6pwwx0ulg64 192.168.91.140:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
添加工作节点到集群
# 使用初始化过程中生成的token加入集群
# sw1节点
docker swarm join --token SWMTKN-1-4n0y4345yg8gk0ph94w3nyt2pyb3b4tduaf51njo4tbrocia3k-8lny6ydmgvl9tv6pwwx0ulg64 192.168.91.140:2377
This node joined a swarm as a worker.
# sm1节点
# 查看已加入的集群
docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
62eoz80ca1mvli7du8iabuw18 * sm1 Ready Active Leader 24.0.6
i7ida3zc6bqbscir1rvzm8dbi sw1 Ready Active 24.0.6
# 如果使用的token已过期,可以再次生成新的加入集群的方法
docker swarm join-token worker
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-4n0y4345yg8gk0ph94w3nyt2pyb3b4tduaf51njo4tbrocia3k-8lny6ydmgvl9tv6pwwx0ulg64 192.168.91.140:2377
# sw2节点
# sw2加入至集群
docker swarm join --token SWMTKN-1-4n0y4345yg8gk0ph94w3nyt2pyb3b4tduaf51njo4tbrocia3k-8lny6ydmgvl9tv6pwwx0ulg64 192.168.91.140:2377
This node joined a swarm as a worker.
# sm1节点
# 查看已加入的集群
docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
62eoz80ca1mvli7du8iabuw18 * sm1 Ready Active Leader 24.0.6
i7ida3zc6bqbscir1rvzm8dbi sw1 Ready Active 24.0.6
wbjcle5g7hk1mxqkvs4j80uum sw2 Ready Active 24.0.6
添加管理节点到集群
# 生成用于添加管理节点加入集群所使用的token
# sm1节点
docker swarm join-token manager
To add a manager to this swarm, run the following command:
docker swarm join --token SWMTKN-1-4n0y4345yg8gk0ph94w3nyt2pyb3b4tduaf51njo4tbrocia3k-d5043ekm9lqknqa6cbl21qt33 192.168.91.140:2377
# sm2节点,加入集群
docker swarm join --token SWMTKN-1-4n0y4345yg8gk0ph94w3nyt2pyb3b4tduaf51njo4tbrocia3k-d5043ekm9lqknqa6cbl21qt33 192.168.91.140:2377
This node joined a swarm as a manager.
# sm3节点,加入集群
docker swarm join --token SWMTKN-1-4n0y4345yg8gk0ph94w3nyt2pyb3b4tduaf51njo4tbrocia3k-d5043ekm9lqknqa6cbl21qt33 192.168.91.140:2377
This node joined a swarm as a manager.
# 查看节点状态,在管理节点sm1、sm2、sm3中的任意一个都可以
docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
62eoz80ca1mvli7du8iabuw18 * sm1 Ready Active Leader 24.0.6
z611qkq43nls6tdi42939aov7 sm2 Ready Active Reachable 24.0.6
pwsn3kjq9jdc2axwp7uynjixn sm3 Ready Active Reachable 24.0.6
i7ida3zc6bqbscir1rvzm8dbi sw1 Ready Active 24.0.6
wbjcle5g7hk1mxqkvs4j80uum sw2 Ready Active 24.0.6
模拟管理节点出现故障
# sm1节点,停止docker服务
systemctl stop docker
# sm2节点,查看节点状态
# 发现sm1不可达,状态为未知,并重启选择出leader
docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
62eoz80ca1mvli7du8iabuw18 sm1 Down Active Unreachable 24.0.6
z611qkq43nls6tdi42939aov7 * sm2 Ready Active Reachable 24.0.6
pwsn3kjq9jdc2axwp7uynjixn sm3 Ready Active Leader 24.0.6
i7ida3zc6bqbscir1rvzm8dbi sw1 Ready Active 24.0.6
wbjcle5g7hk1mxqkvs4j80uum sw2 Ready Active 24.0.6
# sm1节点,启动docker服务并查看结果
systemctl start docker
# sm1是可达状态,但并不是Leader
docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
62eoz80ca1mvli7du8iabuw18 * sm1 Ready Active Reachable 24.0.6
z611qkq43nls6tdi42939aov7 sm2 Ready Active Reachable 24.0.6
pwsn3kjq9jdc2axwp7uynjixn sm3 Ready Active Leader 24.0.6
i7ida3zc6bqbscir1rvzm8dbi sw1 Ready Active 24.0.6
wbjcle5g7hk1mxqkvs4j80uum sw2 Ready Active 24.0.6
docker swarm集群应用
容器镜像准备
准备多个版本的容器镜像,以便于后期使用测试。
# v1版本
mkdir -p /data/test/nginximg1
cd /data/test/nginximg1
echo v1 > index.html
# Dockerfile文件,用于构建容器镜像
FROM nginx:latest
MAINTAINER 'tom<tom@kubetest.com>'
ADD index.html /usr/share/nginx/html
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
EXPOSE 80
CMD /usr/sbin/nginx
# 使用docker build构建容器镜像
docker build -t 172.25.10.67/test/nginx:v1 .
# 推送容器镜像至harbor
docker push 172.25.10.67/test/nginx:v1
# v2版本
mkdir -p /data/test/nginximg2
cd /data/test/nginximg2
echo v2 > index.html
# Dockerfile文件,用于构建容器镜像
FROM nginx:latest
MAINTAINER 'tom<tom@kubetest.com>'
ADD index.html /usr/share/nginx/html
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
EXPOSE 80
CMD /usr/sbin/nginx
# 使用docker build构建容器镜像
docker build -t 172.25.10.67/test/nginx:v2 .
# 推送容器镜像至harbor
docker push 172.25.10.67/test/nginx:v2
发布服务
在docker swarm中,对外暴露的是服务(service),而不是容器。
为了保持高可用架构,它准许同时启动多个容器共同支撑一个服务,如果一个容器挂了,它会自动使用另一个容器
# 下面的操作在管理节点sm1、sm2、sm3中的任意一个都可以,这里在sm1上执行
# 发布服务
# 创建一个服务,名为nginx_svc-1,指定1个副本,将服务内部的80端口发布到外部网络的80端口,使用的镜像为`172.25.10.67/test/nginx:v1`
docker service create --name nginx-svc-1 --replicas 1 --publish 80:80 172.25.10.67/test/nginx:v1
# 查看已发布服务
docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ooeefjpd86pb nginx-svc-1 replicated 1/1 172.25.10.67/test/nginx:v1 *:80->80/tcp
# 查看已发布服务容器
docker service ps nginx-svc-1
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
1ug4dspd10ym nginx-svc-1.1 172.25.10.67/test/nginx:v1 sm1 Running Running 26 seconds ago
# sm1节点
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4b2f39afb438 172.25.10.67/test/nginx:v1 "/docker-entrypoint.…" 49 seconds ago Up 48 seconds 80/tcp nginx-svc-1.1.1ug4dspd10ymhdgxe4mwcvec0
# 下面的响应都是 v1
curl sm1
curl sm2
curl sm3
curl sw1
curl sw2
服务扩展
使用scale指定副本数来扩展
# 下面的操作在管理节点sm1、sm2、sm3中的任意一个都可以,这里在sm1上执行
# 扩展nginx-svc-1服务副本数量为2
docker service scale nginx-svc-1=2
nginx-svc-1 scaled to 2
overall progress: 2 out of 2 tasks
1/2: running
2/2: running
verify: Service converged
docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ooeefjpd86pb nginx-svc-1 replicated 2/2 172.25.10.67/test/nginx:v1 *:80->80/tcp
docker service ps nginx-svc-1
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
1ug4dspd10ym nginx-svc-1.1 172.25.10.67/test/nginx:v1 sm1 Running Running 2 minutes ago
h0vs6bcsm0xq nginx-svc-1.2 172.25.10.67/test/nginx:v1 sw2 Running Running 38 seconds ago
# 上面显示容器在sm1和sw1两个节点上启动的
# 当容器意外退出时,集群会重新启动一个容器(在有运行容器的节点上,使用命令停止容器,集群会重新启动一个容器)
# 扩展nginx-svc-1服务副本数量为3
docker service scale nginx-svc-1=3
服务裁减
# 下面的操作在管理节点sm1、sm2、sm3中的任意一个都可以,这里在sm1上执行
# 服务裁减
docker service scale nginx-svc-1=2
nginx-svc-1 scaled to 2
overall progress: 2 out of 2 tasks
1/2: running
2/2: running
verify: Service converged
docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ooeefjpd86pb nginx-svc-1 replicated 2/2 172.25.10.67/test/nginx:v1 *:80->80/tcp
docker service ps nginx-svc-1
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
1ug4dspd10ym nginx-svc-1.1 172.25.10.67/test/nginx:v1 sm1 Running Running 4 minutes ago
h0vs6bcsm0xq nginx-svc-1.2 172.25.10.67/test/nginx:v1 sw2 Running Running 2 minutes ago
负载均衡
服务中包含多个容器时,每次访问将以轮询的方式访问到每个容器
# 修改sm1节点中容器网页文件
docker ps
4b2f39afb438 172.25.10.67/test/nginx:v1 "/docker-entrypoint.…" 11 minutes ago Up 11 minutes 80/tcp nginx-svc-1.1.1ug4dspd10ymhdgxe4mwcvec0
docker exec -it 4b2f39afb438 bash
# 下面在容器中执行
echo "sm1 web" > /usr/share/nginx/html/index.html
exit
curl sm1
v1
curl sm1
sm1 web
curl sm1
v1
curl sm1
sm1 web
删除服务
docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ooeefjpd86pb nginx-svc-1 replicated 2/2 172.25.10.67/test/nginx:v1 *:80->80/tcp
docker service rm nginx-svc-1
nginx-svc-1
docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
服务版本更新
docker service create --name nginx-svc --replicas 1 --publish 80:80 172.25.10.67/test/nginx:v1
a372ljin4b6cuzqeef0anrayn
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
curl sm1
v1
docker service update nginx-svc --image 172.25.10.67/test/nginx:v2
nginx-svc
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
curl sm1
v2
服务版本回退
docker service update nginx-svc --image 172.25.10.67/test/nginx:v1
nginx-svc
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
curl sm1
v1
服务版本滚动间隔更新
# 先删除服务
docker service rm nginx-svc
docker service create --name nginx-svc --replicas 60 --publish 80:80 172.25.10.67/test/nginx:v1
1f51eqckezkom5wdp06z8j4ld
overall progress: 60 out of 60 tasks
verify: Service converged
# --update-parallelism 5 指定并行更新数量,--update-delay 30s 指定更新间隔时间
docker service update --replicas 60 --image 172.25.10.67/test/nginx:v1 --update-parallelism 5 --update-delay 30s nginx-svc
nginx-svc
overall progress: 60 out of 60 tasks
verify: Service converged
# docker swarm滚动更新会造成节点上有exit状态的容器,可以考虑清除,命令如下:
docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Total reclaimed space: 0B
副本控制器
# 先将nginx-svc服务副本数设置为5
docker service scale nginx-svc=5
docker service ps nginx-svc
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xyom1vzv1jb6 nginx-svc.1 172.25.10.67/test/nginx:v1 sm1 Running Running 11 minutes ago
qrkjjeqb69ty nginx-svc.2 172.25.10.67/test/nginx:v1 sm2 Running Running 11 minutes ago
2ismuigj40oe nginx-svc.5 172.25.10.67/test/nginx:v1 sw1 Running Running 11 minutes ago
srlepmgptlkc nginx-svc.6 172.25.10.67/test/nginx:v1 sw2 Running Running 11 minutes ago
lg5w66urt4h5 nginx-svc.7 172.25.10.67/test/nginx:v1 sm3 Running Running 11 minutes ago
# sm1节点
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ee17511f0235 172.25.10.67/test/nginx:v1 "/docker-entrypoint.…" 12 minutes ago Up 12 minutes 80/tcp nginx-svc.1.xyom1vzv1jb6q2bchbgtod5tb
docker stop ee17511f0235 && docker rm ee17511f0235
# 有容器挂掉,集群会再启动一个相同的容器
docker service ps nginx-svc
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
6pebb2hl3307 nginx-svc.1 172.25.10.67/test/nginx:v1 sm1 Ready Ready 3 seconds ago
xyom1vzv1jb6 \_ nginx-svc.1 172.25.10.67/test/nginx:v1 sm1 Shutdown Failed 3 seconds ago "task: non-zero exit (137)"
qrkjjeqb69ty nginx-svc.2 172.25.10.67/test/nginx:v1 sm2 Running Running 13 minutes ago
2ismuigj40oe nginx-svc.5 172.25.10.67/test/nginx:v1 sw1 Running Running 13 minutes ago
srlepmgptlkc nginx-svc.6 172.25.10.67/test/nginx:v1 sw2 Running Running 13 minutes ago
lg5w66urt4h5 nginx-svc.7 172.25.10.67/test/nginx:v1 sm3 Running Running 13 minutes ago
在指定网络中发布服务
# sm1节点
docker network create -d overlay tomcat-net
d2datynhgfc5soo574i13mzu0
docker network ls
NETWORK ID NAME DRIVER SCOPE
91f1ac9e7de9 bridge bridge local
567a69100ddb docker_gwbridge bridge local
d514380b8f29 host host local
obw5jivmwe13 ingress overlay swarm
db80546ac088 none null local
d2datynhgfc5 tomcat-net overlay swarm
# 创建名为tomcat-net的覆盖网络(Overlay Netowork),这是个二层网络,处于该网络下的docker容器,即使宿主机不一样,也能相互访问
docker network inspect tomcat-net
[
{
"Name": "tomcat-net",
"Id": "d2datynhgfc5soo574i13mzu0",
"Created": "2023-09-15T12:08:15.195137904Z",
"Scope": "swarm",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "10.0.1.0/24",
"Gateway": "10.0.1.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": null,
"Options": {
"com.docker.network.driver.overlay.vxlanid_list": "4097"
},
"Labels": null
}
]
# 创建名为tomcat的服务,使用了刚才创建的覆盖网络
docker service create --name tomcat \
--network tomcat-net \
-p 8080:8080 \
--replicas 2 \
tomcat:7.0.96-jdk8-openjdk
o8bqwvz4mvl1zkzosb6uvea1v
overall progress: 2 out of 2 tasks
1/2: running [==================================================>]
2/2: running [==================================================>]
verify: Service converged
docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
o8bqwvz4mvl1 tomcat replicated 2/2 tomcat:7.0.96-jdk8-openjdk *:8080->8080/tcp
docker service ps tomcat
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
9gdu5kqjpb5m tomcat.1 tomcat:7.0.96-jdk8-openjdk sm2 Running Running 5 minutes ago
nylpwlqidjyu tomcat.2 tomcat:7.0.96-jdk8-openjdk sw2 Running Running 4 minutes ago
# 下面的所有请求都返回tomcat首页
curl sm1:8080
curl sm2:8080
curl sm3:8080
curl sw1:8080
curl sw2:8080
服务网络模式
服务网络模式一共有两种:Ingress和Host,如果不指定,则默认的是Ingress;
Ingress模式下,以上面的tomcat容器为例,到达Swarm任何节点的8080端口的流量,都会映射到任何服务副本的内部8080端口,就算该节点上没有tomcat服务副本也会映射。
docker service rm tomcat
docker service create --name tomcat \
--network tomcat-net \
-p 8080:8080 \
--replicas 2 \
tomcat:7.0.96-jdk8-openjdk
docker service ps tomcat
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
zy2lvmpvmu60 tomcat.1 tomcat:7.0.96-jdk8-openjdk sm2 Running Running 13 seconds ago
ri88i5mqbycy tomcat.2 tomcat:7.0.96-jdk8-openjdk sw2 Running Running 13 seconds ago
# sm2
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1b2dedc4c453 tomcat:7.0.96-jdk8-openjdk "catalina.sh run" About a minute ago Up About a minute 8080/tcp tomcat.1.zy2lvmpvmu60vhf2c6qatxzfy
docker inspect 1b2dedc4c453 | grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "",
"IPAddress": "10.0.0.10",
"IPAddress": "10.0.1.10",
ss -anput | grep ":8080"
tcp LISTEN 0 128 [::]:8080 [::]:* users:(("dockerd",pid=1153,fd=47))
# sw2
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
465ce20c2bb3 tomcat:7.0.96-jdk8-openjdk "catalina.sh run" About a minute ago Up About a minute 8080/tcp tomcat.2.ri88i5mqbycymahnpkmf5exbc
docker inspect 465ce20c2bb3 | grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "",
"IPAddress": "10.0.0.11",
"IPAddress": "10.0.1.11",
ss -anput | grep ":8080"
tcp LISTEN 0 128 [::]:8080 [::]:* users:(("dockerd",pid=1170,fd=38))
# 在其它三个节点上执行下面的命令,显示8080端口也被使用了
ss -anput | grep ":8080"
Host模式下,仅在运行有容器副本的机器上开放端口,使用Host模式的命令如下:
docker service rm tomcat
docker service create --name tomcat \
--network tomcat-net \
--publish published=8080,target=8080,mode=host \
--replicas 3 \
tomcat:7.0.96-jdk8-openjdk
docker service ps tomcat
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
p9pmf4eaiu3v tomcat.1 tomcat:7.0.96-jdk8-openjdk sw2 Running Running 27 seconds ago *:8080->8080/tcp,*:8080->8080/tcp
mysn7zswxtlr tomcat.2 tomcat:7.0.96-jdk8-openjdk sm2 Running Running 27 seconds ago *:8080->8080/tcp,*:8080->8080/tcp
owgo7vi6ojl3 tomcat.3 tomcat:7.0.96-jdk8-openjdk sm3 Running Running 27 seconds ago *:8080->8080/tcp,*:8080->8080/tcp
# sw2
ss -anput | grep ":8080"
tcp LISTEN 0 128 *:8080 *:* users:(("docker-proxy",pid=3176,fd=4))
tcp LISTEN 0 128 [::]:8080 [::]:* users:(("docker-proxy",pid=3184,fd=4))
# sm2
ss -anput | grep ":8080"
tcp LISTEN 0 128 *:8080 *:* users:(("docker-proxy",pid=3170,fd=4))
tcp LISTEN 0 128 [::]:8080 [::]:* users:(("docker-proxy",pid=3176,fd=4))
# sm3
ss -anput | grep ":8080"
tcp LISTEN 0 128 *:8080 *:* users:(("docker-proxy",pid=2836,fd=4))
tcp LISTEN 0 128 [::]:8080 [::]:* users:(("docker-proxy",pid=2842,fd=4))
# sm1、sw1
# 没有被映射端口
ss -anput | grep ":8080"
服务数据持久化存储
本地存储
# 在集群所有主机上创建本地目录
mkdir -p /data/nginxdata
# 发布服务时挂载本地目录到容器中
docker service create --name nginx-svc --replicas 3 --mount "type=bind,source=/data/nginxdata,target=/usr/share/nginx/html" --publish 80:80 172.25.10.67/test/nginx:v1
# 验证是否使用本地目录
docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
wk7qzw7cb2rs nginx-svc replicated 3/3 172.25.10.67/test/nginx:v1 *:80->80/tcp
docker service ps nginx-svc
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
p9lsb1kb9akb nginx-svc.1 172.25.10.67/test/nginx:v1 sm3 Running Running about a minute ago
q65j4257unwm nginx-svc.2 172.25.10.67/test/nginx:v1 sm1 Running Running about a minute ago
mh0i2y8k7lj9 nginx-svc.3 172.25.10.67/test/nginx:v1 sw1 Running Running about a minute ago
# sm3
echo "sm3 web" > /data/nginxdata/index.html
# sm1
echo "sm1 web" > /data/nginxdata/index.html
# sw1
echo "sw1 web" > /data/nginxdata/index.html
# 请求会进行负载均衡
curl sm3
sm3 web
curl sm3
sw1 web
curl sm3
sm1 web
网络存储
- 网络存储卷可以实现跨docker宿主机的数据共享,数据持久保存到网络存储卷中
- 在创建service时添加卷的挂载参数,网络存储卷可以帮助自动挂载(但需要集群节点都创建该网络存储卷)
部署NFS存储
本案例以NFS提供远程存储为例
在新的节点192.168.91.145 nfs上部署NFS服务,共享目录为docker swarm集群主机使用
# nfs节点
mkdir /opt/dockervolume
yum -y install nfs-utils
echo "/opt/dockervolume *(rw,sync,no_root_squash)" > /etc/exports
systemctl enable nfs-server
systemctl start nfs-server
# 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
showmount -e
Export list for nfs:
/opt/dockervolume *
为集群所有主机安装nfs-utils软件
# 在集群5个节点上执行下面的命令
yum -y install nfs-utils
showmount -e 192.168.91.145
Export list for 192.168.91.145:
/opt/dockervolume *
创建存储卷
# 在集群5个节点上执行下面的命令
docker volume create --driver local --opt type=nfs --opt o=addr=192.168.91.145,rw --opt device=:/opt/dockervolume nginx_volume
docker volume ls
DRIVER VOLUME NAME
local nginx_volume
docker volume inspect nginx_volume
[
{
"CreatedAt": "2023-09-18T10:14:37+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/nginx_volume/_data",
"Name": "nginx_volume",
"Options": {
"device": ":/opt/dockervolume",
"o": "addr=192.168.91.145,rw",
"type": "nfs"
},
"Scope": "local"
}
]
发布服务并验证
# sm1
docker service rm nginx-svc
docker service create --name nginx-svc --replicas 3 --publish 80:80 --mount "type=volume,source=nginx_volume,target=/usr/share/nginx/html" 172.25.10.67/test/nginx:v1
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
d1qpwhyma6tp nginx-svc.1 172.25.10.67/test/nginx:v1 sm2 Running Running about a minute ago
i2dkczjhux7o nginx-svc.2 172.25.10.67/test/nginx:v1 sm3 Running Running about a minute ago
w8obtdn88qzl nginx-svc.3 172.25.10.67/test/nginx:v1 sw2 Running Running about a minute ago
# sm2
df -Th | grep nfs
:/opt/dockervolume nfs 37G 1.6G 36G 5% /var/lib/docker/volumes/nginx_volume/_data
# sm3
df -Th | grep nfs
:/opt/dockervolume nfs 37G 1.6G 36G 5% /var/lib/docker/volumes/nginx_volume/_data
# sw2
df -Th | grep nfs
:/opt/dockervolume nfs 37G 1.6G 36G 5% /var/lib/docker/volumes/nginx_volume/_data
# nfs
echo "nfs test" > /opt/dockervolume/index.html
# 下面的请求都返回 nfs test
curl sm1
curl sm2
curl sm3
curl sw1
curl sw2
服务互联与服务发现
如果一个nginx服务与一个mysql服务之间需要连接,在docker swarm如何实现呢?
方法1:
把mysql服务也使用 --publish参数发布到外网,但这样做的缺点是:mysql这种服务发布到外网不安全
方法2:
将mysql服务等运行在内部网络,只需要nginx服务能够连接mysql就可以了,在docker swarm中可以使用overlay网络来实现。
但现在还有个问题,服务副本数发生变化时,容器内部的IP发生变化时,我们希望仍然能够访问到这个服务, 这就是服务发现(service discovery).
通过服务发现,service的使用者都不需要知道service运行在哪里,IP是多少,有多少个副本,就能让service通信
下面使用docker network ls查看到的ingress网络就是一个overlay类型的网络,但它不支持服务发现
# sm1
docker network ls
NETWORK ID NAME DRIVER SCOPE
84decf2c2868 bridge bridge local
567a69100ddb docker_gwbridge bridge local
d514380b8f29 host host local
obw5jivmwe13 ingress overlay swarm
db80546ac088 none null local
d2datynhgfc5 tomcat-net overlay swarm
我们需要自建一个overlay网络来实现服务发现, 需要相互通信的service也必须属于同一个overlay网络
# --driver overlay指定为overlay类型,--subnet 分配网段,self-network 为自定义的网络名称
docker network create --driver overlay --subnet 192.168.100.0/24 self-network
docker network ls
NETWORK ID NAME DRIVER SCOPE
...
9n0w69b2qtw5 self-network overlay swarm
d2datynhgfc5 tomcat-net overlay swarm
验证自动发现
# 发布nignx-svc服务,指定在自建的overlay网络
docker service create --name nginx-svc --replicas 3 --network self-network --publish 80:80 172.25.10.67/test/nginx:v1
# 发布一个busybox服务,也指定在自建的overlay网络
# 服务名为test;busybox是一个集成了linux常用命令的软件,这里使用它可以比较方便的测试与nginx_service的连通性;没有指定副本,默认1个副本;因为它并不是长时间运行的daemon守护进程,所以运行一下就会退出,sleep 100000是指定一个长的运行时间,让它有足够的时间给我们测试
docker service create --name test --network self-network busybox sleep 100000
# 查出test服务在哪个节点运行的容器
docker service ps test
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
b2i4f694y7bc test.1 busybox:latest sm1 Running Running about a minute ago
# sm1
# 去运行test服务的容器节点查找容器的名称
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0b17f54982d9 busybox:latest "sleep 100000" 2 minutes ago Up 2 minutes test.1.b2i4f694y7bcsd88f4rdmm30m
# 使用查找出来的容器名称,执行命令测试
docker exec -it 0b17f54982d9 ping -c 2 nginx-svc
PING nginx-svc (192.168.100.2): 56 data bytes
64 bytes from 192.168.100.2: seq=0 ttl=64 time=0.119 ms
64 bytes from 192.168.100.2: seq=1 ttl=64 time=0.141 ms
...
# 测试的结果为: test服务可以ping通nginx-svc服务,并且返回的IP为自建网络的一个IP(192.168.100.2)
docker service inspect nginx-svc | grep Addr
"Addr": "10.0.0.7/24"
"Addr": "192.168.100.2/24"
# 分别去各个节点查找nginx-svc服务的各个容器(3个副本),发现它们的IP与上面ping的IP都不同
docker service ps nginx-svc
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
sdw4603fjg41 nginx-svc.1 172.25.10.67/test/nginx:v1 sw2 Running Running 19 minutes ago
o0356dca3xss nginx-svc.2 172.25.10.67/test/nginx:v1 sm2 Running Running 19 minutes ago
7q7yuos4npf0 nginx-svc.3 172.25.10.67/test/nginx:v1 sm3 Running Running 19 minutes ago
# sw2
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4b557dd0d396 172.25.10.67/test/nginx:v1 "/docker-entrypoint.…" 19 minutes ago Up 19 minutes 80/tcp nginx-svc.1.sdw4603fjg41jxy6cg6zp2p4l
docker inspect 4b557dd0d396 | grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "",
"IPAddress": "10.0.0.8",
"IPAddress": "192.168.100.3",
# sm2
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e86a33bee48a 172.25.10.67/test/nginx:v1 "/docker-entrypoint.…" 19 minutes ago Up 19 minutes 80/tcp nginx-svc.2.o0356dca3xssp60aqw04hg3rd
docker inspect e86a33bee48a | grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "",
"IPAddress": "10.0.0.9",
"IPAddress": "192.168.100.4",
# sm3
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0b161c366420 172.25.10.67/test/nginx:v1 "/docker-entrypoint.…" 21 minutes ago Up 21 minutes 80/tcp nginx-svc.3.7q7yuos4npf0hfq25sklc3v5c
docker inspect 0b161c366420 | grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "",
"IPAddress": "10.0.0.10",
"IPAddress": "192.168.100.5",
# 后续测试, 将nginx-svc服务扩展,裁减,更新,回退。都不影响test服务访问nginx-svc。
结论: 在自建的overlay网络内,通过服务发现可以实现服务之间通过服务名(不用知道对方的IP)互联,而且不会受服务内副本个数和容器内IP变化等的影响。
docker swarm网络
在 Swarm Service 中有三个重要的网络概念:
- Overlay networks 管理 Swarm 中 Docker 守护进程间的通信。你可以将服务附加到一个或多个已存在的
overlay网络上,使得服务与服务之间能够通信。 - ingress network 是一个特殊的
overlay网络,用于服务节点间的负载均衡。当任何 Swarm 节点在发布的端口上接收到请求时,它将该请求交给一个名为IPVS的模块。IPVS跟踪参与该服务的所有IP地址,选择其中的一个,并通过ingress网络将请求路由到它。 初始化或加入 Swarm 集群时会自动创建ingress网络,大多数情况下,用户不需要自定义配置,但是 docker 17.05 和更高版本允许你自定义。 - docker_gwbridge是一种桥接网络,将
overlay网络(包括ingress网络)连接到一个单独的 Docker 守护进程的物理网络。默认情况下,服务正在运行的每个容器都连接到本地 Docker 守护进程主机的docker_gwbridge网络。docker_gwbridge网络在初始化或加入 Swarm 时自动创建。大多数情况下,用户不需要自定义配置,但是 Docker 允许自定义。
| 名称 | 类型 | 注释 |
|---|---|---|
| docker_gwbridge | bridge | none |
| ingress | overlay | none |
| custom-network | overlay | none |
- docker_gwbridge和ingress是swarm自动创建的,当用户执行了docker swarm init/connect之后。
- docker_gwbridge是bridge类型的负责本机container和主机直接的连接
- ingress负责service在多个主机container之间的路由。
- custom-network是用户自己创建的overlay网络,通常我们都需要创建自己的network并把service挂在上面。
docker stack
docker stack介绍
早期使用service发布,每次只能发布一个service。
yaml可以发布多个服务,但是使用docker-compose只能在一台主机发布。
一个stack就是一组有关联的服务的组合,可以一起编排,一起发布, 一起管理
docker stack与docker compose区别
- Docker stack会忽略了“构建”指令,无法使用stack命令构建新镜像,它是需要镜像是预先已经构建好的。 所以docker-compose更适合于开发场景;
- Docker Compose是一个Python项目,在内部,它使用Docker API规范来操作容器。所以需要安装Docker -compose,以便与Docker一起在您的计算机上使用;
- Docker Stack功能包含在Docker引擎中。你不需要安装额外的包来使用它,docker stacks 只是swarm mode的一部分。
- Docker stack不支持基于第2版写的docker-compose.yml ,也就是version版本至少为3。然而Docker Compose对版本为2和3的 文件仍然可以处理;
- docker stack把docker compose的所有工作都做完了,因此docker stack将占主导地位。同时,对于大多数用户来说,切换到使用docker stack既不困难,也不需要太多的开销。如果您是Docker新手,或正在选择用于新项目的技术,请使用docker stack。
docker stack常用命令
| 命令 | 描述 |
|---|---|
| docker stack deploy | 部署新的堆栈或更新现有堆栈 |
| docker stack ls | 列出现有堆栈 |
| docker stack ps | 列出堆栈中的任务 |
| docker stack rm | 删除一个或多个堆栈 |
| docker stack services | 列出堆栈中的服务 |
部署wordpress案例
# sm1
# stack1.yaml
version: '3'
services:
db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
deploy:
replicas: 1
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8010:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
deploy:
replicas: 1
# placement的constraints限制此容器在manager节点
placement:
constraints: [node.role == manager]
# sm1
# 使用docker stack发布
docker stack deploy -c stack1.yaml stack1
Creating network stack1_default
Creating service stack1_wordpress
Creating service stack1_db
# 如果报错,使用 docker stack rm stack1 删除。排完错再启动
docker stack ls
NAME SERVICES
stack1 2
docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
imcw9769ibsf stack1_db replicated 0/1 mysql:5.7
lketamv98of9 stack1_wordpress replicated 0/1 wordpress:latest *:8010->80/tcp
# 可以通过页面访问网页
http://192.168.91.140:8010/
部署nginx与web管理服务案例
# sm1
# stack2.yaml
version: "3"
services:
nginx:
image: 172.25.10.67/test/nginx:v1
ports:
- 80:80
deploy:
mode: replicated
replicas: 3
visualizer:
image: dockersamples/visualizer
ports:
- "9001:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
replicas: 1
placement:
constraints: [node.role == manager]
portainer:
image: portainer/portainer
ports:
- "9000:9000"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
replicas: 1
placement:
constraints: [node.role == manager]
说明: stack中共有3个service
- nginx服务,3个副本
- visualizer服务: 图形查看docker swarm集群
- portainer服务: 图形管理docker swarm集群
# 虚拟机资源有限,可以先移除stack1
docker stack rm stack1
# 使用docker stack发布
docker stack deploy -c stack2.yaml stack2
Creating network stack2_default
Creating service stack2_nginx
Creating service stack2_visualizer
Creating service stack2_portainer
# 如果报错,使用docker stack rm stack2删除,排完错再启动
docker stack ps stack2
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
yq06vavc6kb1 stack2_nginx.1 172.25.10.67/test/nginx:v1 sm3 Running Running 2 minutes ago
i2s72ops7o1c stack2_nginx.2 172.25.10.67/test/nginx:v1 sw2 Running Running 2 minutes ago
lr2ocrkeuqva stack2_nginx.3 172.25.10.67/test/nginx:v1 sw1 Running Running 2 minutes ago
attve5i7m0pi stack2_portainer.1 portainer/portainer:latest sm2 Running Running 2 minutes ago
nj5hig51wgsv stack2_visualizer.1 dockersamples/visualizer:latest sm1 Running Running about a minute ago
http://192.168.91.140:9001/ 和 http://192.168.91.140:9000/ 可以访问监控页面,集群中所有ip都可以访问
nginx+haproxy+nfs案例
# 在docker swarm管理节点上准备配置文件
mkdir -p /docker-stack/haproxy
cd /docker-stack/haproxy/
# haproxy.cfg
global
log 127.0.0.1 local0
log 127.0.0.1 local1 notice
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
stats uri /status
frontend balancer
bind *:8080
mode http
default_backend web_backends
backend web_backends
mode http
option forwardfor
balance roundrobin
server web1 nginx1:80 check
server web2 nginx2:80 check
server web3 nginx3:80 check
option httpchk GET /
http-check expect status 200
# 编写YAML编排文件 stack3.yaml
version: "3"
services:
nginx1:
image: 172.25.10.67/test/nginx:v1
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
volumes:
- "nginx_vol:/usr/share/nginx/html"
nginx2:
image: 172.25.10.67/test/nginx:v1
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
volumes:
- "nginx_vol:/usr/share/nginx/html"
nginx3:
image: 172.25.10.67/test/nginx:v1
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
volumes:
- "nginx_vol:/usr/share/nginx/html"
haproxy:
image: haproxy:latest
volumes:
- "./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro"
ports:
- "8080:8080"
deploy:
replicas: 1
placement:
constraints: [node.role == manager]
volumes:
nginx_vol:
driver: local
driver_opts:
type: "nfs"
o: "addr=192.168.91.145,rw"
device: ":/opt/dockervolume"
# nfs节点需要启动
# 虚拟机资源有限,可以先移除stack2
docker stack rm stack2
# 发布
docker stack deploy -c stack3.yaml stack3
Creating network stack3_default
Creating service stack3_nginx2
Creating service stack3_nginx3
Creating service stack3_haproxy
Creating service stack3_nginx1
# 下面的请求均返回 nfs test
curl sm1:8080
curl sm2:8080
curl sm3:8080
curl sw1:8080
curl sw2:8080