概述
从kubernetes最早开始,kube-proxy到现在总共支持三种模式,在v1.8之前我们使用的是iptables 以及 userspace两种模式,iptables 模式从 v1.2 版本开始引入并作为kube-proxy 默认的操作模式。在kubernetes 1.8之后引入了ipvs模式,并且在v1.11中正式使用,其中iptables和ipvs都是内核态也就是基于netfilter,只有userspace模式是用户态。下面详细介绍下各个模式:
userspace
在k8s v1.2后就已经被淘汰了,该模式下kube-proxy会为每一个Service创建一个监听端口。发向Cluster IP的请求被Iptables规则重定向到kube-proxy监听的端口上,kube-proxy根据LB算法选择一个提供服务的Pod并和其建立连接,以将请求转发到Pod上。该模式下,Kube-proxy充当了一个四层Load balancer的角色,proxy会为每个svc随机监听一个端口,并增加一个iptables规则,从客户端到 ClusterIP:Port 的报文都会被重定向到 proxy port,kube-proxy 收到报文后,通过 Round Robin (轮询) 或者 Session Affinity分发给对应的 Pod。然而由于kube-proxy运行在userspace中,在进行转发处理时会增加两次内核和用户空间之间的数据拷贝,效率较另外两种模式低一些。如下图为userspace的工作流程。
iptables
iptables模式是从kubernetes1.2开始引入并在v1.12之前使用的默认模式。该模式避免了增加内核和用户空间的数据拷贝操作,提高了转发效率。在这种模式下proxy监控kubernetes对svc和ep对象的改变,直接为service后端的每个Pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod IP。并且这种模式使用iptables来做用户态的入口,而真正提供服务的是内核的Netilter,Netfilter采用模块化设计,具有良好的可扩充性。其重要工具模块IPTables从用户态的iptables连接到内核态的Netfilter的架构中,Netfilter与IP协议栈是无缝契合的,并允许使用者对数据报进行过滤、地址转换、处理等操作。该模式下iptables由kube-proxy动态的管理,kube-proxy不再负责转发,只负责创建iptables规则。数据包的走向完全由iptables规则决定,这样的过程不存在内核态到用户态的切换,效率明显会高很多。流量的转发都是在内核态进行的,所以性能更高更加可靠。
但是随着service的增加,iptables规则会不断增加,导致内核十分繁忙。并且iptables修改了规则后必须得全部刷新才可以生效。如下图为iptables的工作流程。
ipvs
参考文档https://github.com/kubernetes/kubernetes/blob/master/pkg/proxy/ipvs/README.md#prerequisit
IPVS(IP Virtual Server,IP虚拟服务器)实现了传输层的负载平衡,通常称为4 LAN(四层局域网)交换,是Linux内核的一部分。IPVS在主机上运行,在真实服务器集群前面充当负载平衡器。IPVS可以将基于 TCP 和 UDP 的服务请求定向到真实服务器上。
kubernetes从1.8开始增加了IPVS支持,它也是基于netfilter实现的,但定位不同:iptables是为防火墙设计的,IPVS则专门用于高性能,IPVS相对于iptables来说效率会更加高,使用ipvs模式需要在允许proxy的节点上安装ipvsadm,ipset工具包加载ipvs的内核模块。
负载均衡,并使用高效的数据结构Hash表,允许几乎无限的规模扩张。一句话说明:ipvs使用ipset存储iptables规则,在查找时类似hash表查找,时间复杂度为O(1),而iptables时间复杂度则为O(n),使得ipvs可以轻松处理每秒 10 万次以上的转发请求。
当proxy启动的时候,proxy将验证节点上是否安装了ipvs模块。如果未安装的话将回退到iptables模式。在Kubernetes 1.12成为kube-proxy的默认代理模式。对比iptables模式在大规模Kubernetes集群有更好的扩展性和性能,支持更加复杂的负载均衡算法(如:最小负载、最少连接、加权等),支持Server的健康检查和连接重试等功能。ipvs依赖于iptables,由于ipvs 无法提供包过滤、SNAT、masquared(伪装)等功能。因此在某些场景(如Nodeport的实现)下还是要与iptables搭配使用,ipvs 将使用ipset来存储需要DROP或masquared的流量的源或目标地址,以确保 iptables 规则的数量是恒定的。假设要禁止上万个IP访问我们的服务器,则用iptables的话,就需要一条一条地添加规则,会在iptables中生成大量的规则;但是使用ipset的话,只需要将相关的IP地址(网段)加入到ipset集合中即可,这样只需要设置少量的iptables规则即可实现目标。
这种模式,Kube-Proxy 会监视 Kubernetes Service 对象 和 Endpoints,调用 Netlink 接口以相应地创建 IPVS 规则并定期与 Kubernetes Service 对象 和 Endpoints 对象同步 IPVS 规则,以确保 IPVS 状态与期望一致。访问服务时,流量将被重定向到其中一个后端 Pod上。
那么 ipvs 模式和 iptables 模式之间有哪些差异呢?
- ipvs 为大型集群提供了更好的可扩展性和性能
- ipvs 支持比 iptables 更复杂的复制均衡算法(最小负载、最少连接、加权等等)
- ipvs 支持服务器健康检查和连接重试等功能
- 可以动态修改ipset集合。即使iptables的规则正在使用这个集合。
kube-proxy使用ipvs模式
在每台机器上安装依赖包:
[root@k8s-m1 ~]# yum install ipvsadm ipset sysstat conntrack libseccomp -y
##所有机器选择需要开机加载的内核模块,以下是 ipvs 模式需要加载的模块并设置开机自动加载
[root@k8s-m1 ~]# > /etc/modules-load.d/ipvs.conf #先清空
module=(
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack
br_netfilter
)
for kernel_module in ${module[@]};do
/sbin/modinfo -F filename $kernel_module |& grep -qv ERROR && echo $kernel_module >> /etc/modules-load.d/ipvs.conf
done
systemctl enable --now systemd-modules-load.service
上面用systemctl enable
命令如果报错可以用systemctl status -l systemd-modules-load.service
看看哪个内核模块加载不了,在/etc/modules-load.d/ipvs.conf
里注释掉它再enable试试
所有机器需要设定/etc/sysctl.d/k8s.conf的系统参数。
[root@k8s-m1 ~]# cat > /etc/sysctl.d/k8s.conf <<EOF
# https://github.com/moby/moby/issues/31208
# ipvsadm -l --timout
# 修复ipvs模式下长连接timeout问题 小于900即可
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 10
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
net.ipv4.neigh.default.gc_stale_time = 120
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2
net.ipv4.conf.all.arp_announce = 2
net.ipv4.ip_forward = 1
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 1024
net.ipv4.tcp_synack_retries = 2
# 要求iptables不对bridge的数据进行处理
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-arptables = 1
net.netfilter.nf_conntrack_max = 2310720
fs.inotify.max_user_watches=89100
fs.may_detach_mounts = 1
fs.file-max = 52706963
fs.nr_open = 52706963
vm.swappiness = 0
vm.overcommit_memory=1
vm.panic_on_oom=0
EOF
[root@k8s-m1 ~]# sysctl --system
修改Kube-proxy配置文件将mode设置为ipvs
使用kubeadm部署的k8s集群中,kube-proxy的配置文件是用configmap存放的,可以通过edit更改后重启kube-proxy的pod。
[root@k8s-m1 ~]# kubectl edit cm kube-proxy -n kube-system
......
iptables:
masqueradeAll: true
masqueradeBit: 14
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: rr
strictARP: false
syncPeriod: 15s
tcpFinTimeout: 0s
tcpTimeout: 0s
udpTimeout: 0s
kind: KubeProxyConfiguration
metricsBindAddress: ""
mode: ipvs
nodePortAddresses: null
oomScoreAdj: null
......
#修改mode模式为ipvs即可
创建 ClusterIP 类型服务时,IPVS proxier 将执行以下三项操作:
- 确保节点中存在虚拟接口,默认为 kube-ipvs0
- 将Service IP 地址绑定到虚拟接口
- 分别为每个Service IP 地址创建 IPVS virtual servers
## pod的yml文件
[root@k8s-m1 k8s-total]# cat nginx-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
labels:
app: nginx
spec:
selector:
matchLabels:
tier: frontend
replicas: 1
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: nginx-gateway
image: nginx
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 80
#svc的yml文件
[root@k8s-m1 k8s-total]# cat nginx-svc.yml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
ports:
- port: 80
selector:
tier: frontend
#创建
[root@k8s-m1 k8s-total]#kubectl apply -f nginx-deployment.yml -f nginx-svc.yml
#查看svc
[root@k8s-m1 k8s-total]# kubectl get svc nginx-service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service ClusterIP 10.111.157.115 <none> 80/TCP 2d12h
#查看pod
[root@k8s-m1 k8s-total]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
configmap-subpath 1/1 Running 43 43h 10.244.42.136 k8s-m1 <none> <none>
my-nginx-7ff446c4f4-2d9gz 1/1 Running 0 9m42s 10.244.42.143 k8s-m1 <none> <none>
my-nginx-7ff446c4f4-tbtpc 1/1 Running 0 8m8s 10.244.11.12 k8s-m3 <none> <none>
#查看ipvs转发表
[root@k8s-m1 k8s-total]# ipvsadm -Ln
TCP 10.111.157.115:80 rr
-> 10.244.11.12:80 Masq 1 0 0
-> 10.244.42.143:80 Masq 1 0 0
TCP 10.244.42.151:30080 rr
-> 10.244.11.12:80 Masq 1 0 0
-> 10.244.42.143:80 Masq 1 0 0
TCP 10.244.42.151:30119 rr
TCP 10.244.42.151:30404 rr
通过查看ipvs的转发表,发现是按照我们预想的转发。
删除 Kubernetes Service将触发删除相应的 IPVS 虚拟服务器,IPVS 物理服务器及其绑定到虚拟接口的 IP 地址。
端口映射:
IPVS 中有三种代理模式:NAT(masq),IPIP 和 DR。 只有 NAT 模式支持端口映射。 Kube-proxy 利用 NAT 模式进行端口映射。 以下示例显示 IPVS 服务端口80到Pod端口80的映射。
[root@k8s-m1 k8s-total]# ipvsadm -Ln
TCP 10.111.157.115:80 rr
-> 10.244.11.12:80 Masq 1 0 0
-> 10.244.42.143:80 Masq 1 0 0
会话关系:
IPVS 支持客户端 IP 会话关联(持久连接)。 当服务指定会话关系时,IPVS 代理将在 IPVS 虚拟服务器中设置超时值(默认为180分钟= 10800秒)。
ipvs proxier 将在以下5种情况下依赖于 iptables:
- kube-proxy 设置
--masquerade-all = true
- kube-proxy 设置
--cluster-cidr=<cidr>
- Load Balancer 类型的 Service
- NodePort 类型的 Service
- 指定 externalIPs 的 Service
kube-proxy 配置参数 –masquerade-all=true
如果 kube-proxy 配置了–masquerade-all=true参数,则 ipvs 将伪装所有访问 Service 的 Cluster IP 的流量,此时的行为和 iptables 是一致的,由 ipvs 添加的 iptables 规则如下:
[root@k8s-m1 ~]# iptables -t nat -nL
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
KUBE-SERVICES all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
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 */
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
KUBE-POSTROUTING all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes postrouting rules */
Chain KUBE-MARK-MASQ (2 references)
target prot opt source destination
MARK all -- 0.0.0.0/0 0.0.0.0/0 MARK or 0x4000
Chain KUBE-POSTROUTING (1 references)
target prot opt source destination
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */ mark match 0x4000/0x4000
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-LOOP-BACK dst,dst,src
Chain KUBE-SERVICES (2 references)
target prot opt source destination
KUBE-MARK-MASQ all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-CLUSTER-IP dst,dst
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-CLUSTER-IP dst,dst
在 kube-proxy 启动时指定集群 CIDR
如果 kube-proxy 配置了–cluster-cidr=参数,则 ipvs 会伪装所有访问 Service Cluster IP 的外部流量,其行为和 iptables 相同,假设 kube-proxy 提供的集群 CIDR 值为:10.244.16.0/24,那么 ipvs 添加的 iptables 规则应该如下所示:
# iptables -t nat -nL
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
KUBE-SERVICES all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
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 */
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
KUBE-POSTROUTING all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes postrouting rules */
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
Chain KUBE-POSTROUTING (1 references)
target prot opt source destination
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */ mark match 0x4000/0x4000
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-LOOP-BACK dst,dst,src
Chain KUBE-SERVICES (2 references)
target prot opt source destination
KUBE-MARK-MASQ all -- !10.244.16.0/24 0.0.0.0/0 match-set KUBE-CLUSTER-IP dst,dst
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-CLUSTER-IP dst,dst
Load Balancer 类型的 Service
对于loadBalancer类型的服务,ipvs 将安装匹配 KUBE-LOAD-BALANCER 的 ipset 的 iptables 规则。特别当服务的 LoadBalancerSourceRanges 被指定或指定 externalTrafficPolicy=local 的时候,ipvs 将创建 ipset 集合KUBE-LOAD-BALANCER-LOCAL/KUBE-LOAD-BALANCER-FW/KUBE-LOAD-BALANCER-SOURCE-CIDR,并添加相应的 iptables 规则,如下所示规则:
# iptables -t nat -nL
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
KUBE-SERVICES all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
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 */
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
KUBE-POSTROUTING all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes postrouting rules */
Chain KUBE-FIREWALL (1 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-LOAD-BALANCER-SOURCE-CIDR dst,dst,src
KUBE-MARK-DROP all -- 0.0.0.0/0 0.0.0.0/0
Chain KUBE-LOAD-BALANCER (1 references)
target prot opt source destination
KUBE-FIREWALL all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-LOAD-BALANCER-FW dst,dst
RETURN all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-LOAD-BALANCER-LOCAL dst,dst
KUBE-MARK-MASQ all -- 0.0.0.0/0 0.0.0.0/0
Chain KUBE-MARK-DROP (1 references)
target prot opt source destination
MARK all -- 0.0.0.0/0 0.0.0.0/0 MARK or 0x8000
Chain KUBE-MARK-MASQ (2 references)
target prot opt source destination
MARK all -- 0.0.0.0/0 0.0.0.0/0 MARK or 0x4000
Chain KUBE-POSTROUTING (1 references)
target prot opt source destination
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */ mark match 0x4000/0x4000
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-LOOP-BACK dst,dst,src
Chain KUBE-SERVICES (2 references)
target prot opt source destination
KUBE-LOAD-BALANCER all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-LOAD-BALANCER dst,dst
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-LOAD-BALANCER dst,dst
NodePort 类型的 Service
对于 NodePort 类型的服务,ipvs 将添加匹配KUBE-NODE-PORT-TCP/KUBE-NODE-PORT-UDP的 ipset 的iptables 规则。当指定externalTrafficPolicy=local时,ipvs 将创建 ipset 集KUBE-NODE-PORT-LOCAL-TC/KUBE-NODE-PORT-LOCAL-UDP并安装相应的 iptables 规则,如下所示:(假设服务使用 TCP 类型 nodePort)
|
# iptables -t nat -nL
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
KUBE-SERVICES all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
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 */
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
KUBE-POSTROUTING all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes postrouting rules */
Chain KUBE-MARK-MASQ (2 references)
target prot opt source destination
MARK all -- 0.0.0.0/0 0.0.0.0/0 MARK or 0x4000
Chain KUBE-NODE-PORT (1 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-NODE-PORT-LOCAL-TCP dst
KUBE-MARK-MASQ all -- 0.0.0.0/0 0.0.0.0/0
Chain KUBE-POSTROUTING (1 references)
target prot opt source destination
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */ mark match 0x4000/0x4000
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-LOOP-BACK dst,dst,src
Chain KUBE-SERVICES (2 references)
target prot opt source destination
KUBE-NODE-PORT all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-NODE-PORT-TCP dst
指定 externalIPs 的 Service
对于指定了externalIPs的 Service,ipvs 会安装匹配KUBE-EXTERNAL-IP ipset 集的 iptables 规则,假设我们有指定了 externalIPs 的 Service,则 iptables 规则应该如下所示:
# iptables -t nat -nL
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
KUBE-SERVICES all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
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 */
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
KUBE-POSTROUTING all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes postrouting rules */
Chain KUBE-MARK-MASQ (2 references)
target prot opt source destination
MARK all -- 0.0.0.0/0 0.0.0.0/0 MARK or 0x4000
Chain KUBE-POSTROUTING (1 references)
target prot opt source destination
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */ mark match 0x4000/0x4000
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-LOOP-BACK dst,dst,src
Chain KUBE-SERVICES (2 references)
target prot opt source destination
KUBE-MARK-MASQ all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-EXTERNAL-IP dst,dst
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-EXTERNAL-IP dst,dst PHYSDEV match ! --physdev-is-in ADDRTYPE match src-type !LOCAL
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-EXTERNAL-IP dst,dst ADDRTYPE match dst-type LOCAL
IPVS 模式
入流量
入流量是指由集群外部访问 service 的流量。
Iptables 入流量的 chain 路径是 PREROUTING@nat -> INPUT@nat。
ClusterIP
Iptables 入流量的 chain 路径是 PREROUTING@nat -> INPUT@nat。
在 PREROUTING 阶段,流量跳转到 KUBE-SERVICES target chain:
[root@k8s-m1 ~]# iptables -L -n -t nat
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
cali-PREROUTING all -- 0.0.0.0/0 0.0.0.0/0 /* cali:6gwbT8clXdHdC1b1 */
KUBE-SERVICES all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
KUBE-SERVICES chain 如下:
Chain KUBE-SERVICES (2 references)
target prot opt source destination
KUBE-LOAD-BALANCER all -- 0.0.0.0/0 0.0.0.0/0 /* Kubernetes service lb portal */ match-set KUBE-LOAD-BALANCER dst,dst
KUBE-MARK-MASQ all -- 0.0.0.0/0 0.0.0.0/0 /* Kubernetes service cluster ip + port for masquerade purpose */ match-set KUBE-CLUSTER-IP dst,dst
KUBE-MARK-MASQ all -- 0.0.0.0/0 0.0.0.0/0 /* Kubernetes service external ip + port for masquerade and filter purpose */ match-set KUBE-EXTERNAL-IP dst,dst
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 /* Kubernetes service external ip + port for masquerade and filter purpose */ match-set KUBE-EXTERNAL-IP dst,dst PHYSDEV match ! --physdev-is-in ADDRTYPE match src-type !LOCAL
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 /* Kubernetes service external ip + port for masquerade and filter purpose */ match-set KUBE-EXTERNAL-IP dst,dst ADDRTYPE match dst-type LOCAL
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
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 match-set KUBE-LOAD-BALANCER dst,dst
ClusterIP service 的访问流量会交由 KUBE-MARK-MASQ处理,其匹配规则是匹配内核中名为 KUBE-CLUSTER-IP 的 ipset(将源地址不是10.244.0.0/16的IP交由KUBE-MARK-MASQ)。
下一步就是为这些包打上标记:
Chain KUBE-MARK-MASQ (4 references)
target prot opt source destination
MARK all -- 0.0.0.0/0 0.0.0.0/0 MARK or 0x4000
此时封包在 iptables 的路径已经走完,并没有后续的 DNAT 到后端 endpoint 的流程,这一步的工作交由 IPVS 来完成。(这步是仅仅给ipset list中的KUBE-CLUSTER-IP 添加了一个标签0x4000,有此标记的数据包会在KUBE-POSTROUTING chain中统一做MASQUERADE)
检查 ipvs 代理规则
用户可以使用ipvsadm工具检查 kube-proxy 是否维护正确的 ipvs 规则,比如,我们在集群中有以下一些服务:
[root@k8s-m1 k8s-total]# kubectl get svc -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 412d
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 412d
我们可以得到如下的一些 ipvs 代理规则:
[root@k8s-m1 k8s-total]# ipvsadm -Ln
TCP 10.96.0.1:443 rr
-> 192.168.2.140:6443 Masq 1 0 0
-> 192.168.2.141:6443 Masq 1 1 0
-> 192.168.2.142:6443 Masq 1 1 0
TCP 10.96.0.10:53 rr
-> 10.244.11.58:53 Masq 1 0 0
-> 10.244.81.166:53 Masq 1 0 0
出流量
出流量是指由集群内的 pod 访问 service 的流量。
Iptables 出流量的 chain 路径是 OUTPUT@nat -> POSTROUTING@nat。
OUTPUT chain 如下,与入流量情形一样,也是所有流量跳转到 KUBE-SERVICES chain:
[root@k8s-m1 k8s-total]# iptables -L -n -t nat
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
cali-OUTPUT all -- 0.0.0.0/0 0.0.0.0/0 /* cali:tVnHkvAo15HuiPy0 */
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
而后的动作与入流量一致,不论 ClusterIP service 还是 NodePort service,都是为封包打上 0x4000 的标记。区别是至此入流量的 iptables 流程走完,而出流量还需要经过 nat 表的 POSTROUTING chain,其定义如下:
[root@k8s-m1 k8s-total]# iptables -L -n -t nat
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
cali-POSTROUTING all -- 0.0.0.0/0 0.0.0.0/0 /* cali:O3lYWMrLQYEMJtB5 */
KUBE-POSTROUTING all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes postrouting rules */
MASQUERADE all -- 172.16.0.0/16 0.0.0.0/0
MASQUERADE all -- 172.19.0.0/16 0.0.0.0/0
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
进一步跳转到 KUBE-POSTROUTING chain:Chain KUBE-POSTROUTING (1 references)
Chain KUBE-POSTROUTING (1 references)
target prot opt source destination
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 /* Kubernetes endpoints dst ip:port, source ip for solving hairpin purpose */ match-set KUBE-LOOP-BACK dst,dst,src
RETURN all -- 0.0.0.0/0 0.0.0.0/0 mark match ! 0x4000/0x4000
MARK all -- 0.0.0.0/0 0.0.0.0/0 MARK xor 0x4000
MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */
在这里,会为之前打上 0x4000 标记的出流量封包执行 MASQUERADE target,即类似于 SNAT 的一种操作,将其来源 IP 变更为 ClusterIP 或 Node ip。
被打了标记的流量处理方式
[root@k8s-m1 ~]# iptables -L -n
Chain KUBE-FIREWALL (2 references)
target prot opt source destination
DROP all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes firewall for dropping marked packets */ mark match 0x8000/0x8000
DROP all -- !127.0.0.0/8 127.0.0.0/8 /* block incoming localnet connections */ ! ctstate RELATED,ESTABLISHED,DNAT
Chain KUBE-FORWARD (1 references)
target prot opt source destination
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes forwarding rules */ mark match 0x4000/0x4000
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes forwarding conntrack pod source rule */ ctstate RELATED,ESTABLISHED
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes forwarding conntrack pod destination rule */ ctstate RELATED,ESTABLISHED
ipset命令使用(iptables 中 match-set 匹配的就是就这里的地址 )
[root@k8s-m1 ~]# ipset list
Name: KUBE-CLUSTER-IP
Type: hash:ip,port
Revision: 5
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 3192
References: 2
Number of entries: 48
Members:
10.110.139.178,tcp:15443
10.108.18.160,tcp:8443
10.110.240.216,tcp:80
10.96.246.188,tcp:80
10.110.139.178,tcp:80
10.99.232.160,tcp:9411
10.100.7.245,tcp:14250
10.103.189.79,tcp:8000
10.96.0.10,tcp:9153
10.99.242.17,tcp:80
10.98.180.236,tcp:80
10.101.138.128,tcp:16686
10.97.144.128,tcp:853
10.101.230.40,tcp:9080
10.100.7.245,tcp:14268
10.110.57.244,tcp:9080
10.111.157.115,tcp:80
10.108.18.160,tcp:8080
10.101.101.153,tcp:8000
10.96.0.1,tcp:443
10.106.45.53,tcp:15443
10.97.144.128,udp:53
10.106.45.53,tcp:443
10.106.45.53,tcp:80
10.99.172.101,tcp:5000
10.98.90.254,tcp:443
10.97.144.128,tcp:443
10.98.189.216,tcp:20001
10.110.139.178,tcp:443
10.97.144.128,tcp:15012
10.100.7.245,tcp:14267
10.96.0.10,tcp:53
10.97.144.128,tcp:15010
10.104.103.99,tcp:9090
10.111.82.238,tcp:443
10.97.144.128,tcp:15014
10.97.164.0,tcp:80
10.106.150.183,tcp:9090
10.97.154.242,tcp:8080
10.104.131.196,tcp:9411
10.99.22.164,tcp:3306
10.104.193.235,tcp:443
10.110.33.30,tcp:9080
10.110.139.178,tcp:15020
10.110.139.178,tcp:31400
10.96.0.10,udp:53
10.97.172.70,tcp:3000
10.106.73.215,tcp:443
官方文档:https://github.com/kubernetes/kubernetes/blob/master/pkg/proxy/ipvs/README.md
更多关于kubernetes的知识分享,请前往博客主页。编写过程中,难免出现差错,敬请指出