通过网盘分享的文件:MD文档笔记
链接: https://pan.baidu.com/s/1ao2wfyLEdN50YmIqdzfGYQ?pwd=5w5z 提取码: 5w5z
–来自百度网盘超级会员v6的分享

一.Swarm 理论基础

1.简介

Docker Swarm 是由 Docker 公司推出的 Docker原生集群管理系统,它将一个 Docker主机池变成了一个单独的虚拟主机,用户只需通过简单的 API 即可实现与 Docker 集群的通信

Docker Swarm 使用 GO 语言开发

从 Docker 1.12.0 版本开始Docker Swarm 已经内置于 Docker 引擎中,无需再专门的进行安装配置

Docker Swarm 在 Docker 官网的地址为: https://docs.docker.com/engine/swarm/

2.节点架构

(1).架构图

在这里插入图片描述

(2).SwarmNode

从物理上讲一个 Swarm 集群是由若个干安装了 Docker引擎的物理机或者虚拟机组成,这些主机上的 Docker引擎都采用 Swarm 模式运行

从逻辑上讲一个 Swarm 集群由若干节点 node 构成,每个 node 最终会落实在一个物理Docker 主机上,但一个物理 Docker 主机并不一定就是一个 node

即 swarm node 与 Docker主机并不是一对一的关系

swarm node 共有两种类型

  • manager

  • worker

(3).Manager

Manager 节点用于维护 swarm 集群状态、调试 servcie、处理 swarm 集群管理任务

为了防止单点故障问题,一个Swarm集群一般都会包含多个manager,这些Manager间通过Raft算法维护着一致性

(4).Worker

Worker节点用于在其容器中运行 task 任务,即对外提供 service 服务

默认情况下Manager节点同时也充当着 worker 角色,可以运行 task 任务

(5).角色转换

manager 节点与 worker 节点角色并不是一成不变的,它们之间是可以相互转换的

  • manager 转变为 worker 称为节点降级

  • worker 转变为 manager 称为节点升级

3.服务架构

(1).架构图

在这里插入图片描述

在这里插入图片描述

(2).Service

搭建 docker swarm 集群的目的是为了能够在 swarm 集群中运行应用,为用户提供具备更强抗压能力的服务

docker swarm 中的服务 service 就是一个逻辑概念,表示 swarm 集群对外提供的服务

(3).task

一个 service 最终是通过任务 task 的形式出现在 swarm 的各个节点中,而每个节点中的task 又都是通过具体的运行着应用进程的容器对外提供的服务

(4).编排器

在 swarm manager 中具有一个编排器,用于管理副本 task 任务的创建与停止

例如当在 swarm manager 中定义一个具有 3 个 task 副本任务的 service 时,编排器首先会创建 3个 task,为每个 task 分配一个 taskID,并通过分配器为每个 task 分配一个虚拟 IP(即 VIP),然后再将该task 注册到内置的 DNS 中

当 service 的某 task 不可用时,编排器会在 DNS 中注销该 task

(4).分发器

在 swarm manager 中具有一个分发器,用于完成对副本 task 任务的监听、调度等操作

在前面的例子中当编排器创建了 3 个 task 副本任务后,会调用分发器为每个 task 分配节点

分发器首先会在 swarm 集群的所有节点中找到 3 个可用节点,每个节点上分配一个task,而每个 task 就像是一个"插槽",分发器会在每个"插槽"中放入一个应用容器

每个应用容器其实就是一个具体的 task 实例,一旦应用容器运行起来,分发器就可以监测到其运行状态,即 task 的运行状态

如果容器不可用或被终止,task 也将被终止,此时编排器会立即在内置 DNS 中注销该task,然后编排器会再生成一个新的 task,并在 DNS 中进行注册,然后再调用分发器为之分配一个新的可用节点,然后再该节点上再运行应用容器,编排器始终维护着 3 个 task副本任务

分发器除了为 task 分配节点外,还实现了对访问请求的负载均衡

当有客户端来访问 swarm 提供的 service 服务时,该请求会被 manager 处理:根据其内置DNS,实现访问的负载均衡

4.服务部署模式

(1).官方图

service 以副本任务 task 的形式部署在swarm 集群节点上

根据 task 数量与节点数量的关系,常见的service 部署模式有两种

  • replicated 模式

  • global 模式

在这里插入图片描述

(2).replicated 副本模式

副本模式是service 的默认部署模式,需要指定 task 的数量

当需要的副本任务task数量不等于swarm集群的节点数量时就需要使用 replicated 模式

Manager中的分发器会找到指定task个数的可用节点,然后为这些节点中的每个节点分配一个或若干个task

(3).global 全局模式

global 模式即全局模式

分发器会为每个 swarm 集群节点分配一个task,不能指定 task的数量

swarm 集群每增加一个节点编排器就会创建一个 task并通过分发器分配到新的节点上

二.Swarm 集群搭建

现要搭建一个 docker swarm 集群包含 5 个 swarm 节点

这 5 个 swarm 节点的 IP 与暂时的角色分配如下(注意是暂时的)

主机的IP根据自己的实际IP进行替换

HostnameIPRole
Docker1192.168.217.137Manager1
Docker2192.168.217.138Manager2
Docker3192.168.217.139Manager3
Docker4192.168.217.140Worker1
Docker5192.168.217.141Worker2

1.克隆主机

克隆虚拟机分别命名docker1、docker2、docker3、docker4、docker5

克隆完毕后修改主机名

配置文件位置"/etc/hostname"

  • Docker1主机

    echo "docker1" >  /etc/hostname   
    
  • Docker2主机

    echo "docker2" >  /etc/hostname   
    
  • Docker3主机

    echo "docker3" >  /etc/hostname   
    
  • Docker4主机

    echo "docker4" >  /etc/hostname   
    
  • Docker5主机

    echo "docker5" >  /etc/hostname   
    

修改完成后重启虚拟机

2.查看 Swarm 激活状态

在任意 docker1主机上通过docker info命令可以查看到当前 docker 引擎 Server 端对于swarm 的激活状态

由于尚未初始化swarm集群,所以这些 docker主机间没有任何关系且 swarm 均未被激活

# docker1节点执行
docker info

在这里插入图片描述

3.Swarm 初始化

在主机名为"docker1"的主机上运行docker swarm init命令创建并初始化一个 swarm

# docker1节点执行
docker swarm init

在这里插入图片描述

4.添加manager 节点

(1).获取添加命令

若要为 swarm 集群添加 manager 节点,需要首先在 manager节点获取添加命令

# 在docker1节点执行如下命令

docker swarm join-token manager

在这里插入图片描述

(2).添加节点

复制docker swarm join-token master命令生成的 manager 添加命令

# 注意:根据自己生成的结果执行
docker swarm join --token SWMTKN-1-3i3wxxk688c9bqlbgwmtgc5z708d29twfz7lus9chkk0oaz0ph-7cpqijh9bpfkvfys7vcz34grk 192.168.217.137:2377

在 docker2、docker3节点上运行,将这两个节点添加为 manager 节点

  • docker2节点执行

    在这里插入图片描述

  • docker3节点添加

    在这里插入图片描述

5.添加 worker 节点

复制docker swarm init命令的响应结果中添加 wroker 节点的命令

# 注意:该命令根据自己初始化集群时生成的命令做替换,每个人的都不同

docker swarm join --token SWMTKN-1-3i3wxxk688c9bqlbgwmtgc5z708d29twfz7lus9chkk0oaz0ph-cd7mapfzskfrg7cs0my62o4ul 192.168.217.137:2377

在 docker4 与 docker5节点上运行,将这两个节点添加为 worker 节点

  • docker4节点执行

    在这里插入图片描述

  • docker5节点执行

    在这里插入图片描述

6.查看 Swarm 节点

在 manager 节点 docker1、docker2、docker3上通过docker node ls命令可以查看到当前swarm 集群所包含的节点状态数据

# 在docker1节点执行
docker node ls  

在这里插入图片描述

但在 worker 节点上是不能运行"docker node ls"命令的

# 在docker4节点执行
docker node ls

在这里插入图片描述

三.Swarm 集群维护

1.退出 swarm 集群

当一个节点想从swarm集群中退出时,可以通过docker swarm leave命令

不过 worker节点与 manager 节点的退群方式是不同的

1.1.worker 退群/入群/删除

(1).worker退群

对于 worker 节点退群直接运行docker swarm leave命令即可

# 在节点4执行
docker swarm leave

在这里插入图片描述

此时在 manager(如docker1)节点中查看节点情况可以看到 docker4已经 Down了

# 在docker1节点执行
docker node ls

在这里插入图片描述

(2).Worker入群

首先在 manager(如docker1) 节点上运行docker swarm join-token worker命令生成加入 worker 节点的命令

# 在docker1节点执行以下命令
docker swarm join-token worker

在这里插入图片描述

复制生成的命令,在 docker4节点上运行,将此节点添加到 swarm 集群

# 在docker4节点执行加入命令
docker swarm join --token SWMTKN-1-3i3wxxk688c9bqlbgwmtgc5z708d29twfz7lus9chkk0oaz0ph-cd7mapfzskfrg7cs0my62o4ul 192.168.217.137:2377

在这里插入图片描述

(3).查看Woeker节点情况

此时在 docker1节点中查看节点情况,可以看到原来的 docker4依然是 Down,但又新增了一个新的 docker4节点,其状态为Ready

# docker1节点执行
docker node ls

在这里插入图片描述

此时在 docker1(master)节点通过docker info命令可以查看到节点数量变为了6个,增加的一个就是两种状态的 docker4

# docker1节点执行
docker info

在这里插入图片描述

(4).删除 Down 状态节点

对于Down 状态的节点是完全可以将其删除的

通过在docker1(manager)节点运行docker node rm命令完成

# docker1节点执行
docker node rm 7n2h6zz457fqz4higdvrfpjws

在这里插入图片描述

1.2.manager 退群/入群

(1).master退群

对于 manager 节点,原则上是不推荐直接退群的,这样会导致 swarm 集群的一致性受到损坏

如果manager 执意要退群,可在docker swarm leave命令后添加-f 或–force 选项进行强制退群

这里演示将docker2退群

# docker2节点执行

docker swarm leave -f

在这里插入图片描述

在master节点(docker1)节点查看集群列表

docker node ls

在这里插入图片描述

(2).Master入群

在master(如docker1节点)节点运行

# docker1节点运行

docker swarm join-token manager

在这里插入图片描述

# docker2节点运行添加命令
docker swarm join --token SWMTKN-1-3i3wxxk688c9bqlbgwmtgc5z708d29twfz7lus9chkk0oaz0ph-7cpqijh9bpfkvfys7vcz34grk 192.168.217.137:2377

在这里插入图片描述

# 查看docker2是否重新假如master
# docker1节点执行
docker node ls

在这里插入图片描述

# 将Down状态的降级为worker节点后再删除
# docker1节点执行
[root@docker1 ~]# docker node demote bwhu32093hvbycw4b74o0h1zn
[root@docker1 ~]# docker node rm bwhu32093hvbycw4b74o0h1zn

在这里插入图片描述

# 查看节点列表
# docker1节点执行
docker node ls

在这里插入图片描述

2.Swarm 自动锁定

(1).自动锁定原理

在 manager 集群中swarm 通过 Raft 日志方式维护了 manager 集群中数据的一致性,即在 manager 集群中,每个节点通过 manager 间通信方式维护着自己的 Raft 日志

但在通信过程中存在有一种风险:Raft 日志攻击者会通过 Raft 日志数据的传递来访问、篡改 manager 节点中的配置或数据

为了防止被攻击,swarm 开启了一种集群自动锁定功能,为 manager 间的通信启用了 TLS 加密,用于加密和解密的公钥与私钥,全部都维护在各个节点的 Docker 内存中,一旦节点的 Docker 重启则密钥丢失

swarm 中通过 autolock 标志来设置集群的自动锁定功能

  • 为 true 则开启自动锁定
  • 为false 则关闭自动锁定

(2).设置自动锁定

在manager 节点通过docker swarm update --autolock=true命令可以开启当前swarm 集群的自动锁定功能

# 在docker1节点执行
docker swarm update --autolock=true

在这里插入图片描述

此时查看 manager 的 docker info 可以看到autolock 已经为 true 了

# 在docker1节点执行
docker info

在这里插入图片描述

(3).查看解锁密钥

如果没有保存docker swarm update --autolock=true命令中生成的密钥,也可通过在manager节点中运行docker swarm unlock-key命令查看

# 在docker1节点执行命令

docker swarm unlock-key

在这里插入图片描述

(4).解除节点锁定

【示例】

  1. 直接关闭 docker3 的 docker 引擎模拟一个manager 宕机的情况

    # docker3节点执行  
    systemctl stop docker      
    

    在这里插入图片描述

  2. 启动 docker3 的 docker 引擎模拟节点恢复

    # docker3节点执行  
    systemctl start docker 
    

    在这里插入图片描述

  3. 查看节点锁定状态

    此时再查看该节点的docker info可以看到 Swarm值为 locked,即当前节点看到的Swarm集群的状态为锁定状态

    # 在docker3节点执行 
    docker info
    

    在这里插入图片描述

  4. 解除节点锁定

    在 docker3 中运行docker swarm unlock命令解锁 swarm

    # docker3节点运行  
    docker swarm unlock   
    

    在这里插入图片描述

  5. 此时再查看节点信息发现该manager 已经加入

    # docker1节点运行  
    docker node ls  
    

    在这里插入图片描述

四.Swarm 节点维护

1.角色转换

Swarm 集群中节点的角色只有 manager 与 worker,所以其角色也只是在 manager 与worker 间的转换

即worker 升级为 manager,或 manager 降级为worker

1.1.Demote/promote变更方式

(1).升级为 manager

通过docker node promote 节点ID命令可以将 worker 升级为manager

例如下面的命令是将 docker4、docker5节点升级为了 manager,即当前集群中全部为 manager

# docker1节点运行

docker node ls

docker node promote s36jqjna4fkxl1oawp4uhd501 z1wlyxqufj8p2vclp4jtshhw8

docker node ls

在这里插入图片描述

(2).降级为 worker

通过docker node demote命令可以将 manager 降级为 worker

例如下面的命令是将 docker4 与 docker5 两个节点降级为了 worker

# docker1节点运行

docker node ls

docker node demote s36jqjna4fkxl1oawp4uhd501 z1wlyxqufj8p2vclp4jtshhw8

docker node ls

在这里插入图片描述

1.2.Update 变更方式

(1).升级为master

除了通过docker node demote|promote可以变更节点角色外,通过docker node update --role [manager|worker] [node名称]也可变更指定节点的角色

以下命令将 docker4 与 docker5 两个节点又变为了 manager

# docker1节点运行

docker node update --role manager docker4

docker node update --role manager docker5

在这里插入图片描述

(2).降级为worker

以下命令将 docker4 与 docker5 两个节点又变为了 worker

# docker1节点运行
docker node update --role worker docker4

docker node update --role worker docker5

docker node ls

在这里插入图片描述

2.节点标签

swarm 可以通过命令为其节点添加描述性标签,以方便管理员去了解该节点的更多信息

2.1.添加/修改节点标签

通过docker node update --label-add命令可以为指定node 添加指定的key=value的标签

若该标签的key已经存在则会使用新的 value替换掉该key的原value

不过需要注意的是若要添加或修改多个标签则需要通过多个–label-add 选项指定

# 在docker1节点执行
# 模拟给docker2节点添加标签
docker node update --label-add auth=zs --label-add age=30 docker2

在这里插入图片描述

2.2.查看节点标签

(1).JSON形式查看

通过docker node inspect 节点名称在查看该节点详情时可看到添加的标签

# 在docker1节点执行
docker node inspect docker2

在这里插入图片描述

(2).键值对形式查看

docker node inspect --pretty 节点名可以 key:value 的形式显示信息

# 在docker1节点执行
docker node inspect --pretty docker2

在这里插入图片描述

2.3.删除节点标签

通过docker node update --label-rm命令可以为指定的 node 删除指定key 的标签

同样若要删除多个标签则需要通过多个–label-rm 选项指定要删除 key 的标签

# 在docker1节点执行
# 删除docker2节点的标签
docker node update --label-rm auth --label-rm age docker2

在这里插入图片描述

查看节点详情发现这两个标签已经消失

# 在docker1节点执行
# 查看docker2节点的标签
docker node inspect docker2

在这里插入图片描述

3.节点删除

manager 节点通过docker node rm命令可以删除一个 Down 状态的worker 节点

注意该命令只能删除 worker 节点,不能删除manager 节点

3.1.删除Worker

(1).Ready状态的无法删除

对于 Ready 状态的 worker 节点是无法直接删除的

【示例】

删除docker4节点

# 在docker1节点执行

docker node rm p67fvaiq6xu8fsng1l9e33uq3

在这里插入图片描述

(2).正确的删除方式

若要删除一个 worker 节点,首先要将该节点的 Docker 关闭,使该节点变为 Down 状态,然后再进行删除

【示例】删除docker4节点

  1. 关闭 docker4节点的 Docker 引擎

    # 在docker4节点执行  
    systemctl stop docker
    

    在这里插入图片描述

  2. 删除节点

    # 在docker1节点执行   
    docker node rm p67fvaiq6xu8fsng1l9e33uq3
    

    在这里插入图片描述

  3. 将节点4重新加入集群

    # docker1节点运行  
    docker swarm join-token worker
    

    在这里插入图片描述

    # docker4节点退群  
    docker swarm leave            
    

    在这里插入图片描述

    # docker4节点入群  
    docker swarm join --token SWMTKN-1-3i3wxxk688c9bqlbgwmtgc5z708d29twfz7lus9chkk0oaz0ph-cd7mapfzskfrg7cs0my62o4ul 192.168.217.137:2377 
    

    在这里插入图片描述

(3).强制删除方式

前面的删除方式有些麻烦,其实也可以通过添加-f 选项来实现强制删除

【示例】强制删除节点5

# 在docker1节点执行

docker node ls

docker node rm -f rnps2vqopl4rcpr88icmpdu64

docker node ls

在这里插入图片描述

恢复docker5节点

# docker1节点运行
docker swarm join-token worker

在这里插入图片描述

# docker5节点退群
docker swarm leave

在这里插入图片描述

# docker5节点入群
docker swarm join --token SWMTKN-1-3i3wxxk688c9bqlbgwmtgc5z708d29twfz7lus9chkk0oaz0ph-cd7mapfzskfrg7cs0my62o4ul 192.168.217.137:2377

在这里插入图片描述

3.2.删除Master节点

(1).Ready状态的无法删除

对于 Ready状态的manager 节点也是无法删除的

# 在docker1节点执行
docker node ls

docker node rm yo0lhbj4zagejxgtl4u1pliyr

在这里插入图片描述

(2).不能强制删除Master节点

对于 manager 节点强制删除也不能删除

# 在docker1节点执行
docker node ls

docker node rm -f yo0lhbj4zagejxgtl4u1pliyr

在这里插入图片描述

(3).正确删除方式

删除Master的正确方式是先降级为Worker节点再删除

这里模拟删除docker1节点,leader节点是docker3节点

  1. Docker1降级为worker节点

    # docker1节点执行
    
    docker node ls
    
    docker node demote yo8bm1nyrbt2ifx7adf21py9w
    

    在这里插入图片描述

  2. 降级后的worker节点退群

    # docker1节点执行
    
    docker swarm leave
    

    在这里插入图片描述

  3. 删除docker1节点

    # docker3节点执行
    
    docker node ls
    
    docker node rm yo8bm1nyrbt2ifx7adf21py9w
    
    docker node ls
    

    在这里插入图片描述

  4. 将master1节点重新加入master节点

    # docker3节点运行
    docker swarm join-token manager
    

    在这里插入图片描述

    # docker1节点运行入群命令
    
    docker swarm join --token SWMTKN-1-3i3wxxk688c9bqlbgwmtgc5z708d29twfz7lus9chkk0oaz0ph-7cpqijh9bpfkvfys7vcz34grk 192.168.217.139:2377
    

    在这里插入图片描述

    # docker1节点查看节点列表
    docker node ls
    

    在这里插入图片描述

docker node rm -f 命令会使一个节点强制退群,而 docker swarm leave 命令是使当前的docker 主机关闭 swarm 模式

五.Swarm 安全(PKI)

Docker 内置了 PKI(public key infrastructure,公钥基础设施),使得保障发布容器化的业务流程系统的安全性变得很简单

具体来说是当运行"docker swarm init"命令时,Docker 指定当前节点为一个 manager 节点

默认情况下manager 节点会生成一个新的swarm 的CA 根证书以及一对密钥,同时manager 节点还会生成两个token

  • 一个用于添加 worker 节点

  • 一个用于添加 manager 节点

每个 token 包含上了CA 根证书的 digest 和一个随机密钥

CA 根证书、一对密钥和随机密钥都将会被用在节点之间的通信上

1.TLS 安全保障

Swarm 节点之间采用 TLS 来鉴权、授权和加密通信

在这里插入图片描述

当有节点加入 Swarm 时,需要复制 manager 中相应的docker swarm join加入命令,并在该节点中运行,而这个过程主要是通过随机密钥这种对称验证方式保障通信安全的

在这里插入图片描述

一旦节点加入了Swarm 集群,那么它们间的通信全部都是通过 TLS 加密方式进行的

首先是通过CA 证书对通信对方的身份进行验证,在验证通过后再进行数据通信,而通信的数据则是通过随机密钥加密过的

在这里插入图片描述

2.CA 数字证书轮换

(1).轮换周期

Swarm 的CA 数字证书也是有可能被攻击、篡改的

为了保证 swarm 的数字证书的安全性,Swarm 提供了CA 数字证书轮换机制,定期更换 CA 数字证书

默认swarm 的 CA 数字证书 90 天轮换一次

(2).指定证书

通过docker swarm ca命令可以指定外部 CA 数字证书或生成新的 CA 数字证书

无论哪种数字证书变更方式都需要 CA 根证书的加密/解密

而根证书也是会发生变化的,具体见"轮转过程"

(3).轮转过程

当 manager 运行了docker swarm ca --rotate命令后会按顺序发生下面的事情

  • Docker 会生成一个交叉签名(cross-signed)根证书,即新根证书是由旧的根证书签署生成的,这个交叉签名根证书将作为一个过渡性的根证书,这是为了确保节点仍然能够信任旧的根证书,也能使用新的根证书验证签名

  • 在 Docker 17.06 或者更高版本中Docker 会通知所有节点立即更新根证书,根据 swarm中节点数量多少这个过程可能会花费几分钟时间

  • 在所有的节点都更新了新CA 根证书后,manager 会通知所有节点仅信任新的根证书,不再信任旧根证书及交叉签名根证书

  • 所有节点使用新根证书签发自己的数字证书

如果直接使用外部的 CA 根证书就不存在交叉签名根证书的生成过程,直接由运行docker swarm ca命令的节点通知所有节点立即更新根证书,后续过程与前面的就相同了

六.manager 集群容灾

1.热备容灾

Swarm 的 manager 节点集群采用的是热备方式来提升集群的容灾能力,即在manager集群中只有一个处于 leader 状态,用于完成swarm 节点的管理,其余manager 处于热备状态

当 manager leader 宕机,其余 manager 就会自动发起 leader 选举,重新选举产生一个新的 manager leader

2.容灾能力

manager 集群的leader 选举采用的是Raft 算法

Raft 算法是一种比较复杂的一致性算法,具体见后面"Raft 算法"

其选举 leader 的简单思路是所有可用的 manager 全部具有选举权与被选举权,最终获得过半选票的 manager 当选新的 leader

为了保证一次性可以选举出新的leader,官方推荐使用奇数个manager,但并不是说偶数个manager 就无法选举出leader

3.容灾模拟

目前是 docker1、docker2、docker3 三个 manager,其中 docker3为 leader

# docker1节点执行查看

docker node ls

在这里插入图片描述

现在关闭 docker3 主机的 docker daemon,模拟其宕机

# docker3节点执行

systemctl stop docker

在这里插入图片描述

然后在 docker1 或 docker2 主机上查看当前的节点情况,可以看到 docker1 或 docker2已经成为了新的 leader

# docker1节点查看

docker node ls

在这里插入图片描述

此时如果再使某个 manager 宕机,例如使 docker2 的 docker daemon 关闭,那么整个swarm 就会瘫痪,因为剩下的 manager 已经无法达成过半的选票,无法选举出新的 leader

这里不再演示,因为瘫痪后还需要将所有节点都退出集群后再重新组件集群,十分麻烦

恢复docker3节点

# docker1节点运行
# 将docker3降级为worker
docker node ls

docker node demote yo0lhbj4zagejxgtl4u1pliyr

docker node ls

在这里插入图片描述

# docker3节点强制退群

docker swarm leave -f

在这里插入图片描述

# docker1节点执行

docker node ls

docker node rm 8pc2dlbly7x08j12sz2ledtv5

docker node ls

在这里插入图片描述

# docker1节点运行
docker swarm join-token manager

在这里插入图片描述

# docker3节点运行入群命令

docker swarm join --token SWMTKN-1-3i3wxxk688c9bqlbgwmtgc5z708d29twfz7lus9chkk0oaz0ph-7cpqijh9bpfkvfys7vcz34grk 192.168.217.137:2377

在这里插入图片描述

# docker3查看节点列表

docker node ls

在这里插入图片描述

七.Service服务

1.Service 创建

注意service 只能依附于docker swarm集群,所以 service 的创建前提是swarm 集群搭建完毕

1.1.创建 service

docker service create命令用于创建 service,需要在 manager 中运行

其与创建容器的命令 docker run 非常类似,具有类似的选项

目前的节点状态如下:

# docker1节点执行

docker node ls

在这里插入图片描述

现在要在 swarm 中创建一个运行 tomcat:8.5.49 镜像的 service,服务名称为 toms,包含 3 个副本 task,对外映射端口号为 9000

  • docker service create

    创建service

  • –name toms

    指定service的名字

  • –replicas 3

    指定副本数量

  • -p 9000:8080

    执行service的端口是9000,映射到后端服务的8080端口

  • tomcat:8.5.49

    指定使用的镜像

命令下生成的一串码为 service 的 ID

# 在docker1节点执行

docker service create --name toms --replicas 3 -p 9000:8080 tomcat:8.5.49

在这里插入图片描述

1.2.查看服务列表

docker service ls命令用于查看当前 swarm 集群中正在运行的 service 列表信息

一个swarm 中可以运行多个 service

# docker1节点执行

docker service ls

在这里插入图片描述

1.3.查看服务详情

通过docker service inspect [service name|service ID]命令可以查看指定service 的详情

# docker1节点执行

docker service inspect toms

在这里插入图片描述

1.4.用户访问服务

当服务创建完毕后该服务也就运行了起来

此时用户就可通过浏览器进行访问了

用户可以访问swarm 集群中任意主机

  1. 配置hsots

    # 修改"C:\Windows\System32\drivers\etc\hosts"文件
    # 添加如下配置
    192.168.100.137 docker1
    192.168.100.138 docker2
    192.168.100.139 docker3
    192.168.100.140 docker4
    192.168.100.141 docker5
    

    在这里插入图片描述

  2. 访问 manager

    浏览器访问"docker3:9000"

    在这里插入图片描述

  1. 访问 worker

    浏览器访问"docker4:9000"

    在这里插入图片描述

1.5.查看task 节点

docker service ps [service name|service ID]命令可以查看指定服务的各个 task 所分配的节点信息

# docker1节点执行

docker service ps toms

在这里插入图片描述

可以看到toms 服务的 3 个 task 被分配到了 docker1、docker3、docker5 三个主机

其中

  • ID

    为 task ID

  • NAME

    为task 的 name

    task name 是 serviceName 后添加从 1 开始的流水号形成的

1.6.查看节点 task

  • docker node ps默认查看的是当前节点的 task 信息
# docker1节点执行

docker node ps

在这里插入图片描述

  • docker node ps [node]可以查看指定节点中运行的 task 的信息
# docker1节点执行

docker node ps docker5

在这里插入图片描述

1.7.查看服务日志

  • n通过docker service logs命令可以查看指定service 或task 的日志

  • 通过 docker service logs –f 命令可动态监听指定 service 或 task 的日志

(1).查看service 日志

通过docker service logs [service name|service ID]命令可以查看指定service 的日志

这些日志实际是所有task 在节点容器中的运行日志

# docker1节点执行

docker service logs toms

在这里插入图片描述

(2).查看task 日志

通过docker service logs [task ID]命令可以查看指定 task 的日志

注意这里只能指定taskID,不能指定 task name

这些日志实际是指定 task 在节点容器中的运行日志

# docker1节点执行
docker service ps toms

docker service logs srjk7stya86p

在这里插入图片描述

1.8.查看节点容器

在 docker1、docker3、docker5 三个主机中查看正在运行的容器列表,可以看到相应的tomcat 容器

# docker1节点执行

docker ps

在这里插入图片描述

# docker3节点执行

docker ps

在这里插入图片描述

# docker5节点执行

docker ps

在这里插入图片描述

容器的 NAME 是由 task name 后添加 task ID 形成的

不过在 docker2、docker4主机中是没有该服务的 task 容器的

# docker2中执行

docker ps

在这里插入图片描述

# docker4中执行

docker ps

在这里插入图片描述

1.9.负载均衡

当一个 service 包含多个 task 时,用户对 service 的访问最终会通过负载均衡方式转发给各个 task 处理,这个负载均衡为轮询策略,且无法通过修改 service 的属性方式进行变更

但由于该负载均衡为三层负载均衡,所以其可以通过第三方实现负载均衡策略的变更,例如通过Nginx、HAProxy 等

(1).创建service

为了能够展示出service 对访问请求负载均衡的处理方式,这里使用一个镜像containous/whoami,该镜像中应用端口号为 80

通过浏览器访问返回结果中包含很多信息,其中最重要的是处理该请求的容器 ID

为了提高 service 的创建效率,可以先将该镜像下载到所有节点主机

下面的命令用于创建该镜像的一个包含 5个副本 task的service

# docker1节点执行

docker service create --name web --replicas 5 -p 8080:80 containous/whoami

在这里插入图片描述

(2).记录容器 ID

可以看到每个节点上都分配了一个 task,即每个节点上都运行了一个该 task 的容器

# docker1节点执行

docker service ps web

在这里插入图片描述

为了体现负载均衡的效果,这里需要将各个节点主机中该 service 的 task 容器的 ID 查询并记录下来

# docker1节点执行

docker ps

在这里插入图片描述

# docker2节点执行

docker ps

在这里插入图片描述

# docker3节点执行

docker ps

在这里插入图片描述

# docker4节点执行

docker ps

在这里插入图片描述

# docker5节点执行

docker ps

在这里插入图片描述

(3).访问

在任意主机上使用curl 命令访问swarm集群中的任意节点(无论是manager 还是worker)

快速访问后在返回结果中的 Hostname 值就是处理该请求的容器的 ID,第 2 个 IP 为该节点在 Swarm 集群局域网中的 IP

从结果可以看出这些请求被轮询分配给了各个 task 容器进行的处理,实现了 service对访问请求的负载均衡

# docker1节点执行

[root@docker1 ~]# curl 192.168.217.137:8080

在这里插入图片描述

2.service 操作

2.1.Task操作

2.1.1.task 扩容/缩容

根据访问量的变化,需要在不停止服务的前提下对服务的 task 进行扩容/缩容,即对服务进行伸缩变化

有两种实现方式

(1).update 方式
  • update扩容

    通过docker service update --replicas 指定副本数量增加命令可以实现对指定服务的 task 数量进行增加

    # docker1节点执行  
    docker service ps toms  
    
    docker service update --replicas 4 toms  
    
    docker service ps toms    
    

    在这里插入图片描述

  • update缩容

    通过docker service update --replicas 指定副本数量减少命令可以实现对指定服务的 task 数量进行减少

    使用方式参考上面增加

    #  docker1节点运行  
    docker service ps toms  
    
    docker service update --replicas 2 toms  
    
    docker service ps toms        
    

    在这里插入图片描述

(2).scale 方式
  • task扩容

    通过docker service scale命令可以为指定的服务变更 task 数量

    docker service scale serviceName|serviceId=扩容后的数量

    # docker1节点执行
    docker service ps toms
    
    docker service scale toms=7
    
    docker service ps toms
    

    在这里插入图片描述

    此时可以看到新增了5 个 task 节点

    由于共有 5 台主机,现有 7 个 task所以就出现了一个主机上有多个task 的情况

    例如本例中,docker2 与 docker3 中分别有 2 个 task

  • Task缩容

    当然也可以使task 数量减小

    docker service scale serviceName|serviceId=缩容后的数量
    

    例如下面的命令使 task 又变回了 3 个

    # docker1节点执行  
    docker service scale toms=3         
    

    在这里插入图片描述

    这三个 task 分别在 docker1、docker2 与 docker3 主机运行

2.1.2.节点暂停分配task

语法格式

docker node update --availability pause 节点Name

生产环境下可能由于某主机性能不高,在进行 task 扩容时不想再为该主机再分配更多的 task,此时可通过 pause 暂停该主机节点的可用性来达到此目的

例如当前 docker1、docker2 与 docker3 三个主机上的 toms 服务的 task 情况如下

# docker1节点执行

docker service ps toms

在这里插入图片描述

现准备将 toms 服务的 task 扩容为 10,但保持 docker3 节点中的 task 数量仍为 1 不变,此时就可通过docker node update --availability pause命令修改 docker3 节点的可用性

# docker1节点执行

docker node update --availability pause docker3

在这里插入图片描述

查看节点列表

# docker1节点执行

docker node ls

在这里插入图片描述

将 toms 服务的 task 扩容为 10

# docker1节点执行

docker service scale toms=10

在这里插入图片描述

查看各节点分配的 task 情况会发现,原本应该平均分配到每个节点 2 个 task,但 docker3的 task 数量并未增加,所以其它节点主机(docker4)的就多于 2 个了

# docker1节点执行

docker service ps toms

在这里插入图片描述

2.1.3.清空节点task

默认情况下manager 节点同时也具备 worker 节点的功能,可以由分发器为其分配 task,但 manager 节点使用 raft 算法来达成 manager 间数据的一致性,对资源较敏感,因此阻止 manager 节点接收 task 是比较好的选择,或者由于某节点出现了性能问题需要停止服务进行维修,此时最好是将该节点上的task 清空,以不影响service 的整体性能

语法格式

docker node update --availability drain 节点Name

通过docker node update --availability drain命令可以清空指定节点中的所有 task

例如目前各个节点的对于 toms 服务的 task 分配情况如下

# docker1节点执行

docker service ps toms

在这里插入图片描述

现对 docker2 与 docker5 两个节点进行task 清空操作

# docker1节点执行

docker node update --availability drain docker2 

docker node update --availability drain docker5

在这里插入图片描述

此时可以看到toms 服务的 task 总量并没有减少,只是 docker2 与 docker5 两个节点上是没有 task 了,而全部都分配到了 docker1、docker3 与 docker4 三个节点上了

这个结果就是由编排器与分发器共同维护的

# docker1节点执行

docker service ls

docker service ps toms

在这里插入图片描述

2.1.4.task 容错

当某个 task 所在的主机或容器出现了问题时,manager 的编排器会自动再创建出新的task,然后分发器会再选择出一台 available node 可用节点,并将该节点分配给新task

(1).停掉容器

现在通过停掉 docker1、docker3 或 docker4 中某个主机容器的方式来模拟故障情况

例如停掉 docker1 的容器

# docker1节点执行

docker ps

docker stop 5158ab57968e 033a116d33a1 d987da713f28 3c4361c2cac7 68a4afa5bfad

在这里插入图片描述

(2).查看task 节点

此时再查看服务的 task 节点信息可以看到,原来 docker1上的 task 已经是 Shutdown 状态了,而新增了4个task

# docker3节点执行

docker service ps toms

在这里插入图片描述

2.2.Service服务删除

通过docker service rm [serviceName|serviceID]可以删除指定的一个或多个 service

# docker1节点执行

docker service rm toms

在这里插入图片描述

删除后该 service 消失,当然该 service 的所有 task 也全部删除,task 相关的节点容器全部消失

# docker1节点执行
docker service ls

docker service ps toms

在这里插入图片描述

将实验的名为web的service也删除

# docker1节点执行

docker service rm web

在这里插入图片描述

2.3.Service滚动更新

当一个 service 的 task 较多时,为了不影响对外提供的服务,在对 service 进行更新时可采用滚动更新方式

(1).需求

这里要实现的更新时将原本镜像为 tomcat:8.5.49 的 service 的镜像滚动更新为tomcat:latest

(2).创建service

创建一个包含 10 个副本 task 的服务,该服务使用的镜像为 tomcat:8.5.49

# docker1节点执行
docker service create \
--name toms \
--replicas 10 \
--update-parallelism 2 \
--update-delay 3s \
--update-max-failure-ratio 0.2 \
--update-failure-action rollback \
--rollback-parallelism 2 \
--rollback-delay 3s \
--rollback-max-failure-ratio 0.2 \
--rollback-failure-action continue \
-p 9000:8080 \
tomcat:8.5.49

在这里插入图片描述

查看分配情况

# docekr1节点执行

docker service ps toms

在这里插入图片描述

(3).更新service

现要将 service 使用的镜像由 tomcat:8.5.49 更新为 tomcat:latest

# docker1节点执行

docker service update --image tomcat:latest toms

在这里插入图片描述

会发现这个更新的过程就是前面在创建服务时指定的那样,每次更新 2 个 task,更新间隔为 3 秒

更新完毕后再查看当前的task 情况发现已经将所有任务的镜像更新为了latest版本

# docker1节点执行

docker service ps toms

在这里插入图片描述

2.4.Service更新回滚

在更新过程中如果更新失败,则会按照设置的回滚策略进行回滚,回滚到更新前的状态,但用户也可通过命令方式手工回滚

下面的命令会按照前面设置的每次回滚 2 个 task,每次回滚间隔 3 秒进行回滚

下面的是回滚过程中的某个回滚瞬间

# docker1节点执行
docker service update --rollback toms

在这里插入图片描述

以下是回滚完毕后的结果

# docker1节点执行

docker service update --rollback toms

在这里插入图片描述

回滚完毕后再查看当前的task 情况发现,已经将所有任务的镜像恢复为了 8.5.49 版本,但需要注意task name 保持未变,但 task ID 与原来的 task ID 也是不同的,并不是恢复到了更新之前的task ID,即编排器新创建了 task,并由分发器重新为其分配了 node

# docker1节点执行

docker service ps toms

在这里插入图片描述

3.service 全局部署模式

根据 task 数量与节点数量的关系,常见的 service 部署模式有两种

  • replicated 模式
  • global 模式

前面创建的 service 是 replicated 模式的,下面来创建 global 模式的 service

# docker1节点执行

docker service ls

在这里插入图片描述

(1).环境变更

为了后面的演示效果,让 swarm 集群的节点变为 4 个,这里先使 docker5 退群

# docker5节点执行

docker swarm leave

在这里插入图片描述

此时 docker5 的节点状态变为了 Down

# docker1节点执行

docker node ls

在这里插入图片描述

将此节点再从 swarm 集群中删除

# docker1节点执行

docker node rm docker5

在这里插入图片描述

现在 docker5 节点才彻底被删除

# docker1节点执行

docker node ls

在这里插入图片描述

(2).创建 service

在docker service create命令中通过–mode 选项可以指定要使用的 service 部署模式,默认为replicated 模式

# docker1节点执行
# 删除前面的service

docker service rm toms

在这里插入图片描述

# docker1节点执行

docker service create --name toms --mode global -p 9000:8080 tomcat:8.5.49

在这里插入图片描述

该模式会在每个节点上分配一个 task

# docker1节点执行
docker service ps toms

在这里插入图片描述

(3).task 伸缩

对于 global 模式来说,若要实现对 service 的 task 数量的变更,必须通过改变该 servicve所依附的 swarm 集群的节点数量来改变

  • n节点增加则 task 会自动增加

  • 节点减少则 task会自动减少

下面要在这个 2 节点的swarm 集群中增加一个节点以使 toms 服务的 task 也增一

首先在 manager 节点获取新增一个节点的 token

# docker1节点执行

docker swarm join-token worker

在这里插入图片描述

在 docker5 上运行加入命令完成 swarm 的入群

# docker5节点执行

docker swarm join --token SWMTKN-1-3i3wxxk688c9bqlbgwmtgc5z708d29twfz7lus9chkk0oaz0ph-cd7mapfzskfrg7cs0my62o4ul 192.168.217.137:2377

在这里插入图片描述

此时查看 toms 服务的 task 详情发现已经自动增加了一个 task

# docker1节点执行

docker service ps toms

在这里插入图片描述

八.Overlay 网络

1.测试环境 1 搭建

(1).暂停分配 task

# docker1节点执行

docker node ls

在这里插入图片描述

现让 docker2 主机暂停分配 task

# docker1节点执行

docker node update --availability pause docker2

docker node ls

在这里插入图片描述

(2).创建service

现启动一个 包含10个task的service

# docker1节点执行
# 清除先前service数据
docker service rm toms

# 创建新service
docker service create --name toms --replicas 10 -p 9000:8080 tomcat:8.5.49

在这里插入图片描述

当前 swarm 集群共有 5 个节点,10 个 task 被分配到了 4 个可用节点上,其中除了被暂停的docker2 节点上是没有分配 task 外,其余节点都分配了多个 task

# docker1节点执行

docker service ps toms

在这里插入图片描述

2.overlay 网络概述

(1).overlay 网络简介

overlay 网络也称为重叠网络或覆盖网络,是一种构建于underlay网络之上的逻辑虚拟网络

即在物理网络的基础上通过节点间的单播隧道机制将主机两两相连形成的一种虚拟的、独立的网络

Docker Swarm集群中的overlay网络主要是通过iptables、ipvs、vxlan等技术实现的、基于其本身通信需求的网络模型

(2).overlay 网络模型

这里要说的overlay网络模型,确切地说是Docker Swarm集群的overlay 网络模型

Docker Swarm集群的overlay网络模型在创建时会创建出两个网络

  • docker_gwbidge网络
  • ingress 网络

这就是典型的overlay网络(在宿主机的物理网络之上又创建出新的网络),同时还创建出了 docker_gwbidge网关、br0网关、ingress-sbox 容器

当请求到达后会首先经由docker_gwbidge网关跳转到ingress-sbox容器,在其中具有当前整个service的所有容器IP,在其中通过轮询负载均衡方式选择一个容器IP 作为目标地址,然后再跳转到br0 网关

在 br0 网关中会根据目标地址所在主机进行判断

  • 若目标地址为本地容器IP则直接将请求转发给该容器处理即可
  • 若目标地址非本地容器 IP则会将请求经由 vxlan 接口通过vxlan隧道技术将请求转发给目标地址容器

3.docker_gwbridg 网络基础信息

(1).查 docker_gwbridge 网络详情

docker swarm 集群的 overlay 网络模型在创建时会自动创建两个网络

  • docker_gwbridge网络
  • ingress 网络
# docker1节点执行

docker network ls

在这里插入图片描述

查看 docker_gwbridge网络详情可以看到docker_gwbridge网络包含的子网为172.21.0.0/16,其网关为 172.21.0.1

# docker1节点执行

docker network inspect docker_gwbridge

在这里插入图片描述

同时还看到该网络中包含了 3 个容器

  • 2 个为 service的 task容器

  • 1个为ingress-sbox的容器

在这里插入图片描述

(2).ingress-sbox 容器

# docker1节点执行

docker ps -a

在这里插入图片描述

通过"docker ps -a"命令查看当前主机中的所有容器,发现并没有 ingress-sbox 容器

这是因为 docker ps 命令的本质是docker process status,查看的是当前主机中真实存在的容器进程的状态,而 ingress-sbox 容器是由overlay 网络虚拟出的,并不是真实存在的进程,所以通过docker ps命令是查看不到的

从 docker_gwbridge 的网络详情中可以看到,其中2个为 service 的 task 容器,其 ID 由 64 位 16 进制数构成,而 ingress-sbox 容器的 ID 就是 ingress-sbox,与其它2个容器的 ID 构成方式完全不同

在这里插入图片描述

(3).docker_gwbridge 网关

docker_gwbridge的网络详情中的网关"172.21.0.1"是谁呢?

在宿主机中通过ip a命令查看宿主机的网络接口,可以看到docker_gwbridge接口的IP为 172.21.0.1

即 docker_gwbridge 网络中具有一个与网络名称同名的网关

同时还看到下面的3个接口全部都是连接在docker_gwbridge上的

# docker1节点执行

ip a

在这里插入图片描述

(4).查看task 容器的接口

查看 docker_gwbridge 网络的 task 容器的接口情况可以看到这些容器中正好有接口与docker_gwbridge网关中的相应接口构成 veth paire

# docker1节点执行

docker ps

docker exec -it 21d3ff296623 ip a

在这里插入图片描述

# docker1节点执行

docker ps

docker exec -it 72649634acbd ip a

在这里插入图片描述

(5).查看ingress-sbox 容器的接口

每个容器都具有一个独立的网络命名空间,而每个 docker 主机中的网络命名空间,都是以文件的形式保存在目录"/var/run/docker/netns"中

# docker1节点执行

ll /var/run/docker/netns

在这里插入图片描述

其中 ingress_sbox 就是容器 ingress-sbox 的网络命名空间,通过 nsenter 命令可进入该命名空间并查看其接口情况

可以看到该命名空间中正好也存在接口与 docker_gwbridge 网关中的相应接口构成veth paire

# docker1节点执行

nsenter --net=/var/run/docker/netns/ingress_sbox ip a

在这里插入图片描述

4.ingress 网络基础信息

(1).查看ingress 网络详情

overlay 网络除了创建了 docker_gwbridge 网络外,还创建了一个 ingress 网络

# docker1节点执行

docker network ls

在这里插入图片描述

查看 ingress 网络详情可以看到,ingress 网络包含的子网为10.0.0.0/24,其网关为10.0.0.1

# docker1节点执行

[root@docker1 ~]# docker network inspect ingress

在这里插入图片描述

同时还看到,该网络中也包含了3个容器,这 3个容器与 docker_gwbridge 网络中的 3个容器是相同的容器,虽然 Name 不同,IP 不同,但容器 ID 相同,说明这 3 个容器都同时连接在2 个网络中

在这里插入图片描述

(2).br0 网关

10.0.0.1 网关是谁呢?

每个容器都具有一个独立的网络空间,而每个 docker 主机中的网络命名空间,都是以文件的形式保存在/var/run/docker/netns 目录中

查看当前主机的网络空间

# docker1节点执行

ll /var/run/docker/netns

在这里插入图片描述

查看/var/run/docker/netns 目录中的命名空间发现,其包含的 7个命名空间中,有 5 个命名空间是 5个 task 容器的,它们的名称由 12 位长度的 16 进制数构成;ingress_sbox 是 ingress-sbox 容器的命名空间

那么"1-v9bu1e9glh"命名空间是谁呢?进入该命名空间,查看其接口信息

# docker1节点执行

[root@docker1 ~]# nsenter --net=/var/run/docker/netns/1-v9bu1e9glh ip a

在这里插入图片描述

可以看到 2 号接口 br0 的 IP 为 10.0.0.1,即 ingress 网络的网关为 “1-v9bu1e9glh” 命名空间中的 br0,同时还看到br0 上还连接着 4个接口,说明 br0 就是一个网关

那么都是谁连接在这 4个接口上呢?

(3).查看task 容器的接口

查看 ingress 网络的 task 容器的接口情况,可以看到这些容器中正好有接口与br0 网关中的相应接口构成veth paire

docker ps

docker exec -it 21d3ff296623 ip a

在这里插入图片描述

# docker1执行

docker ps

docker exec -it 72649634acbd ip a

在这里插入图片描述

(4).查看ingress-sbox 容器的接口

查看 ingress-sbox 容器的命名空间 ingress_sbox 的接口情况,可以看到该命名空间中正好也存在接口与br0 网关中的相应接口构成 veth paire

# docker1执行

nsenter --net=/var/run/docker/netns/ingress_sbox ip a

在这里插入图片描述

5.宿主机的 NAT 过程

(1).查看宿主机路由

用户提交的192.168.217.137:9000 请求会首先被192.168.217.137 主机的哪个接口接收并处理呢?

通过命令 ip route 可以查看当前网络命名空间中的静态路由信息

# docker1节点执行

ip route

在这里插入图片描述

可以看出所有对 192.168.217.0/24 网络的请求,都需要经过 ens160 接口,而该接口连接的 IP 为 192.168.217.137,即 ens160接口会处理该请求

当然查看该主机的接口情况也可以看到ens160接口地址为 192.168.217.137

# docker1节点执行

ip a

在这里插入图片描述

那么 ens160接口又会将请求转发到哪里呢?这就需要查看宿主机的路由转发表 nat 中的路由规则了

(2).查看ip 转换规则

首先通过iptables –nvL –t nat命令来查看宿主机中网络地址转发表 nat 中的转发规则,nat 表的主要功能是根据规则进行地址映射、端口映射,以完成地址转换

# docker1节点执行

iptables -nvL -t nat

在这里插入图片描述

DOCKER-INGRESS 路由链路中的 DNAT 映射规则中指出,对于任何源 IP,只要其访问端口号为 9000,就会将其转换为 172.21.0.2:9000 的请求,即将请求转发到 172.21.0.2

那么请求是如何到达 172.21.0.2 的呢?

(3).查看宿主机路由

通过 ip route 命令查看当前宿主机的静态路由信息

# docker1节点执行

ip route

在这里插入图片描述

可以看出,所有对 172.21.0.0/16 网络的请求,都需要经过 docker_gwbridge 接口,而该接口连接的 IP 为 172.21.0.1,即 docker_gwbridge 接口会处理该请求,由一个网络去访问另一个网络必须要经过该目标网络的网关

经前面的学习知道,docker_gwbridge 正好就是 172.21.0.0/16 网络的网关

也就是说客户端提交的 192.168.217.137:9000 的请求,经 docker_gwbridge 网关,被路由到了 IP 为 172.21.0.2 的接口,那么谁的 IP 是 172.21.0.2 呢?经过前面网络基础信息查看可知,docker_gwbridge 网络中包含 IP 为 172.21.0.2 的 ingress-sbox 容器

6.ingress_sbox 的负载均衡

客户端请求经宿主机的NAT 已经成功通过 docker_gwbridge 网关转发到了 172.21.0.2,即转发到了 ingress-sbox 容器,或者更确切地说,是转发到了 ingress_sbox 命名空间

那么ingress_sbox 命名空间又会将请求转发到哪里呢?

这就需要查看 ingress_sbox 命名空间的 iptables 的 mangle 表与 IPVS 功能了

(1).查看ingress_sbox 的 mangle 表

mangle 表的主要功能是根据规则修改数据包的一些标志位,以便其他规则或程序可以利用这种标志对数据包进行过滤或路由

# docker1节点执行

[root@docker1 ~]# cd /var/run/docker/netns/
[root@docker1 netns]# 
[root@docker1 netns]# ls
1-v9bu1e9glh  3d2650ca9aa8  7807c018814f  a2723d920589  c4aebbe821fd  e1efabb33a0e  ingress_sbox
[root@docker1 netns]# 
[root@docker1 netns]# nsenter --net=ingress_sbox iptables -nvL -t mangle

在这里插入图片描述

该路由链中为任意源地址端口为 9000 的请求打了一个 MARK 标记 0x106,该MARK 标记将被 IPVS 用于负载均衡

(2).安装 ipvsadm 命令

后面我们需要使用该命令查看 IPVS 实现的负载均衡规则,但由于 CentOS 系统中默认没有安装 ipvsadm 命令,所以需要先 yum 安装

# docker1节点执行

yum install -y ipvsadm

在这里插入图片描述

(3).查看ingress_sbox 负载均衡规则

端口为 9000 的请求被打上了一个数值为 262 的 MARK 标记,该标记通过 LVS 的 IPVS 的负载均衡,将该请求转发到了下面的 10 个 IP 接口,且这 10 个接口的权重 weight 是相同的,都是 1

这 10 个 IP 接口具有一个共同点,全部来自于 10.0.0.0/24 网络

那么如何能到达 10.0.0.0/24 网络呢?

# docker1节点执行

nsenter --net=/var/run/docker/netns/ingress_sbox ipvsadm

在这里插入图片描述

(4).查看命名空间路由

通过前面的学习可知,若要由一个网络转发到另一个网络,则必须要先到目标网络的网关

由于目前尚在 172.21.0.0/16 网络,预转发到 10.0.0.0/24 网络,所以必须要先到 10.0.0.0/24网络的网关 10.0.0.1,即 br0

通过查看 br0 所在命名空间 1-pfq75ijiz4 的静态路由也可看出

# docker1节点执行

[root@docker1 netns]# nsenter --net=/var/run/docker/netns/1-cmdxn2vxzi ip route

在这里插入图片描述

但存在的问题是,请求目前尚在 ingress_sbox 命名空间中,怎样才能从 ingress_sbox 命名空间中出去,然后跳转到 br0 呢

查看 ingress_sbox 命名空间中的静态 IP 路由

# docker1节点执行

[root@docker1 netns]# nsenter --net=/var/run/docker/netns/ingress_sbox ip route

在这里插入图片描述

可以看出,所有对 10.0.0.0/24 网络的请求,都需要经过eth0 接口,而该接口连接的 IP为 10.0.0.11

在 ingress_sbox 命名空间中eth0 接口就是389 号接口,其 veth pair 接口就是 br0中的 390号接口,所以ingress_sbox 命名空间中请求经由 389 号接口跳转到了br0 网关

# docker1节点执行

[root@docker1 netns]# nsenter --net=/var/run/docker/netns/ingress_sbox ip a

在这里插入图片描述

(5).br0 网关的处理

到达 br0 后,再将请求从 br0 的哪个接口转发出去,是由目标地址决定的,而目标地址就是 IPVS 负载均衡选择出的 IP

请求到达 br0 后,首先会将目标地址与本地的 task 容器地址进行比较,若恰好就是当前宿主机中的 task 容器的 IP,那么直接将请求通过相应的接口将其转发;若不是当前宿主机中的 IP,则会将请求转发到 vxlan0 接口

经过 vxlan0 接口,可经由 VXLAN 技术将请求通过"网络隧道"发送到目标地址

7.VXLAN

(1).VXLAN 简介

VXLAN 是一种隧道技术,可以将不同协议的数据包重新封装后发送,新的包头提供了路由信息,从而使被封装的数据包在隧道的两个端点间通过公共互联网络进行路由,被封装的数据包在公共互联网络上传递时所经过的逻辑路径称为隧道,一旦到达网络终点,数据将被解包并转发到最终目的地

(2).测试环境 2 搭建

为了能够看清楚请求在不同主机的容器间所进行了通信,及通信过程中所使用的 VXLAN技术,这里将原来的服务先删除,然后再创建一个新的服务,不过该服务仅有一个副本

首先删除原来的service

# docker1节点执行

docker service rm toms

在这里插入图片描述

然后在任意主机中创建一个新的 servivce,其仅包含一个副本

这里在 docker3 主机创建了服务

# docker3节点执行

docker service create --name toms --replicas 1 -p 9000:8080 tomcat:8.5.49

在这里插入图片描述

查看服务,可以看到这唯一的副本被分配到了 docker5 主机

# docker3节点执行

docker service ps toms

在这里插入图片描述

(3).安装 tcpdump 命令

这里准备使用 tcpdump 命令对 VXLAN 数据进行监听,但在 centOS7 系统中默认是没有安装 tcpdump 命令的,所以需要使用 yum 命令先在 docker5 主机安装

# docker5节点执行

yum install -y tcpdump

在这里插入图片描述

(4).docker5 先监听

无论对哪个主机的该服务进行访问,请求最终都会通过 docker5 主机的 ens160 接口进入,然后再找到该task 容器,所以这里要先监听 docker5 的 ens160 接口

# docker5节点执行

tcpdump -i ens160 port 4789

在这里插入图片描述

(5).docker3 访问

在浏览器可以对任意主机提交访问请求

这里是向 docker3 主机发出的访问请求

http://192.168.217.139:9000/

在这里插入图片描述

(6).docker5 查看抓包数据

当向 docker3 主机发送了访问请求后,docker5 上就会看到抓取的VXLAN 数据包

# docker5节点查看

在这里插入图片描述

九.Raft 算法

1.Raft算法概念

Raft 算法是一种通过对日志复制管理来达到集群节点一致性的算法

这个日志复制管理发生在集群节点中的 Leader 与 Followers 之间

Raft通过选举出的Leader节点负责管理日志复制过程以实现各个节点间数据的一致性

2.Raft算法角色

在这里插入图片描述

在 Raft中节点有三种角色

  • Leader

    唯一负责处理客户端写请求的节点

    也可以处理客户端读请求

    同时负责日志复制工作

  • Candidate

    Leader选举的候选人,其可能会成为 Leader,是一个选举中的过程角色

  • Follower

    处理客户端读请求

    负责同步来自于Leader的日志

    当接收到其它Cadidate的投票请求后可以进行投票

    当发现Leader挂了其会转变为Candidate发起Leader选举

3.leader 选举

通过 Raft算法首先要实现集群中Leader的选举

(1).我要选举

若 follower在心跳超时范围内没有接收到来自于leader的心跳,则认为leader挂了,此时其首先会使其本地term增一,然后 follower 会完成以下步骤

  • 此时若接收到了其它candidate的投票请求则会将选票投给这个candidate

  • 由 follower转变为 candidate

  • 若之前尚未投票则向自己投一票

  • 向其它节点发出投票请求,然后等待响应

(2).我要投票

follower在接收到投票请求后会根据以下情况来判断是否投票

  • 发来投票请求的candidate的 term不能小于我的term
  • 在我当前term内我的选票还没有投出去
  • 若接收到多个candidate的请求将采取"first-come-first-served"方式投票

(3).等待响应

当一个Candidate发出投票请求后会等待其它节点的响应结果

这个响应结果可能有三种情况

  • 收到过半选票成为新的 leader,然后会将消息广播给所有其它节点以告诉大家我是新的 Leader了
  • 接收到别的candidate 发来的新leader通知,比较了新leader的term并不比自己的term小则自己转变为follower
  • 经过一段时间后没有收到过半选票也没有收到新leader通知则重新发出选举

(4).选举时机

在很多时候当Leader真的挂了,Follower几乎同时会感知到,所以它们几乎同时会变为candidate 发起新的选举

此时就可能会出现较多candidate票数相同的情况,即无法选举出 Leader

为了防止这种情况的发生Raft算法其采用了"randomized election timeouts"策略来解决这个问题

其会为这些Follower随机分配一个选举发起时间"election timeout",这个 timeout在 150-300ms 范围内,只有到达了"election timeout"时间的Follower才能转变为candidate,否则等待

那么"election timeout"较小的Follower则会转变为candidate然后先发起选举,一般情况下其会优先获取到过半选票成为新的leader

4.数据同步

在 Leader选举出来的情况下通过日志复制管理实现集群中各节点数据的同步

(1).状态机

Raft算法一致性的实现是基于日志复制状态机的

状态机的最大特征是不同 Server中的状态机若当前状态相同,然后接受了相同的输入,则一定会得到相同的输出

在这里插入图片描述

(2).处理流程

在这里插入图片描述

当 leader接收到 client的写操作请求后大体会经历以下流程

  • leader在接收到client的写操作请求后leader会将数据与term封装为一个box,并随着下一次心跳发送给所有followers,以征求大家对该box的意见,同时在本地将数据封装为日志
  • follower在接收到来自leader的box后首先会比较该box的term与本地记录的曾接受过的 box的最大term,只要不比自己的小就接受该box,并向leader回复同意,同时会将该box中的数据封装为日志
  • 当leader接收到过半同意响应后会将日志commit到自己的状态机,状态机会输出一个结果,同时日志状态变为了committed
  • 同时leader还会通知所有follower将日志commit到它们本地的状态机,日志状态变为了 committed
  • 在 commit通知发出的同时leader也会向client发出成功处理的响应

(3).AP 支持

在这里插入图片描述

Log由term index、log index 及 command构成

为了保证可用性各个节点中的日志可以不完全相同,但leader会不断给follower发送 box,以使各个节点的log 最终达到相同

即 raft算法不是强一致性的,而是最终一致的

5.脑裂

Raft集群存在脑裂问题

在多机房部署中由于网络连接问题很容易形成多个分区,而多分区的形成很容易产生脑裂从而导致数据不一致

由于三机房部署的容灾能力最强,所以生产环境下三机房部署是最为常见的

下面以三机房部署为例进行分析,根据机房断网情况可以分为五种情况

(1).情况一(不确定)

在这里插入图片描述

这种情况下B机房中的主机是感知不到Leader的存在的,所以B机房中的主机会发起新一轮的Leader选举

由于B机房与C机房是相连的,虽然C机房中的Follower能够感知到A机房中的Leader,但由于其接收到了更大term的投票请求,所以C机房的Follower也就放弃了A机房中的Leader,参与了新Leader的选举

若新Leader出现在B机房,A机房是感知不到新Leader的诞生的,其不会自动下课,所以会形成脑裂,但由于A机房Leader处理的写操作请求无法获取到过半响应,所以无法完成写操作,但B 机房Leader的写操作处理是可以获取到过半响应的,所以可以完成写操作,故A机房与B、C机房中出现脑裂,且形成了数据的不一致

若新 Leader出现在 C机房,A机房中的Leader则会自动下课,所以不会形成脑裂

(2).情况二(形成脑裂)

在这里插入图片描述

这种情况与情况一基本是一样的

不同的是一定会形成脑裂,无论新Leader在B还是C机房

(3).情况三(无脑裂)

在这里插入图片描述

A、C 可以正常对外提供服务,但B无法选举出新的Leader

由于B中的主机全部变为了选举状态所以无法提供任何服务,没有形成脑裂

(4).情况四(无脑裂)

在这里插入图片描述

A、B、C 均可以对外提供服务不受影响

(5).情况五(无脑裂)

在这里插入图片描述

A机房无法处理写操作请求,但可以对外提供读服务

B、C 机房由于失去了Leader均会发起选举,但由于均无法获取过半支持,所以均无法选举出新的Leader

6.Leader 宕机处理

(1).请求到达前Leader挂了

client发送写操作请求到达Leader之前Leader就挂了,因为请求还没有到达集群,所以这个请求对于集群来说就没有存在过,对集群数据的一致性没有任何影响

Leader挂了之后会选举产生新的Leader

由于Stale Leader并未向client发送成功处理响应,所以 client会重新发送该写操作请求

(2).未开始同步数据前Leader挂了

client发送写操作请求给Leader,请求到达Leader后,Leader还没有开始向Followers发出数据Leader就挂了,这时集群会选举产生新的Leader

Stale Leader重启后会作为Follower 重新加入集群,并同步新Leader中的数据以保证数据一致性,之前接收到client的数据被丢弃

由于Stale Leader并未向 client发送成功处理响应,所以 client会重新发送该写操作请求

(3).同步完部分后Leader挂了

client发送写操作请求给Leader,Leader 接收完数据后向所有Follower发送数据,在部分 Follower接收到数据后Leader挂了,由于Leader挂了就会发起新的Leader选举

  • 若 Leader产生于已完成数据接收的Follower,其会继续将前面接收到的写操作请求转换为日志,并写入到本地状态机,并向所有Flollower发出询问,在获取过半同意响应后会向所有Followers发送commit指令,同时向client进行响应

  • 若 Leader产生于尚未完成数据接收的Follower,那么原来已完成接收的Follower则会放弃曾接收到的数据,由于client没有接收到响应,所以 client会重新发送该写操作求

(4).commit 通知发出后 Leader挂了

client发送写操作请求给Leader,Leader也成功向所有Followers发出的commit指令,并向 client发出响应后Leader挂了

由于 Stale Leader已经向 client发送成功接收响应,且commit通知已经发出,说明这个写操作请求已经被server成功处理

7.Raft 算法动画演示

在网络上有一个关于 Raft 算法的动画,其非常清晰全面地演示了 Raft 算法的工作原理

该动画的地址为:http://thesecretlivesofdata.com/raft/

更多推荐