1、 Docker Swarm 是什么?
Docker Swarm 是Docker 公司推出的管理Docker集群的平台。它是将一群Docker宿主机变成一个单一的虚拟主机。
2、Docker Swarm的几个关键概念
Swarm:集群管理工具。
子命令有init,
join
, leave, update。(docker swarm --help查看帮助)
Node:节点。node是加入到swarm集群中的一个docker引擎实体,可以在一台物理机上运行多个node,node分为:manager nodes,也就是管理节点;worker nodes,也就是工作节点。
子命令有accept, promote, demote, inspect, update, tasks,
ls
,
rm
。(docker node --help查看帮助)
Service:应用编排,也可以理解为服务。用于定义任务,创建服务时,需要指定要使用的容器镜像。子命令有create, inspect, update, remove, tasks。(docker service--help查看帮助)
Task:应用实例。
3、Swarm 的调度策略
Swarm在调度(scheduler)节点(leader节点)运行容器的时候,会根据指定的策略来计算最适合运行容器的节点,目前支持的策略有:spread, binpack, random.
1)Random
顾名思义,就是随机选择一个Node来运行容器,一般用作调试用,spread和binpack策略会根据各个节点的可用的CPU, RAM以及正在运
行的容器的数量来计算应该运行容器的节点。
2)Spread
在同等条件下,Spread策略会选择运行容器最少的那台节点来运行新的容器,binpack策略会选择运行容器最集中的那台机器来运行新的节点。
使用Spread策略会使得容器会均衡的分布在集群中的各个节点上运行,一旦一个节点挂掉了只会损失少部分的容器。
3)Binpack
Binpack策略最大化的避免容器碎片化,就是说binpack策略尽可能的把还未使用的节点留给需要更大空间的容器运行,尽可能的把容器运行在
一个节点上面。
4、Swarm Cluster模式特性
1)批量创建服务
建立容器之前先创建一个overlay的网络,用来保证在不同主机上的容器网络互通的网络模式
2)强大的集群的容错性
当容器副本中的其中某一个或某几个节点宕机后,cluster会根据自己的服务注册发现机制,以及之前设定的值--replicas n,
在集群中剩余的空闲节点上,重新拉起容器副本。整个副本迁移的过程无需人工干预,迁移后原本的集群的load balance依旧好使!
不难看出,docker service其实不仅仅是批量启动服务这么简单,而是在集群中定义了一种状态。Cluster会持续检测服务的健康状态
并维护集群的高可用性。
3)服务节点的可扩展性
Swarm Cluster不光只是提供了优秀的高可用性,同时也提供了节点弹性扩展或缩减的功能。当容器组想动态扩展时,只需通过scale
参数即可复制出新的副本出来。
仔细观察的话,可以发现所有扩展出来的容器副本都run在原先的节点下面,如果有需求想在每台节点上都run一个相同的副本,方法
其实很简单,只需要在命令中将
"--replicas n"
更换成
"--mode=global"
即可!
复制服务(--replicas n)
将一系列复制任务分发至各节点当中,具体取决于您所需要的设置状态,例如“--replicas 3”。
全局服务(--mode=global)
适用于集群内全部可用节点上的服务任务,例如“--mode global”。如果大家在 Swarm 集群中设有 7 台 Docker 节点,则全部节点之上都将存在对应容器。
4. 调度机制
所谓的调度其主要功能是cluster的server端去选择在哪个服务器节点上创建并启动一个容器实例的动作。它是由一个装箱算法和过滤器
组合而成。每次通过过滤器(constraint)启动容器的时候,swarm cluster 都会调用调度机制筛选出匹配约束条件的服务器,并在这上面运行容器。
------------------Swarm cluster的创建过程包含以下三个步骤----------------------
1)发现Docker集群中的各个节点,收集节点状态、角色信息,并监视节点状态的变化
2)初始化内部调度(scheduler)模块
3)创建并启动API监听服务模块
一旦创建好这个cluster,就可以用命令docker service批量对集群内的容器进行操作,非常方便!
在启动容器后,docker 会根据当前每个swarm节点的负载判断,在负载最优的节点运行这个task任务,用
"docker service ls"
和
"docker service ps + taskID"
可以看到任务运行在哪个节点上。容器启动后,有时需要等待一段时间才能完成容器创建。
5、Swarm集群部署
5.1准备工作
机器环境(三台机器,centos系统)
IP:192.168.1.1 主机名:dockerManager 担任角色:swarm manager
IP:192.168.1.2 主机名:dockerNode1 担任角色:swarm node
IP:192.168.1.2 主机名:dockerNode2 担任角色:swarm node
1)修改主机名
# 192.168.31.43 主机上执行
[root@dockerManager ~]# hostnamectl set-hostname dockerManager
# 192.168.31.188 主机上执行
[root@dockerNode1 ~]# hostnamectl set-hostname dockerNode1
# 192.168.31.139 主机上执行
[root@dockerNode2 ~]# hostnamectl set-hostname dockerNode2
2)配置hosts文件(可配置可不配置)
[root@manager43 ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.1.1 dockerManager
192.168.1.2 dockerNode1
192.168.1.3 dockerNode2
# 使用scp复制到node主机
[root@dockerManager ~]# scp /etc/hosts [email protected]:/etc/hosts
[root@dockerManager ~]# scp /etc/hosts [email protected]:/etc/hosts
3) 设置防火墙
关闭三台机器上的防火墙。如果开启防火墙,则需要在所有节点的防火墙上依次放行2377/tcp(管理端口)、7946/udp(节点间通信端口)、4789/udp(overlay 网络端口)端口。
[root@dockerManager ~]# systemctl disable firewalld.service
[root@dockerManager ~]# systemctl stop firewalld.service
4) 安装docker并配置加速器(在三台主机都要安装哟...)
[root@dockerManager ~]# yum -y install docker
[root@dockerNode1 ~]# yum -y install docker
[root@dockerNode2 ~]# yum -y install docker
5.2 创建Swarm并添加节点
1) 创建Swarm集群
[root@dockerManager ~]# docker swarm init --advertise-addr 192.168.1.1
Swarm initialized: current node (z2n633mty5py7u9wyl423qnq0) is now a manager.
To add a worker to this swarm, run the following command:
# 这就是添加节点的方式(要保存初始化后token,因为在节点加入时要使用token作为通讯的密钥)
docker swarm join --token SWMTKN-1-2lefzq18zohy9yr1vskutf1sfb2a590xz9d0mjj2m15zu9eprw-2938j5f50t35ycut0vbj2sx0s 192.168.1.1:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
上面命令执行后,该机器自动加入到swarm集群。这个会创建一个集群token,获取全球唯一的 token,作为集群唯一标识。后续将其他节点加入集群都会用到这个token值。
其中,--advertise-addr参数表示其它swarm中的worker节点使用此ip地址与manager联系。命令的输出包含了其它节点如何加入集群的命令。
这里无意中遇到了一个小小的问题:
# 在次执行上面的命令,回报下面的错误
[root@dockerManager ~]# docker swarm init --advertise-addr 192.168.1.1
Error response from daemon: This node is already part of a swarm. Use "docker swarm leave" to leave this swarm and join another one.
# 解决方法
[root@dockerManager ~]# docker swarm leave -f
这里的leave就是在集群中删除节点,-f参数强制删除,执行完在重新执行OK
2) 查看集群的相关信息
[root@dockerManager ~]# docker info
上面的命令执行后 找到Swarm的关键字,就可以看到相关信息了
[root@dockerManager ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
3jcmnzjh0e99ipgshk1ykuovd * dockerManager Ready Active Leader 18.06.0-ce
上面的命令是查看集群中的机器(注意上面node ID旁边那个*号表示现在连接到这个节点上)
3) 添加节点主机到Swarm集群
上面我们在创建Swarm集群的时候就已经给出了添加节点的方法
# 192.168.1.2 主机上执行
[root@dockerNode1 ~]# docker swarm join --token SWMTKN-1-2lefzq18zohy9yr1vskutf1sfb2a590xz9d0mjj2m15zu9eprw-2938j5f50t35ycut0vbj2sx0s 192.168.1.1:2377
This node joined a swarm as a worker.
# 192.168.1.3 主机上执行
[root@dockerNode2 ~]# docker swarm join --token SWMTKN-1-2lefzq18zohy9yr1vskutf1sfb2a590xz9d0mjj2m15zu9eprw-2938j5f50t35ycut0vbj2sx0s 192.168.1.1:2377
This node joined a swarm as a worker.
如果想要将其他更多的节点添加到这个swarm集群中,添加方法如上一致
在dockerManager 主机上我们可以看一下集群中的机器及状态
[root@dockerManager ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
3jcmnzjh0e99ipgshk1ykuovd * dockerManager Ready Active Leader 18.06.0-ce
vww7ue2xprzg46bjx7afo4h04 dockerNode2 Ready Active 18.06.1-ce
c5klw5ns4adcvumzgiv66xpyj dockerNode1 Ready Active 18.06.1-ce
--------------------------------------------------------------------------------------------------------------------
温馨提示:更改节点的availablity状态
swarm集群中node的availability状态可以为 active或者drain,其中:
active状态下,node可以接受来自manager节点的任务分派;
drain状态下,node节点会结束task,且不再接受来自manager节点的任务分派(也就是下线节点)
[root@dockerManager ~]# docker node update --availability drain dockerNode2 # 将dockerNode2 节点下线。如果要删除dockerNode2 节点,命令是"docker node rm --force dockerNode2 "
dockerNode2
[root@dockerManager ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
3jcmnzjh0e99ipgshk1ykuovd * dockerManager Ready Active Leader 18.06.0-ce
vww7ue2xprzg46bjx7afo4h04 dockerNode2 Ready Drain 18.06.1-ce
c5klw5ns4adcvumzgiv66xpyj dockerNode1 Ready Active 18.06.1-ce
如上,当node1的状态改为drain后,那么该节点就不会接受task任务分发,就算之前已经接受的任务也会转移到别的节点上。
再次修改为active状态(及将下线的节点再次上线)
[root@dockerManager ~]# docker node update --availability active dockerNode2
dockerNode2
[root@dockerManager ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
3jcmnzjh0e99ipgshk1ykuovd * dockerManager Ready Active Leader 18.06.0-ce
vww7ue2xprzg46bjx7afo4h04 dockerNode2 Ready Active 18.06.1-ce
c5klw5ns4adcvumzgiv66xpyj dockerNode1 Ready Active 18.06.1-ce
5.3 在Swarm中部署服务(nginx为例)
1) 创建网络在部署服务
# 创建网络
[root@dockerManager ~]# docker network create -d overlay nginx_net
a52jy33asc5o0ts0rq823bf0m
[root@dockerManager ~]# docker network ls | grep nginx_net
a52jy33asc5o nginx_net overlay swarm
# 部署服务
[root@dockerManager ~]# docker service create --replicas 1 --network nginx_net --name my_nginx -p 80:80 nginx # 就创建了一个具有一个副本(--replicas 1 )的nginx服务,使用镜像nginx
olexfmtdf94sxyeetkchwhehg
# 使用 docker service ls 查看正在运行服务的列表
[root@dockerManager ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
olexfmtdf94s my_nginx replicated 1/1 nginx:latest *:80->80/tcp
2) 查询Swarm中服务的信息
-pretty 使命令输出格式化为可读的格式,不加 --pretty 可以输出更详细的信息:
[root@dockerManager ~]# docker service inspect --pretty my_nginx
# 查询到哪个节点正在运行my_nginx这个服务
[root@dockerManager ~]# docker service ps my_nginx
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
yzonph0zu7km my_nginx.1 nginx:latest dockerManager Running Running about an hour ago
温馨提示:如果上面命令执行后,上面的 STATE 字段中刚开始的服务状态为 Preparing,需要等一会才能变为 Running 状态,其中最费时间的应该是下载镜像的过程
# 查询运行的容器
[root@dockerManager ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0dc7103f8030 nginx:latest "nginx -g 'daemon of…" About an hour ago Up About an hour 80/tcp my_nginx.1.yzonph0zu7km0211uj0ro5brj
3) 在Swarm中动态扩展服务(scale)
Service提供了复制(类似kubernetes里的副本)功能。可以通过 docker service scale 命令来设置服务中容器的副本数
比如将上面的my_nginx容器动态扩展到4个
[root@dockerManager ~]# docker service scale my_nginx=4
4) 模拟宕机node节点
特别需要清楚的一点:
如果一个节点宕机了(即该节点就会从swarm集群中被踢出),则Docker应该会将在该节点运行的容器,调度到其他节点,以满足指定数量的副本保持运行状态。
比如:
将dockerNode2 宕机后或将dockerNode2 的docker服务关闭,那么它上面的task实例就会转移到别的节点上。当dockerNode2 节点恢复后,它转移出去的task实例不会主动转移回来,
只能等别的节点出现故障后转移task实例到它的上面。使用命令"docker node ls",发现dockerNode2 节点已不在swarm集群中了(状态为:Down)。
5) Swarm 动态缩容服务(scale)
同理,swarm还可以缩容,同样是使用scale命令
如下,将my_nginx容器变为1个
[root@dockerManager ~]# docker service scale my_nginx=1
6) 除了上面使用scale进行容器的扩容或缩容之外,还可以使用docker service update 命令。 可对 服务的启动 参数 进行 更新/修改。
[root@dockerManager ~]# docker service update --replicas 3 my_nginxdocker service update 命令,也可用于直接 升级 镜像等
[root@dockerManager ~]# docker service update --image nginx:new my_nginx
7) 删除service
[root@dockerManager ~]# docker service rm my_nginx
这样就会把所有节点上的所有容器(task任务实例)全部删除了
5.4 Swarm 中使用Volume(挂载目录)
1) 查看volume的帮助信息
[root@dockerManager ~]# docker volume --help
2) 创建一个volume
[root@dockerManager ~]# docker volume create --name testvolume
testvolume# 查看创建的volume
[root@dockerManager ~]# docker volume ls
DRIVER VOLUME NAME
local testvolume
# 查看volume详情
[root@dockerManager ~]# docker volume inspect testvolume
3) 创建新的服务并挂载testvolume(nginx为例)
[root@dockerManager ~]# docker service create --replicas 3 --mount type=volume,src=testvolume,dst=/zjz --name test_nginx nginx温馨提示:
参数src写成source也可以;dst表示容器内的路径,也可以写成target# 查看有没有挂载成功(登录各个节点的容器看看有没有指定的目录并创建文件测试)
# 容器中操作
[root@dockerManager ~]# docker exec -it 63451219cb4e /bin/bash
root@63451219cb4e:/# cd /zjz/
root@63451219cb4e:/zjz# ls
root@63451219cb4e:/zjz# echo "gen wo xue docker" > docker.txt
root@63451219cb4e:/zjz# ls
docker.txt
执行docker volume inspect testvolume 可以看到本地的路径(上面已经执行过了)
本地路径:/var/lib/docker/volumes/testvolume/_data
[root@dockerManager ~]# cd /var/lib/docker/volumes/testvolume/_data
[root@dockerManager _data]# ls
docker.txt
[root@dockerManager _data]# cat docker.txt
gen wo xue docker
还可以将node节点机上的volume数据目录做成软链接
[root@dockerManager _data]# ln -s /var/lib/docker/volumes/testvolume/_data /zjz
[root@dockerManager _data]# cd /zjz/
[root@dockerManager zjz]# ls
docker.txt
[root@dockerManager zjz]# echo "123" > 1.txt
[root@dockerManager zjz]# ll
总用量 8
-rw-r--r-- 1 root root 4 10月 21 11:04 1.txt
-rw-r--r-- 1 root root 18 10月 21 11:00 docker.txt
# 容器中查看
[root@dockerManager zjz]# docker exec -it 63451219cb4e /bin/bash
root@63451219cb4e:/# cd /zjz/
root@63451219cb4e:/zjz# ls
1.txt docker.txt
root@63451219cb4e:/zjz# cat 1.txt
123
root@63451219cb4e:/zjz# cat docker.txt
gen wo xue docker
# 还有一种挂载方式简单说一下吧,上面的会了下面的肯定简单
命令格式:
docker service create --mount type=bind,target=/container_data/,source=/host_data/
其中,参数target表示容器里面的路径,source表示本地硬盘路径
# 示例创建并挂载并使用网络
[root@dockerManager ~]# docker service create --replicas 1 --mount type=bind,target=/usr/share/nginx/html/,source=/opt/web/ --network nginx_net --name zjz_nginx -p 8880:80 nginx
5.5 多服务Swarm集群部署
我们这里部署三个服务(nginx服务,visualizer服务,portainer服务) 都是集群 GUI 管理服务
docker service部署的是单个服务,我们可以使用docker stack进行多服务编排部署
1) 编写docker-compose.yml文件
[root@dockerManager ~]# mkdir testswarm
[root@dockerManager ~]# cd testswarm/
[root@dockerManager testswarm]# vim docker-compose.yml
version: "3"
services:
nginx:
image: nginx
ports:
- 8888:80
deploy:
mode: replicated
replocas: 3
visualizer:
image: dockersamples/visualizer
ports:
- "8080: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]
2) 通过这个yml文件部署服务
[root@dockerManager testswarm]# docker stack deploy -c docker-compose.yml deploy_deamon
通过上面的执行过程可以看出这样创建会默认创建一个网络并使用它,名字都是我们给的名字的前缀加上服务名
# 查看服务
[root@dockerManager testswarm]# docker service ls
# 查看nginx服务
[root@dockerManager testswarm]# docker service ps deploy_deamon_nginxing Running about a minute ago
# 查看portainer服务
[root@dockerManager testswarm]# docker service ps deploy_deamon_portainer
# 查看visualizer服务
[root@dockerManager testswarm]# docker service ps deploy_deamon_visualizer
6、Docker Swarm 容器网络
Docker Swarm在启动时自己会创建一些网络,然后利用它们实现容器之间的通信与负载均衡等功能。
# 查看docker swarm 网络信息
$ docker network ls NETWORK ID NAME DRIVER SCOPE cac91f9c60ff bridge bridge local b55339bbfab9 docker_gwbridge bridge local fe6ef5d2e8e8 host host local f1nvcluv1xnf ingress overlay swarm 8vty8k3pejm5 net1 overlay swarm 893a1bbe3118 none null local
docker_gwbridge:通过这个网络,容器可以连接到宿主机。(Driver为bridge)
ingress:这个网络用于将服务暴露给外部访问,docker swarm就是通过它实现的routing mesh(将外部请求路由到不同主机的容器)。(Driver为overlay)创建一个自定义的overlay
swarm模式的覆盖网络(
overlay)包括以下功能:
1)可以附加多个服务到同一个网络。
2)默认情况下,service discovery为每个swarm服务分配一个虚拟IP地址(vip)和DNS名称,使得在同一个网络中容器之间可以使用服务名称为互相连接。
3)可以配置使用DNS轮循而不使用VIP
4)为了可以使用swarm的覆盖网络,在启用swarm模式之间你需要在swarm节点之间开放以下端口:
5)TCP
/UDP
端口7946 – 用于容器网络发现
6)UDP端口4789 – 用于容器覆盖网络
#创建自定义voerlay网络
$ docker network create -d overlay my-overlay-network
#查看network
$ docker network ls
#创建服务时将服务连接到overlay网络
$ docker service create --replicas 3 --network my-overlay-network --name testService -p 80:80 nginx
#查询某个节点上关于my-overlay-network的详细信息
$ docker network inspect my-overlay-network