赞
踩
在常用的k8s环境中,通常会通过iptables将流量进行负载均衡、snat、dnat等操作,从而流量转发到pod或者外部的服务。本文重点介绍iptables是如何进行流量转发的以及相关转发的iptables和ipvs解析。
service只是一个抽象概念,在逻辑上将一组pod(功能相同)给抽象出来一个统一入口。可以将他简单理解为做了一个服务的负载均衡。我们知道pod在重新部署之后ip会改变,所以一般会通过service来访问pod。core-dns会给service分配一个内部的虚拟ip(节点上根本查询不到这个ip,ping是不通的,具体是怎么访问到的继续往下看),因此内部服务可以通过这个ip或者是serviceName来访问到pod的服务。
service提供的常用type:
apiVersion: v1
kind: Service
metadata:
namespace: app
name: eureka-server
labels:
name: eureka-server
spec:
type: NodePort ##这个位置来指定service的类型
selector:
app: eureka-server
ports:
- port: 80
targetPort: 9101
nodePort: 31101
endpoints也是k8s的一个资源,我们在创建service的时候如果我们设置了selector选中了需要关联的pod,那么就会创建一个与service同名的endpoints。他是用来记录service对应pod的访问地址。
[root@aliyun168-37 nginx]# kubectl get endpoints nginx-svc -n test
NAME ENDPOINTS AGE
nginx-svc 10.244.4.139:80,10.244.4.140:80,10.244.4.141:80 18m
Kubernetes(简称K8s)中的Pod是最小的可部署单元,它是一组相关容器的集合。一个Pod可以包含一个或多个容器,这些容器共享相同的资源(如网络和存储)。Pod提供了一种逻辑上独立的环境,使得应用可以在自己的虚拟空间中运行。
Service、Endpoint 和 Pod 的关系(下图)
Kubernetes服务(svc)能够感知Pod的变化是通过kube-proxy实现的,kube-proxy会监视Kubernetes API中的服务和端点对象,并根据Pod的变化来更新服务的endpoint信息,并进行流量转发。
总的说来,k8s的流量从svc转发到pod,一般来说需要做2个事情
如上2个功能需求,
集群内调用service,通常采用如下方式
整体的转发流程图如下
node节点的iptables是由kube-proxy生成的,kube-proxy只修改了filter和nat表,它对iptables的链进行了扩充,自定义了KUBE-SERVICES,KUBE-NODEPORTS,KUBE-POSTROUTING,KUBE-MARK-MASQ和KUBE-MARK-DROP五个链,并主要通过为 KUBE-SERVICES链(附着在PREROUTING和OUTPUT)增加rule来配制traffic routing 规则
svc涉及的iptables链路过程中的相关流程情况流程图如下
我们将针对一个iptables规则进行解析。
# 获取svc的
kubectl get svc -nkube-system -owide |grep kubernetes-lb
kube-system kubernetes-lb LoadBalancer 192.168.11.23 172.29.163.9 6443:31714/TCP 2y47d cluster.infra.tce.io/component=kube-apiserver,component=kube-apiserver
# 获取svc后段的pod
kubectl get pods -n kube-system -owide |grep kube-apiserver
kube-system kube-apiserver-172.33.0.11 1/1 Running 7 247d 172.33.0.11 172.33.0.11 <none> <none>
kube-system kube-apiserver-172.33.0.25 1/1 Running 1 247d 172.33.0.25 172.33.0.25 <none> <none>
kube-system kube-apiserver-172.33.0.30 1/1 Running 0 247d 172.33.0.30 172.33.0.30 <none> <none>
# 获取对应iptables情况 iptables-save |egrep "kubernetes-lb|KUBE-XLB-EFPSQH5654KMWHJ5|KUBE-SEP-LCXGRT47CYQENZGP|KUBE-SVC-EFPSQH5654KMWHJ5|KUBE-SEP-KJQQYC6E4EGY4UJE|KUBE-SEP-4DJFF4PKJG2GTZWW" :KUBE-SEP-4DJFF4PKJG2GTZWW - [0:0] :KUBE-SEP-KJQQYC6E4EGY4UJE - [0:0] :KUBE-SEP-LCXGRT47CYQENZGP - [0:0] :KUBE-SVC-EFPSQH5654KMWHJ5 - [0:0] :KUBE-XLB-EFPSQH5654KMWHJ5 - [0:0] -A KUBE-FW-EFPSQH5654KMWHJ5 -m comment --comment "kube-system/kubernetes-lb: loadbalancer IP" -j KUBE-XLB-EFPSQH5654KMWHJ5 -A KUBE-FW-EFPSQH5654KMWHJ5 -m comment --comment "kube-system/kubernetes-lb: loadbalancer IP" -j KUBE-MARK-DROP -A KUBE-NODEPORTS -s 127.0.0.0/8 -p tcp -m comment --comment "kube-system/kubernetes-lb:" -m tcp --dport 31714 -j KUBE-MARK-MASQ -A KUBE-NODEPORTS -p tcp -m comment --comment "kube-system/kubernetes-lb:" -m tcp --dport 31714 -j KUBE-XLB-EFPSQH5654KMWHJ5 -A KUBE-SEP-4DJFF4PKJG2GTZWW -s 172.33.0.30/32 -j KUBE-MARK-MASQ -A KUBE-SEP-4DJFF4PKJG2GTZWW -p tcp -m tcp -j DNAT --to-destination :0 --persistent --to-destination :0 --persistent --to-destination 0.0.0.0 --persistent -A KUBE-SEP-KJQQYC6E4EGY4UJE -s 172.33.0.25/32 -j KUBE-MARK-MASQ -A KUBE-SEP-KJQQYC6E4EGY4UJE -p tcp -m tcp -j DNAT --to-destination :0 --persistent --to-destination :0 --persistent --to-destination 0.0.0.0 --persistent -A KUBE-SEP-LCXGRT47CYQENZGP -s 172.33.0.11/32 -j KUBE-MARK-MASQ -A KUBE-SEP-LCXGRT47CYQENZGP -p tcp -m tcp -j DNAT --to-destination :0 --persistent --to-destination :0 --persistent --to-destination 0.0.0.0 --persistent -A KUBE-SERVICES ! -s 172.16.21.0/24 -d 192.168.11.23/32 -p tcp -m comment --comment "kube-system/kubernetes-lb: cluster IP" -m tcp --dport 6443 -j KUBE-MARK-MASQ -A KUBE-SERVICES -d 192.168.11.23/32 -p tcp -m comment --comment "kube-system/kubernetes-lb: cluster IP" -m tcp --dport 6443 -j KUBE-SVC-EFPSQH5654KMWHJ5 -A KUBE-SVC-EFPSQH5654KMWHJ5 -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-LCXGRT47CYQENZGP -A KUBE-SVC-EFPSQH5654KMWHJ5 -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-KJQQYC6E4EGY4UJE -A KUBE-SVC-EFPSQH5654KMWHJ5 -j KUBE-SEP-4DJFF4PKJG2GTZWW -A KUBE-XLB-EFPSQH5654KMWHJ5 -s 172.16.21.0/24 -m comment --comment "Redirect pods trying to reach external loadbalancer VIP to clusterIP" -j KUBE-SVC-EFPSQH5654KMWHJ5 -A KUBE-XLB-EFPSQH5654KMWHJ5 -m comment --comment "masquerade LOCAL traffic for kube-system/kubernetes-lb: LB IP" -m addrtype --src-type LOCAL -j KUBE-MARK-MASQ -A KUBE-XLB-EFPSQH5654KMWHJ5 -m comment --comment "route LOCAL traffic for kube-system/kubernetes-lb: LB IP to service chain" -m addrtype --src-type LOCAL -j KUBE-SVC-EFPSQH5654KMWHJ5 -A KUBE-XLB-EFPSQH5654KMWHJ5 -m comment --comment "Balancing rule 0 for kube-system/kubernetes-lb:" -j KUBE-SEP-LCXGRT47CYQENZGP
# 将访问svc的流量访问打标记,后续流量进出节点时进行NAT转换
-A KUBE-SERVICES ! -s 172.16.21.0/24 -d 192.168.11.23/32 -p tcp -m comment --comment "kube-system/kubernetes-lb: cluster IP" -m tcp --dport 6443 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 192.168.11.23/32 -p tcp -m comment --comment "kube-system/kubernetes-lb: cluster IP" -m tcp --dport 6443 -j KUBE-SVC-EFPSQH5654KMWHJ5
访问192.168.11.23/32:6443的流量跳转到KUBE-SVC-EFPSQH5654KMWHJ5链路进行处理。
-A KUBE-SVC-EFPSQH5654KMWHJ5 -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-LCXGRT47CYQENZGP
-A KUBE-SVC-EFPSQH5654KMWHJ5 -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-KJQQYC6E4EGY4UJE
-A KUBE-SVC-EFPSQH5654KMWHJ5 -j KUBE-SEP-4DJFF4PKJG2GTZWW
将流量进行负载均衡,给各个规则连分配权重
规则链 | 权重 |
---|---|
KUBE-SEP-LCXGRT47CYQENZGP | 0.33333333349 |
KUBE-SEP-KJQQYC6E4EGY4UJE | 0.50000000000 |
KUBE-SEP-4DJFF4PKJG2GTZWW | 1-0.33333333349-0.50000000000=0.16666666651000006 |
其中KUBE-SEP-4DJFF4PKJG2GTZWW不需要设置probability,因为可以计算出来
1 − K U B E − S E P − L C X G R T 47 C Y Q E N Z G P − K U B E − S E P − K J Q Q Y C 6 E 4 E G Y 4 U J E 1- KUBE-SEP-LCXGRT47CYQENZGP - KUBE-SEP-KJQQYC6E4EGY4UJE 1−KUBE−SEP−LCXGRT47CYQENZGP−KUBE−SEP−KJQQYC6E4EGY4UJE
# 第1条规则:Pod通过Service访问自身时匹配,此规则仅作标记(MARK)处理;
-A KUBE-SEP-4DJFF4PKJG2GTZWW -s 172.33.0.30/32 -j KUBE-MARK-MASQ
# 第2条规则:通过DNAT重定向到后端Pod实例上,至此,通过Service最终将流量导向到后端实例上;
-A KUBE-SEP-4DJFF4PKJG2GTZWW -p tcp -m tcp -j DNAT --to-destination :0 --persistent --to-destination :0 --persistent --to-destination 0.0.0.0 --persistent
-A KUBE-SEP-KJQQYC6E4EGY4UJE -s 172.33.0.25/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-KJQQYC6E4EGY4UJE -p tcp -m tcp -j DNAT --to-destination :0 --persistent --to-destination :0 --persistent --to-destination 0.0.0.0 --persistent
-A KUBE-SEP-LCXGRT47CYQENZGP -s 172.33.0.11/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-LCXGRT47CYQENZGP -p tcp -m tcp -j DNAT --to-destination :0 --persistent --to-destination :0 --persistent --to-destination 0.0.0.0 --persistent
按照KUBE-SVC-EFPSQH5654KMWHJ5配置的负载均衡相关的权重,将相关流量转发到后段的pod,完成流量转发和负载均衡。
-A KUBE-FW-EFPSQH5654KMWHJ5 -m comment --comment "kube-system/kubernetes-lb: loadbalancer IP" -j KUBE-XLB-EFPSQH5654KMWHJ5
-A KUBE-FW-EFPSQH5654KMWHJ5 -m comment --comment "kube-system/kubernetes-lb: loadbalancer IP" -j KUBE-MARK-DROP
KUBE-MARK-DROP设置标记的报文则会在KUBE_FIREWALL中全部丢弃
-A KUBE-XLB-EFPSQH5654KMWHJ5 -s 172.16.21.0/24 -m comment --comment "Redirect pods trying to reach external loadbalancer VIP to clusterIP" -j KUBE-SVC-EFPSQH5654KMWHJ5
-A KUBE-XLB-EFPSQH5654KMWHJ5 -m comment --comment "masquerade LOCAL traffic for kube-system/kubernetes-lb: LB IP" -m addrtype --src-type LOCAL -j KUBE-MARK-MASQ
-A KUBE-XLB-EFPSQH5654KMWHJ5 -m comment --comment "route LOCAL traffic for kube-system/kubernetes-lb: LB IP to service chain" -m addrtype --src-type LOCAL -j KUBE-SVC-EFPSQH5654KMWHJ5
-A KUBE-XLB-EFPSQH5654KMWHJ5 -m comment --comment "Balancing rule 0 for kube-system/kubernetes-lb:" -j KUBE-SEP-LCXGRT47CYQENZGP
在KUBE-XLB后,loadbalancer的IP在节点上截获后转给service
-A KUBE-SVC-EFPSQH5654KMWHJ5 -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-LCXGRT47CYQENZGP
-A KUBE-SVC-EFPSQH5654KMWHJ5 -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-KJQQYC6E4EGY4UJE
-A KUBE-SVC-EFPSQH5654KMWHJ5 -j KUBE-SEP-4DJFF4PKJG2GTZWW
由此完成了流量转发。
单个 k 8 s 集群的 i p t a b l e s 规则数量 = 单节点的 i p t a b l e s 规则数量 ∗ 节点数量 单个k8s集群的iptables规则数量=单节点的iptables规则数量*节点数量 单个k8s集群的iptables规则数量=单节点的iptables规则数量∗节点数量
在 IPVS 模式下,kube-proxy监视Kubernetes服务和端点,调用 netlink 接口创建 IPVS 规则, 并定期将 IPVS 规则与 Kubernetes 服务和端点同步。访问服务时,IPVS 将流量定向到后端Pod之一。IPVS代理模式基于类似于 iptables 模式的 netfilter 挂钩函数, 但是使用哈希表作为基础数据结构,执行时间O(1),并且在内核空间中工作。这意味着,与 iptables 模式下的 kube-proxy 相比,IPVS 模式下的 kube-proxy 重定向通信的延迟要短,并且在同步代理规则时具有更好的性能。与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。
IPVS 模式的工作原理,其实跟 iptables 模式类似。当我们创建了前面的 Service 之后,kube-proxy 首先会在宿主机上创建一个虚拟网卡(叫作:kube-ipvs0),并为它分配 Service VIP 作为 IP 地址。接下来,kube-proxy 就会通过 Linux 的 IPVS 模块,为这个 IP 地址设置三个 IPVS 虚拟主机,并设置这三个虚拟主机之间使用轮询模式 (rr) 来作为负载均衡策略。拓扑图如下所示拓扑图:
查看绑定的虚拟网卡
# ip addr
...
73:kube-ipvs0:<BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 1a:ce:f5:5f:c1:4d brd ff:ff:ff:ff:ff:ff
inet 10.0.1.175/32 scope global kube-ipvs0
valid_lft forever preferred_lft forever
查看内部的转发规则
# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.102.128.4:80 rr
-> 10.244.3.6:9376 Masq 1 0 0
-> 10.244.1.7:9376 Masq 1 0 0
-> 10.244.2.3:9376 Masq 1 0 0
因此ipvs模式下,实际上是 ipvs负载均衡 + iptables过滤和NAT,实现svc的流量转发整体流程
KUBE-MARK-MASQ的作用是确保Kubernetes集群中的流量在转发到外部网络时可以正确地进行NAT处理,并提供网络隔离、安全性、负载均衡和故障转移等功能。
KUBE-MARK-DROP设置标记的报文则会在KUBE_FIREWALL中全部丢弃
iptables规则通过list类型数据结构进行管理,执行时间O(n),当svc和node节点数量很多时,iptables规则过多时,新规则的查询和创建会越来越慢,并引发性能问题
出现这样的差异,最主要的原因是
两者的性能压测情况,可以参考 对比 iptables 和 ipvs 的性能差异
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。