Skip to the content.

flannel 是coreos家出品的一个容器网络项目,支持udp/vxlan/host-gw等网络模式。

flannel的核心思想是管理员定义一个初始大网段,比如10.246.0.0/16,每个主机分配一个小的网段10.246.40.0/24,一个主机上所有容器分配小网段的IP,所有主机的容器可以通过分配的IP直接访问。flannel负责大网段的划分,每台主机小网段IP的分配交给了docker管理。这个设计网络拓扑简单,实现起来也很简单,也会带来一些问题,比如:容器跨机迁移IP变化,IP不是统一分配,每台主机势必会浪费一些IP。另外这种设计也与docker的功能绑定了,可能是为了不重复造轮子的原因。

flannel现在也实现了多网络功能,提供多租户网络隔离。目前这部分处于EXPERIMENTAL阶段,估计也是因为k8s还没有要支持多租户网络场景的原因。由于flannel将网桥创建到Host namespace,多个网络会共享route表,所以应该没法支持多网段重复网段。

image

flannel vxlan

手动配置vxlan

先上例子说明,准备两台机器,三层能通就行,比如

node 物理网卡 ip
node1 enp0s8 10.245.1.3
node2 enp0s8 10.245.1.4

在每台机器上执行下面的脚本,记得修改脚本中的ip地址

function setup_flannel_vxlan() {
    # calculate ip address of bridge device and container veth device
    mask=`echo $node_ip_cidr | cut -d/ -f 2`
    ip_prefix=`echo $node_ip_cidr | cut -d/ -f 1 | awk -F\. '{print $1"."$2"."$3"."}'`
    gateway="${ip_prefix}1"
    br_ip="${gateway}/$mask"
    ctn_ip="${ip_prefix}2/$mask"

    # create vxlan device
    ip link add dev vxlan2 type vxlan id 2 local $vtep_ip dev $eth dstport 4789
    ip link set dev vxlan2 up
    ip addr add $ip_cidr dev vxlan2
    ip link set dev vxlan2 mtu 1450

    ip link add dev br0 type bridge
    ip link set dev br0 up

    ip netns add ctn
    ip li add dev vhost mtu 1450 type veth peer name vctn mtu 1450
    ip li set dev vctn netns ctn
    ip link set vhost up
    ip link set vhost master br0
    ip netns exec ctn ip link set dev vctn up
    ip netns exec ctn ip addr add $ctn_ip dev vctn
    ip netns exec ctn ip route add default via $gateway
    ip addr add $br_ip dev br0
}
//node1上执行
eth=enp0s8
vtep_ip=10.245.1.3
ip_cidr=10.250.1.0/16
node_ip_cidr=10.250.1.0/24
setup_flannel_vxlan

//node2上执行
eth=enp0s8
vtep_ip=10.245.1.4
ip_cidr=10.250.2.0/16
node_ip_cidr=10.250.2.0/24
setup_flannel_vxlan

配置FDB表和ARP表

function setup_fdb_arp() {
    bridge fdb add $dst_vxlan_mac dst $dst_vtep_ip self permanent dev vxlan2
    ip neigh add $dst_ctn_ip lladdr $dst_vxlan_mac dev vxlan2 nud permanent
}

//获取每台机器vxlan2网卡的mac地址
cat /sys/class/net/vxlan2/address
//vxlan2_mac_node1=6a:6d:97:62:16:1f
//vxlan2_mac_node2=82:34:e5:45:4f:7b

//node-1
dst_vxlan_mac=82:34:e5:45:4f:7b
dst_vtep_ip=10.245.1.4
dst_ctn_ip=10.250.2.2
setup_fdb_arp

//node-2
dst_vxlan_mac=6a:6d:97:62:16:1f
dst_vtep_ip=10.245.1.3
dst_ctn_ip=10.250.1.2
setup_fdb_arp

验证vxlan网络

验证vxlan网络,dump enp0s8网卡数据包

[root@kubernetes-node-1 vagrant]# ip netns exec ctn ping -c 3 10.250.2.2
PING 10.250.2.2 (10.250.2.2) 56(84) bytes of data.
64 bytes from 10.250.2.2: icmp_seq=1 ttl=62 time=10.7 ms
64 bytes from 10.250.2.2: icmp_seq=2 ttl=62 time=4.00 ms
64 bytes from 10.250.2.2: icmp_seq=3 ttl=62 time=29.7 ms

--- 10.250.2.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2170ms
rtt min/avg/max/mdev = 4.006/14.825/29.721/10.887 ms

[root@kubernetes-node-1 vagrant]# tcpdump -vv -nn -s 0 -e -i enp0s8 udp port 4789
tcpdump: listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes
03:35:49.722825 08:00:27:57:65:f9 > 08:00:27:c3:83:d5, ethertype IPv4 (0x0800), length 148: (tos 0x0, ttl 64, id 44644, offset 0, flags [none], proto UDP (17), length 134)
    10.245.1.3.47561 > 10.245.1.4.4789: [no cksum] VXLAN, flags [I] (0x08), vni 2
e2:1a:0e:91:0c:fc > c2:88:4e:3c:c7:5c, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 62686, offset 0, flags [DF], proto ICMP (1), length 84)
    10.250.1.2 > 10.250.2.2: ICMP echo request, id 18584, seq 1, length 64
03:35:49.723857 08:00:27:c3:83:d5 > 08:00:27:57:65:f9, ethertype IPv4 (0x0800), length 148: (tos 0x0, ttl 64, id 16670, offset 0, flags [none], proto UDP (17), length 134)
    10.245.1.4.44620 > 10.245.1.3.4789: [no cksum] VXLAN, flags [I] (0x08), vni 2
c2:88:4e:3c:c7:5c > e2:1a:0e:91:0c:fc, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 7781, offset 0, flags [none], proto ICMP (1), length 84)
    10.250.2.2 > 10.250.1.2: ICMP echo reply, id 18584, seq 1, length 64
03:35:50.730172 08:00:27:57:65:f9 > 08:00:27:c3:83:d5, ethertype IPv4 (0x0800), length 148: (tos 0x0, ttl 64, id 45332, offset 0, flags [none], proto UDP (17), length 134)
    10.245.1.3.47561 > 10.245.1.4.4789: [no cksum] VXLAN, flags [I] (0x08), vni 2
e2:1a:0e:91:0c:fc > c2:88:4e:3c:c7:5c, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 63318, offset 0, flags [DF], proto ICMP (1), length 84)
    10.250.1.2 > 10.250.2.2: ICMP echo request, id 18584, seq 2, length 64
^C
6 packets captured
6 packets received by filter
0 packets dropped by kernel

flannel vxlan原理

动态配置ARP表

flannel vxlan的实现基本与脚本一致,只是ARP表不是静态配置的,通过监听内核的ARP消息,动态配置的。

// this enables ARP requests being sent to userspace via netlink
echo 3 > /proc/sys/net/ipv4/neigh/flannel.1/app_solicit

image

processNeighMsg验证收到的ARP查询请求,如果是flannel.1网卡的消息,就发送给misses chan *netlink.Neigh,另一个goroutine循环从misses chan拿消息处理。这里的miss.IP是容器中发起请求的目的IP,即其他容器的IP。判断目的IP的网段是否是etcd上已知的子网,如果是,配置ARP表项(IP:目的容器IP,MAC:目的容器所在主机flannel.1网卡的MAC地址),也就是我们上面脚本中执行的 ip neigh add $dst_ctn_ip lladdr $dst_vxlan_mac dev vxlan2 nud permanent

image

静态配置FDB表

这里的静态是相对于vxlan的l2miss(如果在vxlan的FDB表中找不到目的容器mac地址对应的VTEP地址,将消息通过内核的netlink机制发送到用户态,期望用户态监听并补充FDB表)而言,flannel创建vxlan网卡时并没有开启l2miss,而是通过watch etcd,发现新注册的节点时,注册新节点的VTEP地址到FDB表。代码中PublicIP=主机IP地址,VtepMAC=flannel.1的MAC地址。

image

之所以能采用这种静态的方式,是因为ARP表配置的MAC地址是目的容器所在主机flannel.1网卡的MAC地址,而不是容器的MAC地址。采用静态的方式,在容器还未拉起前就配置好了FDB表,相比触发l2miss再查询的方式更快速。并且又因为FDB表配置的是flannel.1 MAC,所以FDB表的表项等同于集群flannel节点的数,只需要配置更少的表项。

flannel存在etcd的VTEP信息

//网络配置,管理员配置的
[root@kubernetes-master hello]# etcdctl --endpoint=http://10.245.1.2:4379 get /coreos.com/network/config
{"Network":"10.246.0.0/16","SubnetLen":24,"Backend":{"Type":"vxlan","Port":4789,"VNI":3}}

//节点信息
[root@kubernetes-master hello]# etcdctl --endpoint=http://10.245.1.2:4379 ls /coreos.com/network/subnets
/coreos.com/network/subnets/10.246.30.0-24
/coreos.com/network/subnets/10.246.13.0-24
/coreos.com/network/subnets/10.246.78.0-24

//VTEP信息
[root@kubernetes-master hello]# etcdctl --endpoint=http://10.245.1.2:4379 get /coreos.com/network/subnets/10.246.30.0-24
{"PublicIP":"10.245.1.2","BackendType":"vxlan","BackendData":{"VtepMAC":"16:8d:1e:23:70:cf"}}