从VETH到Kubernetes网络通信剖析

本文背景

从云计算到云原生,计算资源、存储资源和网络通信是核心的三个模块。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模式切换稳定性验证等等。

参考文献

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/fvRGAt1UUSqUl0P3QULd
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券