Kubernetes — Flannel CNI

Posted 范桂飓

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kubernetes — Flannel CNI相关的知识,希望对你有一定的参考价值。

目录

Flannel CNI

Flannel 是 Kubernetes 最成熟、最简单的 CNI,由 CoreOS 推出,是一种 Overlay 网络方案。Flannel 支持通过 Backend 来选择 L3 Overlay 类型。例如:

{
    "Network": "10.0.0.0/8",
    "SubnetLen": 20,
    "SubnetMin": "10.10.0.0",
    "SubnetMax": "10.99.0.0",
    "Backend": {
        "Type": "udp",
        "Port": 7890
    }
}

Flannel 支持以下类型的 Backend:

  1. UDP 模式(性能损失:30%,不建议使用):使用设备 flannel.0 进行封包解包,由于不是 Kernel 原生支持,所以需要频繁地内核态/用户态间切换,性能非常差。默认使用 8285 端口。

  2. VxLAN 模式(性能损失:20%):使用 flannel.1 进行封装/解封装,Kernel 原生支持,性能较强。默认使用 8472 端口。要求同一个 Node 下各个 Pods 属于同一个 Subnet,不同 Nodes 下的 Pods 属于不同的 Subnets。

  3. host-gw 模式(性能损失:10%):无需 flannel.1 这样的中间设备,直接把 Node 当作 Subnet 的下一跳地址,性能最强。但要求不同的 Nodes 需要处于同一网段,因此不支持跨网络,不适合大规模部署。

  4. AWS VPC 模式:使用 Amazon VPC route table 创建路由,适用于 AWS 上运行的容器。

  5. GCE 模式:使用 Google Compute Engine Network 创建路由,所有 Instances 需要开启 IP forwarding,适用于 GCE 上运行的容器。

  6. ALI VPC 模式:使用阿里云 VPC route table 创建路由,适用于阿里云上运行的容器。

Kubernetes 集成 Flannel CNI

在这里插入图片描述

Flannel CNI 会将 Flannel 网络配置转换为 Linux Bridge(cniX/dockerX)网络配置,并调用 Bridge 给容器的 Network Namespace 配置网络。例如:

  • Flannel 网络配置:
{
    "name": "mynet",
    "type": "flannel",
    "delegate": {
        "bridge": "mynet0",
        "mtu": 1400
    }
}
  • Linux Bridge(cniX/dockerX)网络配置:
{
    "name": "mynet",
    "type": "bridge",
    "mtu": 1472,
    "ipMasq": false,
    "isGateway": true,
    "ipam": {
        "type": "host-local",
        "subnet": "10.1.17.0/24"
    }
}

安装 Flannel CNI:

kube-controller-manager --allocate-node-cidrs=true --cluster-cidr=10.244.0.0/16。
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

安装完毕后,会启动 flanneld Daemon,并配置 CNI 网络插件:

$ ps -ef | grep flannel | grep -v grep
root      8632  8611  0 6月16 ?       00:01:55 /opt/bin/flanneld --ip-masq --kube-subnet-mgr

$ cat /etc/cni/net.d/10-flannel.conf
{
  "name": "cbr0",
  "type": "flannel",
  "delegate": {
    "isDefaultGateway": true
  }
}

flanneld Daemon 启动后会自动连接 Kubernetes API,根据 node.Spec.PodCIDR 配置本地的 Flannel Subnet,并为容器创建 VxLAN 设备以及相关的子网路由。

$ cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1410
FLANNEL_IPMASQ=true

$ ip -d link show flannel.1
12: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1410 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/ether 8e:5a:0d:07:0f:0d brd ff:ff:ff:ff:ff:ff promiscuity 0
    vxlan id 1 local 10.146.0.2 dev ens4 srcport 0 0 dstport 8472 nolearning ageing 300 udpcsum addrgenmode eui64

在这里插入图片描述

UDP 模式

网络模型

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

UDP 模式的核心就是通过 Linux TUN 设备 flannel0 来实现。TUN 设备是工作在三层的虚拟网络设备,功能是:在 Kernel 和用户态应用程序之间传递 IP 报文。由于,TUN 设备,仅在发出 IP 报文的过程中都需要经过多次的用户态到内核态的数据拷贝,所以性能非常差。

转发流程

在这里插入图片描述

  1. 源容器向目标容器发送数据,数据首先发送给 docker0 网桥,查看源容器的路由表:
$ kubectl exec -it -p {Podid} -c {ContainerId} -- ip route
  1. docker0 网桥接受到数据后,docker0 的内核协议栈处理程序会读取这个数据包的目标地址,根据目标地址将数据包发送给下一个路由节点。docker0 首先将报文转交给 flannel.1 虚拟网卡处理。查看源容器 Local Node 的路由信息:
$ ip route
  1. flannel.1 接受到数据后,对数据进行封装,并发给 Local Node 的 eth0。

VxLAN 模式

网络模型

在这里插入图片描述

  • flanneld Deamon:在每个 Node 中运行的 Agent 守护进程。它会为 Local Node 从 Cluster 的网络地址空间中获取一个 Subnet 网段。之后 Local Node 内所有 Pods 和 cni0/docker0 的 IP Address 都会将这个 Subnet 中分配。同时,flanneld 会监听 ETCD,为 flannel.1 提供 Overlay 报文封装时必要的 IP/MAC 等网络配置信息。
$ ps -ef | grep flanneld
...
root      8632  8611  0 13:53 ?        00:00:39 /opt/bin/flanneld --ip-masq --kube-subnet-mgr
  • cni0(Bridge 设备):由 flanneld 初始化创建。并且每创建一个 Pod,flanneld 都会创建一对 vEth Pair,其中一端是 Pod 中的 eth0,另一端则接入到 cni0 中。Pod 从 eth0 发出的流量都会发送到 cni0 上。
$ ethtool -i cni0
driver: bridge
version: 2.3
firmware-version: N/A
expansion-rom-version:
bus-info: N/A
supports-statistics: no
supports-test: no
supports-eeprom-access: no
supports-register-dump: no
supports-priv-flags: no
  • flannel.1(VxLAN VTEP 设备):由 flanneld 初始化创建,并分配了 IP/MAC 地址,以及 VNI 1(默认),所以命名为 flannel.1。用于接受来自 cniX/dockerX Bridge 的数据,并完成 VxLAN 报文的封装/解封装。每个 Node 都会有一个 flannel.1 设备,不通 Node 间的 flannel.1 互相作为 VxLAN Tunnel 的 Endpoint。通过维护路由表,对接收到的数据进行转发。
$ ethtool -i flannel.1
driver: vxlan
version: 0.1
firmware-version:
expansion-rom-version:
bus-info:
supports-statistics: no
supports-test: no
supports-eeprom-access: no
supports-register-dump: no
supports-priv-flags: no

分配 IP 地址

flanneld Daemon 第一次启动时,会从 ETCD 获取 Cluster 配置的网段信息,为 Local Node 分配一个未使用的 Subnet 网段。flanneld 将分配给 Local Node 的 Subnet 网段信息写入 /run/flannel/subnet.env 文件。

$ cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.233.64.0/18
FLANNEL_SUBNET=10.233.64.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

cni0 Bridge 会根据 subnet.env Setup 的 ENV 来设置 IP 地址,作为 Subnet Gateway:

8: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default qlen 1000
    link/ether 52:7e:47:3a:c3:7e brd ff:ff:ff:ff:ff:ff
    inet 10.233.64.1/24 brd 10.233.64.255 scope global cni0
       valid_lft forever preferred_lft forever
    inet6 fe80::507e:47ff:fe3a:c37e/64 scope link
       valid_lft forever preferred_lft forever

Local Node 上所有 Pods 的 IP 地址都从这个 Subnet IP Pool 分配,例如:

$ kubectl get pod -o wide | grep kube-cluster-1
...
fluentd-46vts                                            1/1     Running     2          36h     10.233.64.132   kube-cluster-1   <none>           <none>
harbor-harbor-core-69fd799fd4-5b7z8                      1/1     Running     7          31h     10.233.64.106   kube-cluster-1   <none>           <none>

下发路由规则

flanneld Daemon 第一次启动时还会创建 flannel.1 VxLAN 隧道设备,并且 flanneld 还用于创建 Linux Kernel 的路由表。

  • Local Node 内的 Pods 通信时,路由出口为 cni0
$ route -ne
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
...
10.233.64.0     0.0.0.0         255.255.255.0   U         0 0          0 cni0
  • 跨 Nodes 间的 Pods 通信时,路由出口为 flannel.1:其中 Destination 10.244.2.0 为 node2 的目的网段,出口为 flannel.1 完成 Overlay 隧道的封装/解封装。在这里插入图片描述

flanneld Daemon 从 ETCD 中获取到 Overlay 隧道封装的 MAC、IP、Tunnel ID 等网络配置信息。

例如:SourceIP 采用 Local Node IP,DestIP 采用 Remote Node IP,VxLAN 外层的 DestPort 为 UDP Port 8472。隧道的对端只需要监听这个 Port 即可,当该 Port 收到报文后将报文送到 flanned.1 进行解封装,得到原始报文后查询本地路由表,就可以找到 DestIP 的 Interface 为 cni0 了。
在这里插入图片描述

转发流程

在这里插入图片描述

VxLAN 模式中,要求同一个 Node 下各个 Pods 属于同一个 Subnet,不同 Nodes 下的 Pods 属于不同的 Subnets。Subnet 与 Node 的对应关系储存在 ETCD 中,例如:Node1 Subnet 是 100.96.1.0/24,Pod1 是 100.96.1.2。

转发流程:

  1. 当 flanneld Daemon 处理从 Pod 传入到 flannel1 的 IP 报文时,就可以根据 dstIP(e.g. 100.96.2.3)匹配到对应的 Subnet(e.g. 100.96.2.0/24),然后从 ETCD 中找到这个 Subnet 对应的 Node IP(e.g. 10.168.0.3)。
  2. flanneld Daemon 在收到 container-1 给 container-2 的 IP 报文后,把这个报文直接封装在 VxLAN 报文里,发送给 Node2。
  3. 每个 Node 的 flanneld Deamon 都监听着 UDP 8285 端口,所以 flanneld 只要把 UDP 发给 Node2 的 8285 端口就行了。
  4. 最后,Node2 的 flanneld Deamon 再把 IP 报文发送给 flannel1,flannel1 再转发给 container-2。

报文协议栈:
在这里插入图片描述

host-gw 模式

host-gw(host gateway)是一种纯三层的网络方案,性能最高,即:Node 把自己的 Physical Interface 当做 Pods 的 Gateway 来使用,从而使跨 Nodes 上的 Pods 进行通信,这个性能比 VxLAN 高,因为它没有额外开销。不过他有个缺点,就是所有 Nodes 的 Physical Network 必须在同一个 LAN 中。

在这里插入图片描述

howt-gw 模式的工作原理,就是将每个 Flannel Subnet 的下一跳,设置成了该 Subnet 对应的 Node 的 IP 地址。即:Node 充当了这条容器通信路径的 Gateway,这正是 host-gw 的含义。

所有的 Subnet 和 Node 的关系信息,都保存在了 ETCD 中,flanneld Daemon 只需要 Watch 这些数据的变化,并实时更新路由表就行了。核心是 IP 报文在封装成二层数据桢的时候,使用路由表的 “下一跳” 设置上的 MAC 地址,这样可以经过二层网络到达 dst Node。

另外,如果两个 Pods 所在的不同 Nodes 处于在同一个 LAN 中,可以让 VxLAN 也支持 host-gw 的功能,即:直接通过物理网卡的网关路由转发,而不再使用隧道,从而提高了 VxLAN 的性能,这种 Flannel 的功能叫 directrouting。

以上是关于Kubernetes — Flannel CNI的主要内容,如果未能解决你的问题,请参考以下文章

Kubernetes — Flannel

Kubernetes_CNI_01_Calico

Kubernetes_CNI_Calico_04_Calico的网络策略NetworkPolicy

21-K8S Basic-网络插件体系及flannel基础

flannel overlay network

Flannel网络模型