概述
- 阿里云 K8S 集群网络目前有两种方案
- 一种是 flannel 方案
- 另外一种是基于 calico 和弹性网卡 eni 的 terway 方案
- Terway 和 flannel 类似,不同的地方在于
- terway 支持 Pod 弹性网卡,以及 NetworkPolicy 功能
- 这里,我们以 flannel 为例,分析阿里云 K8S 集群网络的实现方法
- 我们会从两个角度去分析
- 一个是网络的搭建过程
- 另外一个是基于网络的通信
- 我们的讨论基于当前的 1.12.6 版本
- 总体上来说,阿里云 K8S 集群网络配置完成之后
- 如下图,包括集群 CIDR,VPC 路由表,节点网络,节点的 podCIDR,节点上的虚拟网桥 cni0,连接 Pod 和网桥的 veth 等部分

- 类似的图,大家可能在很多文章中都看过,但是因为其中相关配置过于复杂,比较难理解
- 这里我们可以把这些配置,分三种情况来理解:集群配置,节点配置以及 Pod 配置
- 与这三种情况对应的,其实是对集群网络 IP 段的三次划分:
- 首先是集群 CIDR
- 接着为每个节点分配 podCIDR(即集群 CIDR 的子网段)
- 最后在 podCIDR 里为每个 Pod 分配自己的 IP

集群网络搭建
- 集群的创建,基于云资源 VPC 和 ECS,在创建完 VPC 和 ECS 之后
- 我们基本上可以得到如下图的资源配置
- 我们得到一个 VPC,这个 VPC 的网段是 192.168.0.0/16
- 我们得到若干 ECS,他们从 VPC 网段里分配到 IP 地址

- 在以上出初始资源的基础上,我们利用集群创建控制台得到集群 CIDR
- 这个值会以参数的形式传给集群节点 provision 脚本,并被脚本传给集群节点配置工具 kubeadm
- kubeadm 最后把这个参数写入集群控制器静态 Pod 的 yaml 文件 kube-controller-manager.yaml

- 集群控制器有了这个参数,在节点 kubelet 注册节点到集群的时候
- 集群控制器会为每个注册节点,划分一个子网出来,即为每个节点分配 podCIDR
- 如上图,Node B 的子网是 172.16.8.1/25,而 Node A 的子网是 172.16.0.128/25
- 这个配置会记录到集群 node 的 podCIDR 数据项里
节点阶段
- 经过以上集群阶段,K8S 有了集群 CIDR,以及为每个节点划分的 podCIDR
- 在此基础上,集群会下发 flanneld 到每个阶段上,进一步搭建节点上,可以给 Pod 使用的网络框架
- 这里主要有两个操作,第一个是集群通过 Cloud Controller Manager 给 VPC 配置路由表项
- 路由表项对每个节点有一条
- 每一条的意思是,如果 VPC 路由收到目的地址是某一个节点 podCIDR 的 IP 地址
- 那么路由会把这个网络包转发到对应的 ECS 上
- 第二个是创建虚拟网桥 cni0,以及与 cni0 相关的路由
- 这些配置的作用是,从阶段外部进来的网络包,如果目的 IP 是 podCIDR,则会被节点转发到 cni0 虚拟局域网里
- 注意:实际实现上,cni0 的创建,是在第一个使用 Pod 网络的 Pod 被调度到节点上的时候
- 由 flannal cni 创建的,但是从逻辑上来说,cni0 属于节点网络,不属于 Pod 网络,所以在此描述

Pod 阶段
- 在前边的三个阶段,集群实际上已经为 Pod 之间搭建了网络通信的干道
- 这个时候,如果集群把一个 Pod 调度到节点上,kubelet 会通过 flannel cni 为这个 Pod
本身创建网络命名空间和 veth 设备 - 然后,把其中一个 veth 设备加入到 cni0 虚拟网桥里,并为 Pod 内的 veth 设备配置 ip 地址
- 这样 Pod 就和网络通信的干道连接在了一起
- 这里需要强调的是,前一节的 flanneld 和这一节的 flannel cni 完全是两个组件
- flanneld 是一个 daemonset 下发到每个节点的 pod,它的作用是搭建网络(干道)
- 而 flannel cni 是节点创建的时候,通过 kubernetes-cni 这个 rpm 包安装的 cni 插件,其被 kubelet 调用,用来为具体的 pod 创建网络(分枝)
- 理解这两者的区别,有助于我们理解 flanneld 和 flannel cni 相关的配置文件的用途
- 比如 /run/flannel/subnet.env,是 flanneld 创建的,为 flannel cni 提供输入的一个环境变量文件
- 又比如 /etc/cni/net.d/10-flannel.conf,也是 flanneld pod(准确的说,是 pod 里的脚本 install-cni)从 pod 里拷贝到节点目录,给 flannel cni使用的子网配置文件

通信
- 以上完成 Pod 网络环境搭建
- 基于以上的网络环境,Pod 可以完成四种通信:
- 本地通信,同节点 Pod 通信,跨节点 Pod 通信
- 以及 Pod 和 Pod 网络之外的实体通信

- 其中本地通信,说的是 Pod 内部,不同容器之前通信
- 因为 Pod 内网容器之间共享一个网络协议栈,所以他们之间的通信,可以通过 loopback 设备完成
- 同节点 Pod 之间的通信,是 cni0 虚拟网桥内部的通信,这相当于一个二层局域网内部设备通信
- 跨节点 Pod 通信略微复杂一点,但也很直观,发送端数据包,通过 cni0 网桥的网关,流转到节点上,然后经过节点 eth0 发送给 VPC 路由,这里不会经过任何封包操作
- 当 VPC 路由收到数据包时,它通过查询路由表,确认数据包目的地,并把数据包发送给对应的 ECS 节点
- 而进去节点之后,因为 flanneld 在节点上创建了真的 cni0 的路由,所以数据包会被发送到目的地的 cni0 局域网,再到目的地 Pod
- 最后一种情况,Pod 与非 Pod 网络的实体通信,需要经过节点上 iptables 规则 做 snat
- 而此规则就是 flanneld 依据命令行 --ip-masq 选项做的配置
总结
- 以上是阿里云 K8S 集群网络的搭建和通信原理
- 我们主要通过网络搭建和通信两个角度去分析 K8S 集群网络
- 其中网络搭建包括初始阶段,集群阶段,节点阶段以及 Pod 阶段
- 这么分类有助于我们理解这些复杂的配置。而理解了各个配置,集群通信原理就比较容易理解了