flannel原理简析及安装

flannel是CoreOS提供用于解决Dokcer集群跨主机通讯的覆盖网络工具。它的主要思路是:预先留出一个网段,每个主机使用其中一部分,然后每个容器被分配不同的ip;让所有的容器认为大家在同一个直连的网络,底层通过UDP/VxLAN
等进行报文的封装和转发。
flannel项目地址:https://github.com/coreos/flannel
flannel架构介绍
flannel默认使用8285端口作为UDP
封装报文的端口,VxLan使用8472端口。


那么一条网络报文是怎么从一个容器发送到另外一个容器的呢?
容器直接使用目标容器的ip访问,默认通过容器内部的eth0发送出去。
报文通过veth pair
被发送到vethXXX

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

flanneld
通过etcd
维护了各个节点之间的路由表,把原来的报文UDP
封装一层,通过配置的iface
发送出去。
报文通过主机之间的网络找到目标主机。
报文继续往上,到传输层,交给监听在8285端口的flanneld
程序处理。
数据被解包,然后发送给flannel0
虚拟网卡。
查找路由表,发现对应容器的报文要交给docker0

docker0
找到连到自己的容器,把报文发送过去。

flannel安装配置
环境准备
一共三台机器:一个etcd集群,三台机器安装flannel和Docker。
节点名称
IP地址
软件环境

etcd1
192.168.2.210
etcd、flannel、docker

etcd2
192.168.2.211
etcd、flannel、docker

etcd3
192.168.2.212
etcd、flannel、docker

安装etcd
关于etcd的安装使用已经在「etcd使用入门」和「通过静态发现方式部署etcd集群」中做了比较详细的讲解,如果你还不会安装etcd可先阅读下这两篇文章。这里就不再重复讲解了。
安装flannel
三个节点都需安装配置flannel,这里以etcd1节点为例。
flannel和etcd一样,直接从官方下载二进制执行文件就可以用了。当然,你也可以自己编译。
1
2
3

$ curl -L https://github.com/coreos/flannel/releases/download/v0.7.0/flannel-v0.7.0-linux-amd64.tar.gz -o flannel.tar.gz
$ mkdir -p /opt/flannel
$ tar xzf flannel.tar.gz -C /opt/flannel

解压后主要有flanneld
、mk-docker-opts.sh
这两个文件,其中flanneld
为主要的执行文件,sh脚本用于生成Docker启动参数。
配置flannel
由于flannel
需要依赖etcd
来保证集群IP分配不冲突的问题,所以首先要在etcd
中设置 flannel
节点所使用的IP段。
1
2
3
4

$ etcdctl --endpoints "http://etcd1.hi-linux.com:2379"
set /coreos.com/network/config '{"NetWork":"10.0.0.0/16", "SubnetMin": "10.0.1.0", "SubnetMax": "10.0.20.0"}'

{"NetWork":"10.0.0.0/16", "SubnetMin": "10.0.1.0", "SubnetMax": "10.0.20.0"}

flannel预设的backend type
是udp,如果想要使用vxlan
作为backend
,可以加上backend
参数:
1
2

$ etcdctl --endpoints "http://etcd1.hi-linux.com:2379"
set /coreos.com/network/config '{"NetWork":"10.0.0.0/16", "Backend": {"Type": "vxlan"}}'

flannel backend为vxlan比起预设的udp性能相对好一些。

启动flannel
命令行方式运行

1

$ /opt/flannel/flanneld --etcd-endpoints="http://etcd1.hi-linux.com:2379" --ip-masq=true >> /var/log/flanneld.log 2>&1 &

后台服务方式运行

给flannel创建一个systemd服务,方便以后管理。创建flannel配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

$ cat <<EOF | sudo tee /etc/systemd/system/flanneld.service
[Unit]
Description=Flanneld
Documentation=https://github.com/coreos/flannel
After=network.target
Before=docker.service

[Service]
User=root
ExecStart=/opt/flannel/flanneld
--etcd-endpoints="http://etcd1.hi-linux.com:2379,http://etcd2.hi-linux.com:2379,http://etcd3.hi-linux.com:2379"
--iface=192.168.2.210
--ip-masq
Restart=on-failure
Type=notify
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

注意:--iface
参数为要绑定的网卡的IP地址,请根据实际情况修改。

启动flannel服务
1

$ systemctl start flanneld

flannel启动过程解析
flannel服务需要先于Docker启动。flannel服务启动时主要做了以下几步的工作:
从etcd中获取network的配置信息。
划分subnet,并在etcd中进行注册。
将子网信息记录到/run/flannel/subnet.env
中。

验证flannel网络
在etcd1节点上看etcd中的内容
1
2
3

$ etcdctl --endpoints "http://etcd1.hi-linux.com:2379" ls /coreos.com/network/subnets

/coreos.com/network/subnets/10.0.2.0-24

查看flannel0的网络情况:
1
2
3
4
5
6
7
8

$ ifconfig flannel0
flannel0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:10.0.2.0 P-t-P:10.0.2.0 Mask:255.255.0.0
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1472 Metric:1
RX packets:85 errors:0 dropped:0 overruns:0 frame:0
TX packets:75 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:500
RX bytes:7140 (7.1 KB) TX bytes:6300 (6.3 KB)

可以看到flannel0网卡的地址和etcd中存储的地址一样,这样flannel网络配置完成。
配置Docker
在各个节点安装好以后最后要更改Docker
的启动参数,使其能够使用flannel
进行IP分配,以及网络通讯。
flannel
运行后会生成一个环境变量文件,包含了当前主机要使用flannel
通讯的相关参数。
查看flannel分配的网络参数

1
2
3
4
5
6

$ cat /run/flannel/subnet.env

FLANNEL_NETWORK=10.0.0.0/16
FLANNEL_SUBNET=10.0.2.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=true

创建Docker运行参数

使用flannel提供的脚本将subnet.env转写成Docker启动参数,创建好的启动参数位于/run/docker_opts.env
文件中。
1
2
3
4

$ /opt/flannel/mk-docker-opts.sh -d /run/docker_opts.env -c

$ cat /run/docker_opts.env
DOCKER_OPTS=" --bip=10.0.2.1/24 --ip-masq=false --mtu=1472"

修改Docker启动参数

修改docker的启动参数,并使其启动后使用由flannel生成的配置参数,修改如下:
1
2
3
4
5
6

编辑 systemd service 配置文件

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

在启动时增加flannel提供的启动参数

ExecStart=/usr/bin/dockerd $DOCKER_OPTS

指定这些启动参数所在的文件位置(这个配置是新增的,同样放在Service标签下)

EnvironmentFile=/run/docker_opts.env

然后重新加载systemd配置,并重启Docker
即可
1
2

$ systemctl daemon-reload
$ systemctl restart docker

此时可以看到docker0
的网卡ip地址已经处于flannel
网卡网段之内。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

$ ifconfig flannel0
flannel0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:10.0.2.0 P-t-P:10.0.2.0 Mask:255.255.0.0
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1472 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:500
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

$ ifconfig docker0
docker0 Link encap:Ethernet HWaddr 02:42:cf:87:3c:f7
inet addr:10.0.2.1 Bcast:0.0.0.0 Mask:255.255.255.0
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

到此节点etcd1的flannel安装配置完成了,其它两节点按以上方法配置完成就行了。
测试flannel
三台机器都配置好了之后,我们在三台机器上分别开启一个docker容器,测试它们的网络是否可相互联通的。
etcd1
1
2
3
4
5

$ docker run -it busybox sh

查看容器IP

$ cat /etc/hosts
10.0.2.2 9de86bfde6cc

etcd2
1
2
3
4
5

$ docker run -it busybox sh

查看容器IP

$ cat /etc/hosts
10.0.5.2 9ddd4a4e455b

etcd3
1
2
3
4
5

$ docker run -it busybox sh

查看容器IP

$ cat /etc/hosts
10.0.6.2 cbb0d891f353

从不同宿主机容器到三台宿主机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

/ # ping -c3 192.168.2.210
PING 192.168.2.210 (192.168.2.210): 56 data bytes
64 bytes from 192.168.2.210: seq=0 ttl=64 time=0.089 ms
64 bytes from 192.168.2.210: seq=1 ttl=64 time=0.065 ms

/ # ping -c5 192.168.2.211
PING 192.168.2.211 (192.168.2.211): 56 data bytes
64 bytes from 192.168.2.211: seq=0 ttl=63 time=1.712 ms
64 bytes from 192.168.2.211: seq=1 ttl=63 time=0.356 ms
64 bytes from 192.168.2.211: seq=2 ttl=63 time=2.201 ms

/ # ping -c3 192.168.2.212
PING 192.168.2.212 (192.168.2.212): 56 data bytes
64 bytes from 192.168.2.212: seq=0 ttl=63 time=0.467 ms
64 bytes from 192.168.2.212: seq=1 ttl=63 time=0.477 ms
64 bytes from 192.168.2.212: seq=2 ttl=63 time=0.532 ms

从容器到到跨宿主机容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/ # ping -c3 10.0.5.2
PING 10.0.5.2 (10.0.5.2): 56 data bytes
64 bytes from 10.0.5.2: seq=0 ttl=60 time=0.692 ms
64 bytes from 10.0.5.2: seq=1 ttl=60 time=0.565 ms
64 bytes from 10.0.5.2: seq=2 ttl=60 time=1.135 ms

/ # ping -c3 10.0.6.2
PING 10.0.6.2 (10.0.6.2): 56 data bytes
64 bytes from 10.0.6.2: seq=0 ttl=60 time=0.678 ms
64 bytes from 10.0.6.2: seq=1 ttl=60 time=0.907 ms
64 bytes from 10.0.6.2: seq=2 ttl=60 time=1.272 ms

/ # ping -c3 10.0.2.2
PING 10.0.2.2 (10.0.2.2): 56 data bytes
64 bytes from 10.0.2.2: seq=0 ttl=60 time=0.644 ms
64 bytes from 10.0.2.2: seq=1 ttl=60 time=0.915 ms
64 bytes from 10.0.2.2: seq=2 ttl=60 time=1.032 ms

测试容器到到跨宿主机容器遇到一个坑,开始怎么都不通,后找到原因是宿主机iptables
给阻挡掉了。附:Ubuntu一键清除iptables规则脚本
1
2
3
4
5
6
7
8
9
10

$ cat clear_iptables_rule.sh

!/bin/bash

iptables -F
iptables -X
iptables -Z
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT

参考文档
http://www.google.comhttp://t.cn/RcnGQ02http://t.cn/RXVHGpIhttp://t.cn/RXfavPGhttp://t.cn/RXfEThAhttp://t.cn/RXfEmS8http://t.cn/R5Xgfnx

推荐阅读更多精彩内容