首先调整kubeproxy 的模式为iptables
kubectl edit configmap kube-proxy -n kube-system
修改
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: "ipvs" ##### 修改这一行留空则为iptables
nodePortAddresses: null
然后杀掉kubeproxy的pod ,让deployment 自动拉起来新配置pod
输出kubeproxy的pod 日志,看是否采用了iptables
kubectl logs kube-proxy-xxx -n kube-system
已经修改模式为iptables了。
目前集群已有svc
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default go-web-service NodePort 10.96.97.253 <none> 3009:30009/TCP 20d
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 72d
观察启动go-web-service这条的iptables 脉络
[root@ebs-76642 ~]# iptables -t nat -L KUBE-SERVICES |egrep 'go-web-service'
target prot opt source destination
KUBE-MARK-MASQ tcp -- !ebs-76642/16 ebs-76642 /* default/go-web-service:http cluster IP */ tcp dpt:pxc-ntfy
KUBE-SVC-KG4IN6CR36NHNLPD tcp -- anywhere ebs-76642 /* default/go-web-service:http cluster IP */ tcp dpt:pxc-ntfy
[root@ebs-76642 ~]# iptables -t nat -L KUBE-SVC-KG4IN6CR36NHNLPD
Chain KUBE-SVC-KG4IN6CR36NHNLPD (2 references)
target prot opt source destination
KUBE-SEP-TKPLZBGAW27QFDLO all -- anywhere anywhere statistic mode random probability 0.50000000000
KUBE-SEP-LEFZ7V5H5GEG5L4O all -- anywhere anywhere
[root@ebs-76642 ~]# iptables -t nat -L KUBE-SEP-TKPLZBGAW27QFDLO
Chain KUBE-SEP-TKPLZBGAW27QFDLO (1 references)
target prot opt source destination
KUBE-MARK-MASQ all -- 172.16.1.43 anywhere
DNAT tcp -- anywhere anywhere tcp to:172.16.1.43:3009
[root@ebs-76642 ~]# k get pods -A -o wide|grep '172.16.1.43'
default go-web-app-68797989b8-2vctz 1/1 Running 3 20d 172.16.1.43 ebs-82413 <none> <none>
观察kube-dns这条iptables 脉络,这里暴露了三个ports(dns-tcp,dns,metrics)
[root@ebs-76642 ~]# iptables -t nat -L KUBE-SERVICES |egrep 'kube-dns'
KUBE-MARK-MASQ tcp -- !ebs-76642/16 ebs-76642 /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:domain
KUBE-SVC-ERIFXISQEP7F7OF4 tcp -- anywhere ebs-76642 /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:domain
KUBE-MARK-MASQ udp -- !ebs-76642/16 ebs-76642 /* kube-system/kube-dns:dns cluster IP */ udp dpt:domain
KUBE-SVC-TCOU7JCQXEZGVUNU udp -- anywhere ebs-76642 /* kube-system/kube-dns:dns cluster IP */ udp dpt:domain
KUBE-MARK-MASQ tcp -- !ebs-76642/16 ebs-76642 /* kube-system/kube-dns:metrics cluster IP */ tcp dpt:9153
KUBE-SVC-JD5MR3NA4I4DYORP tcp -- anywhere ebs-76642 /* kube-system/kube-dns:metrics cluster IP */ tcp dpt:9153
- 先看ports name为dns-tcp 这个的
[root@ebs-76642 ~]# iptables -t nat -L KUBE-SVC-ERIFXISQEP7F7OF4
Chain KUBE-SVC-ERIFXISQEP7F7OF4 (1 references)
target prot opt source destination
KUBE-SEP-7LK5TTMYU3GPSJJW all -- anywhere anywhere statistic mode random probability 0.50000000000
KUBE-SEP-3NE7D5ZQGKE4MF2Z all -- anywhere anywhere
- 对应dns-tcp后端的两个pods
[root@ebs-76642 ~]# iptables -t nat -L KUBE-SEP-7LK5TTMYU3GPSJJW
Chain KUBE-SEP-7LK5TTMYU3GPSJJW (1 references)
target prot opt source destination
KUBE-MARK-MASQ all -- 172.16.0.15 anywhere
DNAT tcp -- anywhere anywhere tcp to:172.16.0.15:53
[root@ebs-76642 ~]# iptables -t nat -L KUBE-SEP-3NE7D5ZQGKE4MF2Z
Chain KUBE-SEP-3NE7D5ZQGKE4MF2Z (1 references)
target prot opt source destination
KUBE-MARK-MASQ all -- 172.16.1.44 anywhere
DNAT tcp -- anywhere anywhere tcp to:172.16.1.44:53
对于iptable方式的service:
流量从pod network namespace(cluster ip类型的service)或者外部(node port类型的service)进入到host netwok namespace之中。
在host netwok namespace的PREROUTING chain中会经过一系列target,KUBE-SERVICES(cluster ip类型的service),KUBE-NODEPORTS (node port类型的service),KUBE-SVC-XXX,KUBE-SEP-XXX。
在这些target里根据iptable内核随机模块random来实现匹配endpoint target,实现负载均衡。
在endpoint target(KUBE-SEP-XXX)里实现了DNAT,也就是将目标地址cluster ip转化为实际的pod的ip。
数据包经过以上修改根据host network namespace的路由表做下一跳路由选择。
对于ipvs方式的service:
流量从pod network namespace(cluster ip类型的service)或者外部(node port类型的service)进入到host netwok namespace之中。
对于clutser ip类型的service,在host netwok namespace的PREROUTING chain中经过匹配ipset KUBE-CLUSTER-IP做mask标记操作。
对于node port类型的service,在PREROUTING chain中经过匹配ipset KUBE-NODE-PORT-TCP做mask标记操作。
对于clutser ip类型的service,由于host network namespace中有创建网络设备kube-ipvs0,并且绑定所有cluster ip,这样从pod发出的数据包目标ip为cluster ip,有kube-ipvs0网络设备对应,数据进入INPUT chain中。
对于node port类型的service,由于数据包的目标ip是host的ip地址,所以也进入了host network namespace的INPUT chain中。
利用linux内核模块ipvs,数据在INPUT chain中被ipvs的规则修改(可由ipvsadm查看规则),完成负载均衡和DNAT,然后将数据直接送入POSTROUTING chain。
数据在POSTROUTING chain中,经过KUBE-POSTROUTING target,根据之前的mark操作完成MASQUERADE SNAT。
数据包经过以上修改根据host network namespace的路由表做下一跳路由选择。
对于iptable和ipvs方式的service:
两者都是采用linux内核模块完成负载均衡和endpoint的映射,所有操作都在内核空间完成,没有在应用程序的用户空间。
iptable方式依赖于linux netfilter/iptable内核模块。
ipvs方式依赖linux netfilter/iptable模块,ipset模块,ipvs模块。
iptable方式中,host宿主中ipatble的entry数目会随着service和对应endpoints的数目增多而增多。举个例子,比如有10个cluster ip类型的service,每个service有6个endpoints。那么在KUBE-SERVICES target中至少有10个entries(KUBE-SVC-XXX)与10个service对应,每个KUBE-SVC-XXX target中会有6个KUBE-SEP-XXX与6个endpoints来对应,每个KUBE-SEP-XXX会有2个enrties来分别做mark masq和DNAT,这样算起来至少有1062=120个entries在iptable中。试想如果application中service和endpoints数目巨大,iptable entries也是非常庞大的,在一定情况下有可能带来性能上的问题。
ipvs方式中host宿主中iptable的entry数目是固定的,因为iptable做匹配的时候会利用ipset(KUBE-CLUSTER-IP或者KUBE-NODE-PORT-TCP)来匹配,service的数目决定了ipset的大小,并不会影响iptable的大小。这样就解决了iptable模式下,entries随着service和endpoints的增多而增多的问题。
对于负载均衡,iptable方式采用random模块来完成负载均衡,ipvs方式支持多种负载均衡,例如round-robin,least connection,source hash等(可参考http://www.linuxvirtualserver.org/),并且由kubelet启动参数--ipvs-scheduler控制。
对于目标地址的映射,iptable方式采用linux原生的DNAT,ipvs方式则利用ipvs模块完成。
ipvs方式会在host netwok namespace中创建网络设备kube-ipvs0,并且绑定了所有的cluster ip,这样保证了cluster-ip类型的service数据进入INPUT chain,从而让ipvs来完成负载均衡和目标地址的映射。
iptable方式不会在host netwok namespace中创建额外的网络设备。
iptable方式数据在host network namespace的chain中的路径是:PREROUTING-->FORWARDING-->POSTROUTING
在PREROUTING chain中完成负载均衡,mark masq和目标地址映射。
ipvs方式数据在host network namespace的chain中的路径是:
PREROUTING-->INPUT-->POSTROUTING
在PREROUTING chain中完成mark masq SNAT,在INPUT chain利用ipvs完成负载均衡和目标地址映射。
iptable和ipvs方式在完成负载均衡和目标地址映射后都会根据host network namespace的路由表做下一跳路由选择。
关于iptable和ipvs方式的选择并没有固定答案,要根据项目的需求和实际情况而定。
iptables nat表中的详细过程
[root@docker3 ~]# iptables -nL -t nat -v
Chain PREROUTING (policy ACCEPT 2 packets, 1152 bytes)
pkts bytes target prot opt in out source destination
10837 3999K KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
====》1. 进入的请求,首先匹配PREROUTING 链里的该规则,并进入KUBE-SERVICES链
238 27286 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 2 packets, 1152 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 8 packets, 480 bytes)
pkts bytes target prot opt in out source destination
61563 3722K KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT 8 packets, 480 bytes)
pkts bytes target prot opt in out source destination
61840 3742K KUBE-POSTROUTING all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes postrouting rules */
====》6. 需要路由到另一个主机的连接经由POSTROUTING该规则,进入KUBE-POSTROUTING 链
62 4323 MASQUERADE all -- * !docker0 10.2.39.0/24 0.0.0.0/0
0 0 MASQUERADE all -- * !docker0 10.1.33.0/24 0.0.0.0/0
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
Chain KUBE-MARK-DROP (0 references)
pkts bytes target prot opt in out source destination
0 0 MARK all -- * * 0.0.0.0/0 0.0.0.0/0 MARK or 0x8000
Chain KUBE-MARK-MASQ (6 references)
pkts bytes target prot opt in out source destination
2 128 MARK all -- * * 0.0.0.0/0 0.0.0.0/0 MARK or 0x4000
Chain KUBE-NODEPORTS (1 references)
pkts bytes target prot opt in out source destination
2 128 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/k8s-nginx: */ tcp dpt:30780
2 128 KUBE-SVC-2RMP45C4XWDG5BGC tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/k8s-nginx: */ tcp dpt:30780
====》3.请求命中上述两个规则,一个进行进入KUBE-MARK-MASQ 链进行标记,一个进入KUBE-SVC-2RMP45C4XWDG5BGC链
Chain KUBE-POSTROUTING (1 references)
pkts bytes target prot opt in out source destination
1 64 MASQUERADE all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */ mark match 0x4000/0x4000
====》7. 执行源地址转换(在flannel网络这里转换的地址是flannel.1即flannel在ifconfig里输出接口的地址),发往另一个node
Chain KUBE-SEP-D5T62RWZFFOCR77Q (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.2.39.3 0.0.0.0/0 /* default/k8s-nginx: */
1 64 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/k8s-nginx: */ tcp to:10.2.39.3:80
====》5-2. DNAT到本机,交给INPUT
Chain KUBE-SEP-IK3IYR4STYKRJP77 (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.2.39.2 0.0.0.0/0 /* kube-system/kube-dns:dns-tcp */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* kube-system/kube-dns:dns-tcp */ tcp to:10.2.39.2:53
Chain KUBE-SEP-WV6S37CDULKCYEVE (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.2.39.2 0.0.0.0/0 /* kube-system/kube-dns:dns */
0 0 DNAT udp -- * * 0.0.0.0/0 0.0.0.0/0 /* kube-system/kube-dns:dns */ udp to:10.2.39.2:53
Chain KUBE-SEP-X7YOSBI66WAQ7F6X (2 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 172.16.199.17 0.0.0.0/0 /* default/kubernetes:https */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/kubernetes:https */ recent: SET name: KUBE-SEP-X7YOSBI66WAQ7F6X side: source mask: 255.255.255.255 tcp to:172.16.199.17:6443
Chain KUBE-SEP-YXWG4KEJCDIRMCO5 (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.2.4.2 0.0.0.0/0 /* default/k8s-nginx: */
1 64 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/k8s-nginx: */ tcp to:10.2.4.2:80
====》5-1. DNAT到另一个node,随后执行postrouting
Chain KUBE-SERVICES (2 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-SVC-2RMP45C4XWDG5BGC tcp -- * * 0.0.0.0/0 169.169.148.143 /* default/k8s-nginx: cluster IP */ tcp dpt:80
0 0 KUBE-SVC-NPX46M4PTMTKRN6Y tcp -- * * 0.0.0.0/0 169.169.0.1 /* default/kubernetes:https cluster IP */ tcp dpt:443
0 0 KUBE-SVC-TCOU7JCQXEZGVUNU udp -- * * 0.0.0.0/0 169.169.0.53 /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
0 0 KUBE-SVC-ERIFXISQEP7F7OF4 tcp -- * * 0.0.0.0/0 169.169.0.53 /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
2 128 KUBE-NODEPORTS all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL
====》2.连接随后并该规则匹配,进入KUBE-NODEPORTS 链
Chain KUBE-SVC-2RMP45C4XWDG5BGC (2 references)
pkts bytes target prot opt in out source destination
1 64 KUBE-SEP-D5T62RWZFFOCR77Q all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/k8s-nginx: */ statistic mode random probability 0.50000000000
1 64 KUBE-SEP-YXWG4KEJCDIRMCO5 all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/k8s-nginx: */
====》4.请求被该链中两个规则匹配,这里只有两个规则,按照50% RR规则进行负载均衡分发,两个目标链都是进行DNAT,一个转到本node的pod IP上,一个转到另一台宿主机的pod上,因为该service下只有两个pod
Chain KUBE-SVC-ERIFXISQEP7F7OF4 (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-SEP-IK3IYR4STYKRJP77 all -- * * 0.0.0.0/0 0.0.0.0/0 /* kube-system/kube-dns:dns-tcp */
Chain KUBE-SVC-NPX46M4PTMTKRN6Y (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-SEP-X7YOSBI66WAQ7F6X all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/kubernetes:https */ recent: CHECK seconds: 10800 reap name: KUBE-SEP-X7YOSBI66WAQ7F6X side: source mask: 255.255.255.255
0 0 KUBE-SEP-X7YOSBI66WAQ7F6X all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/kubernetes:https */
Chain KUBE-SVC-TCOU7JCQXEZGVUNU (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-SEP-WV6S37CDULKCYEVE all -- * * 0.0.0.0/0 0.0.0.0/0 /* kube-system/kube-dns:dns */