k8s网络原理-ipvs

如题所述

第1个回答  2022-07-13

一、背景知识

  本文主要介绍k8s网络中service 的两种模式(clusterIp、nodeport),数据是如何通过ipvs&iptables流转的。在学习上述知识的同时,还需要了解一下ipset、conntrack的相关知识。 往期回顾文章

1.1、ipset

  ipset是什么?ipset其实是iptables的扩展,可以定义一些列地址的集合。拿黑名单来举例,我想让黑名单里面的ip拒绝访问网站(黑名单有很多个),按照传统iptables做法,需要在filter表添加很多规则匹配时一条一条匹配效率很低(严重影响性能),而有了ipset,则只用添加一条规则即可,使用hash结构效率很高。

而使用ipset命令如下

  当然,ipset还支持 hash:ip,hash:ip,port,ip等多种hash key的组成,具体可以通过 ipset -h 查看。接下来说明一下 -m set 后面 src 和 dst 两个的含义。src 指来源,dst 指目标,此规则的意思是来自192.178.113.100 ip 访问本机8410端口的流量给DROP掉。
ipset使用hash结构,比iptables的链表遍历效率要高很多。ipset还有很多更加高级的玩法,本文就不在阐述了。

1.2、ipvs

  lvs是什么?全称是Linux Virtual Server,是由章文嵩博士主导的开源负载均衡项目,目前已经集成到linux内核中。lvs提供了丰富的负载均衡能力,接收到用户请求后根据具体的负载均衡算法在内核态把请求转发到后端的某个server上,也就是说lvs不需要监听具体的端口。接下来我们看一下lvs的一些基本概念。

  ipvs的原理如下。ipvs工作在iptables 的 input链上,VIP一般定义在DS节点上的一个虚拟ip,拿nat模式举例如下。

① : 当请求数据包到DS上最先经过iptables 的PREROUTING链,判断目标ip (VIP) 是本机的ip,于是把请求转发到INPUT链上。
② : 因为lvs工作在INPUT链上,数据到达INPUT链上后lvs会将用户请求和定义的后端服务做对比,如果是请求的后端服务,则使用某种负载均衡算法找到一个后端RIP,修改数据包的目的ip和端口为某个RIP的(DNAT转换)。
③ : 此时数据到达POSTROUTING链(不会做SNAT),数据包的源ip 为CIP,目的ip为RIP,数据包发往RIP上。

lvs提供了三种包转发模式,如下所示

由于k8s使用的是NAT模式,接下来看下 NAT模式下的数据包流向 。如下图所示

①:请求数据包到达DS,数据包经过PREROUTING链,此时ip 包 src ip为CIP,dst ip 为VIP
②:由于请求的VIP是DS上的虚拟ip,数据包发往INPUT链。
③:数据包到INPUT链上后,ipvs发现数据包请求是定义的集群服务,于是使用定义好的负载均衡算法找到一个具体的RS节点,做DNAT,修改数据包dst ip为RIP,数据包到达POSTROUTING链,发送给RS。
④:RS收到数据包后对比dst ip 发现是自己,接收数据包做处理,处理完成后ip 数据包 src ip 为RIP,dst ip 为CIP,把数据包发给DS。
⑤:DS 接收到RS的响应包,修改src ip 为自身的VIP,dst ip 为CIP,把数据包发送给client端。

三种模式对比&优缺点

接下来在简单聊一下ipvs的负载均衡策略,简单介绍下面四种。

  上面介绍完了ipvs内核态的基本原理,接下来介绍一下如何使用 ipvsadm 用户态命令来操作ipvs。说明:此次试验是在四个虚拟机上,ipvs的模式使用的nat模式,RS的网关没有指向DS的ip(没办法做到)在DS节点上手动创建SNAT命令,下文有详细介绍。创建一个vip,在ip为192.168.113.101上

为vip添加RS

添加完成RS后,查看ipvs规则,如下图所示

client端的ip地址为192.168.113.102,client端要想直接访问vip的话,需要在client端添加静态路由,添加命令如下

添加完命令后,在client端curl 10.10.0.1:8410 发现不通,此时去某个RS上抓包如下

  上图抓包显示,client 直接访问的vip,而数据包的目的ip 变为了rs的ip,因此可以看出ipvs做了DNAT转换。因为做了DNAT,RS发送响应数据直接发给client,client收到RS的数据包。client给vip发的包却收到了RS的响应包(client 想我从来没有给RS发过数据),因此client端会把此数据包丢弃。

  因为ipvs没有做SNAT,接下来在DS上添加iptables规则自己实现SNAT的功能,添加完SNAT后, RS就看不到真实的CIP了

  此时还是不通,查找资料后发现ipvs 的 conntrack 没有开,手动打开,后续文章介绍conntrack是什么,设置完成后可以愉快的访问了。

  总结:通过ipvs提供的DNAT功能和负载均衡功能,很容易实现外部用户访问内网的需求。但是还要考虑高可用层面,比如主DS宕机VIP要漂移到备DS上,后端RS重启或宕机,ipvs负载均衡列表中要及时把有问题的RS剔除,这样才能真正的实现高可用。

1.3、conntrack

  大家在家上网时用到的都是192.168.x.x的ip地址,这是私网ip地址。那么大家是如何能够成功的访问外网的呢?答案是路由器帮我们做了SNAT的功能,使我们发出的数据包的src ip变为路由器的公网ip,这样数据包就能在互联网上愉快的转发了。从而实现了对内网的保护。

  那么问题来了,既然做了SNAT转换,那响应数据包回来以后路由器怎么知道转到哪台PC上呢?路由器可能链接了很多PC,不可能都给每一个PC转发吧。。。答案就是conntrack实现的。

  接下来我拿上面ipvs的例子举例,我们手动实现了在DS上SNAT转换,在client上curl vip:8410,这时候查看DS上和client上的conntrack表如下

先从client上的连接跟踪分析起:主要看 src、dst、sport、dport这几个字段。
client发送数据包

client端发出数据包的src ip 为192.168.113.102,dst ip 为10.10.0.1 (VIP), sport 为35562这个端口,dport为8410(VIP 定义端口)。

client端接收响应数据包

期望src ip 为vip(10.10.0.1),dst ip 为CIP(192.168.113.102),sport为8410,dport为35562

DS接收数据包

DS接收到src ip 为CIP(192.168.113.102),dst ip 为vip(10.10.0.1),sport为35562,dport为8410的数据包

DS接收响应数据包

  由于在DS侧做了DNAT转换,根据负载均衡策略找到了一个RS(RIP 192.168.113.99),同时也做了SNAT转换(判断是否是VIP和端口),转换为DS的DIP。所以当DS收到src ip 为192.168.113.99(RIP),dst ip 为192.168.113.101(DIP),sport为8080,dport为35562,会根据连接跟踪表找到这个包是192.168.113.102这个client发过来的,因此把数据包在转发给192.168.113.102:35562 上。

conntrack各个字段的含义

总结:

  本文只是简单的说明了一下conntrack,并没有具体说明数据流经netfilter时何时创建记录,数据存储的数据结构啥样,底层比较复杂,感兴趣的大佬可以自行研究~

二、k8s网络通信

  介绍完了ipset、ipvs、conntrack,接下来进入正题,看一下ipvs模式下k8s的网络通信。kube-proxy 的主要作用是watch apiserver,当监听到pod 或service变化时,修改本地的iptables规则或ipvs规则。

2.1、clusterIp模式

clusterIp模式为一个集群内部可访问的ip,集群外部没办法访问这个ip,试验环境如下:

创建完deployment和service后,查看一下service的ip如下。

接下来看下宿主机网卡、ipvs规则、ipset规则有什么变化

查看iptables 的nat表和filter表,看一下k8s创建了哪些规则以及经过哪些链

接下来分析一下curl 10.108.113.237 数据是如何走的,只讨论在nat表和filter表的流向,因为在mangle和raw都没有规则。

1、nat表PREROUTING链
①:数据首先进入PREROUTING链,所有请求都会进入KUBE-SERVICES链。
②:进入KUBE-SERVICES后,查看对应在此链上的规则,发现请求的目的ip和port在KUBE-CLUSTER-IP 对应的ipset里面(上面已有展示),匹配上了则跳往KUBE-MARK-MASQ链。

③:数据流向KUBE-MARK-MASQ链,主要做了mark 打标记的功能,iptables命令如下

④:之后走向KUBE-NODE-PORT链,因为没有定义nodepode 类型的service,此处先略过。 2、filter表的INPUT链
⑤:首先进入INPUT链,所有数据转向KUBE-FIREWALL链。
⑥:进入KUBE-FIREWALL链,如果发现数据包打了0x8000/0x8000,DROP掉。因为ipvs工作在INPUT链上,做完DNAT之后直接转发到POSTROUTING链上。
3、nat表POSTROUTING链
⑦:进入POSTROUTING链,所有数据转向KUBE-POSTROUTING链
⑧:进入KUBE-POSTROUTING链,对有0x4000/0x4000标记的数据包做SNAT转换,因为ipvs只有DNAT功能。

4、数据转发给flannel网卡,进行转发
⑨:flannel 根据具体的backend模式,对数据做封包等操作,然后发出去。flannel的网络模式比较复杂,之后会专门文章进行说明。

2.2、nodeport模式

  要想把集群内部的服务可以让集群外部访问,可以使用nodeport模式在物理机上开一个端口,这样外部就能访问集群内部的服务了。说明:还是使用上面创建的deployment。

查看创建service的信息,发现也创建了集群内部的一个ip。

iptables规则如下

接下来看下ipset规则有什么变化,发现KUBE-NODE-PORT-TCP下的一个成员是刚才我们指定的那个nodePort的值。

接下来看一下iptables规则,nat表和filter表
1、nat表PREROUTING链
①:数据首先进入PREROUTING链,所有请求都会进入KUBE-SERVICES链。
②:ip和port匹配不上KUBE-CLUSTER-IP 的ipset,判断是访问的本地地址,进入KUBE-NODE-PORT链。

③:进入KUBE-NODE-PORT链后,判断访问端口在 KUBE-NODE-PORT-TCP ipset规则中,因此进入KUBE-MARK-MASQ链。

④:进入KUBE-MARK-MASQ链,对数据做mark标记

后续流程跟clusterIp一样,此处就不在阐述。
2.3、dns相关

  k8s中的dns默认使用的是coredns,通过以下命令查看。k8s中定义的service是有域名的,访问域名要通过dns解析,此时coredns就发挥它的作用了。

  上面的试验时我们创建了一个my-service 的nodePort的service,此时查看一下此域名对应的ip,如下图所示,域名解析出来的ip与service对应的ip相同,大功告成。

参考:


以上相关内容介绍了k8s service ipvs的相关实现,如有错误欢迎指出~

相似回答