1. 概述
网络组件在vxlan overlay
网络模式下比host-gw
多维护了vxlan设备mac表,以及fdb表,在上一章介绍,这两个表作用是把vxlan mac地址转化为vxlan VTEP地址。由于k8s集群每个节点网络组件需要知道其他节点vxlan地址mac地址。以flannel为例子,
- 添加节点,都把创建vxlan mac地址存放到
etcd
数据库上 - 其他节点更新路由表,更新vxlan设备mac表,fdb表
- 删除节点,其他节点把对应mac,fdb表项目删除,在etcd把对应节点信息删除
为下图为最终配置目标
分成几个部分去介绍
- 网桥配置
- 路由表项目
- vxlan配置
- mac表项目添加
- fdb表项目添加
容器命名空间创建和配置:
配置容器网络网关一般为网桥, 对应上图
br0
设备- 创建和初始化
vxlan
设备flannel0 - 动态更新路由表:
- 启动获取k8s集群所以节点网段,路由到
flannel0
设备; - 启动以后获取k8s添加、删除节点事件,添加、删除路由条目
- 启动获取k8s集群所以节点网段,路由到
- 动态更新
vxlan
网络配置: fdb, mac2.1 节点上创建容器网络网关
同一台机器上多个容器网络空间怎么通信,需要有交换机switch
,然后把容器网络空间在root network namespace
一端veth接入这个设备上。Linux网桥作用类似于网络switch。它会在连接到其上的接口间转发网络包。并且因为它是switch,它是在L2层完成这些转发的。容器网关就是switch角色。
同样外部网络需要访问容器POD时候,应该转发哪个容器,需要需要通过路由器去转发,这时候作为路由器角色。2.1.1 创建容器网关br0
容器网关是通过网桥实现的,flannel vxlan模式下容器网桥名字为cni0
,我们这边用br0
代表吧, 网桥是放在根网络空间上的,如图:
在根网络空间创建名字为br0
网桥接口$ ip link add br0 type bridge
2.1.2 配置容器网关br0
网桥接口创建以后也和容器接口一样,需要配置ip地址和启动.2.1.2.1 配置网关网关IP
配置容器网关br0
ip地址为10.239.0.1/24host1$ ip addr add 10.239.0.1/24 dev br0
2.3.2.2 启动网段接口
容器网关需要启动以后才可以使用
网桥接口启动成功以后,系统自动添加路由策略,这个不需要用户手工去添加。host1$ ip link set br0 up
2.2 节点上创建VXLAN设备
跨节点容器通信,都是通过vxlan
隧道设备发送,这张主要介绍vxlan
设备创建,已经通过路由表把跨节点网络数据包路由到这个设备上。
由于vxlan
作为对端节点跨节点路由下一跳地址,需要有IP地址,通常使用网段保留地址, flannel一般使用最好一位为0的IP地址, 例如10.239.0.0/24
网段机器,vxlan
地址一般使用10.239.0.0/32
。2.2.1 创建vxlan设备
Flannel网络组件,各个节点vxlan
mac地址保存etcd数据库,即使集群节点重新启动以后,agent重建vxlan
设备时候在etcd里面读取mac, 配置接口创建参数上。网络组件可以在etcd读取其他节点vxlan
mac地址。FDB, ARP表由网络组件去初始化,这样避免ARP风暴问题。
创建vxlan设备flannel0
,设备指定vtep端口为8472host1$ ip link add flannel0 type vxlan id 42 dstport 8472 dev eth0 nolearning
2.2.2 配置vxlan设备IP地址
配置vxlan
IP地址为10.239.0.0/32
, 启动vxlan接口:host1$ ip addr add 10.239.0.0/32 dev flannel0
host1$ ip addr link set flannel0 up
2.3 在节点Host2配置接口
根据2.1,2.2 步骤在Host2创建,配置,启动br0
和flannel0
接口,配置完毕后如图
2.3.1 获取vxlan接口mac地址
2台机器创建vxlan, br0 完毕以后,需要获取两个节点上vxlan MAC地址,为后面配置vxlan 路由传输作准备
host1 节点上获取flannel0设备mac
host1$ ip a | grep -C 2 flannel0
...
24: flannel0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether 0e:45:9c:a8:1f:e3 brd ff:ff:ff:ff:ff:ff
inet 10.239.0.0/32 scope global flannel0
valid_lft forever preferred_lft forever
inet6 fe80::c45:9cff:fea8:1fe3/64 scope link
...
host2 节点上获取flannel0设备mac
host2$ ip a | grep -C 2 flannel0
...
19: flannel0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether cc brd ff:ff:ff:ff:ff:ff
inet 10.239.1.0/32 scope global flannel0
valid_lft forever preferred_lft forever
inet6 fe80::f8a3:5fff:fe6a:b6df/64 scope link
...
2.4 配置隧道网络路由
自动获取k8s集群节点添加,删除事件动态修改路由,是flannel
网络组件后台进程主要工作。和host-gw,vxlan 网络路由过程更加复杂,具体来说就是把容器网段->对端vxlan接口路由,转化为对端VTEP地址和端口过程。
图中路由器其实虚拟出来,真实不存在。在各个flannel-agent
都维护一致vxlan的隧道路由,犹如这些vxlan接口之间存在一台路由器,vxlan由于有下面几个表组合而成的:
- root namespace 路由表条目: 路由表添加容器网段条目,告诉主机10.239.x.0 网络由什么路由器转发【网段对应节点vxlan接口】,有主机上vxlan设备发送
- flannel0【vxlan】设备MAC表: 记录各个网段对各个节点上flannel0 MAC地址
- fdb: flannel0投递表,把节点flannel0 MAC地址转化为vxlan VTEP地址:端口号
2.4.1 配置系统路由表
系统路由表作用,把每个agent节点网段段执行对应网络段0地址,例如10.239.1.0/24
地址段,指向对应路由地址10.239.1.0
,使用本地vxlan设备flannel0
传输。其实配置这个段由那台节点vxlan接口接收,这个节点当前系统还不知道怎么去,只是知道由flannel0
发送。具体怎么样,需要下一节交代。
Host1 添加路由条目:
host1$ ip route add 10.239.1.0/24 via 10.239.1.0 dev flannel0 onlink
Host2 添加路由条目:
host2$ ip route add 10.239.0.0/24 via 10.239.0.0 dev flannel0 onlink
2.4.2 配置vxlan隧道路由
按照正常路由配置,host1
上面并没有10.239.1.0/24
对应网段网卡,常理不允许这样配置,由于有onlink
参数,说明这个路由需要在接口里面做路由的。下面配置都是配置vxlan
接口里面生效,不是在系统整个root namespace
有效的。
隧道路由下面两张表组成,每个节点相同配置,当然去除本节点网段, vxlan内部投递如图:
目前只有host1
和host2
两台机器。由于两个vxlan
物理接口通信,需要知道对方MAC地址,由于这些接口mac地址存储flannel
制定etchd集群上,都是互相知道的,不需要vxlan
之间进行学习。通过手动配置、程序化配置就可以了。
VXLAN 投递表【fdb】, 根据MAC地址,知道对应vxlan接口所在机器和端口。
2.4.2.1 配置vxlan设备ARP
两个设备进行链路层通信,需要知道对端MAC地址,网络组件MAC表由程序化配置的,固定,不用arp协议学习。由于ARP表配置到flannel0
设备里面去,下面图深入设备内部配置:
Host1 配置命令:
# host2 flannel0 MAC: fa:a3:5f:6a:b6:df,flannel0 IP: 10.239.1.0/32
host1$ ip neigh add 10.239.1.0 lladdr fa:a3:5f:6a:b6:df dev flannel0
Host2 配置命令:
# host1 flannel0 MAC: 0e:45:9c:a8:1f:e3,flann0 IP: 10.239.0.0/32
host2$ ip neigh add 10.239.0.0 lladdr 0e:45:9c:a8:1f:e3 dev flannel0
2.4.2.2 配置vxlan设备FDB
FDB是vxlan设备投递数据库,配置以后主要确定vxlan外层封包配置,确定对应设备需要发送那个VTEP地址和端口。和ARP表通过MAC地址关联的。
Host1 配置命令
# host2 flannel0 MAC: fa:a3:5f:6a:b6:df,Host2 IP: 172.1.1.3
host1$ bridge fdb append fa:a3:5f:6a:b6:df dev flannel0 dst 172.1.1.3 port 8472
Host2 配置命令
# host1 flannel0 MAC: 0e:45:9c:a8:1f:e3,Host1 IP: 172.1.1.2
host2$ bridge fdb append 0e:45:9c:a8:1f:e3 dev flannel0 dst 172.1.1.2 port 8472
2.4.3 测试隧道
配置完毕vxlan ARP 和 FDB 表以后,两个vxlan设备可以互相通信了。可以通过对端br0地址测试vxlan接口联通性。
测试Host1 flannel0 -> Host2 flannel0 接口测试
host1$ ping 10.239.1.1
测试Host2 flannel0 -> Host1 flannel0 接口测试
host2$ ping 10.239.0.1
3 CNI插件那些事
flannel
负责配置容器在k8s节点之间POD路由。CNI插件是k8s节点kubelet进程在创建/删除POD时调用这个接口,接口只有两个方法,创建Pod网络, 删除Pod网络。
当然创建POD, 使用CNI创建网络。CNI创建POD对应命名空间网络,接口,配置IP地址,POD路由表。IP地址由CNI接口代码分配的。flannel0
使用第三方CNI接口代码,因此只有基本网络功能,没有网络限制。由于每个节点管理一个网络段,所以使用本地数据库存放IP地址分配情况。
当销毁POD,使用CNI销毁对应网络,网络空间,接口对;当然还有IP地址。
3.1 创建容器网络空间
容器网络隔离核心就是网络命名空间,一般容器网络命名空间名和container ID命令空间创建以后,命名空间默认包含lo
接口, 空路由表, 空iptables
,如图:
如上图创建名字为container1
网络命名空间
host1$ ip netns add container1
3.2 为容器添加网络
容器网络空间和主机根网络空间通过虚拟设备veth-pair
进行连接, veth-pair
由两个网络接口成对组成,一头接口容器网络空间,另外一头接入根网络空间网桥上, 这个网桥在flannel k8s网络一般命名为cni0
。下面是以主机Host1为网络规划为例子,进行配置。
3.2.1 创建接口对
创建veth-pair
默认在根网络空间上成对创建的, 创建以后如图:
创建网络接口对veth0
->veth0-tmp
, tmp一端用于移动到指定网络空间上
host1$ ip link add veth0 type veth peer name veth0-tmp
3.2.2 移动接口到指定网络空间
把veth-pair
一端名字为veth0-tmp
移动到container1
网络空间, 如图:
$ip link set dev veth0-tmp netns container1
3.2.3 配置网络空间接口
接口移动到网络命名空间以后, 配置对接口进行一些配置,配置步骤如下
- 修改veth接口名字:
vethx-tmp
->eth0
- veth接口添加ip地址
- 启动网络空间中
eth0
接口和lo
接口: 接口添加以后默认状态是down
上面这些操作都有一个共性,在容器网络命名空间上配置。在网络空间执行命令可以通过这个方式执行
$ ip netns exec [网络空间名字] [cmd] [param1, parm2, ....]
# 例如在网络命名空间 container1 执行 ping 命令
$ ip netns exec container1 ping 127.0.0.1
3.2.3.1 修改网络空间接口名称
网络空间一端veth接口, 在主空间创建时候为了减少名字冲突,使用随机名称,移动到指定命名空间,为了后面配置脚本统一,把 接口随机名字修改eth0
,方面用户程序使用。
把网络命名空间container1
接口veth0-tmp
修改为eth0
$ ip netns exec container1 ip link set veth0-tmp name eth0
3.2.3.2 配置网络空间接口ip地址
主机host1容器网络段是10.239.0.0/24
,其中10.239.0.1/24
留容器网关使用,这里容器1网络空间配置10.239.0.2/24
ip地址到接口eth0
上,eth0
是容器默认接口,需要在此接口配置ip地址,如图:
$ ip netns exec container1 ip addr add 10.239.0.2/24 dev eth0
3.2.3.3 启动网络空间接口
默认情况下,新建接口和容器空间自动创建接口lo
都是没有启动,需要手工启动以后才可以使用。网络空间里面两个接口都需要启动:
eth0
: 需要和外界通信lo
: 回环接口不启动自己ping自己就不通了, ping 127.0.0.1 或者 ping eth0 ip 10.239.0.2
# 启动容器命名空间1 lo接口
$ ip netns exec container1 ip link set lo up
# 启动容器命名空间1 eth0 接口
$ ip netns exec container1 ip link set eth0 up
3.3 把容器网络连接网关上
把容器在根分区一端veth
接口接入到根网络空间交换机上br0
,同一台主机多个容器之间可以通过交换转发数据了。
3.3.1 把接口对一端接入网桥
把veth接口根根网络空间一端接入到网桥接口上,并且启用这个接口。如图:
# 把接口接入到br0上
$ip link set veth0 master br0
# 启动veth0 接口
$ip link set veth0 up
3.3.2 配置容器空间默认网关
跨网段通信的时候需要通过路由器转发, 容器需要其他网段通信时候需要通过路由转发,需要在容器网络空间中配置默认路由,默认路由地址就是网桥地址
容器和容器网段外的节点通信需要通过br0
作为路由器转发,说最终去了br0
以后通过root network namespace
路由策略进行转发的。
在容器容器1添加默认路由, 路由器地址为br0
ip。对应vxlan
模式flannel, 数据包通过br0到达根分区TCP/IP栈以后,在根据路由表,跳转本节点vxlan接口,进行隧道封装后在通过网络接口eth0
发送数据。如图:
Host1 对应网桥地址是:10.239.0.1, 命令如下:
$ ip netns exec container1 ip route add default via 10.239.0.1 dev eth0
3.3.3 测试路由配置正确性
在容器1如果能够ping
通主机上br0接口就正面上门路由配置正确, 当前Host1
网桥接口br0
ip地址为:
$ ip netns exec container1 ping 10.239.0.1
3.4 总结
按照3.1 ~ 3.3 步骤在Host2
创建容器。步骤基本是一样,除了容器IP配置,容器路由配置。
Host2 容器IP配置,由于这台机器使用10.239.1.0/24, 网段地址,所以容易分配10.239.1.2 ~ 10.239.1.254之间分配,例如:
$ ip netns exec container1 ip addr add 10.239.1.2/24 dev eth0
Host2 上容器路由配置命令如下:
$ ip netns exec container1 ip route add default via 10.239.1.1 dev eth0
完成以后如图:
全部完成后可以两个容器互相ping测试
Host1-容器1上测试 Host2-容器1是否连通
host1$ ip netns exec container1 ping 10.239.1.2
Host2-容器1上测试 Host1-容器1是否连通
host2$ ip netns exec container1 ping 10.239.0.2
完成!!!!