【云原生Docker】10-Docker网络-flannel介绍

【云原生Docker】10-Docker网络-flannel介绍

前言

	上一章节介绍了Docker网络的几种模式,其中包括bridge,host,none,container,自定义等几种网络模式。同时我们也介绍了如何让同一宿主机上的Docker容器相互通信,本章节将着重介绍Dokcer容器的跨主机通信,已经跨主机通信的关键网络插件flannel。

Dokcer跨主机通信

跨主机通信方式介绍

host模式:

	容器直接使用宿主机的网络,这样天生就可以支持跨主机通信。虽然可以解决跨主机通信问题,但这种方式应用场景很有限,容易出现端口冲突,也无法做到隔离网络环境,一个容器崩溃很可能引起整个宿主机的崩溃。

端口绑定:

	通过绑定容器端口到宿主机端口,跨主机通信时,使用主机IP+端口的方式访问容器中的服务。显而易见,这种方式仅能支持网络栈的四层及以上的应用,并且容器与宿主机紧耦合,很难灵活的处理,可扩展性不佳。

docker外定制容器网络:

	在容器通过docker创建完成后,然后再通过修改容器的网络命名空间来定义容器网络。典型的就是很久以前的pipework,容器以none模式创建,pipework通过进入容器的网络命名空间为容器重新配置网络,这样容器网络可以是静态IP、vxlan网络等各种方式,非常灵活,容器启动的一段时间内会没有IP,明显无法在大规模场景下使用,只能在实验室中测试使用

第三方SDN定义容器网络:

	使用Open vSwitch或Flannel等第三方SDN工具,为容器构建可以跨主机通信的网络环境。这些方案一般要求各个主机上的docker0网桥的cidr不同,以避免出现IP冲突的问题,限制了容器在宿主机上的可获取IP范围。并且在容器需要对集群外提供服务时,需要比较复杂的配置,对部署实施人员的网络技能要求比较高。

docker跨主机通信按原理可通过以下三种方式实现

  • 直接路由方式;
  • 桥接方式(如pipework);
  • Overlay隧道方式(如flannel、ovs+gre)等。

Docker 多节点Overlay网络模式可以分为两类:

  • 一类是基于 VxLAN ,对跨节点网络的原生支持;
  • 另一种是通过插件(plugin)方式引入的第三方实现方案,比如 Flannel,Calico 等等。

Flannel网络

简介

	Flannel是一种基于overlay网络的跨主机容器网络解决方案,即将TCP数据包封装在另一种网络包里面进行路由转发和通信,Flannel是CoreOS开发,专门用于docker多机互联的一个工具,让集群中的不同节点主机创建的容器都具有全集群唯一的虚拟ip地址;Flannel使用go语言编写。

原理说明

Flannel为每个host分配一个subnet,容器从这个subnet中分配IP,这些IP可以在host间路由,容器间无需使用nat和端口映射即可实现跨主机通信
每个subnet都是从一个更大的IP池中划分的,flannel会在每个主机上运行一个叫flanneld的agent,其职责就是从池子中分配subnet
Flannel使用etcd存放网络配置、已分配 的subnet、host的IP等信息   
Flannel数据包在主机间转发是由backend实现的,目前已经支持UDP、VxLAN、host-gw、AWS VPC和GCE路由等多种backend

flannel数据转发

在这里插入图片描述

  • 容器直接使用目标容器的ip访问,如下容器路由表所示,容器在路由表中找不到目标ip,则会通过默认路由进行转发,将数据包丢给容器的eth0

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tMlqI7sj-1680852318236)(D:\学习\学习笔记\图片\53.png)]

  • 报文通过veth pair被发送到宿主机上的vethXXX。
  • vethXXX是直接连接到虚拟交换机docker0的,报文通过虚拟bridge docker0发送出去。
  • 查找路由表,外部容器ip的报文都会转发到flannel.1虚拟网卡,这是一个P2P的虚拟网卡,然后报文就被转发到监听在另一端的flanneld。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fLBubj67-1680852318237)(D:\学习\学习笔记\图片\54.png)]

  • flanneld通过etcd维护了各个节点之间的路由表,把原来的报文UDP封装一层,通过配置的iface发送出去。查看etcd中的路由表.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9jGtaR3J-1680852318237)(D:\学习\学习笔记\图片\55.png)]

  • 报文通过主机之间的网络找到目标主机。
  • 报文继续往上,到传输层,交给监听在8285端口的flanneld程序处理。
  • 数据被解包,然后发送给flannel.1虚拟网卡。
  • 查找路由表,发现对应容器的报文要交给docker0。
  • docker0找到连到自己的容器,把报文发送过去。

flannel安装

环境介绍

也可以将etcd安装在docker1或者docker2上;后面为了防止etcd单点故障我们会做etcd集群,将docker1和docker2上都安装etcd。这里只做etcd的单节点部署。

节点名称 节点IP 安装软件
docker-01 192.168.194.128 etcd,docker,flannel
docker-02 192.168.194.130 docker,flannel

安装etcd

  1. 在github上下载最新版的etcd
# 这里以linux-amd64的3.5.7版本为例
wget https://github.com/etcd-io/etcd/releases/download/v3.5.7/etcd-v3.5.7-linux-amd64.tar.gz
  1. 解压下载的etcd的gz安装包
#  解压
tar -xvf etcd-v3.5.7-linux-amd64.tar.gz -C /usr/local/bin/
# 文件夹重命名
mv  /usr/local/bin/etcd-v3.5.7-linux-amd64   /usr/local/bin/etcd
  1. 解压出来的实际上就是extc的二进制可执行文件。
[root@clinet etcd]# tree /usr/local/bin/etcd/
/usr/local/bin/etcd/
├── Documentation
│   ├── dev-guide
│   │   └── apispec
│   │       └── swagger
│   │           ├── rpc.swagger.json
│   │           ├── v3election.swagger.json
│   │           └── v3lock.swagger.json
│   └── README.md
├── etcd                                       ## etcd服务端二进制
├── etcdctl									   #etcd客户端二进制
├── etcdutl
├── README-etcdctl.md
├── README-etcdutl.md
├── README.md
└── READMEv2-etcdctl.md
  1. 添加环境变量
# 临时添加
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/usr/local/bin/etcd
  • 添加完成之后可直接使用etcd,etcdctl,etcdutl等命令;
  • 上述环境变量为临时添加,我们需要永久设置可以将其添加到/etc/profile文件,以将环境变量添加到~/.bashrc文件中。添加完成之后执行source可立即生效
  1. 启动etcd
etcd --name etcd1 --data-dir /data/etcd --listen-client-urls \ http://192.168.194.128:2379,http://127.0.0.1:2379 --advertise-client-urls http://192.168.194.128:2379
  • /usr/local/bin/etcd: Etcd二进制文件的路径。
  • --name etcd1: Etcd节点的名称,这个名称将在Etcd集群中唯一标识节点。
  • --data-dir /data/etcd: Etcd数据存储目录的路径,Etcd会在这个目录中存储所有的键值信息。
  • --listen-client-urls http://192.168.209.166:2379,http://127.0.0.1:2379: Etcd客户端监听的URL地址,这里指定了两个地址,分别是192.168.209.166:2379127.0.0.1:2379。这些地址是客户端用于连接Etcd节点的地址。
  • --advertise-client-urls http://192.168.209.166:2379: Etcd节点公告给客户端的URL地址,这里指定了192.168.209.166:2379。客户端将使用这个地址来连接Etcd节点。

常用参数:

  • --initial-cluster: 用于指定Etcd集群中所有节点的列表,格式为<name>=<url>[,<name>=<url>...]
  • --initial-cluster-state: 用于指定Etcd集群的初始状态,可以是newexisting
  • --initial-cluster-token: 用于指定Etcd集群的标识符,用于确保不同的Etcd集群之间不会发生冲突。
  • --listen-peer-urls: 用于指定Etcd节点监听的URL地址,允许其他Etcd节点连接到它。
  • --advertise-peer-urls: 用于指定Etcd节点公告给其他节点的URL地址。
  • --initial-advertise-peer-urls: 用于指定Etcd节点在初始启动时公告给其他节点的URL地址。
  • --initial-cluster-token-cmd: 用于指定一个命令,Etcd将会执行该命令生成一个集群标识符。
  • --cert-file--key-file: 用于指定Etcd节点的证书和私钥,用于加密通信。
  1. 通过systemd的方式管理etcd
[root@clinet system]# cat /lib/systemd/system/etcd.service 
[Unit]
Description=etcd
Documentation=https://github.com/coreos/etcd
Conflicts=etcd.service
[Service]
Type=notify
Restart=always
RestartSec=5s
LimitNOFILE=40000
TimeoutStartSec=0
ExecStart=/usr/local/bin/etcd/etcd --name etcd1 --data-dir /data/etcd \
--listen-client-urls http://192.168.194.128:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.194.128:2379


[Install]
WantedBy=multi-user.target

[root@clinet system]# 
  1. 启动etcd服务
[root@clinet system]# systemctl daemon-reload 
[root@clinet system]# systemctl start etcd.service
  1. 查看相关服务是否正常
[root@clinet system]# netstat -ntpl |grep etcd
tcp        0      0 192.168.194.128:2379    0.0.0.0:*               LISTEN      3129/etcd           
tcp        0      0 127.0.0.1:2379          0.0.0.0:*               LISTEN      3129/etcd           
tcp        0      0 127.0.0.1:2380          0.0.0.0:*               LISTEN      3129/etcd           
[root@clinet system]#
  1. 添加flannel网络配置信息到etcd
etcdctl --endpoints http://127.0.0.1:2379 set /coreos.com/network/config '{"Network": "10.0.0.0/16", "SubnetLen": 24, "SubnetMin": "10.0.1.0","SubnetMax": "10.0.20.0", "Backend": {"Type": "vxlan"}}'

##可能会提示你没有set指令,这时需要将set改为put(可能是因为你使用的是较老版本的 etcdctl 工具,或者是因为你的 etcd 集群版本不支持 set 命令。在较老版本的 etcdctl 中,可能使用的是 etcdctl set 命令的前身,即 etcdctl put 命令。)
  • Network:用于指定Flannel地址池
  • SubnetLen:用于指定分配给单个宿主机的docker0的ip段的子网掩码的长度
  • SubnetMin:用于指定最小能够分配的ip段
  • SudbnetMax:用于指定最大能够分配的ip段,在上面的示例中,表示每个宿主机可以分配一个24位掩码长度的子网,可以分配的子网从10.0.1.0/24到10.0.20.0/24,也就意味着在这个网段中,最多只能有20台宿主机
  • Backend:用于指定数据包以什么方式转发,默认为udp模式,host-gw模式性能最好,但不能跨宿主机网络
  1. 查看是否添加成功
[root@clinet ~]# /usr/local/bin/etcd/etcdctl get  /coreos.com/network/config
/coreos.com/network/config
{"Network": "10.0.0.0/16", "SubnetLen": 24, "SubnetMin": "10.0.1.0","SubnetMax": "10.0.20.0", "Backend": {"Type": "vxlan"}}
[root@clinet ~]#

安装flannel

  • dokcer-01安装:
  1. 在github上下载相应的flannel包
wget https://github.com/flannel-io/flannel/releases/download/v0.21.4/flannel-v0.21.4-linux-amd64.tar.gz
  1. 解压flannel-v0.21.4-linux-amd64.tar.gz至/usr/local/bin/flannel目录
mkdir /usr/local/bin/flannel
tar -xvf  flannel-v0.21.4-linux-amd64.tar.gz  -C /usr/local/bin/flannel
  1. 解压出来的文件如下
[root@clinet ~]# tree /usr/local/bin/flannel/
/usr/local/bin/flannel/
├── flanneld						##flannel启动二进制文件
├── mk-docker-opts.sh
└── README.md

0 directories, 3 files
[root@clinet ~]# 
  • mk-docker-opts.sh文件说明
在 flannel 网络中,每个容器都会被分配一个唯一的 IP 地址,以便它们能够相互通信。但是,Docker 默认不会将容器的 IP 地址暴露给外部网络,因此需要使用 flannel 提供的网络代理来实现容器之间的通信。mk-docker-opts.sh 文件就是用来生成这个代理参数的。

具体来说,mk-docker-opts.sh 文件会读取 flannel 的配置文件,然后生成以下参数:

--bip: 指定 Docker 内部网络的 IP 地址段。

--mtu: 指定 Docker 内部网络的 MTU 值。

--ip-masq: 开启 IP 地址伪装,以便 Docker 容器能够访问外部网络。

--iptables: 开启 Docker 容器的防火墙规则,以便它们能够与外部网络进行通信。

生成这些参数后,mk-docker-opts.sh 文件会将它们输出到标准输出中,以便其他脚本或命令能够使用它们。通常情况下,这些参数会被传递给 Docker 启动命令,以便 Docker 能够正确地与 flannel 网络进行通信。
  1. 创建systemd文件来管理flannel
# cat /etc/systemd/system/flanneld.service

[Unit]
Description=Flanneld
Documentation=https://github.com/coreos/flannel
After=network.target
Before=docker.service

[Service]
User=root
ExecStartPost=/usr/local/bin/flannel/mk-docker-opts.sh
ExecStart=/usr/local/bin/flannel/flanneld \
--etcd-endpoints="http://192.168.194.128:2379" \
--iface=192.168.194.128 \
--ip-masq=true \
--etcd-prefix=/coreos.com/network
Restart=on-failure
Type=notify
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
  • /usr/local/bin/flannel/flanneld 是 Flannel 程序的路径。
  • --etcd-endpoints="http://192.168.194.128:2379" 指定了与 etcd 集群建立连接的地址,这里指定的是 etcd 集群的地址和端口号。
  • --iface=192.168.194.128 指定了 Flannel 使用的网络接口,这里指定的是 IP 地址 192.168.194.128 所在的网络接口。
  • --ip-masq=true 启用了 Flannel 的 IP 地址伪装功能,可以将容器的 IP 地址转换为节点的 IP 地址,使得容器可以与外部网络通信。
  • --etcd-prefix=/coreos.com/network 指定了 Flannel 在 etcd 中保存的键值对的前缀,这里指定的是 /coreos.com/network
  1. 启动flannel
[root@clinet ~]#  systemctl daemon-reload
[root@clinet ~]#systemctl  start flanneld

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-THeZSCpN-1680852318239)(D:\学习\学习笔记\图片\56.png)]

Flannel启动过程解析:

  • 从etcd中获取network的配置信息
  • 划分subnet,并在etcd中进行注册
  • 将子网信息记录到/run/flannel/subnet.env中
  1. flannel启动之后会生成一个flannel1.1的桥接网卡。
[root@clinet ~]#ip  add
8: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default 
    link/ether be:1a:58:3a:35:5f brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.0/32 scope global flannel.1
       valid_lft forever preferred_lft forever
    inet6 fe80::bc1a:58ff:fe3a:355f/64 scope link 
       valid_lft forever preferred_lft forever
  • docker-02安装:
  1. 安装步骤与docker-01节点一致。只需要主要启动flannel的时候,–iface指定本地ip地址。

注意事项:

1.  flananel正常启动之后,此时通过ip  add可以查看到多了一个flananel1.1的网卡,但是此时flannel1.1的网卡并未接管docker0完成,即docker0网卡的ip段任然是默认的,还是flannel1.1的段。

配置docker

Docker安装完成以后,需要修改其启动参数以使其能够使用flannel进行IP分配,以及网络通讯在Flannel运行之后,会生成一个环境变量文件,包含了当前主机要使用flannel通讯的相关参数,如下:

(flannel启动之后自动生成,不需要手动执行)
[root@clinet ~]# cat /run/flannel/subnet.env 
FLANNEL_NETWORK=10.0.0.0/16
FLANNEL_SUBNET=10.0.2.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

使用flannel提供的脚本将subnet.env转写成Docker启动参数,创建好的启动参数默认生成在/run/docker_opts.env文件中:

(需要手动执行生成)
[root@lvs-1 ~]# /usr/local/bin/flannel/mk-docker-opts.sh -c
[root@lvs-1 ~]# cat /run/docker_opts.env 
DOCKER_OPTS=" --bip=10.0.17.1/24 --ip-masq=false --mtu=1450"
[root@lvs-1 ~]# 

修改docker的服务启动文件如下:

# vim /lib/systemd/system/docker.service

EnvironmentFile=/run/docker_opts.env
ExecStart=/usr/bin/dockerd $DOCKER_OPTS -H fd://

重启docker:

systemctl daemon-reload
systemctl restart docker

这时可以看到docker0的ip已经位于flannel网卡的网段之中:

  • docker-01节点:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-owIjgQdW-1680852318240)(D:\学习\学习笔记\图片\57.png)]

  • docker-02节点:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gpm1Gloi-1680852318240)(D:\学习\学习笔记\图片\58.png)]

连通性测试:

  • Docker-01节点:
  1. 启动一个busybox容器
docker run -it --name busybox  busybox /bin/sh

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WkcCcKO7-1680852318241)(D:\学习\学习笔记\图片\59.png)]

  • Docker-02节点:
  1. 同样启动一个busybox容器
docker run -it --name busybox_2  busybox /bin/sh

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A0b3LdOG-1680852318242)(D:\学习\学习笔记\图片\60.png)]

  • 测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g4fh2D6q-1680852318242)(D:\学习\学习笔记\图片\61.png)]

总结:

1. 注意部署顺序:部署启动etcd-->etcd中添加flannel网络配置信息-->部署启动flannel-->修改docker启动项并重启docker
2. 修改docker配置项之前需要手动执行mk-docker-opts.sh,该文件是flannel包中解压出来的。

直接路由模式

  1. 确保两台宿主机上的docker0桥接网络不在同一个网段,避免ip冲突,修改其中一台的bip
[root@clinet ~]# cat /etc/docker/daemon.json 
{
  "bip": "172.18.0.1/16",
  "registry-mirrors": ["https://mqkiky4e.mirror.aliyuncs.com"]
}
[root@clinet ~]#

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TKLg0HtV-1680852318243)(D:\学习\学习笔记\图片\40.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4nh9e1bg-1680852318244)(D:\学习\学习笔记\图片\41.png)]

  1. 所有宿主机上开启ip_forword
 echo "net.ipv4.ip_forward= 1" >> /etc/sysctl.conf
 sysctl  -p
  1. 在宿主机A上添加路由
ip route add 172.17.0.0/16  via 192.168.194.130  dev  ens33

172.17.0.0/16 表示宿主机B上的docker0网桥的ip段;
192.168.194.130表示宿主机B的ip地址。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cy09RN32-1680852318244)(D:\学习\学习笔记\图片\44.png)]

  1. 在宿主机B上添加路由
ip route add 172.18.0.0/16  via 192.168.194.128  dev  ens33

172.18.0.0/16 表示宿主机A上的docker0网桥的ip段;
192.168.194.128表示宿主机A的ip地址。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O8tamcaB-1680852318245)(D:\学习\学习笔记\图片\45.png)]

  1. 其中一台机器上创建一个nginx容器
docker run -d --name nginx nginx

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n6TUYEf1-1680852318245)(D:\学习\学习笔记\图片\42.png)]

  1. 另外一台也创建一个busybox的容器
docker run -it --name busybox_test busybox

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S5LcRdna-1680852318246)(D:\学习\学习笔记\图片\46.png)]

路由分析:

  • 首先查看宿主机B的busybox容器中的路由表:可以看出,要想与172.18.0.2通讯,该容器是在路由表中找不到172.18.0.2的路由的,但是只有一条默认路由(无论到底哪里的数据包,都通过默认网关转发),通过默认路由到达docker0(172.17.0.1)<上的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SlgWYhXW-1680852318247)(D:\学习\学习笔记\图片\48.png)]

  • 数据包到达docker0网桥上,相当于到达了宿主机,此时在宿主机B上查看路由表:可以看出,主机1实际上也没有172.18.0.2的路由,因此也会从默认路由转发出去,这样就会导致无法到达.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HnPEyMxX-1680852318247)(C:\Users\xhz\AppData\Roaming\Typora\typora-user-images\50.png)]

  • 随后我们在宿主机B上添加了一个到172.18.0.0、16的路由,此时查看宿主机B的路由表:可以看出,到达172.18.0.0/16网段,需要走网关192.168.194.128。而网关就是宿主机A的ip地址,因此数据包就到达了宿主机A

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bRtDfV9u-1680852318248)(D:\学习\学习笔记\图片\45.png)]

  • 数据包到达主机A后,查看宿主机A的路由表:可以看出,路由表存在172.18.0.0/16的路由,该路由会到达docker0,随后转发至容器。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R5OtzZmi-1680852318248)(D:\学习\学习笔记\图片\51.png)]

  • 在网络通讯中,路由要求有来有回,因此我们在宿主机上也添加相应的返回路由即可。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FeB9Wvsz-1680852318249)(D:\学习\学习笔记\图片\44.png)]

猜你喜欢

转载自blog.csdn.net/qq_43714097/article/details/130013438