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-第 2 页.jpg
分成几个部分去介绍

  • 网桥配置
  • 路由表项目
  • vxlan配置
    • mac表项目添加
    • fdb表项目添加
  • 容器命名空间创建和配置:

    • 网络设备添加配置
    • 默认路由配置
    • 接入制定网桥

      2. 容器网络组件所的那些事

      flannel容器网络组件vxlan模式为例子,容器网络所做事情包括
  • 配置容器网络网关一般为网桥, 对应上图br0设备

  • 创建和初始化vxlan设备flannel0
  • 动态更新路由表:
    • 启动获取k8s集群所以节点网段,路由到flannel0设备;
    • 启动以后获取k8s添加、删除节点事件,添加、删除路由条目
  • 动态更新vxlan网络配置: fdb, mac

    2.1 节点上创建容器网络网关

    同一台机器上多个容器网络空间怎么通信,需要有交换机switch,然后把容器网络空间在root network namespace一端veth接入这个设备上。Linux网桥作用类似于网络switch。它会在连接到其上的接口间转发网络包。并且因为它是switch,它是在L2层完成这些转发的。容器网关就是switch角色。
    同样外部网络需要访问容器POD时候,应该转发哪个容器,需要需要通过路由器去转发,这时候作为路由器角色。

    2.1.1 创建容器网关br0

    容器网关是通过网桥实现的,flannel vxlan模式下容器网桥名字为cni0,我们这边用br0代表吧, 网桥是放在根网络空间上的,如图:
    添加容器网关br0-第 1 页.jpg
    在根网络空间创建名字为br0网桥接口
    1. $ ip link add br0 type bridge

    2.1.2 配置容器网关br0

    网桥接口创建以后也和容器接口一样,需要配置ip地址和启动.

    2.1.2.1 配置网关网关IP

    配置容器网关br0ip地址为10.239.0.1/24
    添加容器网关br0-配置IP.jpg
    1. host1$ ip addr add 10.239.0.1/24 dev br0

    2.3.2.2 启动网段接口

    容器网关需要启动以后才可以使用
    1. host1$ ip link set br0 up
    网桥接口启动成功以后,系统自动添加路由策略,这个不需要用户手工去添加。
    添加容器网关br0-startup.jpg

    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网络组件,各个节点vxlanmac地址保存etcd数据库,即使集群节点重新启动以后,agent重建vxlan设备时候在etcd里面读取mac, 配置接口创建参数上。网络组件可以在etcd读取其他节点vxlanmac地址。FDB, ARP表由网络组件去初始化,这样避免ARP风暴问题。
    创建vxlan设备flannel0 ,设备指定vtep端口为8472
    添加vxlan设备.jpg
    1. host1$ ip link add flannel0 type vxlan id 42 dstport 8472 dev eth0 nolearning

    2.2.2 配置vxlan设备IP地址

    配置vxlanIP地址为10.239.0.0/32, 启动vxlan接口:
    配置vxlanIP.jpg
    1. host1$ ip addr add 10.239.0.0/32 dev flannel0
    2. host1$ ip addr link set flannel0 up

    2.3 在节点Host2配置接口

    根据2.1,2.2 步骤在Host2创建,配置,启动br0flannel0接口,配置完毕后如图

2.3.1 获取vxlan接口mac地址

2台机器创建vxlan, br0 完毕以后,需要获取两个节点上vxlan MAC地址,为后面配置vxlan 路由传输作准备

host1 节点上获取flannel0设备mac

  1. host1$ ip a | grep -C 2 flannel0
  2. ...
  3. 24: flannel0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default qlen 1000
  4. link/ether 0e:45:9c:a8:1f:e3 brd ff:ff:ff:ff:ff:ff
  5. inet 10.239.0.0/32 scope global flannel0
  6. valid_lft forever preferred_lft forever
  7. inet6 fe80::c45:9cff:fea8:1fe3/64 scope link
  8. ...

host2配置.jpg
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地址和端口过程。
vxlan隧道路由.jpg
图中路由器其实虚拟出来,真实不存在。在各个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发送。具体怎么样,需要下一节交代。
vxlan路由-配置路由.jpg

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内部投递如图:
容器vxlan世界-第 2 页.jpg
目前只有host1host2两台机器。由于两个vxlan物理接口通信,需要知道对方MAC地址,由于这些接口mac地址存储flannel 制定etchd集群上,都是互相知道的,不需要vxlan之间进行学习。通过手动配置、程序化配置就可以了。

VXLAN 投递表【fdb】, 根据MAC地址,知道对应vxlan接口所在机器和端口。

2.4.2.1 配置vxlan设备ARP

两个设备进行链路层通信,需要知道对端MAC地址,网络组件MAC表由程序化配置的,固定,不用arp协议学习。由于ARP表配置到flannel0设备里面去,下面图深入设备内部配置:
vxlan路由-arp表配置.jpg
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地址关联的。
vxlan路由-fdb.jpg
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

如果测试成功vxlan配置没有问题了。

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,如图:
添加容器命名空间.jpg
如上图创建名字为container1网络命名空间

host1$ ip netns add container1

3.2 为容器添加网络

容器网络空间和主机根网络空间通过虚拟设备veth-pair 进行连接, veth-pair由两个网络接口成对组成,一头接口容器网络空间,另外一头接入根网络空间网桥上, 这个网桥在flannel k8s网络一般命名为cni0。下面是以主机Host1为网络规划为例子,进行配置。

3.2.1 创建接口对

创建veth-pair默认在根网络空间上成对创建的, 创建以后如图:
添加容器-添加接口对.jpg
创建网络接口对veth0->veth0-tmp, tmp一端用于移动到指定网络空间上

host1$ ip link add veth0 type veth peer name veth0-tmp

3.2.2 移动接口到指定网络空间

veth-pair一端名字为veth0-tmp移动到container1网络空间, 如图:
添加容器-移动网络接口到命名空间.jpg

$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,方面用户程序使用。
添加容器-修改容器接口名称.jpg

把网络命名空间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/24ip地址到接口eth0上,eth0是容器默认接口,需要在此接口配置ip地址,如图:
添加容器-配置容器接口IP.jpg

$ 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

添加容器-启动容器网络接口.jpg

# 启动容器命名空间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接口根根网络空间一端接入到网桥接口上,并且启用这个接口。如图:
添加容器-veth0-master-br0.jpg

# 把接口接入到br0上 
$ip link set veth0 master br0

# 启动veth0 接口
$ip link set veth0 up

3.3.2 配置容器空间默认网关

跨网段通信的时候需要通过路由器转发, 容器需要其他网段通信时候需要通过路由转发,需要在容器网络空间中配置默认路由,默认路由地址就是网桥地址

容器和容器网段外的节点通信需要通过br0作为路由器转发,说最终去了br0以后通过root network namespace路由策略进行转发的。

在容器容器1添加默认路由, 路由器地址为br0ip。对应vxlan模式flannel, 数据包通过br0到达根分区TCP/IP栈以后,在根据路由表,跳转本节点vxlan接口,进行隧道封装后在通过网络接口eth0发送数据。如图:

添加容器-添加容器默认路由.jpg
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网桥接口br0ip地址为:

$ 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

完成以后如图:

添加容器-in-Host2.jpg

全部完成后可以两个容器互相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

完成!!!!