k8s笔记25-- k8s 跨主机网络flannel
简介
在单机环境下,容器间可以通过 docker0 网桥来通信,但其无法实现不同主机容器之间的通信问题,为了解决不同主机之间容器的通信问题,跨主机网络方案便诞生了。
当前社区提供了近20种网络方案,具体见 Kubernetes 文档/概念/集群管理/安装扩展(Addon);其中,Flannel 是最经典的几种网络方案之一,它是 CoreOS 公司主推的容器网络方案。当前Flannel可以支持很多网络后端, UDP、VXLAN、host-gw是最为经典的3种后端,本文主要通过这3种后端来了解、熟悉K8S跨主机网络方案。
不同机器上网络设备区别
-
有docker无k8s
-
单节点master k8s 节点
注意: master节点pod ip 为 10.244.0.0/24,当新增一个节点后(节点podip为 10.224.1.0/24),master 节点多了一条路由, 它表明访问 10.224.1.0/24 pod 都会从 flannel.1 网络设备出去
-
k8s node 节点
加入集群后,多了一条 flannel.1 的路由,表明访问其它节点上的pod是会从该虚拟网络设备出去;
后续每新加一个节点,现有集群都会多一条相应路由。
3节点的集群,node01核心路由如下:10.244.0.0 10.244.0.0 255.255.255.0 UG 0 0 0 flannel.1 10.244.1.0 0.0.0.0 255.255.255.0 U 0 0 0 cni0 10.244.2.0 10.244.2.0 255.255.255.0 UG 0 0 0 flannel.1
-
除了了解上述设备和路由信息外,还可以通过 brctl show (需要 apt install bridge-utils) 查看当前的机器上的网桥信息;如下图,发现cni0下有3个veth设备,docker0下有1个veth设备,它表明通过K8S起了3个pod,通过docker run起了一个容器。
flannel 网络常见三大后端模式
UDP
flannel官文说明: Use UDP only for debugging if your network and kernel prevent you from using VXLAN or host-gw.
使用udp后端的节点会创建一个 flannel0的TUN设备(Tunnel设备)。
在 Linux 中,TUN 设备是一种工作在三层(Network Layer)的虚拟网络设备。TUN 设备的功能非常简单,即:在操作系统内核和用户应用程序之间传递 IP 包。
安装flannel udp 后端的时候,需要修改kube-flannel.yaml 中的 net-conf.json 和 pod securityContext.privileged: true
......
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "udp",
"Port": 7890
}
}
......
securityContext:
privileged: true
capabilities:
add: ["NET_ADMIN", "NET_RAW"]
......
注意: 不设置privileged: true 会导致flannel pod无法拉起,报如下错误
E0429 02:31:36.131167 1 main.go:317] Error registering network: failed to open TUN device: open /dev/net/tun: no such file or directory
I0429 02:31:36.131328 1 main.go:434] Stopping shutdownHandler...
安装udp后端的单节点 master 设备网络接口和路由如下:
从路由可以看到, 10.224.0.0/24 网段都会经过 cni0, 同一主机网段的pod之间的访问都会从容器网络到 cni0 设备,然后再转发到目标容器中。
pod网段中非本机的网段 10.224.0.0/16 都会经过 flannel0 设备,然后通过flanneld进程封包后抓发到目标机器的udp端口。
基于UDP后端,假设Node1 上的 100.96.1.2 访问Node2上的100.96.2.3 pod, 其通信原理图如下:
Node1 容器->cni0设备->flannel0设备->flanneld 进程将数据封装在UDP包->Node1 eth0->Node2 eth0(8285 udp端口)->flanneld 进程解析出原始数据->flannel0设备->cni0设备->Node 2容器。
和两台主机直接通信相比,Flannel UDP模式多了一个 flanneld 进程的处理,该处理会导致数据多次在用户态和内核态传递。如下图所示:
第一次,用户态的容器进程发出的 IP 包经过 cni0 网桥进入内核态;
第二次,IP 包根据路由表进入 TUN(flannel0)设备,从而回到用户态的 flanneld 进程;
第三次,flanneld 进行 UDP 封包之后重新进入内核态,将 UDP 包通过宿主机的 eth0 发出去。
在 Linux 操作系统中,上述这些上下文切换和用户态操作的代价其实是比较高的,这也正是造成 Flannel UDP 模式性能不好的主要原因。
Flannel 的VXLAN后端使用Linux 内核本身支持的VXLAN网络虚拟化技术,可以完全在内核态实现上述封装和解封装的工作,能明显提高网络性能,这也是Flannel支持的VXLAN网络成为主流容器网络方案的原因。
VXLAN
flannel官文说明: Use in-kernel VXLAN to encapsulate the packets.
Virtual eXtensible Local Area Networking documentation
What Is VXLAN
vxlan 基础:
VXLAN协议是一种隧道协议,旨在解决IEEE 802.1q中限制VLAN ID(4096)的问题。 使用 VXLAN,标识符的大小扩展到 24 位 (16777216)。
VXLAN的特点是将L2的以太帧封装到UDP报文(即L2 over L4)中,并在L3网络中传输。 在三层网络上构建一个逻辑的二层网络。
VXLAN本质上是一种隧道技术,在源网络设备与目的网络设备之间的IP网络上,建立一条逻辑隧道,将用户侧报文经过特定的封装后通过这条隧道转发。
使用flannel后设备路由表、设备信息、fdb转发数据库信息如下:
使用vxlan 后数据包传输过程:
假设 kmaster 节点上的pod1 需要访问 knode01 节点上的pod2
- 容器路由
根据容器路由表,数据从容器的 eth0 发出 - 主机路由
数据包接入到主机网络设备 cni0后, 根据主机路由到 flannel.1 设备,即隧道入口。 - vxlan 封装
加上 vxlan 相关的header,封装成一个普通的数据帧 - 封装为udp包转发到目的机器
从FDB获取目标设备对应的 ip 和 mac,形成一个UDP包并转发出去。 - 数据到达目标机器
数据包到达knode01节点,在内核解封装发现是VXLAN数据包,把它交给flannel.1设备。flannel.1设备则会进一步拆包,取出原始IP包(源容器IP和目标容器IP),通过cni0网桥转发给容器。
host-gw
flannel官文说明: Use host-gw to create IP routes to subnets via remote machine IPs. Requires direct layer2 connectivity between hosts running flannel.
假设Node 1 上的 Infra-container-1,要访问 Node 2 上的 Infra-container-2, 其数据流项图如下所示:
host-gw 模式的工作原理,其实就是将每个 Flannel 子网(Flannel Subnet,比如:10.244.1.0/24)的“下一跳”,设置成了该子网对应的宿主机的 IP 地址;从而让这台主机充当容器网络的网关角色。
如何查看集群用哪种网络模式
以ds 的形式部署在每个节点上
挂载项目:
volumeMounts:
- mountPath: /etc/cni/net.d
name: cni
- mountPath: /etc/kube-flannel/
name: flannel-cfg # configmap
- mountPath: /var/lib/cni
name: ipam
查看方式:
自搭k8s
# kubectl -n kube-flannel get cm kube-flannel-cfg -oyaml
apiVersion: v1
data:
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
kind: ConfigMap
阿里云k8s
# kubectl -n kube-system get cm kube-flannel-cfg -oyaml
apiVersion: v1
data:
cni-conf.json: |
{
"name": "cb0",
"cniVersion":"0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"isDefaultGateway": true,
"hairpinMode": true
},
"dataDir": "/var/run/cni/flannel",
"ipam": {
"type": "host-local",
"dataDir": "/var/run/cni/networks"
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
},
"externalSetMarkChain": "KUBE-MARK-MASQ"
}
]
}
net-conf.json: |
{
"Network": "10.251.0.0/16",
"Backend": {
"Type": "alloc"
}
}
kind: ConfigMap
......
/run/cni/networks/cb0 下面会记录各个节点非ds pod 的ip情况
/run/cni/networks/cb0 # ls
10.251.7.164 10.251.7.165 last_reserved_ip.0 lock
阿里云使用的是 alloc(腾讯使用 tencent-vpc): Alloc performs subnet allocation with no forwarding of data packets.
阿里云flannel容器网络 alloc
阿里云ecs 节点网络设备如下:,
节点上没有 flannel.x 设备,可见不是直接通过vxlan构建大二层网络
节点内pod访问模式:
数据链路 ECS1 Pod1 eth0 -> vethxxx1 -> cni0 -> vethxxx2 -> ECS1 Pod2 eth0
不同节点间pod访问模式:
数据链路 ECS1 pod1 eth0 … -> ECS1 eth0-> 交换机->ECS2 eth0->…> ECS2 pod2 eth0
阿里云每个VPC都会有一个路由表,路由表绑定到该VPC对应的交换机上, 如下图每创建一个交换机的时候都会新增一条路由
集群每次新增一个节点时候,都会在自定义路由中加一条访问该节点上pod的路由
阿里云 flannel 模式缺点:
每加一个节点到集群都会在VPC路由表中加一条路由,导致需要经常让阿里云的同学调整VPC路由条数,目前阿里云最多能支持1500条路由;当改VPC的节点数量达到上限(阿里云也不愿意调整)的时候,只能新建VPC并将新集群部署在新VPC下。
如下图,该VPC下最多能新增78个节点
参考文档
github.com/flannel-io/flannel
扁平网络 Flannel
Documentation/backends.md
k8s(十六): VXLAN和Flannel
KUbernetes Flannel:VXLAN模式
k8s 网络四篇文章
03-K8S网络模型
ACK容器网络数据链路(Flannel)
Virtual eXtensible Local Area Networking documentation
What Is VXLAN
Flannel Networking Demystify