Docker Swarm是Docker官方三剑客项目之一,提供Docker容器集群服务,是Docker官方对容器云生态进行支持的核心方案。使用它,用户可以将多个Docker主机抽象为大规模的虚拟Docker服务,快速打造一套容器云平台。
Swarm集群(Cluster):Swarm集群(Cluster)为一组被统一管理起来的Docker主机。
节点:
服务:一个服务可以由若干个任务组成,每个任务为某个具体的应用。服务还包括对应的存储、网络、端口映射、副本个数、访问配置、升级配置等附加参数。
任务:任务是Swarm集群中最小的调度单位,即一个指定的应用容器。
准备3台linux主机(swarm191,swarm192,swarm193);
分别配置好ip地址,使它全可以互通;
分配一下/etc/hosts文件,使3台主机可以使用主机名通讯(比较方便一点);
在swarm191这台主机上安装好docker
参照本文2.2
在swarm191上安装machine(其它节点使用machine来部署)
具体参照本文6.2
docker swarm init [OPTIONS]
Options:
--advertise-addr:指定监听的IP地址和端口 (format:
--autolock:自动锁定管理服务的启停操作,对服务进行启动或停止都需要通过口令来解锁;
--availability string:节点的可用性,包括active、pause、drain三种,默认为active;
--cert-expiry duration:根证书的过期时长,默认为90天;
--data-path-addr:指定数据流量使用的网络接口或地址;
--dispatcher-heartbeat duration:分配组件的心跳时长,默认为5秒;
--external-ca external-ca:指定使用外部的证书签名服务地址;
--force-new-cluster:强制创建新集群;
--max-snapshots uint:Raft协议快照保留的个数;
--snapshot-interval uint:Raft协议进行快照的间隔(单位为事务个数),默认为10 000个事物;
--task-history-limit int:任务历史的保留个数,默认为5。
群集创建成功,上图中红框内的一串字符需要保存下来,用于其它节点加入集群时使用。
使用netstat –tupln命令看一下端口:
红框内的三个端口,就是swarm开放的三个端口,我们需要在防火墙中把它们放通
firewall-cmd --add-port=2377/tcp --permanent
firewall-cmd --add-port=2377/tcp
firewall-cmd --add-port=7946/tcp --permanent
firewall-cmd --add-port=7946/tcp
firewall-cmd --add-port=4789/udp --permanent
firewall-cmd --add-port=4789/udp
查看一下集群的信息:
docker info
这里会把集群的基本信息列出来。
查看节点信息:
docker node ls
目前只有一个节点,也就是创建群集的本机。
docker swarm join [OPTIONS] HOST:PORT
options:
--advertise-addr:指定管理节点的IP地址和端口 (format:
--availability string:节点的可用性,包括active、pause、drain三种,默认为active;
--data-path-addr:指定数据流量使用的网络接口或地址;
--listen-addr node-addr 监听端口 (format:
--token:标识字符串(就是创建集群时产生的那串字符)
我们到另一台主机上执行:
docker swarm join –token xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ip:port
这里显示已经加入了集群。
到管理节点上看一下节点信息:
发现已经有三个节点了。swarm191那台是leader,也就是管理节点。
看下工作节点上的端口信息:
工作节点会开放两个端口,我们同样也需要在防火墙中把这两个端口打开。
firewall-cmd --add-port=7946/udp--permanent
firewall-cmd --add-port=7946/udp
firewall-cmd --add-port=4789/udp --permanent
firewall-cmd --add-port=4789/udp
docker service COMMAND
COMMAND:
create:创建一个服务
inspect:查看服务的详细信息
logs:获取服务的日志
ls:列出服务
ps:列出服务内的任务(容器)
rm:删除服务
rollback:恢复服务的配置变更
scale:扩展一个或多个被复制的服务
update:更新服务
6.4.5.1创建建服务
docker service create [OPTIONS] IMAGE [COMMAND] [ARG...]
options:
--config config:指定暴露给服务的配置;
--constraint list:应用实例在集群中被放置时的位置限制;
-d,detach:不等待创建后对应用进行状态探测即返回;
--dns list:自定义使用的DNS服务器地址;
--endpoint-mode string:指定外部访问的模式,包括vip(虚地址自动负载均衡)或dnsrr(DNS轮询);
--e,-env list:环境变量列表;
--health-cmd string:进行健康检查的指令;
--l,-label list:执行服务的标签;
--mode string:服务模式,包括replicated(默认)或global;
--replicas uint:指定实例的复制份数;
--secret secret:向服务暴露的秘密数据;
--u,-user string:指定用户信息,UID:[GID];
--w,-workdir string:指定容器中的工作目录位置。
-p, --publish:指定宿主机到容器的端口映射
docker service create --name nginx -p 8080:80 --replicas 2 nginx
这里我们创建一个nginx服务,服务名为nginx,端口映射为宿主机的8080端口映射到服务的80端口,
实例复制份数为2份。
6.4.5.2列出服务
docker service ls
这里服务已经起来了。
6.4.5.3查看服务详细信息
docker service inspect nginx
6.4.5.4列出任务
docker service ps nginx
有两个任务(容器),分别运行在swarm192和swarm191这两个节点上。
我们看下,nginx有没有正常启动。
这里我们发现,使用三外节点的IP都可以访问nginx.
即使任务并没有跑在swarm193上,用swarm193的地址,也可以正常访问,这里有点神奇,下面一节我们再来研究这个。
如果我们让一个节点上的任务(容器)停止一下,发现还是可以正常访问,因为我们这个任务(容器)有两个,可以帮到冗余的效果,且swarm还会做负载均衡,将负载分别分配到各个任务中。
6.4.5.5扩展服务
将服务的复制份数增加或减少。
如上例中我,我们创建nginx服务时,选择的复制份数是2份,但我们有三个节点,就可以将复制份数扩展到3份。
docker service scale nginx=3
已经扩展为3份了
6.4.5.6更新服务
docker service update [OPTIONS] SERVICE
Options:
--args command:服务的命令参数;
--config-add config:增加或更新一个服务的配置信息;
--config-rm list:删除一个配置文件;
--constraint-add list:增加或更新放置的限制条件;
--constraint-rm list:删除一个限制条件;
--d,-detach:执行后返回,不等待服务状态校验完整;
--dns-add list:增加或更新DNS服务信息;
--dns-rm list:删除DNS服务信息;
--endpoint-mode string:指定外部访问的模式,包括vip(虚地址自动负载均衡)或dnsrr(DNS轮询);
--entrypoint command:指定默认的入口命令;
--env-add list:添加或更新一组环境变量;
--env-rm list:删除环境变量;
--health-cmd string:进行健康检查的指令;
--label-add list:添加或更新一组标签信息;
--label-rm list:删除一组标签信息;
--no-healthcheck:不进行健康检查;
--publish-add port:添加或更新外部端口信息;
--publish-rm port:删除端口信息;
--q,-quiet:不显示进度信息;
--read-only:指定容器的文件系统为只读;
--replicas uint:指定服务实例的复制份数;
--rollback:回滚到上次配置;
--secret-add secret:添加或更新服务上的秘密数据;
--secret-rm list:删除服务上的秘密数据;
--update-parallelism uint:更新执行的并发数;
我们给nginx这个服务增加一个端口映射:
docker service update --publish-add 8081:80 nginx
这样,我们通过8081端口也可以访问nginx了。
上面我们只是从表面上看到了swarm的基本功能,接下来,我们深入探索一下swarm的工作原理。
6.4.6.1 观测外部访问swarm时的效果
先做一个小实验:
还是有三个节点,swarm191,swarm192,swarm193
创建一个nginx服务,复制份数为2。
docker service create --name nginx -p 8080:80 --replicas 2 nginx:1.0
可以看到,这个nginx服务运行在swarm191和swarm193上。
在三个节点上分别用docker ps这个命令查看一下容器:
发现在swarm191和swarm192上各生成了一个容器,而swarm193上没有。
现在我们先用docker exec –it <container name> bash这个命令进入 swarm191和swarm192上的容器,修改一下nginx的index.html文件。
swarm191上的容器里的/usr/share/nginx/html/index.html文件内容改为swarm191
swarm193上的容器里的/usr/share/nginx/html/index.html文件内容改为swarm193
改完以后,访问swarm191节点,显示的内容是swarm191,说明访问的是在节点swarm191上的那个容器。
访问swarm193节点,显示的内容是swarm193,说明访问的是在节点swarm193上的那个容器。
那么访问swarm192节点,会显示什么内容呢?它会访问哪个节点上的容器呢?
最开始,它会访问到swarm191,你尝试不断刷新网页后,它又会访问到swarm193。
现在,我们把191上的容器暂停掉,再来观测:
这里我们发现,它会跳转到swarm193去
从这个实验我可以得出以下结论:
6.4.6.2理解网络命名空间
要想深入探索swarm的工作原理,我们首先需要理解网络命名空间。
在 Linux 中,网络名字空间可以被认为是隔离的拥有单独网络栈(网卡、路由转发表、iptables)的环境。网络名字空间经常用来隔离网络设备和服务,只有拥有同样网络名字空间的设备,才能看到彼此。
我们重新开一下虚拟来做网络命名空间的实验。
ip netns add test0
ip netns add test1
ip netns ls
这里我们可以看到刚刚创建的网络命名空间了。
ip link add 第一个veth名称 type veth peer name 第二个veth名称
ip link add veth0 type veth peer name veth1
使用ip link show查看一下
已经生成了veth对
ip link set veth0 netns test0
ip link set veth1 netns test0
ip netns exec test0 ip addr add 192.168.10.10/24 dev veth0
ip netns exec test1 ip addr add 192.168.11.11/24 dev veth1
ip netns exec test0 ip link set up dev veth0
ip netns exec test1 ip link set up dev veth1
ip netns exec test0 ip a
ip netns exec test1 ip a
注意红框的数字,他们交叉相等,说明这两个命空间已经成功建立了veth对,应该是可以互通的。
ip netns exec test0 ping 192.168.10.11
我们再建一个命名空间test3,并重新创建一块虚拟网卡,看会不会通
ip netns add test2
ip link add veth2 type veth
ip link set veth2 netns test2
ip netns exec test2 ip addr add 192.168.10.12/24 dev veth2
ip netns exec test2 ip link set up dev veth2
ip netns exec test2 ip a
发现,是ping不通的。
6.4.6.2swarm集群通讯过程探索
上面,我们只是看到了swarm集群工作的表面,接下来,我们来尝试探索一下swarm集群整个通讯的过程。
我们应用上一小节的知识,就可以很容易地搞清楚swarm的网络结构了。
首先,docker swarm会生成三个网络命名空间,具体位置在/run/docker/netns这个目录下面。
但是我们无法使用ip netns来管理这几个网络命名空间,但可以使用nsenter这个工具来管理。
nsenter --net=
分别看一下三个网络命名空间、宿主机容器的IP。
6.4.7 创建服务栈
看一下容器的IP
发现容器的IP和e6058a169c2f是一样的,可以认定,容器实际上使用的是e6058a169c2f这个网络命名空间。
再看一下docker系统中的网络,docker network ls
这里docker_gwbridge和ingress这两个网络是swarm创建的。
我们先看一下ingress这个网络的详细信息
注意看这个ID,和这个/run/docker/netns/1-u48p3plmon网络命名空间太像了,我认定,这个ingress和/run/docker/netns/1-u48p3plmon,就是同一个命名空间。
至于docker里的docker_gwbridge这个网络,就是宿主机上的那个docker_gwbridge网络。
而且这个docker_gwbridge又桥接了两块虚拟网卡。
就是这两块,veth51和50.
根据我们上面的连线,51和50其实是连接到ingress-box这个命名空间的。
再看一下iptables
这里有一条指向172.18.0.2:8080的源地址NAT,也就是指向Ingress-box.
根据以上分析,我可以得出以下网络拓扑图:
结论:
经过以上的探索,我们可以猜想,swarm集群的拓扑应该如下图:
当外部访问到宿主机IP时,通过iptables DNAT,将数据包丢到ingress-box,再由ingress-box通过ingress网络选择丢到哪个容。
至于ingress-box如何选择哪个容器,这个不在我们本次的讨论范围之内。
6.3小节,我们讲过compose可用于服务栈的编排,但compose无法在swarm环境中部署,在swarm环境中部署栈,就需要使用docker stack。
docker stack [OPTIONS] COMMAND
Options:
--orchestrator指定集群类型,可选项:swarm|kubernetes|all
Command:
deploy:部署或更新服务栈
ls:列出已部署的服务栈
ps:列出服务栈中的任务(容器)
rm:删除服务栈
services:列出服务栈中的服务
6.4.7.1部署服务栈
docker stack deploy [OPTIONS] STACK
Options:
-c, --compose-file:指定一个compose文件
--orchestrator:指定编排格式,可选项:swarm|kubernetes|all
--prune:删除没有被引用的服务,默认为false
--resolve-image:查询注册表以解决图像摘要和支持的平台(“always”|“changed”|“never”),默认为 alwys.
--with-registry-auth:向Swarm代理发送注册表认证详细信息,默认为false
要部署服务栈,首先要创建一个compose.yml文件,这个文件和6.3小节中docker-compose中的yml文件差不多,但是不支持以下命令。
build #docker stack不支持创建镜像,所以必须先在节点上部署好镜像文件。
cgroup_parent
container_name
devices
tmpfs
external_links
links
network_mode
restart
security_opt
userns_mode
我们以6.3.5小节中,部署lnmp服务栈为例。
step 1:在集群的各个节点上部署好容器镜像
这两个镜像的创建过程,详见6.3.5小节
step 2:创建一个nfs共享存储,并挂载到群集的各个节点中,挂载的目录名和路径需要一致,用于存储数据(nginx数据和mysql数据)
yum install nfs-utils
mkdir lnmp_data
mkdir lnmp_data/html
mkdir lnmp_data/data
chown -R nfsnobody:nfsnobody lnmp_data/
红框中的all_squash表示,所有连到nfs的用户,都被映射到nfs服务器的nfsnobody用户
systemctl restart rpcbind
systemctl enable rpcbind
systemctl start nfs-server
systemctl enable nfs-server
firewall-cmd --add-service=rpc-bind
firewall-cmd --add-service=rpc-bind --permanent
firewall-cmd --add-service=nfs
firewall-cmd --add-service=nfs --permanent
firewall-cmd --add-service=mountd
firewall-cmd --add-service=mountd --permanent
看到这个信息,说明nfs服务已经启动成功。
mkdir /root/lnmp/lnmp_data
mount -t nfs 192.168.0.125:/root/lnmp_data /root/lnmp/lnmp_data
partprobe
df -h
已经挂载成功
vim /etc/fstab
确保三个节点都可以访问到nfs的共享目录
step 3:创建一个compose文件
version: '3.4'
services:
nginx: #第一个服务nginx
image: lnmp_nginx#定义创建服务所使用的镜像文件
labels:#打一个标签
- nginx
env_file:#使用一个环境变量文件
- ./myenv.env
depends_on:#该服务依赖于mysql这个服务
- mysql
ports:#将宿主机的8080端口映射到服务的80端口
- 8080:80
deploy:#定义容器复本数为2
replicas: 2
volumes:#将nginx数据目录挂载到宿主机
- /root/lnmp/lnmp_data/html:/usr/share/nginx/html
mysql:#第二个服务mysql
image: lnmp_mysql#定义创建服务所使用的镜像文件
labels:#打一个标签
- mysql
env_file:#使用一个环境变量文件
- ./myenv.env
ports:#将宿主机的3306端口映射到服务的3306端口
- 3306:3306
deploy:#定义容器复本数为1
replicas: 1
volumes:#将mysql数据目录映射到宿主机
- /root/lnmp/lnmp_data/mysql:/var/lib/mysql
step 4 创建一个环境变量文件
DATABASE_NAME=wordpress #定义wordpress使用的数据库名称
DATABASE_USER=wordpress#定义wordpress使用的数据库用户名
DATABASE_PASSWORD_ROOT=123qweasd#定义mysql的root密码
DATABASE_PASSWORD_USER=123qweasd#定义给wordpress使用的mysql用户名密码
DATABASE_HOST=mysql#定义给wordpress使用的数据库服务器地址或主机名
step 5 创建服务
docker stack deploy -c docker-stack.yml lnmp
这里的docker-stack.yml就是我们在step3中创建的那个compose文件。
这里我们看到,创建服务时做了三件事件上:
然后我们看一下,我们的lnmp网站有没有起来:
网站已经起来了。
6.4.7.2列出服务栈
docker stack ls [OPTIONS]
options:
--format:指定输出的显示格式
--orchestrator:--orchestrator:指定编排格式,可选项:swarm|kubernetes|all
docker stack ls
这里已经列出了服务栈,里面有两个服务,编排格式为swarm.
6.4.7.3列出服务
docker stack services [OPTIONS] STACK
options:
-f, --filter: 根据提供的条件筛选输出
--format: 指定输出的显示格式
--orchestrator:--orchestrator:指定编排格式,可选项:swarm|kubernetes|all
-q, --quiet:安静模式,只显示id
这里列出了,lnmp这个服务栈有两人个服务,分别是lnmp_mysql和lnmp_nginx
6.4.7.4列出任务
docker stack ps [OPTIONS] STACK
options:
-f, --filter: 根据提供的条件筛选输出
--format: 指定输出的显示格式
--no-resolve: 不将id映射到名称
--no-trunc:不截断输出
--orchestrator:--orchestrator:指定编排格式,可选项:swarm| kubernetes|all
-q, --quiet:安静模式,只显示id
这里看到,服务栈中有三个任务(容器),一个mysql,在swarm191上,两个nginx分别在node swarm192和swarm193上。
6.4.7.5删除服务栈
docker stack rm [OPTIONS] STACK [STACK...]
options:
--orchestrator:--orchestrator:指定编排格式,可选项:swarm|kubernetes|all