本文来自 vivo AI 研究院计算平台在 Kubernetes 网络方案落地过程中的技术积累与经验总结,vivo AI 计算平台打造具备高性能计算能力、弹性调度能力、高性能网络和海量存储的一体化平台。从支撑高性能、大规模分布式训练到 AI 在线业务容器化部署的过程中,底层网络架构经历了 Falnnel 覆盖网络演变成 Calico 扁平网络,Pod 之间通信从使用主机网络通信改造成使用 Pod 地址通信。
本文背景
从云计算到云原生,计算资源、存储资源和网络通信是核心的三个模块。Kubernetes 作为主流容器编排工具,也是云原生提及最多的名词,在计算资源调度与存储资源使用两个方面表现出色。
在网络方面,由于容器架构层次深,网络通信过程复杂,Kubernetes 对网络通信支持不尽完美,云计算领域普遍存在这个问题。尽管如此,Kubernetes 网络表现出很灵活,提供 CNI 模式给用户使用第三方插件自定义网络。该自定义网络依赖传统网络,或者说与传统网络融合。
摘要
Pod 是 Kubernetes 最小的原子调度单位,Kubernetes 上的服务应用跑在 Pod 里面。从业务迁移 Kubernetes 的角度来看,是应用从物理主机或者虚拟主机迁移部署到 Pod 的过程。
要保证 Pod 上的应用正常提供服务,之前在物理主机需要解决的问题,现在 Pod 里面也要解决:机房内 Pod 中服务可以相互通信;Pod 网络 IP 地址分配;机房外可以访问 Pod 中服务;有高可用方式来访问 Pod 中服务;对进出 Pod 中网络流量管控等等。
1 虚拟化网络
在 Pod 中运行着应用的是一组 Docker 容器,而 Docker 使用的核心技术是 Linux 虚拟化[1]。所以,虚拟化网络是 Kubernetes 网络通信一个重要基础。
01 网络隔离
在 Linux 内核中有 Namespace(命名空间)的概念,其作用是资源隔离。同一个命名空间资源共享,不同命名空间资源相互独立。Docker 使用这种隔离技术,使容器自身看起来是一个独立的操作系统,拥有属于自己 6 个命名空间下的资源:Mount、UTS、IPC、PID、Network、User。
其中 Network 是网络命名空间,用来隔离网络设备,拥有独立的网络协议栈以及网络资源。以下运行容器进程 PID=5146,拥有自己独立的命名空间资源,[4026532000]是我们关注的网络空间。
02 虚拟网卡对
网络与通信是离不开的,单个网络空间即使有独立网络资源,但并不能与其他网络空间或者主机网络进行通信。Veth Pair 是虚拟网络技术中常用的一对虚拟以太网卡,一端在当前空间充当网卡设备,另一端在其他空间充当网卡设备或者是主机网络上的虚拟网卡设备。
Veth Pair 打通了不同网络空间的通信隔离,但是网络资源上不同空间仍然独立。容器 id=[482218a11ab7]网卡 eth0 与主机网卡 veth972d941 使用 Veth Pair 连接,设备 if34 与设备 if35 建立 link 链接。
Veth Pair 工作在同一主机下,两个虚拟设备端到端连接。多设备连接,Linux 网络虚拟化实现了 Bridge 虚拟网桥设备,逻辑上的交换设备,有多个端口连接多个虚拟设备。
在不同主机中网络命名空间通信场景,数据报文从一台主机上的网络空间到另外一台主机的网络空间。除了路由的方案,还能在通信网络空间之间创建隧道网络。虚拟设备 tun(tunnel,隧道)一边连着用户态程序,一边连着内核态的网络协议栈。
03 自定义网络
从网路命名空间 1 流出的数据报文,经过 tunnel 设备完成隧道报文封装,回到主机网络协议栈,由主机网络设备发送给目标网络命名空间所在主机网络设备。
网络命名空间上添加虚拟设备,在主机内可以创建局部虚拟网络拓扑。甚至在不同主机之间,结合已有的物理网络可以创建隧道网络。在 Linux 网络虚拟化技术支持下,可以灵活创建网络,同时,网络链路也变长加深。
2 Docker 网络
Docker 容器拥有自己独立的网络空间,容器创建过程中按照定义的网络模式,进行网络配置初始化,添加特定功能的虚拟网络设备。常用的 Docker 网络模式有 Bridge、Host、Link、None4 种。
Bridge:桥接网络,上面提及 Bridge 虚拟设备,功能与 Veth Pair 类似,网络流量从设备一端流入,在另外一端流出,但是 Bridge 设备支持多个端口,像是物理设备上的交换机。
Docker 初始化网络环境过程,在容器的网络命名空间,新建了一个虚拟的以太网卡 eth0,并且绑定 eth0 在 VethPair 上一端,Veth Pair 另外一端与 docker0 连接。docker0 是主机上 Docker 服务创建的 Bridge 网桥,使用 Bridge 方式的容器默认会连接到这个网桥,用户也可以自定义网桥。
数据报文从 Veth Pair 一端进入 docker0,在上面执行路由判断,决定是流向其他容器还是转发到主机的网卡,再流向主机以外,使用 NAT 网络地址端口转发,修改源地址为流出网卡地址,向目的地址发出去。
Host:主机网络,容器加入到主机的网络命名空间,与主机共享网络协议栈和网络资源,主机容器数量较大时,容易造成资源冲突,不易于管理。
Link:共享网络,容器加入到其他容器的网络命名空间,与其他容器共享网络协议栈和网络资源。
None:不使用 Docker 提供的几种网络模式,启动的容器只有一个 lo 网卡(回环网络),用户可以自定义网络方式。
Macvlan 技术在主机网卡上创建多个子网卡,并且分配不同的 IP 地址和 MAC 地址,每个子网卡相互隔离,并且分配给容器使用。
Overlay 在原有的网络拓扑结构上创建一层虚拟的通信网络,上面提到的隧道网络是一个 Overlay 例子。
3 Kubernetes 网络
Kubernetes 网络主要解决 Pod 网络 IP 地址分配和网络通信的问题,在创建 Pod 过程中,Kubelet 支持 CNI(Container Network Interface)插件,为 Pod 定义网络。同理,在 Pod 删除过程中,由 CNI 接口销毁网络。常用的 CNI 插件有:Flannel[2]、Calico[3]、Waeve 等等。
01 Flannel 网络
最早实现 CNI 标准的网络插件,架构简单明了。在集群节点上运行 flanneld 常驻进程,监听获得 Etcd 中设置的网段,修改 Docker 启动参数中网段配置,确定节点上 Pod 的网络地址范围,并且对进出 Pod 的数据报文做封包解包。
Etcd 集群保存 Kubernetes 集群节点划分的网络信息,节点 flanneld 从 Etcd 中拉取数据,并且生成路由信息,内容为访问某个 Pod 地址,从 flannel0 设备出去,下一跳是 Pod 所在的主机地址。
其实 Flannel 常用 Overlay、VXLAN、Host-gw 几种模式。Overlay 模式数据报文经过 flannel0 虚拟 tunnel 设备(一端连接协议栈一端连接用户态进程),flanneld 对报文封装成 UDP 报文,转给协议栈处理,最后由当前节点网卡发送到目的节点网卡。同理接收端流程,数据报文由 flanneld 对 UDP 报文解包,转给网络协议栈发给目的 Pod 网卡。
VXLAN 模式数据报文传输过程与 Overlay 模式相似,但是报文不再经过 flanneld 封包解包,flannel0 网卡替换成 flannel.1 的 VXLAN 网卡,在 flannel.1 上完成内核态内核 VXLAN 封包解包,不再流向用户态处理,提升效率。Linux 内核 3.7 开始支持 VXLAN,内核 3.12 完备 VXLAN 功能,所以在内核 3.7 以前系统不支持使用 VXLAN。
Host-gw 模式效率最高,以主机网卡作为出口网关,跨节点通信使用路由表完成,要求集群节点都在同一个网络内,限制了集群规模,并且路由表规则数量过多也会影响性能。
02 Calico 网络
Calico 性能较好,功能较全面的 CNI 网络插件。与 Flannel 大有不同,集群外部可以直接访问 Pod 的网络地址。在架构上分四个部分:Felix、BGP Client、中心节点、Etcd。
Felix 与 BGP Client 运行在集群节点,负责路由和 ACL 规则配置,以及通过 BGP(Border Gateway Protocol)协议[4]同步路由到集群其他节点;Etcd 保存网络数据,提供 watch 机制协同组件交互;Route Reflector(中心节点)是集群规模较大时使用的路由交换中心,负责集中式路由分发。
Calico 基于 BGP 路由协议支持扁平网络,也支持 Overlay 的方式。BGP 路由模式 Pod 中数据报文从虚拟的 eth0 流向虚拟网卡 calixxx,两个虚拟网卡使用 Veth Pair 连接起来。报文来到主机内核网络协议栈并且匹配路由表,判断同主机的 Pod 访问,为是则转发到对应 calixxx 虚拟网卡,为否则转发到目的 Pod 所在主机的 IP 地址,并且从 eth0 网卡出去。
有时候,机房网络不支持 BGP 模式,例如开启源和目的地址检测等,使用 Overlay 的模式代替。基于 IPIP 的隧道网络,与前面 Flannel Overlay 网络不同,IPIP 是在内核层完成报文封装。同样,Pod 数据报文出来,同节点访问是一样的链路。而不同节点通过 tunl0 虚拟网卡进行节点主机源和目的地址的 IP 头部封包,再由 eth0 发送出去。
IPIP 隧道,比常规的数据报文多了一层 IPIP 的网络层数据包头,报文进入内核网络协议栈,解析到第一层 IP 头部,发现协议是 IPIP,即进行第二次 IP 协议解析,拿到原始的 IP 报文。
CNI 在地址管理上有三种分配 IP 地址的方式,Kubernetes 注解方式、CNI 配置以及基于节点选择器的 IP 池。Calico 网络常用方式为集群节点基于节点选择器的 IP 池,为集群节点打上不同的网络标示 Label,把 Pod 的网络分配给相应的节点。
同节点 pod 链路,Pod 中的 eth0 连通 Veth Pair 绑定在节点主机上的虚拟网卡,进入内核路由选择,流向另外一个 Pod 的 Veth Pair 端口。
跨节点 pod 链路,Pod 中的 eth0 连通 Veth Pair 绑定在节点主机上的虚拟网卡,进入内核路由选择,或者进行封包,经过 eth0 流向目的主机,然后逆向前面的过程。
外部到 pod 链路,通过 Ingress、Service 的 NodePort、公网 IP 绑定 LoadBalance 几种方式
4 Kubernetes Service
Kubernetes 引入了 Service 的概念做负载均衡,通过 Label 匹配的方式绑定后端 Pod 的 IP 地址作为 Endpoints,从而提高服务稳定性与可用性。Service 实现的负载均衡操作由集群节点上的 Kube-Proxy 来完成。实现了 ClusterIP、NodePort、LoadBalance 三种方式。
ClusterIP 使用集群 IP 地址的方式表示 Service,访问该地址就会负载均衡到 Label 匹配的 Pod。NodePort 使用端口映射的方式,集群内全部节点暴露端口,做网络地址端口映射到后端的 Pod。LoadBalance 需要接入负载均衡服务。
Kube-Proxy 分别在用户空间、Iptables、IPVS 三种模式下实现 Service 的功能。Iptables 是常用的一种,IPVS 性能较好,也要依赖 Iptables。
01 Netfilter 钩子
Netfliter[5]是 Linux 内核网络数据报文处理子模块,IPVS 与 Iptables 内核层实现在 Netfliter 上钩子处理各自定义函数。
在 5 个地方创建钩子:PRE、IN、OUT、FWD、POST,分别对应 Netfliter 数据解析流中:流入主机、进入本机、流出本机、转发、流出主机。这里,进出主机指无论数据报文是否发给当前主机 IP 地址,数据报文都经过当前主机,进出本机指当前数据报文以本机 IP 地址作为目的地址。
图片来源:Netfilter 官网
02 Iptables 模式
Iptables 有 5 张表,按照执行先后顺序为(Raw\Mangle\NAT\Filter\Security)表除了执行顺序还有一个作用是进行规则归类。Raw 做链路跟踪(与上图 Conntrack 对应),Mangle 修改 IP 数据报文,NAT 做数据报文地址转发,Filter 做数据报文匹配过滤,Security 是安全规则匹配。有 5 条链对应 Netfilter 上 5 个钩子,在链上定义规则,转发成 Netfilter 内核处理函数:PREROUTING\INPUT\FORWARD\OUTPUT\POSTROUTING
Kube-Proxy 使用 Iptables:app-vs-pre 对外暴露 NodePort=30035 服务,通过节点访问 30035 端口或者 ClusterIP 访问 10086 端口,转发到 NAT 表上面定义:随机转发概率均等(0.50000000000)的流量转发规则,并且目的地址修改成 app-vs-pre Service 后端 Endpoint。
03 IPVS 模式
IPVS 与 Iptables 不同,只在 IN、FWD、POST 3 个点挂钩子,支持多种负载均衡方式(NAT、TUN、DR)和调度算法。Service 中 NodePort 方式数据报文经过本机 LOCAL_IN 链,ip_vs_in 将其转到 POSTROUTING 进行目的地址端口转发,指向真实的 Endpoint。
图片来源:Interaction between LVSand Netfilter (iptables)
Kube-Proxy 使用 IPVS 示例:Kube-Proxy 为 IPVS 创建 dummy 网卡,默认命名为 kube-ipvs0,将所有新建 Service 的 ClusterIP 绑定到该网卡,上面提到 IPVS 的 DNAT 钩子挂载在 LOCAL_IN 生效,所以 ClusterIP 要被认为是本机 IP 地址才会走到这一步。
以下 my-Nginx 的 ClusterIP(10.144.18.2)绑定在 kube-ipvs0,后端设置地址为 19.18.112.102,19.18.15.42 的两个 Endpoint。ipvsadm 工具查询 IPVS 规则列表,10.144.18.2:80 作为 VIP 负载两个后端 Endpoint,RR 轮询算法并且做 NAT 地址端口转换。
5 Kubernetes Ingress
工作在 HTTP\HTTPS 层,可以做外部访问内部入口。Kubernetes 提供原生的 Controller,也可以自定义其他的 HTTP 负载均衡插件。原生 Ingress Controller 基于 Nginx 转发,配置多个 Service 域名转发到 Service 的 ClusterIP。Ingress 可以绑定 Service 的 NodePort 暴露或者通过公网 IP 映射暴露给外部访问。
以下是 Nginx 官方[6]提供作为 Ingress Controller 服务转发示例,Controller 部署后通过 client-go 的 watch 机制感知 Service 的 Endpoint 变化,实时设置 Nginx 服务中转发配置,HTTP 请求会根据不同的 hostname,按照 Nginx 转发配置,重定向到后端 Pod 的服务。
总结展望
回顾虚拟网络,Linux 虚拟化技术中命名空间进行资源隔离,包括网络隔离。不同命名空间网络相互独立,使用虚拟以太网卡对 Veth Pair,相互独立的网络命名空间可以相互通信。Bridge 网桥技术支持多个端口,同时支持多个隔离空间通信。命名空间与 cgroup 在 Docker 容器中占重要地位,命名空间使得 Docker 像独立的操作系统运行。
Kubernetes 中最小原子调度单位,由一组 Docker 容器组成,在网络方面共享网络命名空间。Kubernetes 提供 CNI 插件支持用户灵活自定义网络,使用路由、覆盖网络等技术,实现 Pod 的通信。
后续工作,Kubernetes CNI 插件中:Flannel、Calico 等广泛应用到生产环境,在网络可用性、网络性能和网络功能上,仍然存在部分有待开展的工作。网络质量监控方案调研与落地;Ingress 网关方案调研与落地;Service 中 LoadBalance 方式基础原理与实现过程;Kube-Proxy Service 模式切换稳定性验证等等。
参考文献
个人介绍:
梁大钊,曾就职于百度、启明星辰等公司,目前是 vivo AI 研究院计算平台组的资深工程师,参与平台中训练、调度、网络等方向建设。
评论