全景剖析阿里云容器网络数据链路(一):Flannel
作者:余凯
本系列文章由余凯执笔创作,联合作者:阿里云云原生应用平台 谢石 对本文亦有贡献
前言
近几年,企业基础设施云原生化的趋势越来越强烈,从最开始的 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。
点击此处了解阿里云容器服务
版权声明: 本文为 InfoQ 作者【阿里巴巴云原生】的原创文章。
原文链接:【http://xie.infoq.cn/article/2df076a852112cb91c9c59576】。文章转载请联系作者。
评论