Kubernetes 容器网络及 Flannel 插件详解
1.1. 容器网络基础
Kubernetes 是一个开源容器调度编排引擎,管理大规模容器化应用,采用典型的 Master-Worker 主从分布式技术架构,由集中式管理节点(Master Node),分布式的工作节点(Worker Node)组成。向下屏蔽底层差异化的分布式基础设施,以应用为中心构建云计算的基础操作系统能力(即云原生操作系统),面向用户提供云原生时代的云计算的新界面。
其中,Kubernetes 网络至关重要,如果管理节点是控制大脑,运行节点是执行载体,那么网络就是将整个容器集群打通形成一个整体的神经网络;与 Docker 网络相比,Kubernetes 网络最大的特点就是让容器组(Pod)拥有自己的身份证,即独立 IP,实现在任何节点上的 Pod 都可以互相直接通信,而不需要任何的 NAT 地址转换;在不做限制时,Pod 可以访问任何网络,并且拥有独立的网络栈,集群内部可见地址与外部可见地址保持一致。
在容器网络的具体实现上,Kubernetes 通过开放的 CNI 标准,以插件化方式引入多种容器网络实现,从而支持各种差异化的场景的需求;当前社区比较常见的网络插件主要有 Flannel、Calico、Cilium、OVN 等,每个插件有不同的模式,需要按照实际的场景来选择使用。按照 POD 通信方式,有同主机的容器通信与跨主机的容器通信两大类型。
(一)同主机的容器通信
在 kubernetes 集群的节点上,会创建一个 veth(virtual ethernet)虚拟设备,同时将 veth 一端插入到容器网络的命名空间中,一端连接到主机上的网桥(linux bridge)。这样在同一主机上的 POD 通过 veth 实现 IP 地址相互通信。网桥也会分配一个 IP 地址,充当从 POD 到不同节点的出口流量网关。
(二)跨主机的容器通信
在不同主机上运行的容器 POD 通过 IP 地址相互通信,需要通过网络插件实现,按照依赖底层的技术大致可以分为 Overlay 模式,路由模式,Underlay 模式三大类:
① Overlay 模式是在二层或三层网络之上再构建起来一个独立的网络,这个网络通常会有自己独立的 IP 地址空间、交换或者路由的实现。VXLAN 协议是目前最流行的 Overlay 网络隧道协议之一,显著优势就是灵活,对底层网络没有侵入性。
② 路由模式放弃了跨主机容器在 L2 的连通性,而专注于通过路由协议提供容器在 L3 的通信方案;路由模式更易于集成到现在的数据中心的基础设施之上,便捷地连接容器和主机,并在报文过滤和隔离方面有着更好的扩展能力及更精细的控制模型。
③ Underlay 模式是借助驱动程序将宿主机的底层网络接口直接暴露给容器使用的一种网络构建技术,较为常见的解决方案有 MAC VLAN、IP VLAN 和直接路由等。
1.2. Flannel 网络插件
Flannel 是由 go 语言开发,是一种基于 overlay 网络的跨主机容器网络插件。Flannel 插件为集群中所有节点重新规划 IP 地址的分配规则,使得不同节点上的容器能够在同一个子网内,且 IP 地址不重复,实现不同节点上的容器通过内网 IP 直接通信。Flannel 目前支持 udp、vxlan、host-gw、aws-vpc、gce 和 alloc 路由等多种灵活模式。但 Flannel 缺少必要的安全隔离,Qos 等能力,适合常见简单,安全隔离要求较低的场景。以下是 Flannel 三种模式比较:
在 Flannel VXLAN 模式时,插件会在 kubernetes 集群的每个节点上创建 vxlan 设备与路由表。发往不同主机上容器数据包都会通过 vxlan 设备,封装成 UDP 数据发往目的地;在目的主机上,封装的 UDP 数据包会被解压并路由到目标 POD。但是具体到 POD 如何分配 IP,以及整个容器生命周期过程中 CNI 插件与其他组件怎么交互配合,是一个相当复杂的过程,本文通过 Flannel 插件,以及容器运行时 Containerd,详细的说明容器网络运行的全过程。
1.3. Kubelet、Container Runtime 和 CNI 插件
1.3.1. Pod IP 地址分配机制
当节点首次向集群注册时,nodeipam 作为选项传递给 kube-controller-manager 的--controllers 参数,控制器就会从集群 CIDR(集群网络的 IP 范围)中为每个节点分配一个专用子网(podCIDR)。由于节点子网 podCIDR 保证不会重复,所以在为节点 POD 分配的 IP 地址也确保了唯一;如果需要更改节点的 podCIDR,可以通过取消注册节点再重新注册实现。使用以下命令列出节点的 podCIDR:
$ kubectl get no <nodeName> -o json | jq '.spec.podCIDR'
10.244.0.0/24
1.3.2. Pod 启动时网络配置过程
当一个 POD 被调度到节点时,会有很多初始化操作,以下是网络配置相关的配置及初始化过程详细步骤:
① POD 被调度到容器集群的某个节点上
② 节点的 kubelet 通过调用 CRI 插件来创建 POD
③ CRI 插件创建 POD Sandbox ID 与 POD 网络命名空间
④ CRI 插件通过 POD 网络命名空间和 POD Sandbox ID 来调用 CNI 插件
⑤ CNI 插件配置 POD 网络,调用顺序从 Flannel CNI 插件,Bridge CNI 插件到主机 IPAM CNI 插件,最后返回 POD IP 地址。
⑥ 创建 Pause 容器,并将其添加第三步创建的 POD 的网络命名空间
⑦ Kubelet 调用 CRI 插件拉取应用容器镜像
⑧ 容器运行时 containerd 拉取应用容器镜像
⑨ Kubelet 调用 CRI 插件来启动应用容器
⑩ CRI 插件调用容器运行时 containerd 来启动和配置在 pod cgroup 和 namespaces 中的应用容器。
1.3.3. CRI 插件与 CNI 插件的交互
在 POD 初始化过程中,CRI 插件会调用 CNI 插件来完成对 POD 网络的配置。CNI 网络插件都会在 kubernetes 节点上安装配置代理,并附带 CNI 配置,然后 CRI 插件通过该配置来确定要调用那个 CNI 插件。CNI 配置文件的位置默认值为/etc/cni/net.d/<config-file>,可以自定义配置。同时在每个节点上都会有 CNI 插件,CNI 插件位置默认值为/opt/cni/bin,也可以自定义配置。在 containerd 作为容器运行时,CNI 配置和 CNI 插件二进制文件的路径可以在 containerd 配置的[plugins."io.containerd.grpc.v1.cri".cni]中指定。
对于 Flannel 插件,Flanneld 作为 Flannel 守护进程,通常安装在 kubernetes 集群的节点上,而 install-cni 作为 init 容器,在每个节点上创建 CNI 配置文件-/etc/cni/net.d/10-flannel.conflist。Flanneld 创建 vxlan 设备,从 API-SERVER 获取并监听 POD 网络元数据。在创建 POD 时,为整个集群中的所有 POD 分配路由,通过路由实现 POD IP 地址相互通信。CRI 插件与 CNI 插件之间的交互如下:
① Kubelet 通过 CRI 插件调用容器运行时
② CRI 插件通过 CNI 配置文件调用 CNI 插件,为 POD 创建网络命名空间,网络命名空间在/var/run/netns/文件下
③ Flannel CNI 插件配置和调用 Bridge CNI plugin
④ Bridge CNI 插件在主机上创建 cniO 网桥,并创建了 veth 对,一端插入容器网络命名空间,另一端连接到 cniO 网桥。然后调用已配置的 IPAM 插件。
⑤ host-local IPAM 插件返回容器的 IP 地址和网关(cniO 桥作为容器的网关),将 IP 地址分配给 POD,桥接插件将网关 IP 地址分配给 cniO 桥接。所有分配的 IP 地址都存储在本地磁盘上的/var/lib/cni/networks/cni0/目录下。
1.3.4. CNI 插件间的交互
CNI 插件配置 POD 网络,调用顺序从 Flannel CNI 插件,Bridge CNI 插件到主机 IPAM CNI 插件,最后返回 POD IP 地址。详情如下:
① Flannel CNI 插件:Containerd CRI 插件使用 CNI 配置文件- /etc/cni/net.d/10-flannel.conflist 调用 Flannel CNI 插件:
$ cat /etc/cni/net.d/10-flannel.conflist
{
"name": "cni0",
"plugins": [
{
"type": "flannel",
"delegate": {
"ipMasq": false,
"hairpinMode": true,
"isDefaultGateway": true
}
}
]
}
当 Flanneld 启动时,会从 API-SERVER 获取 podCIDR 和其他与网络相关的详细信息,并存储在 -/run/flannel/subnet.env 文件中:
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=false
Flannel CNI 插件使用/run/flannel/subnet.env 中的信息来配置和调用网桥 CNI 插件。
② Bridge CNI 插件:Flannel CNI 插件调用 Bridge CNI 插件,配置如下:
{
"name": "cni0",
"type": "bridge",
"mtu": 1450,
"ipMasq": false,
"isGateway": true,
"ipam": {
"type": "host-local",
"subnet": "10.244.0.0/24"
}
}
首次调用 Bridge CNI 插件时,会创建一个 linux 网桥,并在配置文件中指定“名称”:“cni0”。 然后为每一个 POD 创建 veth 对,veth 对一端位于容器的网络命名空间中,另一端连接到主机网络的网桥上,使用 Bridge CNI 插件,主机上的所有容器都连接到主机网络的网桥上。
配置 veth 对后,Bridge 插件调用主机本地 IPAM CNI 插件,可以在 CNI config CRI 插件中配置使用具体 IPAM 插件。
③ 主机本地 IPAM CNI 插件:Bridge CNI 插件使用以下配置调用主机本地 IPAM CNI 插件:
{
"name": "cni0",
"ipam": {
"type": "host-local",
"subnet": "10.244.0.0/24",
"dataDir": "/var/lib/cni/networks"
}
}
Host-local IPAM(IP 地址管理)插件从子网返回容器的 IP 地址,并将分配的 IP 本地存储在主机上 dataDir 下指定的目录下- /var/lib/cni/networks/<network-name= cni0>/<ip>。 /var/lib/cni/networks/<network-name=cni0>/<ip>文件包含分配 IP 的容器 ID。
调用时,主机本地 IPAM 插件返回以下结果:
{
"ip4": {
"ip": "10.244.4.2",
"gateway": "10.244.4.3"
},
"dns": {}
}
1.4. 总结
总体来说,容器网络是 Kubernetes 最复杂部分,同时也是设计精华所在,通过 CNI 标准开放,根据实际需求实现不同的 CNI 来满足差异化的需求,尤其是与底层基础云能力深度集成的场景,每个云厂商都有实现各自高效的网络插件。整个容器网络大致从以下三个方面理解:
首先,在整体设计上,让 POD 有唯一的 IP 地址,通过为每个节点分配一个子网,从而保证节点为 POD 分配的 IP 地址,在整个集群内部不会重复。
其次,在容器网络是实现上,通过 CNI 将容器网络标准与具体实现解耦分类,通过插件引入不同的容器网络插件。
最后,在容器 POD 创建时,通过 Kubelet,CRI 插件,以及 CNI 插件相互配合,实现容器 POD 网络配置及初始化。
关注“巨子嘉”,巨子出品,必属精品
版权声明: 本文为 InfoQ 作者【巨子嘉】的原创文章。
原文链接:【http://xie.infoq.cn/article/1a45c8eca65710ed8c4ca331f】。文章转载请联系作者。
评论