Kubernetes容器网络及Flannel插件详解
theme: smartblue highlight: a11y-dark
持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天, 点击查看活动详情
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三种模式比较:
模式 | 底层网络要求 | 实现模式 | 封包/解包 | overlay网络 | 转发效率 |
Flannel UDP | 三层网络 | overlay | 用户态 | 三层 | 低 |
Flannel VXLAN | 三层网络 | overlay | 内核态 | 二层 | 中 |
Flannel host-gw | 二层网络 | 路由 | 无 | 三层 | 高 |
在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-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/,可以自定义配置。同时在每个节点上都会有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/。/var/lib/cni/networks/文件包含分配 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网络配置及初始化。