Kubernetes kube-proxy工作原理

kube-proxy


 它本身也是控制器模式的组件,基本上任何kubernetes任何组件,它的模式都是一样的,针对kube-proxy来说,它有它所关注的对象,它关注的是service和endpoint以及endpoint slice的变化,它通过操作系统的内核能力来配置负载均衡。

负载均衡最主要的是NAT,是最常见的实现方式,内核协议栈有一定的NAT能力,kube-proxy基于内核的能力实现了网络地址转换,最终达到负载均衡的目的。

内核不会去解应用包,所谓你内核就是给我TCP包,在内核层面只是处理这些TCP包头或者UDP的包头,这个数据包最终会丢回用户态,由用户的应用程序来读取具体的数据的。

所以内核只能处理TCP UDP这层,更加高层的应用层的数据它是碰不到的。

kube-proxy支持几种模式,最早期userspace,相当于用go语言写了一个代理软件,当你访问一个服务,这个服务最终将请求转到kube-proxy里面go语言的负载均衡逻辑,然后由他来决定往哪转。

当你任何的网络调用,你客户端发起的请求都会先交给内核,如果它是在用户态处理负载均衡,它还会从内核态转会用户态,这样反复的跳转对整个网络效率都不高。

所以userspace是一种低效的方式,它只有在你内核没有办法支持所说的网络协议栈的网络地址转化的能力才会去使用。

所谓的iptables和ipvs都是Linux内核协议栈里面,有个框架叫做netfilter,iptables和ipvs是netfilter框架里面不同的plugin。

Linux内核处理数据包:Netfilter框架


 这个图展示是一个数据包,被一个Linux接收到之后,内核在处理这些数据包的时候会经历哪些扭转,这个扭转的框架叫做netfilter。

整个网络的分层为应用层,传输层,网络层,链路层。

应用层跑的是各种用户态的进程,下部分就是内核态,就是由Linux内核去处理数据包的时候可以采取的一些动作。

比如一个数据包被网卡接收到了,链路层接收到数据包之后,如果它是本机的数据包,那么它会将这个包发送到netfilter框架,这个包回去被网络层处理,在处理的时候整个net filter会提供一个一个的hook点,就是一个一个的钩子,在这个钩子里面,这个数据包在处理的时候首先有个柔婷 descision,它会去看这个数据包的五元组,这个数据包里面存的是源IP,源端口,目标IP和端口,以及协议,代表协议的请求和流向。

一个数据包在Linux内核处理的时候,它首先要去判断数据包应该往哪走,是本机要接受的还是不是本机要接受的,如果是我本机的地址,它就会丢到local的process去处理,如果不是它就将包转发出去。

所以在这里就有路由判决,在路由判决前后,它就提供了很多的hook点,所谓的hook点就是一个一个的钩子,它允许你在用户态设置一些规则,它来读取这些规则,这些规则对什么元组的数据我要做什么样的行为。

绿色的方框都是一个一个的hook点,它会去hook点去读取你的用户态配置的规则,来看你这个规则和它现在所处理的数据包是不是一致,规则里面有匹配条件,第二会有行为,如果是防火墙规则,那么就会将数据包丢掉,如果是负载均衡的策略,就需要做类似于网络地址转换,就将目标地址换一下。

我们这里最关心的就是nat和filter,他就是用来修改数据包的,包头里面地址的,所谓的filter,filter一般都是过滤的,所以它就是防火墙规则,nat就是负载均衡的规则。

既然有了这些能力,那么就可以在不同的点上面插入一些不同的规则,然后来控制数据包的流转。

 Netfilter和Iptables


一个数据包被网卡接收到的之后,在用户态Linux允许用户去配置一些具体的规则,如果针对kube-proxy这种目的,我们要去做这种负载均衡的,我们要去访问clusterip,clusterip是一个虚拟地址,用户去访问虚拟地址的时候,要由我们的kube-proxy配置的一些规则将其转到后端真实的pod里面。

所以需要在用户态配置一些nat规则,这些规则要去定义说如果你要访问clusterip,我就要将你的数据包跳转到,也就是将目标地址转换为后端的pod,这是我们需要达到的一个目的,正是iptables支持了这种目的。

一个数据包被网卡接收到之后都是在用户态处理,网卡收到这个包之后,它会发起一个中断,它要告诉cpu我这边有数据包了,cpu不会立刻去读取这些数据,因为cpu被中断之后它就不能响应其他事件了,通常硬件中断要快速结束,所以cpu基本上会响应这个中断,会去调用软中断,在linux里面你可以看到softirqd这样的一些进程,这些进程就是用来处理软中断的,cpu就会去调用softirqd里面的handler来真正的处理数据包。

网卡驱动会通过直接内存访问的方式将这些数据复制到kernel里面某块内存空间。

内存空间里面softirq,会从内存空间里面读取这些数据包,并且构造一个一个的skb,它数据结构叫做skbuffer,skbuffer里面就包含了真实的数据以及header。

构造完数据包之后,skb会被交到netfilter的framework,就是说有这样的数据包了,你需要来处理。

netfilter的framework在处理数据包的时候,它就会去用户态读取配置的iptables规则,如果匹配上执行相应的action,这个action往往都是修改目标地址。

IPtables


 prerouting:就是一个网络包发送过来,在做任何的路判断之前,有个prerouting的hook点,这里面是可以插入iptables规则的。

过了prerouting之后就是系统去做路由判断的,他会去判断如果包的目标地址是我本机的,它会走local_in的这个hook,这里面又会有一些hook点可以插入iptables规则。local_in进来之后就会被本地程序处理,如果发现地址不是本机地址,他就会走forward表把这个包丢出去。
 

如果是本地进程要往外发请求,作为一个客户端往外发请求,它就会走local_out,在进行路由判决之后,有个postrouting,这个网络包就出去了。

我们要改写目标地址,希望将clusterip改为真实的pod ip,我们就需要找到dnat去哪里做,prerouting是可以去做dnat的。

猜你喜欢

转载自blog.csdn.net/qq_34556414/article/details/126260114