作者:余凯
本系列文章由余凯执笔创作,联合作者:阿里云云原生应用平台 谢石 对本文亦有贡献
前言
近几年,企业基础设施云原生化的趋势越来越强烈,从最开始的 IaaS 化到现在的微服务化,客户的颗粒度精细化和可观测性的需求更加强烈。容器网络为了满足客户更高性能和更高的密度,也一直在高速的发展和演进中,这必然对客户对云原生网络的可观测性带来了极高的门槛和挑战。为了提高云原生网络的可观测性,同时便于客户和前后线同学增加对业务链路的可读性,ACK 产研和 AES 联合共建,合作开发 ack net-exporter 和云原生网络数据面可观测性系列,帮助客户和前后线同学了解云原生网络架构体系,简化对云原生网络的可观测性的门槛,优化客户运维和售后同学处理疑难问题的体验 ,提高云原生网络的链路的稳定性。
鸟瞰容器网络,整个容器网络可以分为三个部分:Pod 网段,Service 网段和 Node 网段。这三个网络要实现互联互通和访问控制,那么实现的技术原理是什么?整个链路又是什么,限制又是什么呢?Flannel, Terway 有啥区别?不同模式下网络性能如何?这些,需要客户在下搭建容器之前,就要依据自己的业务场景进行选择,而搭建完毕后,相关的架构又是无法转变,所以客户需要对每种架构特点要有充分了解。比如下图是个简图,Pod 网络既要实现同一个ECS的Pod间的网络互通和控制,又要实现不同 ECS Pod 间的访问, Pod 访问 SVC 的后端可能在同一个 ECS 也可能是其他 ECS,这些在不同模式下,数据链转发模式是不同的,从业务侧表现结果也是不一样的。
本文是[全景剖析容器网络数据链路]第一部分,主要介绍 Kubernetes Flannel 模式下,数据面链路的转转发链路,一是通过了解不同场景下的数据面转发链路,从而探知客户在不同的场景下访问结果表现的原因,帮助客户进一步优化业务架构;另一方面,通过深入了解转发链路,从而在遇到容器网络抖动时候,客户运维以及阿里云同学可以知道在哪些链路点进行部署观测手动,从而进一步定界问题方向和原因。
系列二:
全景剖析阿里云容器网络数据链路(二):Terway ENI
系列三:
全景剖析阿里云容器网络数据链路(三):Terway ENIIP
系列四:
全景剖析阿里云容器网络数据链路(四):Terway IPVLAN+EBPF
系列五:
全景剖析阿里云容器网络数据链路(五):Terway ENI-Trunking
系列六:
全景剖析阿里云容器网络数据链路(六):ASM Istio
Flannel 模式架构设计
Flannel 模式下,ECS 只有一个主网卡 ENI,无其他附属网卡,ECS 和节点上的 Pod 与外部通信都需要通过主网卡进行。ACK Flannel 会在每个节点创建 cni0 虚拟网卡作为 Pod 网络和 ECS 的主网卡 eth0 之间的桥梁。
集群的每个节点会起一个 flannel agent,并且会给每个节点预分配一个 Pod CIDR,这个 Pod CIDR 是ACK集群的 Pod CIDR 的子集。
容器的网络命名空间内会有一个 eth0 的虚拟网卡,同时存在下一跳指向该网卡的路由,该网卡会作为容器和宿主内核进行数据交换的出入口。容器和宿主机之间的数据链路是通过 veth pair 进行交换的,现在我们已经找到 veth pair 其中一个,如何去找另一个 veth 呢?
如上图所示,我们可以容器的网络命名空间中通过 p addr 看到一个 eth0@if81 的标志位,其中 ‘81’ 这个将会协助我们在 ECS 的 OS 内找到找到和容器网络命名空间中的 veth pair 相对一个。在 ECS OS 内我们通过 ip addr | grep 81: 可以找到 vethd7e7c6fd 这个虚拟网卡,这个就是 veth pair 在 ECS OS 侧相对的那一个。
到目前为止容器内和 OS 数据链路已经建立链接了,那么 ECS OS 内对于数据流量是怎么判断去哪个容器呢?通过 OS Linux Routing 我们可以看到,所有目的是 Pod CIDR 网段的流量都会被转发到 cni0 这张虚拟网卡,那么 cni0 是通过 bridge 方式将不同目的的数据链路指向到不同的 vethxxx。到这里为止,ECS OS 和 Pod 的网络命名空间已经建立好完整的出入链路配置了。
Flannel 模式容器网络数据链路剖析
针对容器网络特点,我们可以将 Flannel 模式下的网络链路大体分为以 Pod IP 对外提供服务和以 SVC 对外提供服务两个大的 SOP 场景,进一步细分可以拆分到 10 个不同的小的 SOP 场景。
对这 10 个场景的数据链路梳理合并,这些场景可以归纳为下面 5 类典型的场景:
- Client 和服务端 Pod 部署于同一个 ECS
- Client 和服务端 Pod 部署于不同 ECS
- 访问 SVC External IP, ExternalTrafficPolicy 为 Cluster 时,Client 和服务端 Pod 部署于不同ECS,其中client为集群外
- 访问 SVC External IP, ExternalTrafficPolicy 为 Local 时, Client 和服务端 Pod 部署于不同 ECS,其中client为集群内
- 访问 SVC External IP, ExternalTrafficPolicy 为 Local 时, Client 和服务端 Pod 部署于不同 ECS,其中 client 为集群外
场景一:Client和服务端Pod部署于同一个ECS
此场景包含下面几个子场景,数据链路可以归纳为一种:
- 以 Pod IP 对外提供服务,Client 和 Pod 部署于同一个节点;
- 以 SVC ClusterIP 对外提供服务,Client 和 SVC 后端 Pod 部署于同一节点;
- 以 SVC ExternalIP 对外提供服务,ExternalTrafficPolicy 为 Cluster/Local 情况下,Client 和 SVC 后端 Pod 部署于同一节点
环境
ap-southeast-1.10.0.0.180 节点上存在两个pod:centos-67756b6dc8-rmmxt IP地址172.23.96.23 和 nginx-7d6877d777-6jkfg 和172.23.96.24
内核路由
centos-67756b6dc8-rmmxt IP地址172.23.96.23,该容器在宿主机表现的 PID 是503478,该容器网络命名空间有指向容器 eth0 的默认路由
该容器 eth0 在 ECS OS 内对应 veth pair 是 vethd7e7c6fd
通过上述类似的办法,可以找到nginx-7d6877d777-6jkfg IP地址172.23.96.24,该容器在宿主机表现的 PID 是2981608,该容器 eth0 在 ECS OS 内对应veth pair是vethd3fc7ff4
在 ECS OS 内,有指向 Pod CIDR,下一跳为 cni0 的路由,以及 cni0 中有两个容器的vethxxx 网桥信息
小结:可以访问到目的端
▲数据链路转发示意图
▲内核协议栈示意图
- 数据链路:ECS1 Pod1 eth0 -> vethxxx1 -> cni0 -> vethxxxx2 -> ECS1 Pod2 eth0
- 数据链路要经过三次内核协议栈,分别是 Pod1 协议栈, ECS OS 协议栈 和 Pod2 协议栈
场景二:Client 和服务端 Pod 部署于不同 ECS
此场景包含下面几个子场景,数据链路可以归纳为一种:
- 以 Pod IP 对外提供服务,Client 和 Pod 部署于不同节点;
- 以 SVC ClusterIP 对外提供服务,Client和SVC 后端Pod部署于不同节点;
- 以 SVC ExternalIP 对外提供服务,ExternalTrafficPolicy 为 Cluster 情况下,集群内 Client 和 SVC 后端 Pod 部署于不同节点;
环境
ap-southeast-1.10.0.0.180 节点上存在两个 pod:centos-67756b6dc8-rmmxt IP地址172.23.96.23 和 nginx1-76c99b49df-7plsr IP 地址172.23.96.163
Service nginx1 的 ExternalTrafficPlicy 为 Cluster
内核路由
Pod 网络空间和 ECS OS 网络空间的数据交换在 2.1 场景一中已经做了详细描述,此处不再果断篇幅描述。
源端 Pod 所在 ECS 的 IPVS 规则
可以看到,源端数据链路访问 svc 的clusterip 192.168.13.23 时,如果链路到达 ECS 的OS 内,会命中 ipvs 规则,被解析到 svc 的后端 endpoint 之一(本实例中只有一个 pod,所以 endpoint 只有一个)
小结:可以成功访问到目的端
▲数据链路转发示意图
VPC 路由表会自动配置目的地址是 pod CIDR, 下一跳为 POD 网段所归属的 ECS 的自定义路由条目,该规则由 ACK 管控测通过 openapi 调用 VPC 去配置,无需手动配置和删除
▲内核协议栈示意图
Conntack 表信息(访问 SVC 情况)
Node1:
src是源pod IP,dst是svc的ClusterIP,并且期望是由svc的其中一个endpoint 172.23.96.163 来回消息给源端 pod
Node2:
目的 pod 所在 ECS 上 conntrack 表记录是由源端 pod 访问目的 pod,不会记录 svc 的clusterip 地址
- 数据链路:ECS1 Pod1 eth0 -> vethxxx1 -> cni0 -> ECS 1 eth0 -> VPC -> ECS2 eth0 -> cni0 -> vethxxxx2 -> ECS2 Pod2 eth0
- 数据链路要经过四次内核协议栈,分别是 Pod1 协议栈, ECS1 OS 协议栈 , ECS2 OS 协议栈和 Pod2 协议栈
- VPC 路由表会自动配置目的地址是 pod CIDR, 下一跳为 POD 网段所归属的 ECS 的自定义路由条目,该规则由 ACK 管控测通过 openapi 调用 VPC 去配置,无需手动配置和删除
- 如果访问的 SVC 的 cluster IP,或者是 Cluster 模式下,访问 SVC 的 externalIP,数据链路通过veth pair进到 ECS OS 内后,会命中相应的 IPVS 规则,并根据负载规则,选择IPVS的某一个后端,进而打到其中的一个 SVC 的后端 endpoint,SVC 的 IP 只会再 PoD 的 eth0 , veth pair vethxxx 被捕捉到,其他链路环节不会捕捉到 svc 的 IP
场景三:ExternalTrafficPolicy 为 Local时,Client 和服务端 Pod 部署于集群内不同 ECS
此场景包含下面几个子场景,数据链路可以归纳为一种:
1.以SVC ExternalIP对外提供服务,ExternalTrafficPolicy 为 Local情况下,集群内 Client 和 SVC 后端 Pod 部署于不同节点;
环境
ap-southeast-1.10.0.0.180 节点上存在两个pod:centos-67756b6dc8-rmmxt IP地址172.23.96.23 和 nginx1-76c99b49df-7plsr IP 地址172.23.96.163
Service nginx1 ExternalTrafficPolicy 为 Local
内核路由
Pod 网络空间和 ECS OS 网络空间的数据交换在 2.1 场景一中已经做了详细描述,此处不再果断篇幅描述。
源端 Pod 所在 ECS 的 IPVS 规则
可以看到,源端数据链路访问 svc 的 externalip 8.219.164.113 时,如果链路到达 ECS 的OS内,会命中ipvs规则,但是我们可以看到EcternalIP 并没有相关的后端 endpoint,链路达到 OS 后,会命中 IPVS 规则,但是没有后端 pod,所以会出现 connection refused
小结:不可以访问到目的端,此时会访问失败,Connection refused
▲数据链路转发示意图
▲内核协议栈示意图
- 数据链路:ECS1 Pod1 eth0 -> vethxxx1 ->
- 数据链路要经过一次半内核协议栈,分别是 Pod1 协议栈,半个 ECS1 OS 协议栈
- 如果访问的 SVC 的 External IP,或者是 Local 模式下,访问 SVC 的 externalIP,数据链路通过 veth pair 进到 ECS OS内后,会命中相应的 IPVS 规则,但是由于 Local 模式,External IP的 IPVS 为空,所以命中规则但是无转发后端,整个链路会在 ipvs 终止,访问失败。所以建议集群内采用 clusterip 访问,这也是 k8s 官方推荐的最佳实践。
场景四:ExternalTrafficPolicy 为 Local 时,Client 来自于集群外
此场景包含下面几个子场景,数据链路可以归纳为一种:A.访问SVC External IP, ExternalTrafficPolicy 为 Local时, Client 和服务端 Pod 部署于不同 ECS,其中 client 为集群外
环境
Deployment为nginx1, 分别为三个pod nginx1-76c99b49df-4zsdj和nginx1-76c99b49df-7plsr 部署在 ap-southeast-1.10.0.1.206 ECS上,最后一个pod nginx1-76c99b49df-s6z79 部署在其他节点 ap-southeast-1.10.0.1.216 上
Service nginx1 的ExternalTrafficPlicy 为 Local
内核路由
Pod 网络空间和 ECS OS 网络空间的数据交换在 2.1 场景一中已经做了详细描述,此处不再果断篇幅描述。
SLB 相关配置
从 SLB 控制台,可以看到 SLB 后端的虚拟服务器组中只有两个 ECS 节点ap-southeast-1.10.0.1.216 和 ap-southeast-1.10.0.1.206。集群内的其他节点 ,比如 ap-southeast-1.10.0.0.180 并未被加到 SLB 的后端虚拟服务器组中。虚拟服务器组的 IP 为 ECS 的 IP,端口为 service 里面的 nodeport 端口 32580.
故 ExternalTrafficPolicy 为 Local 模式下,只有有 Service 后端 pod 所在的 ECS 节点才会被加入到 SLB 的后端虚拟服务器组中,参与 SLB 的流量转发,集群内的其他节点不参与 SLB 的转发
SLB 虚拟服务器组 ECS 的 IPVS 规则
从SLB的虚拟服务器组中的两个 ECS 可以看到,对于 nodeip+nodeport 的 ipvs转发规则是不同。ExternalTrafficPolicy 为 Local 模式下,只有该节点上的护短 pod 才会被加到该节点的 ipvs 转发规则中,其他节点上的后端 pod 不会加进来,这样保证了被 SLB 转发的链路,只会被转到该节点上的 pod,不会转发到其他节点上。
node1:ap-southeast-1.10.0.1.206
node1:ap-southeast-1.10.0.1.216
小结:可以访问到目的端
▲数据链路转发示意图
该图示意了只有后端 pod 所在 ECS 才会加到 SLB 后端中,从集群外部访问 SVC 的externalIP(SLB IP)的情况,可见数据链路只会被转发到虚拟服务器组中的 ECS,不会再被转发到集群内其他节点上。
▲内核协议栈示意图
Conntack 表信息
Node:
src 是集群外部客户端IP,dst 是节点 IP,dport 是 SVC 中的 nodeport。并且期望是由该 ECS 上的 pod 172.23.96.82 来回包给源端
- 数据链路:client -> SLB -> ECS eth0 + ECS nodeport -> cni0 -> vethxxxxx -> ECS1 Pod1 eth0
- 数据链路要经过两次内核协议栈,分别是 Pod1 协议栈, ECS1 OS 协议栈
- ExternalTrafficPolicy 为 Local 模式下,只有有 Service 后端 pod 所在的 ECS 节点才会被加入到SLB的后端虚拟服务器组中,参与 SLB 的流量转发,集群内的其他节点不参与 SLB 的转发
场景五:ExternalTrafficPolicy 为 Cluster 时,Client 来自于集群外
此场景包含下面几个子场景,数据链路可以归纳为一种:
1.访问 SVCExternal IP, ExternalTrafficPolicy 为 Cluster 时, Client 和服务端 Pod部署于不同 ECS,其中 client 为集群外
环境
Deployment为nginx1, 分别为三个pod nginx1-76c99b49df-4zsdj和nginx1-76c99b49df-7plsr 部署在 ap-southeast-1.10.0.1.206 ECS上,最后一个pod nginx1-76c99b49df-s6z79 部署在其他节点 ap-southeast-1.10.0.1.216 上
Service nginx2 的 ExternalTrafficPlicy 为 Cluster
内核路由
Pod 网络空间和 ECS OS 网络空间的数据交换在 2.1 场景一中已经做了详细描述,此处不再果断篇幅描述。
SLB 相关配置
从SLB 控制台,集群内所有节点ap-southeast-1.10.0.0.180、ap-southeast-1.10.0.1.216 和 ap-southeast-1.10.0.1.206 都被加到SLB的虚拟服务器组中。其中 虚拟服务器组的 IP 为 ECS 的 IP,端口为 service 里面的 nodeport 端口 30875.
故 ExternalTrafficPolicy 为 CLuster 模式下,集群内所有的 ECS 节点都会被加入到 SLB 的后端虚拟服务器组中,参与 SLB 的流量转发。
SLB 虚拟服务器组 ECS 的 IPVS 规则
从SLB的虚拟服务器组中的可以看到,对于 nodeip+nodeport 的 ipvs 转发规则是一致的。ExternalTrafficPolicy为 CLuster 模式下,所有的 service 后端 pod 都会被加到所有节点的 ipvs 的转发规则中,即使是该节点有后端 pod,流量也不一定会被转发到该节点上 pod,可能会被转发到其他节点上的后端 pod。
node1:ap-southeast-1.10.0.1.206 (该节点有后端pod)
node1:ap-southeast-1.10.0.1.216 (该节点有后端pod)
node3: ap-southeast-1.10.0.0.180 (该节无后端pod)
小结:可以访问到目的端
▲数据链路转发示意图
该图示意了集群内所有 ECS 都会被加到 SLB 后端中,从集群外部访问 SVC 的externalIP(SLB IP)的情况,数据流量可能会被转发到其他节点上
内核协议栈示意图:
内核协议栈示意图已经在 2.4 场景一中已经做了详细描述,此处不再果断篇幅描述。
Conntack 表信息
链路1:
ap-southeast-1.10.0.0.180:
此时数据链路对应示意图中的链路1,可以看到数据链路被转到 ap-southeast-1.10.0.0.180 节点,该节点上并没有 service 的后端 pod,通过 conntrack 信息,可以看到
src 是集群外部客户端 IP,dst 是节点 IP,dport 是 SVC 中的 nodeport。并且期望是172.23.96.163 来回包给 10.0.0.180 。通过前述信息,可以得知172.23.96.163 是nginx1-76c99b49df-7plsr pod,部署在 ap-southeast-1.10.0.1.206
ap-southeast-1.10.0.1.206:
通过此节点conntrack 表,可以看到src是 node ap-southeast-1.10.0.0.180,dst 是 172.23.96.163的80 端口,回包也是直接回给 node ap-southeast-1.10.0.0.180.
综上可以看到 src 变换了多次,故在 CLuster 模式下,会存在丢失真实客户端 IP 的情况
链路2:
src 是集群外部客户端 IP,dst 是节点IP,dport 是 SVC 中的 nodeport。并且期望是由该 ECS 上的 pod 172.23.96.82 来回包给 172.23.96.65,此地址是 SLB 集群中的一个地址
- 数据链路:情景一:client -> SLB -> ECS eth0 + ECS nodeport -> cni0 -> vethxxxxx -> ECS1 Pod1 eth0情景二:client -> SLB -> ECS1 eth0 + ECS1 nodeport -> VPC Routing -> ECS2 eth0 + pod port -> cni0 -> vethxxxxx -> ECS2 Pod1 eth0
- 数据链路要经过三次内核协议栈,分别是 ECS1 OS、 ECS2 OS 协议栈 和 Pod 协议栈
- ExternalTrafficPolicy 为 CLuster 模式下,kubernetes 所有 ECS 节点都会被加入到 SLB 的后端虚拟服务器组中,参与 SLB 的流量转发,此时会存在数据路在集群内被多个 ECS 转发的场景,该情况下会丢失真实客户端IP的情况
总结
本篇文章主要聚焦 ACK 在 Flannel 模式下,不同 SOP 场景下的数据链路转发路径。随着微服务化和云原生化,网络场景日趋复杂,作为 kubernetes 原生的网络模型——Flannel,不同的访问环境,一共可以分为10个 SOP 场景。通过深入简出的剖析,可以归纳为5个场景,并对这五个场景的转发链路,技术实现原理,云产品配置等一一梳理并总结,这对我们遇到 Flannel 架构下的链路抖动、最优化配置,链路原理等提供了初步指引方向。接下来将进入到阿里自研的 CNI 接口 Terway 模式,也是目前线上集群使用最多的模式,下一系列我们先带来 Terway ENI 模式的全景解析——ACK 全景剖析阿里云容器网络数据链路(二):Terway ENI。
点击此处了解阿里云容器服务