Docker基础30--6.4 Docker三剑客之Swarm

6.4 Docker三剑客之Swarm

Docker SwarmDocker官方三剑客项目之一,提供Docker容器集群服务,是Docker官方对容器云生态进行支持的核心方案。使用它,用户可以将多个Docker主机抽象为大规模的虚拟Docker服务,快速打造一套容器云平台。

6.4.1几个概念

Swarm集群(Cluster):Swarm集群(Cluster)为一组被统一管理起来的Docker主机。

节点:

  1. 管理节点(manager node):负责响应外部对集群的操作请求,并维持集群中资源,分发任务给工作节点。同时,多个管理节点之间通过Raft协议构成共识。一般推荐每个集群设置5个或7个管理节点;
  2. 工作节点(worker node):负责执行管理节点安排的具体任务。默认情况下,管理节点自身也同时是工作节点。每个工作节点上运行代理(agent)来汇报任务完成情况。

服务:一个服务可以由若干个任务组成,每个任务为某个具体的应用。服务还包括对应的存储、网络、端口映射、副本个数、访问配置、升级配置等附加参数。

  1. 复制服务(replicated services)模式:默认模式,每个任务在集群中会存在若干副本,这些副本会被管理节点按照调度策略分发到集群中的工作节点上。此模式下可以使用-replicas参数设置副本数量;
  2. 全局服务(global services)模式:调度器将在每个可用节点都执行一个相同的任务。该模式适合运行节点的检查,如监控应用等。

任务:任务是Swarm集群中最小的调度单位,即一个指定的应用容器。

6.4.2环境准备

准备3linux主机(swarm191,swarm192,swarm193);

分别配置好ip地址,使它全可以互通;

分配一下/etc/hosts文件,使3台主机可以使用主机名通讯(比较方便一点);

 

swarm191这台主机上安装好docker

参照本文2.2

swarm191上安装machine(其它节点使用machine来部署)

具体参照本文6.2

6.4.3创建集群

docker swarm init [OPTIONS]

Options:

--advertise-addr:指定监听的IP地址和端口 (format: [:port]),默认为所有网口的2377端口。

--autolock:自动锁定管理服务的启停操作,对服务进行启动或停止都需要通过口令来解锁;

--availability string:节点的可用性,包括activepausedrain三种,默认为active

--cert-expiry duration:根证书的过期时长,默认为90天;

--data-path-addr:指定数据流量使用的网络接口或地址;

--dispatcher-heartbeat duration:分配组件的心跳时长,默认为5秒;

--external-ca external-ca:指定使用外部的证书签名服务地址;

--force-new-cluster:强制创建新集群;

--max-snapshots uintRaft协议快照保留的个数;

--snapshot-interval uintRaft协议进行快照的间隔(单位为事务个数),默认为10 000个事物;

--task-history-limit int:任务历史的保留个数,默认为5

 

群集创建成功,上图中红框内的一串字符需要保存下来,用于其它节点加入集群时使用。

使用netstat –tupln命令看一下端口:

Docker基础30--6.4 Docker三剑客之Swarm_第1张图片

 

红框内的三个端口,就是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基础30--6.4 Docker三剑客之Swarm_第2张图片

 

这里会把集群的基本信息列出来。

查看节点信息:

docker node ls

 

目前只有一个节点,也就是创建群集的本机。

6.4.4其它节点加入集群

docker swarm join [OPTIONS] HOST:PORT

options:

--advertise-addr:指定管理节点的IP地址和端口 (format: [:port]),默认为2377端口。

--availability string:节点的可用性,包括activepausedrain三种,默认为active

--data-path-addr:指定数据流量使用的网络接口或地址;

--listen-addr node-addr   监听端口 (format: [:port]) (default 0.0.0.0:2377)

--token:标识字符串(就是创建集群时产生的那串字符)

我们到另一台主机上执行:

docker swarm join –token xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  ip:port

 

这里显示已经加入了集群。

到管理节点上看一下节点信息:

 

发现已经有三个节点了。swarm191那台是leader,也就是管理节点。

看下工作节点上的端口信息:

Docker基础30--6.4 Docker三剑客之Swarm_第3张图片

 

工作节点会开放两个端口,我们同样也需要在防火墙中把这两个端口打开。

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

6.4.5服务管理

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有没有正常启动。

Docker基础30--6.4 Docker三剑客之Swarm_第4张图片

Docker基础30--6.4 Docker三剑客之Swarm_第5张图片 

Docker基础30--6.4 Docker三剑客之Swarm_第6张图片 

 

这里我们发现,使用三外节点的IP都可以访问nginx.

即使任务并没有跑在swarm193上,用swarm193的地址,也可以正常访问,这里有点神奇,下面一节我们再来研究这个。

如果我们让一个节点上的任务(容器)停止一下,发现还是可以正常访问,因为我们这个任务(容器)有两个,可以帮到冗余的效果,且swarm还会做负载均衡,将负载分别分配到各个任务中。

6.4.5.5扩展服务

将服务的复制份数增加或减少。

如上例中我,我们创建nginx服务时,选择的复制份数是2份,但我们有三个节点,就可以将复制份数扩展到3份。

docker service scale nginx=3

Docker基础30--6.4 Docker三剑客之Swarm_第7张图片

 

 

已经扩展为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

Docker基础30--6.4 Docker三剑客之Swarm_第8张图片 

 

这样,我们通过8081端口也可以访问nginx了。

6.4.6探索swarm的工作原理

上面我们只是从表面上看到了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上的那个容器。

Docker基础30--6.4 Docker三剑客之Swarm_第9张图片

 

访问swarm193节点,显示的内容是swarm193,说明访问的是在节点swarm193上的那个容器。

Docker基础30--6.4 Docker三剑客之Swarm_第10张图片

 

那么访问swarm192节点,会显示什么内容呢?它会访问哪个节点上的容器呢?

Docker基础30--6.4 Docker三剑客之Swarm_第11张图片

Docker基础30--6.4 Docker三剑客之Swarm_第12张图片 

最开始,它会访问到swarm191,你尝试不断刷新网页后,它又会访问到swarm193。

现在,我们把191上的容器暂停掉,再来观测:

Docker基础30--6.4 Docker三剑客之Swarm_第13张图片

 

这里我们发现,它会跳转到swarm193去

从这个实验我可以得出以下结论:

  1. 当任务(容器)在本节点上,有外部访问时,会优先转发到本节点的容器上;
  2. 当任务(容器)不在本节点上,有外部访问时,会转发到其它节点上,并且会访问其它不同的节点,也就是可以实现负载均衡的功能。
  3. 当本节点上的容器故障,有外部访问时,会转发到其它节点上,也就是可以实现冗余的功能。

6.4.6.2理解网络命名空间

要想深入探索swarm的工作原理,我们首先需要理解网络命名空间。

 在 Linux 中,网络名字空间可以被认为是隔离的拥有单独网络栈(网卡、路由转发表、iptables)的环境。网络名字空间经常用来隔离网络设备和服务,只有拥有同样网络名字空间的设备,才能看到彼此。

我们重新开一下虚拟来做网络命名空间的实验。

  1. 添加两个网络命名空间

ip netns add test0

ip netns add test1

  1. 查看网络命名空间

ip netns ls

 

这里我们可以看到刚刚创建的网络命名空间了。

  1. 创建一对veth,两个网络空间之间要通信的话,需要veth。把网络空间比喻成两个水池的话,veth就是连接水池的管道有了管道,两个水池的水才能相互流通

ip link add 第一个veth名称 type veth peer name 第二个veth名称

ip link add veth0 type veth peer name veth1

使用ip link show查看一下

已经生成了veth

Docker基础30--6.4 Docker三剑客之Swarm_第14张图片

 

  1. 将两个虚拟网卡分别放到不同的命名空间中

ip link set veth0 netns test0

ip link set veth1 netns test0

  1. 为veth添加ip地址

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

  1. 启动两块网卡

ip netns exec test0 ip link set up dev veth0

ip netns exec test1 ip link set up dev veth1

  1. 查看两个命名空间的IP地址

ip netns exec test0 ip a

ip netns exec test1 ip a

Docker基础30--6.4 Docker三剑客之Swarm_第15张图片

 

注意红框的数字,他们交叉相等,说明这两个命空间已经成功建立了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= [command]

分别看一下三个网络命名空间、宿主机容器的IP。

6.4.7 创建服务栈

Docker基础30--6.4 Docker三剑客之Swarm_第16张图片

 看一下容器的IP

Docker基础30--6.4 Docker三剑客之Swarm_第17张图片

发现容器的IP和e6058a169c2f是一样的,可以认定,容器实际上使用的是e6058a169c2f这个网络命名空间。

再看一下docker系统中的网络,docker network ls

Docker基础30--6.4 Docker三剑客之Swarm_第18张图片

 

这里docker_gwbridge和ingress这两个网络是swarm创建的。

我们先看一下ingress这个网络的详细信息

Docker基础30--6.4 Docker三剑客之Swarm_第19张图片

 

注意看这个ID,和这个/run/docker/netns/1-u48p3plmon网络命名空间太像了,我认定,这个ingress和/run/docker/netns/1-u48p3plmon,就是同一个命名空间。

至于docker里的docker_gwbridge这个网络,就是宿主机上的那个docker_gwbridge网络。

Docker基础30--6.4 Docker三剑客之Swarm_第20张图片

 

而且这个docker_gwbridge又桥接了两块虚拟网卡。

Docker基础30--6.4 Docker三剑客之Swarm_第21张图片

 

就是这两块,veth51和50.

根据我们上面的连线,51和50其实是连接到ingress-box这个命名空间的。

再看一下iptables

 

这里有一条指向172.18.0.2:8080的源地址NAT,也就是指向Ingress-box.

根据以上分析,我可以得出以下网络拓扑图:

结论:

经过以上的探索,我们可以猜想,swarm集群的拓扑应该如下图:

Docker基础30--6.4 Docker三剑客之Swarm_第22张图片

 

当外部访问到宿主机IP时,通过iptables DNAT,将数据包丢到ingress-box,再由ingress-box通过ingress网络选择丢到哪个容。

至于ingress-box如何选择哪个容器,这个不在我们本次的讨论范围之内。

6.4.7服务栈管理

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数据)

  1. 新开一台linux主机,作为 nfs服务器,主机名设为 nfs195,ip地址设为192.168.0.195
  2. 在nff195上安装nfs服务

yum install nfs-utils

  1. 创建一个共享目录,将将共享目录的拥有者设为nfsnobody

mkdir lnmp_data

mkdir lnmp_data/html

mkdir lnmp_data/data

chown -R nfsnobody:nfsnobody lnmp_data/

Docker基础30--6.4 Docker三剑客之Swarm_第23张图片

 

  1. 配置并启动NFS
  2. vim /etc/exports

 

红框中的all_squash表示,所有连到nfs的用户,都被映射到nfs服务器的nfsnobody用户

  1. 重启rpcbind服务,并将rpcbind服务设置为开机自启动

systemctl restart rpcbind

systemctl enable rpcbind

  1. 启动NFS服务,并将NFS服务设置为开机自启动

systemctl start nfs-server

systemctl enable nfs-server

  1. 放通防火墙相关服务

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

  1. 到三个节点上查看NFS共享信息

 

看到这个信息,说明nfs服务已经启动成功。

  1. 在三个节点上创建用来挂载NFS的目录

mkdir /root/lnmp/lnmp_data

  1. 在三个节点上挂载NFS共享目

mount -t nfs 192.168.0.125:/root/lnmp_data /root/lnmp/lnmp_data

  1. 让内核重新生成挂载信息:

partprobe

  1. 查看挂载情况:

df -h

Docker基础30--6.4 Docker三剑客之Swarm_第24张图片

 

已经挂载成功

  1. 在三个节点上将NFS共享挂载写入分区配置文件:

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#定义mysqlroot密码

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文件。

 

这里我们看到,创建服务时做了三件事件上:

  1. 创建了一个名为lnmp_default的网络
  2. 创建了一个名为lnmp_nginx的服务
  3. 创建了一个名为lnmp_mysql的服务

然后我们看一下,我们的lnmp网站有没有起来:

Docker基础30--6.4 Docker三剑客之Swarm_第25张图片

 

网站已经起来了。

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

你可能感兴趣的:(docker基础,docker,运维,容器)