一、Linux网络收包
要了解kube-proxy如何实现负载均衡,要先了解Linux网络收包机制,kube-proxy利用Linux的内核实现的负载均衡。
在TCP/IP网络分层模型里,整个协议栈被分成了物理层、链路层、网络层,传输层和应用层。物理层对应的是网卡和网线,应用层对应的是我们常见的Nginx,FTP等等各种应用。Linux实现的是链路层、网络层和传输层这三层。内核对更上层的应用层提供socket接口来供用户进程访问。我们用Linux的视角来看到的TCP/IP网络分层模型是下面这个样子的。
从硬件的视角看:
当网卡上收到数据以后,首先会以DMA的方式把网卡上收到的帧写到内存里。再向CPU发起一个中断,以通知CPU有数据到达。当CPU收到中断请求后,会去调用网络驱动注册的中断处理函数。 网卡的中断处理函数并不做过多工作,发出软中断请求,然后尽快释放CPU。ksoftirqd检测到有软中断请求到达,调用poll开始轮询收包,收到后交由各级协议栈处理。最后数据包会被放到用户socket的接收队列中。
二、Netfilter 框架
Netfilter 是 Linux 内核数据包处理框架。该框架在Linux处理数据包的关键流程中定义了一系列Hook钩子点(如PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING),并在这些钩子点中注册一系列函数对数据包进行处理。换句话说,这些函数的处理结果决定了这些网络数据包的“命运”。
- PREROUTING:数据包进入路由表之前。
- INPUT:通过路由表后目的地为本机。
- FORWARD:通过路由表后,目的地不为本机。
- OUTPUT:由本机产生,向外发送。
- POSTROUTIONG:发送到网卡接口之前。
Netfilter在网络层上拦截和处理来自网络的数据包,根据配置的规则进行过滤和操作,然后将数据包交由上层协议栈进行进一步的处理和传递。
三、Iptables
Netfilter是Linux内核中的一个框架,而iptables是一个用户空间的工具,用于配置和操作Netfilter的规则。iptables利用Netfilter的hook函数,根据配置的规则来处理网络数据包。通过这种方式,Netfilter和iptables共同协作实现了Linux中的网络过滤和操作功能。
四、kube-proxy工作原理
kube-proxy利用iptables命令生成一些规则,来达到dnat的作用。
4.1 iptables示例
定义了一个service和分别对应三个pod,并通过命令查看本机iptable配置规则:
iptables-save -t nat
配置的意思是:
有一条KUBE-SERVICES的规则,如果数据包的目标ip是10.105.164.239,那么转到KUBE-SVC-WWRFY3PZ7W3FGMQW规则。
KUBE-SVC-WWRFY3PZ7W3FGMQW以33%的概率转到192.168.166.164:80,以50%概率转到192.168.166.165:80,如果概率都没有命中,最后以100%的概率转到192.168.166.167:80.iptable规则是自上而下执行的。
那么KUBE-SERVICES规则又是加在哪里的呢
可以看到配到了PREROUTING和OUTPUT这两个hook点。也就是说所有进入和发出主机的数据包都要经过这个规则。
还有一个需要注意的是,service ip 10.105.164.239是一个虚ip,主机上并没有一个设备去响应,是ping不通的
但是,iptable有很明显的问题,一个是负载均衡的策略需要用很多的规则去堆叠起来,还有一个是按照概率去命中执行注定转发的效率不会很高,如果集群比较大,pod数量较多,这种问题就更明显了。
4.2 IPVS
需要注意的是,Netfilter的IPVS模式没有PREROUTING这个hook点,只有LOCAL_IN、LOCAL_OUT和FORWARD这三个hook点。
也就是说经过路由判决的时候,数据包的目的ip还是service ip,不像iptable那样,在经过PREROUTING的时候就把数据包的目标ip换成了pod的ip,所以service ip必须挂在主机的某个设备上,否则经过路由判决的时候会被认为是一个无效的地址。
kube-proxy会在主机上创建一个类似kube-ipvs0这样的设备,所有的service ip都绑在这个设备上,这个ip是能ping通的,这也是ipvs模式和iptable模式的一个区别。
查看IPVS转发规则:
IPVS转发规则比较清晰简洁。所以,一般建议能使用IPVS尽量使用IPVS。