上一篇文章中我们以nginx-ingress-controller-service为例子,主要介绍了集群中node port类型的cluster ip实现原理,当然是基于iptable的nat的模式,也就是说利用OS的网络内核来完成负载均衡。在这里我们主要介绍集群中的网络通讯,在以前文章中介绍过,对于容器之间的网络通讯基本分为两种,underlay方式和overlay方式。underlay方式在通讯过程中没有额外的封包,通常将容器的宿主作为路由来实现数据包的转发。overlay方式在通讯过程中有额外的封包,当然这里只是学习,具体生产环境中使用哪种方式要根据自己的实际情况和需求来。

我们在之前文章里采用的是基于flannel的underlay网络方式,所以这里主要介绍flannel underlay网络,以之前文章中安装的nginx-app为例:

  • nginx-app的service名称为service-nginx-app

  • 这个service有2个endpoints, 分别对应2个pod

  • 一个pod的ip是10.1.79.3,在host机器172.20.11.43上

  • 一个pod的ip是10.1.27.4,在host机器172.20.11.42上

  • 我们分析数据包(例如curl http://10.1.27.4 from 10.1.79.3)如何从pod 10.1.79.3到pod 10.1.27.4

kubectl describe service service-nginx-app -n default
kubectl get pods -o wide -n default|grep nginx-app

数据包从源pod到宿主

当在pod 10.1.79.3里向pod 10.1.27.4里发送数据包的时候,根据以前文章,pod 10.1.79.3的网卡是veth的一个端点。根据pod network namespace中的路由规则,数据一定是发送到10.1.79.1,也是就是宿主network namespace的docke0 linux bridge设备。由于pod 10.1.79.3网卡veth另一个端点attahe在docker0 bridge设备上,所以数据被docker0 bride接收,也就是数据从pod的network namesapce流动到了host的network namespace里。

数据包在源pod宿主中的路由

由于数据包的目标ip地址是10.1.27.4,而例子中pod 10.1.79.3的宿主ip是172.20.11.43。我们在宿主机上开启了转发功能(net.ipv4.ip_forward = 1),所以主机发现目标ip10.1.27.4不是自己的ip时候,就对这个数据包做路由转发。我们查看宿主172.20.11.43的路由表:

ip addr|grep 43
route -n

在路由表里发现10.1.27.0/24网段的数据下一跳是172.20.11.42,也就是pod10.1.27.4的宿主机器。所以进行arp目标mac地址封包,将数据发往172.20.11.42。这里注意一下,目标pod的下一跳地址是目标pod所在的host,也就是说数据会从原始pod所在的host通过下一跳发往目标pod所在的host。弦外之音,原始pod的host必须和目标pod的host在同一个二层网络里,因为只有这样才可以下一跳路由可达。当然,这个也是flannel的underlay网络host gw方式的限制,既要求所有的k8s worker node节点都在同一个二层网络里(也可以认为是在同一个ip子网里)。

数据包在目标pod宿主中的路由

当数据包路由到目标pod10.1.27.4的host172.20.11.4的时候(通过二层交换),目标pod宿主机上开启了转发功能(net.ipv4.ip_forward = 1),所以主机发现目标ip10.1.27.4不是自己的ip时候,就对这个数据包做路由转发。我们查看宿主172.20.11.42的路由表:

ip addr|grep 72.20.11.42
route -n

在路由表里发现10.1.27.0/24网段的数据下一跳是直连路由,并且由设备docker0转发。到这里我们就非常熟悉了,根据以前文章docker0(10.1.27.1)作为linux bridge,会把数据通过veth pair从host network namespace发送到目标pod的10.1.27.4的network namespace里。然后由内核交给应用程序处理,从而完成了pod到pod的通讯。

我们可以利用以前文章里的kubectl-debug进入pod10.1.79.3,然后利用traceroute命令来观察数据包从10.1.79.3到10.1.27.4的经过节点。

kubectl-debug deployment-nginx-app-69b6bbfd6d-4b89m
ip addr
traceroute 10.1.27.4

所以综上总结,对于underlay(flannel host gw方式),数据包:

  • 从源pod的network namespace到host network namespace的docker0 linux bridge上。

  • 在源pod所在的host里做三层路由选择,下一跳地址为目标pod所在的host。

  • 数据包从源pod所在的host发送到目标pod所在的host。

  • 在目标pod所在的host里做三层路由选择,本地直连路由到目标pod里。

  • 要求所有的worker node必须开启路由转发功能(net.ipv4.ip_forward = 1)。

  • 要求所有的worker node都在同一个二层网络里,来完成目标pod所在host的下一跳路由。

目前先写到这里,下一篇文章里我们继续介绍k8s集群网络中基于ipvs的service实现方式。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐