k8s中的ipvs

原文

当我们部署kube-prox时都会存在一个选择, 是选择ipvs模式还是iptables哪个好呢? 也还存在其它的疑问外部流量访问到svc,然后svc跳转pod访问应用.
svc怎么跳转访问pod的呢?
kube-proxy实现svc到pod的访问和负载均衡. 让我们带着问题阅读下面的文章

1.IPVS工作原理

IPVS基本上是一种高效的Layer-4交换机,它提供负载平衡的功能。当一个TCP连接的初始SYN报文到达时,IPVS就选择一台服务器,将报文转发给它。此后通过查发报文的IP和TCP报文头地址,保证此连接的后继报文被转发到相同的服务器。这样,IPVS无法检查到请求的内容再选择服务器,这就要求后端的服务器组是提供相同的服务,不管请求被送到哪一台服务器,返回结果都应该是一样的。但是在有一些应用中后端的服务器可能功能不一,有的是提供HTML文档的Web服务器,有的是提供图片的Web服务器,有的是提供CGI的Web服务器。这时,就需要基于内容请求分发 (Content-Based Request Distribution),同时基于内容请求分发可以提高后端服务器上访问的局部性。

因为当我们指定--proxy-mode=ipvs 时IPVS使用的NAT模式做服务映射, 所以我们主要说NAT模式工作原理

IPVS/NAT模式

由于IPv4中IP地址空间的日益紧张和安全方面的原因,很多网络使用保留IP地址(10.0.0.0/255.0.0.0、 172.16.0.0/255.128.0.0和192.168.0.0/255.255.0.0)[64, 65,66]。这些地址不在Internet上使用,而是专门为内部网络预留的。当内部网络中的主机要访问Internet或被Internet访问时,就需要采用网络地址转换(Network Address Translation, 以下简称NAT),将内部地址转化为Internets上可用的外部地址。

NAT的工作原理是报文头(目标地址、源地址和端口等)被正确改写后,客户相信它们连接一个IP地址,而不同IP地址的服务器组也认为它们是与客户直接相连的。由此,可以用NAT方法将不同IP地址的并行网络服务变成在一个IP地址上的一个虚拟服务。

客户通过Virtual IPAddress(虚拟服务的IP地址)访问网络服务时,请求报文到达调度器,调度器根据连接调度算法从一组真实服务器中选出一台服务器,将报文的目标地址 Virtual IPAddress改写成选定服务器的地址,报文的目标端口改写成选定服务器的相应端口,最后将修改后的报文发送给选出的服务器。同时,调度器在连接Hash 表中记录这个连接,当这个连接的下一个报文到达时,从连接Hash表中可以得到原选定服务器的地址和端口,进行同样的改写操作,并将报文传给原选定的服务器。当来自真实服务器的响应报文经过调度器时,调度器将报文的源地址和源端口改为Virtual IPAddress和相应的端口,再把报文发给用户。我们在连接上引入一个状态机,不同的报文会使得连接处于不同的状态,不同的状态有不同的超时值

图没了

  1. 当用户向负载均衡调度器(Director Server)发起请求,调度器将请求发往至内核空间
  2. PREROUTING链首先会接收到用户请求,判断目标IP确定是本机IP,将数据包发往INPUT链
  3. IPVS是工作在INPUT链上的 ,当用户请求到达INPUT时,IPVS会将用户请求和自己已定义好的集群服务进行比对,如果用户请求的就是定义的集群服务,那么此时IPVS会强行修改数据包里的目标IP地址及端口,并将新的数据包发往FORWORD链
  4. FORWORD链将数据将数据包发给POSTROUTING链
  5. POSTROUTING链接收数据包后发现目标IP地址刚好是自己的后端服务器,那么此时通过选路,将数据包最终发送给后端的服务器

2.Service提供常用的类型

  • ClusterIP,也是默认方式。Service会分配一个集群内部的固定虚拟IP,实现集群内通过该IP来对POD进行访问。这个又有两类,上面说到的最普通的Service,ClusterIP还有一种是Headless Service,这种形式不会分配IP也不会通过kube-proxy做反向代理或者负载均衡,而是通过DNS提供稳定的网络ID来访问,DNS会将headless service的后端直接解析为POD的IP列表,这种主要是共StatefulSet类型使用。
  • NodePort,这种类型的Service是除了使用ClusterIP的功能外还会映射一个宿主机随机端口到service上,这样集群外部可以通过宿主机IP+随机端口来访问。
  • LoadBalancer:和nodePort类似,不过除了使用ClusterIP和NodePort之外还会向使用的公有云申请一个负载均衡器,从而实现集群外部通过LB来访问服务
  • ExternalName:是Service的一种特例,此模式主要面对运行在集群外部的服务,通过它可以将外部服务映射到k8s集群,具备k8s内服务的一些特性,来为集群内部提供服务。

我们主要说的是ClusterIP和NodePort

3.kube-proxy如何处理请求

图没了

如上图所示Iptables的链的规则从pod发起的请求到达主机以后首先会到达PREROUTEING链, kube-proxy 中主要用到了iptables的NAT表

NAT表有三种内建链:

  • PREROUTING链 – 处理刚到达本机并在路由转发前的数据包。它会转换数据包中的目标IP地址(destination ip address),通常用于DNAT(destination NAT)。
  • POSTROUTING链 – 处理即将离开本机的数据包。它会转换数据包中的源IP地址(source ip address),通常用于SNAT(source NAT)。
  • OUTPUT链 – 处理本机产生的数据包。
$ iptables-save
...
*nat
:PREROUTING ACCEPT [7:420]
:INPUT ACCEPT [7:420]
:OUTPUT ACCEPT [9:556]
:POSTROUTING ACCEPT [9:556]
:DOCKER - [0:0]
:KUBE-FIREWALL - [0:0]
:KUBE-KUBELET-CANARY - [0:0]
:KUBE-LOAD-BALANCER - [0:0]
:KUBE-MARK-DROP - [0:0]
:KUBE-MARK-MASQ - [0:0]
:KUBE-NODE-PORT - [0:0]
:KUBE-POSTROUTING - [0:0]
:KUBE-SERVICES - [0:0]
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING
-A POSTROUTING -s 10.243.0.0/16 -d 10.243.0.0/16 -j RETURN
-A POSTROUTING -s 10.243.0.0/16 ! -d 224.0.0.0/4 -j MASQUERADE
-A POSTROUTING ! -s 10.243.0.0/16 -d 10.243.104.0/21 -j RETURN
-A POSTROUTING ! -s 10.243.0.0/16 -d 10.243.0.0/16 -j MASQUERADE
-A KUBE-FIREWALL -j KUBE-MARK-DROP
-A KUBE-LOAD-BALANCER -j KUBE-MARK-MASQ
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000
-A KUBE-NODE-PORT -p tcp -m comment --comment "Kubernetes nodeport TCP port for masquerade purpose" -m set --match-set KUBE-NODE-PORT-TCP dst -j KUBE-MARK-MASQ
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -m mark --mark 0x4000/0x4000 -j MASQUERADE
-A KUBE-POSTROUTING -m comment --comment "Kubernetes endpoints dst ip:port, source ip for solving hairpin purpose" -m set --match-set KUBE-LOOP-BACK dst,dst,src -j MASQUERADE
-A KUBE-SERVICES ! -s 10.243.0.0/16 -m comment --comment "Kubernetes service cluster ip + port for masquerade purpose" -m set --match-set KUBE-CLUSTER-IP dst,dst -j KUBE-MARK-MASQ
-A KUBE-SERVICES -m addrtype --dst-type LOCAL -j KUBE-NODE-PORT
-A KUBE-SERVICES -m set --match-set KUBE-CLUSTER-IP dst,dst -j ACCEPT
COMMIT
...

3.1集群内部流量

流程如下:

+------+     +--------------+     +--------------------+     +------+
| POD1 | --> | OUTPUT CHAIN | --> | KUBE-SERVICES RULE | --> | IPVS |
+------+     +--------------+     +--------------------+     +------+

OUTPUT CHAIN规则如下, 所有流量跳转到KUBE-SERVICES CHAIN

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
KUBE-SERVICES  all  --  0.0.0.0/0            0.0.0.0/0            /* kubernetes service portals */
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

KUBE-SERVICES CHAIN规则如下

Chain KUBE-SERVICES (2 references)
target     prot opt source               destination
KUBE-MARK-MASQ  all  -- !10.243.0.0/16        0.0.0.0/0            /* Kubernetes service cluster ip + port for masquerade purpose */ match-set KUBE-CLUSTER-IP dst,dst
KUBE-NODE-PORT  all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            match-set KUBE-CLUSTER-IP dst,dst

ClusterIP service 的访问流量会匹配ACCEPT,其匹配规则是match-set KUBE-CLUSTER-IP dst,dst,即封包的IP:port匹配内核中名为KUBE-CLUSTER-IP的 ipset。

IPVS的配置如下

$ ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.244.0.1:443 rr
  -> 10.40.58.153:6443            Masq    1      0          0
  -> 10.40.58.154:6443            Masq    1      0          0
  -> 10.40.61.116:6443            Masq    1      1          0

3.2集群外部流量

流程如下:

+--------+     +---------+     +------------------+     +--------------------+     +----------------+     +------+
| CLIENT | --> | SERVICE | --> | PREROUTING CHAIN | --> | KUBE-SERVICES RULE | --> | KUBE-MARK-MASQ | --> | IPVS |
+--------+     +---------+     +------------------+     +--------------------+     +----------------+     +------+

PREROUTING CHAIN规则如下, 所有流量跳转到KUBE-SERVICES CHAIN

$ iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
KUBE-SERVICES  all  --  anywhere             anywhere             /* kubernetes service portals */
DOCKER     all  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL

KUBE-SERVICES CHAIN规则如下

Chain KUBE-SERVICES (2 references)
target     prot opt source               destination
KUBE-MARK-MASQ  all  -- !10.243.0.0/16        0.0.0.0/0            /* Kubernetes service cluster ip + port for masquerade purpose */ match-set KUBE-CLUSTER-IP dst,dst
KUBE-NODE-PORT  all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            match-set KUBE-CLUSTER-IP dst,dst

ClusterIP service 的访问流量会匹配KUBE-MARK-MASQ,其匹配规则是match-set KUBE-CLUSTER-IP dst,dst 并且源地址不是pod的CIDR地址段,所有流量jump 到KUBE-MARK-MASQ链

Chain KUBE-MARK-MASQ (3 references)
target     prot opt source               destination
MARK       all  --  0.0.0.0/0            0.0.0.0/0            MARK or 0x4000

为所有的流封包打上0x4000的标记, 后续交给IPVS处理

3.3 NodePort

流程如下:

+-----+     +------------------+     +--------------------+     +----------------+     +----------------+     +------+
| pod | --> | PREROUTINH CHAIN | --> | KUBE-SERVICES RULE | --> | KUBE-NODE-PORT | --> | KUBE-MARK-MASQ | --> | IPVS |
+-----+     +------------------+     +--------------------+     +----------------+     +----------------+     +------+

对于访问 NodePort service 来说,其流量第一步依然是经由PREROUTING chain,而后跳转到KUBE-SERVICES chain。在这里封包会jump到KUBE-NODE-PORT target chain,其匹配条件是ADDRTYPE match dst-type LOCAL,即目标地址类型是 Node 配置的地址(eth0),这符合 NodePort 的定义。

KUBE-NODE-PORT 的规则如下

Chain KUBE-NODE-PORT (1 references)
target     prot opt source               destination
KUBE-MARK-MASQ  tcp  --  0.0.0.0/0            0.0.0.0/0            /* Kubernetes nodeport TCP port for masquerade purpose */ match-set KUBE-NODE-PORT-TCP dst

对于匹配到KUBE-NODE-PORT的封包jump到KUBE-MARK-MASQ打上0x4000标记

KUBE-NODE-PORT-TCP ipset 规则如下:

$ ipset  -L KUBE-NODE-PORT-TCP 
Name: KUBE-NODE-PORT-TCP
Type: bitmap:port
Revision: 1
Header: range 0-65535
Size in memory: 524432
References: 1
Members:
32106

ipvs的配置如下

$ ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.40.61.116:32106 rr
TCP  10.243.104.0:32106 rr
TCP  10.243.104.1:32106 rr

4.summary

kube-proxy IPVS 模式依然会使用 iptables 来做封包 MASQUERADE,但是其使用 ipset match 的方式保持了 iptables 规则数量恒定(几条),随着 service 的数量增多,不会出现 iptables 规则过多以及线性匹配的性能瓶颈。 这里使用 iptables 与否不是关键,iptalbes 与 IPVS 理论上都是内核 netfilter 模块的客户端,IPVS 这里只是借用 iptables 方便地设置 netfilter 转发。

流程如下:

+--------+     +---------+     +------+     +-----+
| CLIENT | --> | SERVICE | --> | IPVS | --> | POD |
+--------+     +---------+     +------+     +-----+

架构如下:

+--------+     +------+     +-------+
| CLIENT | --> |      | --> | POD01 |
+--------+     |      |     +-------+
               |      |     +-------+
               | NODE | --> | POD02 |
               |      |     +-------+
               |      |     +-------+
               |      | --> | POD03 |
               +------+     +-------+
  1. 集群中的每台NODE都是Director, 因为所有的iptables和ipvs规则会配置在集群中的每个node上
  2. Service上的CLusterIP为VIP

使用ip addr show会发现存在kube-ipvs0的虚拟网卡,IP地址cluster的IP

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值