三个主机均安装docker,然后修改主机名:
# 192.168.1.135 hostnamectl set-hostname manager135 # 192.168.1.136 hostnamectl set-hostname node136 # 192.168.1.137 hostnamectl set-hostname node137关闭三台机器上的防火墙。如果开启防火墙,则需要在所有节点的防火墙上依次放行2377/tcp(管理端口)、7946/udp(节点间通信端口)、4789/udp(overlay 网络端口)端口。
sudo systemctl stop firewalld #关闭防火墙 sudo systemctl disable firewalld #禁止开机启动 sudo systemctl stop iptables.service sudo systemctl disable iptables.service sudo systemctl status iptables.service运行 Docker 的主机可以主动初始化一个Swarm集群或者加入一个已存在的Swarm集群,这样这个运行 Docker 的主机就成为一个 Swarm集群的节点(node)。节点分为管理(manager)节点和工作(worker)节点。
管理节点用于Swarm集群的管理,docker swarm命令基本只能在管理节点执行(节点退出集群命令docker swarm leave可以在工作节点执行)。一个Swarm集群可以有多个管理节点,但只有一个管理节点可以成为leader,leader通过raft协议实现。工作节点是任务执行节点,管理节点将服务(service)下发至工作节点执行。管理节点默认也作为工作节点。你也可以通过配置让服务只运行在管理节点。上面命令执行后,该机器自动加入到swarm集群。这个会创建一个集群token,获取全球唯一的 token,作为集群唯一标识。后续将其他节点加入集群都会用到这个token值。 其中,–advertise-addr参数表示其它swarm中的worker节点使用此ip地址与manager联系。命令的输出包含了其它节点如何加入集群的命令。
上面的命令是查看集群中的机器(注意上面node ID旁边那个*号表示现在连接到这个节点上)
如果想要将其他更多的节点添加到这个swarm集群中,添加方法如上一致
swarm集群中node的availability状态可以为 active或者drain,其中:
active状态下,node可以接受来自manager节点的任务分派;
drain状态下,node节点会结束task,且不再接受来自manager节点的任务分派(也就是下线节点)。
下线一个节点:
docker node update --availability drain node137上线节点:
docker node update --availability active node137服务(Services)是指一组任务的集合,服务定义了任务的属性。服务有两种模式
replicated services 按照一定规则在各个工作节点上运行指定个数的任务。global services 每个工作节点上运行一个任务两种模式通过docker service create的–mode参数指定。
创建一个覆盖网络,用来保证在不同主机上的容器网络互通的网络模式
# 在manager上执行 docker network create -d overlay my_nginx_net [qinye@manager135 ~]$ docker network ls NETWORK ID NAME DRIVER SCOPE 1c2fe0443ea9 bridge bridge local 0e18f07fc40a docker_gwbridge bridge local 83d769e51902 elknetwork bridge local 433807b7390d host host local t2jv5evzm2nr ingress overlay swarm bql108kyy815 my_nginx_net overlay swarm b3953a9c99c4 none null local在manager135节点上使用上面这个覆盖网络创建nginx服务, --replicas 参数指定服务由几个实例组成。
无需提前pull nginx镜像,会自动pull
# 在manager上执行 docker service create --replicas 1 --network my_nginx_net --name nginx -p 80:80 nginx创建了一个具有一个副本(–replicas 1 )的nginx服务,使用镜像nginx
查询Swarm中服务的信息,-pretty 使命令输出格式化为可读的格式
docker service inspect --pretty nginx ID: kz44o04dqxs76zqcy9tnw1hnr Name: nginx 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: nginx:latest@sha256:b0ad43f7ee5edbc0effbc14645ae7055e21bc1973aee5150745632a24a752661 Init: false Resources: Networks: my_nginx_net Endpoint Mode: vip Ports: PublishedPort = 80 Protocol = tcp TargetPort = 80 PublishMode = ingress查询到哪个节点正在运行该服务。如下该容器被调度到node137节点上启动了,然后访问http://192.168.1.137即可访问这个容器应用(如果调度到其他节点,访问也是如此)
docker service ps nginx ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS evasv3zs35wg nginx.1 nginx:latest node137 Running Running 6 minutes ago登录 node137,后查询 docker 容器:
# 192.168.1.137 node137 docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b54c9628d5b5 nginx:latest "/docker-entrypoint.…" 11 minutes ago Up 11 minutes 80/tcp nginx.1.evasv3zs35wgp9ggwuqx34ugf如果只是通过service启动容器,swarm也算不上什么新鲜东西了。Service还提供了复制功能。可以通过 docker service scale 命令来设置服务中容器的副本数:
比如将上面的nginx容器动态扩展到3个,命令如下:
# manager节点 192.168.1.135 执行 docker service scale nginx=3 nginx scaled to 3 overall progress: 3 out of 3 tasks 1/3: running [==================================================>] 2/3: running [==================================================>] 3/3: running [==================================================>] verify: Service converged和创建服务一样,增加scale数之后,将会创建新的容器,这些新启动的容器也会经历从准备到运行的过程,过一分钟左右,服务应该就会启动完成,这时候可以再来看一下 nginx 服务中的容器
# manager节点 192.168.1.135 执行 docker service ps nginx ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS evasv3zs35wg nginx.1 nginx:latest node137 Running Running 16 minutes ago 3qvc406fiscu nginx.2 nginx:latest node136 Running Running 41 seconds ago elff20742h7a nginx.3 nginx:latest manager135 Running Running about a minute ago原先在 node137 上有一个实例,现在又增加了2个实例分别在 node136 和 manager135 上 ,分别登录 node136 和 manager135 用 docker ps 查看容器
如果一个节点宕机了(即该节点就会从swarm集群中被踢出),则Docker应该会将在该节点运行的容器,调度到其他节点,以满足指定数量的副本保持运行状态。
登录其它节点,使用docker ps查看,会发现容器被stop而非rm
除了上面使用scale进行容器的扩容或缩容之外,还可以使用docker service update 命令。 可对 服务的启动 参数 进行 更新/修改。
# manager节点 192.168.1.135 执行 docker service update --replicas 5 nginx overall progress: 5 out of 5 tasks 1/5: running [==================================================>] 2/5: running [==================================================>] 3/5: running [==================================================>] 4/5: running [==================================================>] 5/5: running [==================================================>] verify: Service converged docker service ps nginx ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS evasv3zs35wg nginx.1 nginx:latest node137 Running Running 28 minutes ago 3qvc406fiscu nginx.2 nginx:latest node136 Running Running 12 minutes ago elff20742h7a nginx.3 nginx:latest manager135 Running Running 13 minutes ago 6zs5nqa0hvgp nginx.4 nginx:latest manager135 Running Running 53 seconds ago 51yzdqokgql2 nginx.5 nginx:latest node137 Running Running 31 seconds agodocker service update 命令,也可用于直接 升级 镜像等:
docker service update --image nginx:1.19.2 nginx一个swarm的service 会有多个Task,而每个Task 都会对应一个容器。如果这些服务的数据没有挂载到宿主主机中,那么容器一旦停止运行,那么久意味着数据丢失。swarm集群中运行的服务可以通过 volme,volme NFS 来实现应用数据持久化,和 docker数据持久化是一样的。
volume 默认模式: 工作节点宿主机将数据映射到容器中。volume NFS : 管理节点宿主机同步到工作节点,工作节点宿主映射到容器中。在进行以下操作之前,先删除前面建立的服务:
# manager节点 192.168.1.135 执行 docker service rm nginx # 这样就会把所有节点上的所有容器(task任务实例)全部删除了现在因为没有创建任何容器,卷是空的。现在创建一个 volume:
# manager节点 192.168.1.135 执行 docker volume create volume-nginx docker volume ls DRIVER VOLUME NAME local volume-nginx docker volume inspect volume-nginx [ { "CreatedAt": "----------------", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/volume-nginx/_data", "Name": "volume-nginx", "Options": {}, "Scope": "local" } ]可以看到创建的volume 挂载到了/var/lib/docker/volumes/volume-nginx/_data 目录下
创建服务的时候,使用指定的卷:
# manager节点 192.168.1.135 执行 docker service create --replicas 3 \ --mount type=volume,source=volume-nginx,destination=/usr/share/nginx/html/ \ --env LC_ALL=en_US.UTF-8 \ --network my_nginx_net --name nginx -p 80:80 nginx docker service ps nginx ID NAME IMAGE NODE DESIRED STATE CURRENT STATE 5j15d5d3t4t4 nginx.1 nginx:latest node136 Running Running 5 seconds ago vwtsoz9yxcjf nginx.2 nginx:latest node137 Running Running 6 seconds ago lbql4x35xoju nginx.3 nginx:latest manager135 Running Running 28 seconds ago–mount 参数:
type=volume 使用卷模式source=volume-nginx 上面通过 docker volume create 命令创建的卷,挂载目录为 /var/lib/docker/volumes/volume-nginx/_datadestination=/usr/share/nginx/html/ 映射到容器内的 /usr/share/nginx/html/ 目录上可以看到,在manager节点上创建服务后,其它节点上也创建了服务,那node主机上使用的 卷是什么呢?
# node136节点 192.168.1.136 执行 docker volume ls DRIVER VOLUME NAME local volume-nginx docker volume inspect volume-nginx [ { "CreatedAt": "===========", "Driver": "local", "Labels": null, "Mountpoint": "/var/lib/docker/volumes/volume-nginx/_data", "Name": "volume-nginx", "Options": null, "Scope": "local" } ] # 查看卷中的文件,这些文件是容器中/usr/share/nginx/html/ 目录下的内容 sudo ls /var/lib/docker/volumes/volume-nginx/_data 50x.html index.html可以看到,创建服务的时候,使用了 source=volume-nginx, 其它节点上没有这个 volume的时候,会自动使用 docker volume crate volume-nginx 来创建这个卷。
在node137节点 192.168.1.137 执行 上面的命令查看卷结果同 node136节点 192.168.1.136 是一样的。
需要注意的是: 各个节点宿主主机中的 /var/lib/docker/volumes/volume-nginx/_data 目录并不是共享的,也就是说集群中节点主机中的/var/lib/docker/volumes/volume-nginx/_data 目录中的内容并不会保持同步
如果创建集群的时候,使用下面的命令:
docker service create --replicas 3 \ --mount type=bind,source=/home/www,destination=/usr/share/nginx/html/ \ --env LC_ALL=en_US.UTF-8 \ --network my_nginx_net --name nginx -p 80:80 nginx与上面不同的是,这里 type=bind 即采用绑定的方式, source 不再是 volume的名称,而是宿主机的一个绝对路径,其它的参数含义都是一样的。也就是说 type=bind 的情况下,可以通过source 指定宿主主机上的一个绝对地址,映射到 容器中,如果有多个目录需要映射,可以将 --mount 选项写多次。这种方式与 type=volume 是一样的,但是type =bind 要求所有节点上的 source指定的目录都必须是存在的,否则 创建服务的时候,节点上docker容器就会创建失败。
不管是mount type=bind 还是 mount type=volume ,集群节点之间都不能共享数据目录。
既然 volume 不能实现集群节点之间的数据共享,那么可以通过 NFS(Network File System),将网路中的某个存储 mount 到各个节点的卷中,这样各个节点就可以实现数据共享了。
共享数据存放到一台新的主机 192.168.1.134 上,然后将共享目录mount到 每个节点上。
# NFS 服务端 192.168.1.134 sudo yum install -y nfs-utils # 安装服务 mkdir -p /home/qinye/nfs/nginx_data # 创建共享目录 sudo vim /etc/exports #编辑NFS配置文件,以下是配置文件内容 /home/qinye/nfs/nginx_data *(rw,sync,no_root_squash)配置文件说明:
/nfs/nginx_data 共享目录* : 可以访问的主机网络,型号表示所有网络rw: 读写权限,如果是只读,则使用 rosync: 通过,数据更暗转,速度慢async: 异步,速度快,效率高,安全性低no_root_squash :NFS 服务共享的目录属性,如果是root,则对这个目录就有root权限 # NFS 服务端 192.168.1.134 sudo systemctl restart nfs # 重启nfs服务 # 在共享目录中放一个测试文件 echo "Hello NFS" >> /home/qinye/nfs/nginx_data/hello.txt ls /home/qinye/nfs/nginx_data # 查看文件夹下内容 hello.txt # 关闭 192.168.1.134 防火墙 sudo systemctl stop iptables.service sudo systemctl disable iptables.service所有的节点配置(192.168.1.135,192.168.1.136,192.168.1.137),即NFS客户端配置:
sudo yum install -y nfs-utils rpcbind # 安装NFS客户端 rpcbind 服务 sudo systemctl start rpcbind # 启动 rpcbind 服务 # 创建挂载目录 mkdir -p ~/nginx_data # 挂载共享目录到 ~/nginx_data下 sudo mount -t nfs 192.168.1.134:/home/qinye/nfs/nginx_data ~/nginx_data ls ~/nginx_data # 查看共享目录下的内容 hello.txt # 卸载命令 # sudo umount -l ~/nginx_data上面测试成功后,说明NFS已经可以工作了。
下面使用 NFS共享来创建 swarm服务:
# manager节点 192.168.1.135 执行 docker service ls # 查看所有服务 docker service rm nginx # 删除先前创建的服务 docker volume ls # 查看卷,因为删除容器不会删除卷 DRIVER VOLUME NAME local volume-nginx docker volume rm volume-nginx # 删除先前创建的卷 # 创建服务 source 可以简写成src, destination 可以简写成 dst docker service create \ --replicas 3 \ --name nginx \ -p 80:80 \ --mount 'type=volume,source=volume-nginx,destination=/usr/share/nginx/html,volume-driver=local,volume-nocopy=true,volume-opt=type=nfs,volume-opt=device=192.168.1.134:/home/qinye/nfs/nginx_data,"volume-opt=o=addr=192.168.1.134,vers=4,soft,timeo=180,bg,tcp,rw"' \ nginx– mount 参数说明
type=volume 必须为volume 挂载,不能用bind的挂载方式source或 src : 挂载的卷为 volume-nginx , 不存在则会自动创建destination 或dst :映射到容器中的路径volume-opt=type=nfs 指定股灾卷类型为nfs模式volume-opt=device=:/home/qinye/nfs/nginx_data : nfs服务器的共享目录volume-opt=o=addr=192.168.1.134,vers=4,soft,timeo=180,bg,tcp,rw : nfs服务器地址,读写权限等,因为包含了逗号,所以用双引号包围起来这样创建服务的时候,就会自动将nfs共享目录挂载到卷 volume-nginx 中
# manager节点 192.168.1.135 执行 # 查看服务 docker service ps nginx ID NAME IMAGE NODE DESIRED STATE nwjz10a8lnhl nginx.1 nginx:latest node136 Running p3tbyrbfyp8t nginx.2 nginx:latest node137 Running jsx9lgd0ezpj nginx.3 nginx:latest manager135 Running # 查看容器 docker volume ls DRIVER VOLUME NAME local volume-nginx docker volume inspect volume-nginx [ { "CreatedAt": "===================", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/volume-nginx/_data", "Name": "volume-nginx", "Options": { "device": "192.168.1.134:/home/qinye/nfs/nginx_data", "o": "addr=192.168.1.134,vers=4,soft,timeo=180,bg,tcp,rw", "type": "nfs" }, "Scope": "local" } ] sudo ls /var/lib/docker/volumes/volume-nginx/_data hello.txt # node136节点 192.168.1.136 执行 sudo ls /var/lib/docker/volumes/volume-nginx/_data hello.txt # node137节点 192.168.1.137 执行 sudo ls /var/lib/docker/volumes/volume-nginx/_data hello.txt可以看到,共享目录已经挂载过来了。
浏览器访问:http://192.168.1.135/hello.txt http://192.168.1.136/hello.txt http://192.168.1.137/hello.txt
一个nginx中往往会配置多个站点,还有证书的配置。可以将这些配置放到 NFS共享中,然后映射到nginx容器中。
# NFS 服务端 192.168.1.134 共享存储中创建几个目录 # wwwlogs 存放nginx日志 # www 存放站点静态文件 # ssl_certificate 存放ssl证书 # conf.d 虚拟主机配置 mkdir -p /home/qinye/nfs/nginx_data/wwwlogs /home/qinye/nfs/nginx_data/www /home/qinye/nfs/nginx_data/ssl_certificate /home/qinye/nfs/nginx_data/conf.d创建服务:
# manager节点 192.168.1.135 执行 docker service rm nginx docker volume rm -f volume-nginx # 强制删除卷宿主机需要映射到容器中的目录:
宿主机容器共享目录/www/www共享目录/wwwlogs/wwwlogs共享目录/conf.d/etc/nginx/conf.d共享目录/ssl_certificate/ssl_certificatedocker service create 参数
# manager节点 192.168.1.135 执行 docker service create --replicas 3 \ --mount 'type=volume,source=nginx-www,destination=/www,volume-driver=local,volume-nocopy=true,volume-opt=type=nfs,volume-opt=device=192.168.1.134:/home/qinye/nfs/nginx_data/www,"volume-opt=o=addr=192.168.1.134,vers=4,soft,timeo=180,bg,tcp,rw"' \ --mount 'type=volume,source=nginx-wwwlogs,destination=/wwwlogs,volume-driver=local,volume-nocopy=true,volume-opt=type=nfs,volume-opt=device=192.168.1.134:/home/qinye/nfs/nginx_data/wwwlogs,"volume-opt=o=addr=192.168.1.134,vers=4,soft,timeo=180,bg,tcp,rw"' \ --mount 'type=volume,source=nginx-ssl_certificate,destination=/ssl_certificate,volume-driver=local,volume-nocopy=true,volume-opt=type=nfs,volume-opt=device=192.168.1.134:/home/qinye/nfs/nginx_data/ssl_certificate,"volume-opt=o=addr=192.168.1.134,vers=4,soft,timeo=180,bg,tcp,rw"' \ --mount 'type=volume,source=nginx-conf,destination=/etc/nginx/conf.d,volume-driver=local,volume-nocopy=true,volume-opt=type=nfs,volume-opt=device=192.168.1.134:/home/qinye/nfs/nginx_data/conf.d,"volume-opt=o=addr=192.168.1.134,vers=4,soft,timeo=180,bg,tcp,rw"' \ --mount type=bind,source=/etc/localtime,destination=/etc/localtime \ --network my_nginx_net --name nginx -p 80:80 nginx docker volume ls local nginx-conf local nginx-ssl_certificate local nginx-www local nginx-wwwlogs docker volume inspect nginx-www [ { "CreatedAt": "======================", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/nginx-www/_data", "Name": "nginx-www", "Options": { "device": "192.168.1.134:/home/qinye/nfs/nginx_data/www", "o": "addr=192.168.1.134,vers=4,soft,timeo=180,bg,tcp,rw", "type": "nfs" }, "Scope": "local" } ] # node136节点 192.168.1.136 执行 docker volume ls local nginx-conf local nginx-ssl_certificate local nginx-www local nginx-wwwlogs # node137节点 192.168.1.137 执行 docker volume ls local nginx-conf local nginx-ssl_certificate local nginx-www local nginx-wwwlogs在NFS共享目录下配置虚拟主机:
# NFS 服务端 192.168.1.134 共享存储中创建几个目录 cd /home/qinye/nfs/nginx_data/conf.d vim default.conf # 服务配置,下面是 default.conf内容 server { listen 80; server_name _; # 生产中使用域名 client_max_body_size 4M; access_log /wwwlogs/default.log main; root /www/default; location / { root /www/default; index index.html; } } # 创建站点内容 mkdir -p /home/qinye/nfs/nginx_data/www/default cd /home/qinye/nfs/nginx_data/www/default vim index.html <html> <head> <title>hello</title> </head> <body> hello swarm </body> </html>因为更改nginx配置后需要重新加载配置,所以可以依次重启每个节点的nginx服务,也可以强制重启swarm服务:
# manager节点 192.168.1.135 执行 docker service update --force nginx我们可以使用 volume的方式创建集群服务:
# manager节点 192.168.1.135 执行 docker service rm nginx # 删除docker服务 docker volume rm -f volume-nginx # 强制删除卷 docker service create --replicas 3 \ --mount type=volume,source=volume-nginx,destination=/usr/share/nginx/html/ \ --env LC_ALL=en_US.UTF-8 \ --network my_nginx_net --name nginx -p 80:80 nginx docker service ps nginx ID NAME IMAGE NODE DESIRED STATE qokpqxuwwboy nginx.1 nginx:latest manager135 Running 6w0z77rbfjeo nginx.2 nginx:latest node136 Running owu5mjxzhbv7 nginx.3 nginx:latest node137 Running执行后每个节点下的主机下都会有 /var/lib/docker/volumes/volume-nginx/_data/index.html 文件,因为这种方式下 所有节点的卷不会同步,所以可以通过修改 index.html文件中的内容,然后使用一个IP来进行访问,已验证是否发生了负载均衡。
# 192.168.1.135 主机 /var/lib/docker/volumes/volume-nginx/_data/index.html 内容 <body> <h1>Welcome to nginx! 135</h1> </body> # 192.168.1.136 主机 /var/lib/docker/volumes/volume-nginx/_data/index.html 内容 <body> <h1>Welcome to nginx! 136</h1> </body> # 192.168.1.137 主机 /var/lib/docker/volumes/volume-nginx/_data/index.html 内容 <body> <h1>Welcome to nginx! 137</h1> </body>使用同一个IP地址访,发现确实请求的是不同的节点,这说明负载均衡起作用了。