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:
-
UDP 模式(性能损失:30%,不建议使用):使用设备 flannel.0 进行封包解包,由于不是 Kernel 原生支持,所以需要频繁地内核态/用户态间切换,性能非常差。默认使用 8285 端口。
-
VxLAN 模式(性能损失:20%):使用 flannel.1 进行封装/解封装,Kernel 原生支持,性能较强。默认使用 8472 端口。要求同一个 Node 下各个 Pods 属于同一个 Subnet,不同 Nodes 下的 Pods 属于不同的 Subnets。
-
host-gw 模式(性能损失:10%):无需 flannel.1 这样的中间设备,直接把 Node 当作 Subnet 的下一跳地址,性能最强。但要求不同的 Nodes 需要处于同一网段,因此不支持跨网络,不适合大规模部署。
-
AWS VPC 模式:使用 Amazon VPC route table 创建路由,适用于 AWS 上运行的容器。
-
GCE 模式:使用 Google Compute Engine Network 创建路由,所有 Instances 需要开启 IP forwarding,适用于 GCE 上运行的容器。
-
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 报文的过程中都需要经过多次的用户态到内核态的数据拷贝,所以性能非常差。
转发流程
- 源容器向目标容器发送数据,数据首先发送给 docker0 网桥,查看源容器的路由表:
$ kubectl exec -it -p {Podid} -c {ContainerId} -- ip route
- docker0 网桥接受到数据后,docker0 的内核协议栈处理程序会读取这个数据包的目标地址,根据目标地址将数据包发送给下一个路由节点。docker0 首先将报文转交给 flannel.1 虚拟网卡处理。查看源容器 Local Node 的路由信息:
$ ip route
- 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。
转发流程:
- 当 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)。
- flanneld Daemon 在收到 container-1 给 container-2 的 IP 报文后,把这个报文直接封装在 VxLAN 报文里,发送给 Node2。
- 每个 Node 的 flanneld Deamon 都监听着 UDP 8285 端口,所以 flanneld 只要把 UDP 发给 Node2 的 8285 端口就行了。
- 最后,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的主要内容,如果未能解决你的问题,请参考以下文章