-
环境说明
CentOS7
etcd-v3.4.3-linux-amd64.tar.gz
flannel-v0.11.0-linux-amd64.tar.gz官方文档:https://github.com/coreos/flannel
下载地址:https://github.com/coreos/flannel/releases/download/v0.11.0/flannel-v0.11.0-linux-amd64.tar.gz
1. Docker跨主机容器间网络配置
方式方式有:Pipework、Flannel、Weave、Open vSwitch(虚拟交换机)、Calico, 其中
Pipework、Weave、Flannel是使用的最多。
本文介绍的是Flannel
2. Flannel是什么
1.Flannel是CoreOS提供用于解决Dokcer集群跨主机通讯的覆盖网络工具。它的主要思路是:预先留出一个网段,
每个主机使用其中一部分,然后每个容器被分配不同的ip;让所有的容器认为大家在同一个直连的网络,
底层通过UDP/VxLAN等进行报文的封装和转发。
2.Flannel实质上是一种覆盖网络(overlay network),即表示运行在一个网上的网(应用层网络),并不依靠ip地址来传递消息,
而是采用一种映射机制,把ip地址和identifiers做映射来资源定位。也就是将TCP数据包装在另一种网络包里面进行路由转发和通信,
目前已经支持UDP、VxLAN、AWS VPC和GCE路由等数据转发方式
注1:Flannel支持多种Backend协议,但是不支持运行时修改Backend。官方推荐使用以下Backend:
VXLAN,性能损耗大概在20~30%;
host-gw, 性能损耗大概10%,要求Host之间二层直连,因此只适用于小集群;
UDP, 建议只用于debug,因为性能烂到家了,如果网卡支持 enable udp offload,直接由网卡进行拆包解包,性能还是很棒的。
AliVPC。
3.Flannel使用etcd存储配置数据和子网分配信息。flannel 启动之后,后台进程首先检索配置和正在使用的子网列表,
然后选择一个可用的子网,然后尝试去注册它。etcd也存储这个每个主机对应的ip。flannel使用etcd的watch机制
监视/coreos.com/network/subnets下面所有元素的变化信息,并且根据它来维护一个路由表。
注1:etcd可以理解成springcloud中的注册中心,用于服务注册和发现,这里是节点(即宿主机)注册和发现
小结:Flannel是一种覆盖网络(overlay network),
使用etcd存储配置数据和子网分配信息
通过UDP/VxLAN等进行报文的封装和转发,建议使用VxLAN性能更好
3. 情景描述
某公司有一个简单的集群网络,在该集群内,有两台服务器甲和乙,每台服务器上都有两张网卡,分别连接公网和私网,
两台服务器可以通过私网互联,在两个服务器节点上分别安装了Docker,并且运行了A/B/C/D 4个容器。
每台服务器节点上都有一个docker0网桥,这是docker启动后初始化的虚拟设备,每个容器都与docker0网桥连接,
并且,容器的IP由docker自动分配
问题分析
默认情况下此网络设置不支持跨主机的容器互联,原因有两方面:
1.跨主机访问容器,没有有效路由
2.多个节点上的容器网段冲突,docker启动后初始化docker0网桥时,会随机分配一个IP段,那么,如果不加以协调,
多个节点内的容器网络有可能会冲突,容器C和D的IP都是“192.168.1.3”
4. Flannel实现的容器的跨主机通信通过如下过程实现
1.每个主机上安装并运行etcd和flannel;
2.在etcd中规划配置所有主机的docker0子网范围
3.每个主机上的flanneld根据etcd中的配置,为本主机的docker0分配子网,保证所有主机上的docker0网段不重复,
并将结果(即本主机上的docker0子网信息和本主机IP的对应关系)存入etcd库中,这样etcd库中就保存了所有主机
上的docker子网信息和本主机IP的对应关系;
4.当需要与其他主机上的容器进行通信时,查找etcd数据库,找到目的容器的子网所对应的outip(目的宿主机的IP);
5.将原始数据包封装在VXLAN或UDP数据包中,IP层以outip为目的IP进行封装;
6.由于目的IP是宿主机IP,因此路由是可达的;
7.VXLAN或UDP数据包到达目的宿主机解封装,解出原始数据包,最终到达目的容器。
注1:flannel默认使用8285端口作为UDP封装报文的端口,VxLan使用8472端口
5. Flannel的工作原理
1.数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡,
这是个P2P的虚拟网卡,flanneld服务监听在网卡的另外一端
2.Flannel通过Etcd服务(重点)维护了一张节点间的路由表,该张表里保存了各个节点主机的子网网段信息
flannel默认使用etcd作为配置和协调中心,首先使用etcd设置集群的整体网络。
3.源主机的flanneld服务将原本的数据内容UDP封装后根据自己的路由表投递给目的节点的flanneld服务,
数据到达以后被解包,然后直接进入目的节点的flannel0虚拟网卡,然后被转发到目的主机的docker0虚拟网卡,
最后就像本机容器通信一样的由docker0路由到达目标容器
这样整个数据包的传递就完成了,这里需要解释三个问题:
1)UDP封装是怎么回事
在UDP的数据内容部分其实是另一个ICMP(也就是ping命令)的数据包。原始数据是在起始节点的Flannel服务上进行UDP封装的,
投递到目的节点后就被另一端的Flannel服务还原成了原始的数据包,两边的Docker服务都感觉不到这个过程的存在
2)为什么每个节点上的Docker会使用不同的IP地址段
这个事情看起来很诡异,但真相十分简单。其实只是单纯的因为Flannel通过Etcd分配了每个节点可用的IP地址段后,
偷偷的修改了Docker的启动参数。在运行了Flannel服务的节点上可以查看到Docker服务进程运行参数(ps aux|grep docker|grep “bip”),
例如“–bip=182.48.25.1/24”这个参数,它限制了所在节点容器获得的IP范围。这个IP范围是由Flannel自动分配的,
由Flannel通过保存在Etcd服务中的记录确保它们不会重复。
3)为什么在发送节点上的数据会从docker0路由到flannel0虚拟网卡,在目的节点会从flannel0路由到docker0虚拟网卡
例如现在有一个数据包要从IP为10.1.15.2的容器发到IP为10.1.20.3的容器。根据数据发送节点的路由表,
它只与10.1.0.0/16匹配这条记录匹配,因此数据从docker0出来以后就被投递到了flannel0。
同理在目标节点,由于投递的地址是一个容器,因此目的地址一定会落在docker0对于的10.1.20.1/24这个记录上,
自然的被投递到了docker0网卡。
6. Flannel安装配置
0.机器环境(CentOS7)
一共三台机器:一个etcd集群,三台机器安装flannel和Docker
节点名称 IP地址(根据自己机器变化) 软件环境
node-1 192.168.238.130 etcd、flannel、docker
node-2 192.168.238.131 etcd、flannel、docker
node-3 192.168.238.132 etcd、flannel、docker
注1:通常按照需求将集群节点部署为3,5,7,9个节点。这里能选择偶数个节点吗?最好不要这样。原因有二:
1.偶数个节点集群不可用风险更高,表现在选主过程中,有较大概率或等额选票,从而触发下一轮选举
2.偶数个节点集群在某些网络分割的场景下无法正常工作。当网络分割发生后,将集群节点对半分割开。
此时集群将无法工作。按照RAFT协议,此时集群写操作无法使得大多数节点同意,从而导致写失败,集群无法正常工作
###################################################
以下均为node-0机器上的操作
###################################################
1.docker安装与配置(略)
如果不了解请看:Docker入门
2.etcd集群安装与配置(略)
如果不了解请看:etcd快速集群
注1:ETCD3.4版本中ETCDCTL_API=3和etcd --enable-v2=false成为了默认配置
flannel操作etcd使用的是v2的API,而kubernetes操作etcd使用的v3的API
为了兼容flannel,将默认开启v2版本,故配置文件中还要设置
ETCD_ENABLE_V2="true"
经实践:ETCD_ENABLE_V2无效,flannel连接不上etcd,最后不得不修改环境变量(之后再考虑兼容性问题)
vi /etc/profile
export ETCDCTL_API=2 # 指定etcdctl命令的版本为v2
刷新配置:
source /etc/profile
注2:etcdctl2和etcdctl3是不兼容的,两者的api参数也不一样,最最关键的是“v2/v3的数据是不互通的”
api 2 使用方法
ETCDCTL_API=2 etcdctl set key value
ETCDCTL_API=2 etcdctl ls /
ETCDCTL_API=2 etcdctl del / --prefix
api 3 使用方法
ETCDCTL_API=3 etcdctl put key value
ETCDCTL_API=3 etcdctl get /
ETCDCTL_API=3 etcdctl del / --prefix
注3:是不是要用v2的命令保存数据,flannel才能通过v2命令获得数据
3.向etcd注册网段,供flanneld使用
错误:“failed to retrieve network config: 100: Key not found (/coreos.com)”
这里有个巨大的坑:etcd-v3.4.3中,虽然开启了兼容模式,但v2/v3命令保存的数据是不互通的,
所以要使用v2的set命令而非v3的put命令保存数据,因为etcd开启了兼容模式,所以在命令前面添加
ETCDCTL_API=2,切换到v2版本的命令即可
##1.使用v2版的set(而非put)命令向ETCD中保存flannel覆盖网络信息
ETCDCTL_API=2 etcdctl --endpoints "http://192.168.238.130:2379,http://192.168.238.131:2379,http://192.168.238.132:2379" \
set /coreos.com/network/config '{"NetWork":"10.0.0.0/16","SubnetMin": "10.0.1.0", "SubnetMax": "10.0.20.0","Backend": {"Type": "vxlan"}}'
##2.v2命令获得网络信息
ETCDCTL_API=2 etcdctl --endpoints "http://192.168.238.130:2379,http://192.168.238.131:2379,http://192.168.238.132:2379" get /coreos.com/network/config
注1:etcd保存的网络参数说明
'{"Network": "10.0.0.0/16", "SubnetLen": 24, "SubnetMin": "10.0.1.0","SubnetMax": "10.0.20.0", "Backend": {"Type": "vxlan"}}'
10.0.0.0/16:请参考附录三
Network: 用于指定Flannel地址池, 整个overlay(覆盖)网络为10.0.0.0/16网段.
SubnetLen: 用于指定分配给单个宿主机的docker0的ip段的子网掩码的长度,默认值也是24
SubnetMin: 用于指定最小能够分配的ip段
SudbnetMax: 用于指定最大能够分配的ip段,在上面的示例中,表示每个宿主机可以分配一个24位掩码长度的子网,
可以分配的子网从10.0.1.0/24到10.0.20.0/24,也就意味着在这个网段中,最多只能有20台宿主机
Backend: 用于指定数据包以什么方式转发,默认为udp模式, 这里使用的是vxlan模式.因为为vxlan比起预设的udp性能相对好一些
注2:如何知道v2/v3命令保存的数据是不兼容的呢
## v3命令查询,无数据
etcdctl --endpoints "http://192.168.238.130:2379,http://192.168.238.131:2379,http://192.168.238.132:2379" get /coreos.com/network/config
## v2命令查询,有数据
ETCDCTL_API=2 etcdctl --endpoints "http://192.168.238.130:2379,http://192.168.238.131:2379,http://192.168.238.132:2379" get /coreos.com/network/config
注3:flannel访问etcd时使用的key默认为:/coreos.com/network,可修改,但必须重新配置flanneld.conf的参数etcd-prefix,
例如:-etcd-prefix=/zking.com/network
4.安装flannel
##1.下载或rz上传flannel安装包
安装包:https://pan.baidu.com/s/1o6d4WgT_2uu6B7IGInHwQQ
提取码:vuv2
cd /usr/local/mytools
rz
##2.创建flannel安装目录
mkdir -p /opt/flannel
##3.解压安装文件至指定目录
tar xzf flannel-v0.11.0-linux-amd64.tar.gz -C /opt/flannel
##4.查看解压后文件:主要有flanneld、mk-docker-opts.sh这两个文件,其中flanneld为主要的执行文件,sh脚本用于生成Docker启动参数
cd /opt/flannel && ls
5.为flannel创建一个systemd服务,用于后台启动
vim /etc/systemd/system/flanneld.service
flanneld.service:
[Unit]
Description=Flanneld
After=network.target
After=network-online.target
Wants=network-online.target
##1.flannel服务需要先于Docker启动,后于etcd启动
After=etcd.service
Before=docker.service
[Service]
User=root
##2.ExecStart即flanneld启动程序位置
##3.--etcd-endpoints参数为ectd集群客户端地址
##4.--iface参数为要绑定的网卡的IP地址,或是网卡名(ifconfig查看获得)请根据实际情况修改
ExecStart=/opt/flannel/flanneld \
--etcd-endpoints=http://192.168.238.130:2379,http://192.168.238.131:2379,http://192.168.238.132:2379 \
--etcd-prefix=/coreos.com/network \
--iface=ens33 \
--ip-masq
Restart=on-failure
Type=notify
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
1.启动脚本关键参数:
##1.flannel服务需要先于Docker启动,后于etcd启动
After=etcd.service
Before=docker.service
##2.ExecStart即flanneld启动程序位置
ExecStart=/opt/flannel/flanneld
##3.--etcd-endpoints参数为ectd集群客户端地址
--etcd-endpoints="http://192.168.199.160:2379,http://http://192.168.199.157:2379,http://http://192.168.199.158:2379" \
##4.--iface参数为要绑定的网卡的网卡名(或IP地址)请根据实际情况修改,注意:IP或网卡名都可用ifconfig命令获得
--iface=ens33
2.flannel启动过程解析
flannel服务需要先于Docker启动。flannel服务启动时主要做了以下几步的工作:
1.从etcd中获取network的配置信息
2.划分subnet,并在etcd中进行注册
3.将子网信息记录到/run/flannel/subnet.env中
6.flanneld服务启用/自启/停止/重启
systemctl daemon-reload && systemctl enable flanneld && systemctl start flanneld && systemctl status flanneld
systemctl stop flanneld
systemctl restart flanneld
调试相关命令:
cd /var/lib/etcd && rm -rf *
systemctl daemon-reload && systemctl restart flanneld
如果启动失败,可通过如下命令查看原因
## 显示概要
systemctl status flanneld.service
## 可以查看启动参数
ps -ef |grep flanneld
## 查看启动详情
journalctl -xe
## 显示实时日志
journalctl -f
## 查看本机监听端口
netstat -tunlp|grep etcd
7.验证flannel网络
1.flanneld服务启动成功后,使用ifconfig命令查看系统,会发现系统多了一个虚拟网卡“flannel.1”
注:“flannel.1”为生成的虚拟网卡名,此名字不固定,以实际情况为准
2.在etcd集群的任一节点上都可以查看到子网IP
ETCDCTL_API=2 etcdctl --endpoints "http://127.0.0.1:2379" ls /coreos.com/network/subnets
3.查看“flannel.1”的网络详细情况
ifconfig flannel.1
可以看到flannel.1网卡的地址和etcd中存储的地址一样,这样flannel网络配置完成
8.配置Docker
在各个节点安装好Docker,然后更改Docker的启动参数,使其能够使用flannel进行IP分配,以及网络通讯
1.查看flannel分配的网络参数
##在Flannel运行之后,会生成一个环境变量文件,包含了当前主机要使用flannel通讯的相关参数
cat /run/flannel/subnet.env
##控制台显示如下内容
FLANNEL_NETWORK=10.0.0.0/16 ##10.0.1~20.1~254(0和255是特殊值不能用)
FLANNEL_SUBNET=10.0.14.1/24 ##10.0.14(不一定是14,可以在1~20之间).1~254
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
2.创建Docker运行参数
##使用flannel提供的脚本将subnet.env转写成Docker启动参数
/opt/flannel/mk-docker-opts.sh -d /run/flannel/docker_opts.env -c
##创建好的启动参数位于/run/flannel/docker_opts.env文件中,可使用cat命令查看
cat /run/flannel/docker_opts.env
注1:网上很多将docker_opts.env保存到/run目录下,run目录下重启系统后docker_opts.env文件会自动消失,引发错误
所以改到/run/flannel目录下
4.修改Docker启动参数
##编辑 systemd service 配置文件
vim /lib/systemd/system/docker.service
##下面是docker.service要修改的2个地方
##1.[Service]节中,指定启动参数所在的文件位置(这个配置是新增的)
EnvironmentFile=/run/flannel/docker_opts.env
##在原有ExecStart后面添加$DOCKER_OPTS
##修改前
#ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
##2.修改后
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock $DOCKER_OPTS
5.重新加载systemd配置,并重启Docker即可,
systemctl daemon-reload && systemctl restart docker && systemctl status docker
6.查看overlay(覆盖)网络
##查看宿主机网络情况,此时可以看到docker0的网卡ip地址已经处于flannel网卡网段之内
ifconfig
7.测试flannel
三台机器(node1/node2/node3)都配置好了之后,我们在三台机器上分别开启一个docker容器,测试它们的网络是否可相互联通的
##下载centos镜像,因为此镜像中其它软件及命令均有安装
docker pull centos
## 依次查看3台机器的flannel.1网络IP
1.node-0
##创建/启动/进入容器
docker run -it centos /bin/bash
##进入容器后,查看容器IP
cat /etc/hosts
##控制台输出如下内容
10.0.14.2 ff43c3e20bde
##另外,请注意宿主机flannel.1网络的IP为
10.0.14.0
2.node2
##创建/启动/进入容器
docker run -it centos /bin/bash
##进入容器后,查看容器IP
cat /etc/hosts
##控制台输出如下内容
10.0.7.2 45f1155a3e25
##另外,请注意宿主机flannel.1网络的IP为
10.0.7.0
3.node3
##创建/启动/进入容器
docker run -it alpine:latest /bin/sh
##进入容器后,查看容器IP
cat /etc/hosts
##控制台输出如下内容
10.0.8.2 dd061df9ad27
##另外,请注意宿主机flannel.1网络的IP为
10.0.8.0
4.从不同宿主机容器到三台宿主机
#-c count是数量,即发三个ping 包,请注意控制台信息
ping -c3 192.168.238.130
ping -c3 192.168.238.131
ping -c3 192.168.238.132
5.从容器到到跨宿主机容器
ping -c3 10.0.17.2
ping -c3 10.0.8.2
ping -c3 10.0.8.2
6.node1再启动一个容器
##创建/启动/进入容器
docker run -it cet:latest /bin/sh
##进入容器后,查看容器IP
cat /etc/hosts
##控制台输出如下内容
10.0.14.3 fbdddd756a16
##另外,请注意宿主机flannel.1网络的IP为
10.0.14.0
7.测试同一宿主机不同容器的连接
#10.0.14.3<-->10.0.14.3互ping
ping -c3 10.0.14.2
ping -c3 10.0.14.3
附录一:eth0 eth0:1 eth0.1 的区别
eth0 eth0:1 和eth0.1三者的关系对应于物理网卡、子网卡、虚拟VLAN网卡
附录二:veth是什么?
Linux container 中用到一个叫做veth的东西,这是一种新的设备,专门为 container 所建。veth 从名字上来看是
Virtual ETHernet(虚拟网络设备)的缩写,它的作用很简单,就是要把从一个 network namespace 发出的数据包转发到另一个 namespace。
veth 设备是成对的,一个是 container 之中,另一个在 container 之外,即在真实机器上能看到的
附录三:ip地址后边加个/8(16,24,32)是什么意思?
是子网掩码的位数,由于255相当于二进制的8位11111111,所以也缩写成“/8”,表示网络号占了8位
A类IP地址的默认子网掩码为255.0.0.0(/8)
B类的为255.255.0.0(/16);
C类的为255.255.255.0(/24)
/30就是255.255.255.252
/32就是255.255.255.255
例如:
xx.xx.xx.0/24 ——>表示一个网段,并且24告诉了当前具体的子网掩码
举例说吧,192.168.0.0/24”就表示,这个网段的IP地址从192.168.0.1开始,到192.168.0.254结束
(192.168.0.0和192.168.0.255有特殊含义,不能用作IP地址);子网掩码是255.255.255.0
附录四: flanneld启动失败,报错的原因如下:
Failed to create SubnetManager: parse first path segment in URL cannot contain colon
ExecStart=/usr/local/bin/flanneld
-etcd-cafile=/etc/etcd/ssl/ca.pem
-etcd-certfile=/etc/etcd/ssl/server.pem
-etcd-keyfile=/etc/etcd/ssl/server-key.pem
###是不能加引号的
##错误
##-etcd-endpoints="http://192.168.238.130:2379,http://192.168.238.131:2379,http://192.168.238.132:2379 "
##正确
-etcd-endpoints=http://192.168.238.130:2379,http://192.168.238.131:2379,http://192.168.238.132:2379
-etcd-prefix=/coreos.com/network
-iface=ens33
-ip-masq
附录五:解决flannel下容器无法跨主机互通问题
经测试,发现是防火墙的问题,关闭防火墙后问题解决。
但作为服务的防火墙是不能关闭的,如何解决呢?
这是由于linux还有底层的iptables,所以在node上分别执行
iptables -P INPUT ACCEPT && iptables -P FORWARD ACCEPT && iptables -F && iptables -L -n
问题完美解决