在k8s集群中,每个pod有自己独立的podIP及服务端口,但为了保证pod重启后(重启后IP发生变化,这不同于传统的物理机或虚拟环境)对客户端保持透明,k8s引入了clusterIP的概念,clusterIP用于集群间服务调用,对于外部是不可访问的,这个IP同时是个不存在的IP,k8s通过iptables实现了clusterIP的负载均衡及重启透明。

        今天我们通过测试环境的iptables分析来展示clusterIP的实现,测试集群包括10.244.1.3、10.244..2.3、10.244.3.3三个节点,每个节点都活跃一个frontendpod,如下:

	root@dev-4-control-plane# sh svc.sh
	namespace is:default
	|NAME           |CLUSTERIP       |PORT              |TARGETPORT  |ENDPOINTS
	|frontend       |10.96.214.50    |<unset>%80/TCP    |80/TCP      |10.244.1.3:80,10.244.2.3:80,10.244.3.3:80

        由于clusterIP用于集群间服务调用,对于调用方而言clusterIP到后端endpoint的负载均衡及NAT转换位于调用方iptables的OUTPUT链路上,如下:

	#1:root@dev-4-worker# iptables -t nat -nvL OUTPUT //可以看到这个服务交给了KUBE-SERVICES
	Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
	 pkts bytes target     prot opt in     out     source               destination
	  427 29227 KUBE-SERVICES  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kubernetes service portals */
		2   120 CNI-HOSTPORT-DNAT  all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

       那么我们看看KUBE-SERVICE链中做了什么?

	#2:root@dev-4-worker# iptables -t nat -nvL KUBE-SERVICES 
	Chain KUBE-SERVICES (2 references)
	 pkts bytes target     prot opt in     out     source               destination
		0     0 KUBE-SVC-YU5RV2YQWHLZ5XPR  tcp  --  *      *       0.0.0.0/0            10.96.165.129        /* default/redis-follower cluster IP */ tcp dpt:6379
		0     0 KUBE-SVC-LRNEBRA3Z5YGJ4QC  tcp  --  *      *       0.0.0.0/0            10.96.21.91          /* default/redis-leader cluster IP */ tcp dpt:6379
		0     0 KUBE-SVC-ENODL3HWJ5BZY56Q  tcp  --  *      *       0.0.0.0/0            10.96.214.50         /* default/frontend cluster IP */ tcp dpt:80
		0     0 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
		

        上面的内容中可以看到对于目的地址为10.96.214.50(frontend的clusterIP) 且目标端口为80的调用交给了KUBE-SVC-ENODL3HWJ5BZY56Q链,那么看看KUBE-SVC-ENODL3HWJ5BZY56Q链中做了什么呢?

	#3:root@dev-4-worker:/jinfh# iptables -t nat -nvL KUBE-SVC-ENODL3HWJ5BZY56Q 
	Chain KUBE-SVC-ENODL3HWJ5BZY56Q (1 references)
	 pkts bytes target     prot opt in     out     source               destination
		0     0 KUBE-MARK-MASQ  tcp  --  *      *      !10.244.0.0/16        10.96.214.50         /* default/frontend cluster IP */ tcp dpt:80
		0     0 KUBE-SEP-PUYWAIYQ7Z2WPEMT  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/frontend -> 10.244.1.3:80 */ statistic mode random probability 0.33333333349
		0     0 KUBE-SEP-BVLU66VZEIAHD7Y7  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/frontend -> 10.244.2.3:80 */ statistic mode random probability 0.50000000000
		0     0 KUBE-SEP-XH63RNNYRQN757HJ  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/frontend -> 10.244.3.3:80 */

        到了这里算是柳暗花明了吧,我们测试环境中的三个podIP终于在这里出现了。 这里定义了三条链路,分别对应10.244.1.3、10.244.2.3及10.244.3.3三个pod上的服务,其中1.3链路负载1/3的交易,2.3链路负载1/2的交易,而3.3链路则负载剩余1/6交易。接下来我们再看看这几条链路的具体定义,如下:

	#4.1:root@dev-4-worker# iptables -t nat -nvL KUBE-SEP-PUYWAIYQ7Z2WPEMT
	Chain KUBE-SEP-PUYWAIYQ7Z2WPEMT (1 references)
	 pkts bytes target     prot opt in     out     source               destination
		0     0 KUBE-MARK-MASQ  all  --  *      *       10.244.1.3           0.0.0.0/0            /* default/frontend */
		0     0 DNAT       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/frontend */ tcp to:10.244.1.3:80
		
	#4.2root@dev-4-worker# iptables -t nat -nvL KUBE-SEP-BVLU66VZEIAHD7Y7 
	Chain KUBE-SEP-BVLU66VZEIAHD7Y7 (1 references)
	 pkts bytes target     prot opt in     out     source               destination
		0     0 KUBE-MARK-MASQ  all  --  *      *       10.244.2.3           0.0.0.0/0            /* default/frontend */
		0     0 DNAT       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/frontend */ tcp to:10.244.2.3:80
		
	#4.3root@dev-4-worker# iptables -t nat -nvL KUBE-SEP-XH63RNNYRQN757HJ 
	Chain KUBE-SEP-XH63RNNYRQN757HJ (1 references)
	 pkts bytes target     prot opt in     out     source               destination
		0     0 KUBE-MARK-MASQ  all  --  *      *       10.244.3.3           0.0.0.0/0            /* default/frontend */
		0     0 DNAT       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/frontend */ tcp to:10.244.3.3:80

        从上面的链路定义中可以看到,每个链路分别包含一个DNAT操作,即destination NAT,也就将目标IP(clusterIP)进行转换,三条链路分别将转换成10.244.1.3:80、10.244.2.3:80、10.244.4.3:80,即我们交易网络数据流出时clusterIP:serverPort的组合会转换成对应链路后端的podIP: containerPort组合。

        到这里,读者大概看到通过iptables是如何实现clusterIP的了吧?

        最后我们在看一下,去往三条链路的网络数据是怎么流出的,如下:

	#5:root@dev-4-worker:/jinfh# route -n //通过路由表可以看到10.244.1.3、10.244.3.3的流量通过eth0分别转发到72.18.0.2、172.18.0.4,而10.244.2.3的流量则由虚拟网卡进行处理
	Kernel IP routing table
	Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
	0.0.0.0         172.18.0.1      0.0.0.0         UG    0      0        0 eth0
	10.244.0.0      172.18.0.5      255.255.255.0   UG    0      0        0 eth0
	10.244.1.0      172.18.0.2      255.255.255.0   UG    0      0        0 eth0
	10.244.2.2      0.0.0.0         255.255.255.255 UH    0      0        0 vethc528da70
	10.244.2.3      0.0.0.0         255.255.255.255 UH    0      0        0 veth321d2a93
	10.244.3.0      172.18.0.4      255.255.255.0   UG    0      0        0 eth0

         从上图可以看到,流向集群内其他节点的数据通过eth0流出,网关分别对应pod所在node的nodeIP,而本机数据则是通过veth(node上每个pod都有一个对应的veth)流出。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐