微信公众号:LinuGo,欢迎关注Pod如何找到请求的Pod,以及Pod之间如何负载均衡(iptables模式下)。要想知道这个问题,首先需要了解一些iptables的知识。iptables知识链五iptables是Linux内核中集成的IP信息过滤规则,负责对发往主机的网络数据包进行分发和转换。当客户端请求服务器的某项服务时,请求信息首先会通过网卡进入服务器内核。这时候iptables会对数据包进行过滤,判断这些数据包是发送给用户态的服务进程,还是转发给其他主机。确定这些路径的方式在iptables中称为链。刚进入内核的请求流会经过PREROUTING链。根据路由规则判断是否发往本机请求。会经过FORWARD链并匹配相应的规则,最终流出机器;如果请求是本机发出的,会经过OUTPUT链进一步到POSTROUTINE链流出本机,或者转发给其他机器或者回复客户端。总结上述链条:PREROUTINE:流入本地路由之前POSTROUTINE:流出本地路由之前FORWARD:转发路径OUTPUT:本地用户程序发送的INPUT:发送给本地用户程序的两个动作SNAT源地址转换,它是指将消息发送方的IP地址进行转换,使得对应方回复请求时,回复的是发送方的地址。例子明白,当客户端向服务器端发送请求时,需要经过网关。如果网关不对报文进行源地址转换(SNAT),发送给服务器的网络报文中携带的源地址仍然是客户端,服务器会响应源地址,但客户端不识别服务器的地址,这将导致本次请求出错。DNAT目标地址转换是将报文的目标地址进行转换,以将请求转发到其他目的地。k8s基础知识下面我们来了解一下k8s中的几种IP类型。VirtualIP虚拟IP(以下简称VIP)有ClusterIP(即serviceIP),由集群自己生成,无法ping通,与PodIP不在同一网段,避免请求混淆。在创建服务时,k8s会为服务分配一个IP地址,集群中所有的kube-proxy都会观察到这个地址,kube-proxy会在主机上安装一系列iptables规则,kube-dns会做出相应的响应插入域名解析IP规则。当请求到来时,如果符合规则,iptables就会将VIP转换成实际的IP并使用。实际IP和realIP包括PodIP等。IP由CNI插件分配。k8s集群启动时,需要安装CNI插件,通常由一个DaemonSet控制器来控制,保证每个节点都有这个进程。它的作用是在集群内部生成一组网络,并为每个pod插上“网线”,保证pod与node、pod与pod的互通性。Pod之间的通信方式可以通过实际的PodIP,但是IP会随着pod的变化而变化。这种方法不合适。您还可以通过ClusterIP方法进行通信。比较稳定,就是不好记,还可以。该方法通过svc.ns的域名格式请求kube-dns域名解析,获取域名对应的IP。案例研究接下来,我们讨论一个案例研究。案例介绍集群中有两个gateway-pod,服务类型为NodePort,端口暴露在集群外部,接收外部请求并进行负载均衡,并将请求转发给下游的pod。有一个basedaopod,负责接收和响应gatewaypod发送的请求。对应的服务名称为sts-basedao,命名空间名称为basedao。Kube-dnspod将域名解析为IP地址并将其返回给发件人。检查每个pod的IP,是真实的IP。[centos@guozhao-50~]$kubectlgetpod--all-namespaces-owideNAMESPACENAMEIPbasedaosts-basedao-010.34.0.2gatewaysts-gateway-59d5fdbc54-4d6kv10.40.0.0gatewaysts-gateway-59d5fdbc54-dmncq10.40.0.0gateway.0.1kube-systemcoredns-6c76c8bb89-kztxk10.32.0.4查看三个pod对应的服务IP,可以看到网关服务代理的30001端口映射到外部31001端口。[centos@guozhao-50~]$kubectlgetsvc--all-namespacesNAMESPACENAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGEbasedaosvc-basedaoClusterIP10.111.143.22330003/TCP70mgatewaysvc-gateway1Node0Port2.10.103.130001:31001/TCP70mkube-systemkube-dnsClusterIP10.96.0.1053/UDP,53/TCP,9153/TCP14d1.客户端通过iptables-save发起查看所有iptables的请求命令规则。当客户端发起请求时,会请求服务端对应的端口,对应的是NodePort端口。我们查看对应的iptables规则,PREROUTING后会跳转到KUBE-SERVICES规则。-APREROUTING-mcomment--comment"kubernetesserviceportals"-jKUBE-SERVICES在KUBE-SERVICE中查找匹配规则,--dst-typeLOCAL表示本地,会继续跳转到KUBE-NODEPORTS-AKUBE-SERVICES-mcomment--comment"kubernetesservicenodeports;NOTE:thismustbethelastruleinthischain"-maddrtype--dst-typeLOCAL-jKUBE-NODEPORTS请求会跳转到KUBE-MARK-MASQ去请求a标签并跳转到下一条规则KUBE-SVC-4QJQIYWH2AWK2TSA。-AKUBE-NODEPORTS-ptcp-mcomment--comment"gateway/svc-gateway:gateway-port"-mtcp--dport31001-jKUBE-MARK-MASQ-AKUBE-NODEPORTS-ptcp-m评论--comment"gateway/svc-gateway:gateway-port"-mtcp--dport31001-jKUBE-SVC-4QJQIYWH2AWK2TSA负载均衡遵循上述规则,对应的规则有两条。--probability0.50000000000表示有0.5的概率输入上面的规则,如果不输入上面的规则,就会输入下面的规则,在iptables层面实现负载均衡。-AKUBE-SVC-4QJQIYWH2AWK2TSA-m评论--comment"gateway/svc-gateway:gateway-port"-mstatistic--moderandom--probability0.50000000000-jKUBE-SEP-XEQTQPKSTZECBET2-AKUBE-SVC-4QJQIY2WH2SAWmcomment--comment"gateway/svc-gateway:gateway-port"-jKUBE-SEP-OCFEUGRRJNZZEOHZ查看两条规则分别对应的下条规则。该规则指定目标地址并执行DNAT操作转发目标地址。它们分别对应两个pod实例的实际IP地址,请求会按照这个规则转发到实际的网关。-AKUBE-SEP-XEQTQPKSTZECBET2-ptcp-m评论--comment"gateway/svc-gateway:gateway-port"-mtcp-jDNAT--to-destination10.34.0.1:30001-AKUBE-SEP-OCFEUGRRJNZZEOHZ-ptcp-mcomment--comment"gateway/svc-gateway:gateway-port"-mtcp-jDNAT--to-destination10.40.0.0:30001将在最终发送请求之前经过POSTROUTING链,并且request会被执行一次SNAT操作,MASQUERADE代表已经执行了SNAT操作修改请求源地址。-APOSTROUTING-mcomment--comment"kubernetespostroutingrules"-jKUBE-POSTROUTING-AKUBE-POSTROUTING-mcomment--comment"kubernetesservicetrafficrequiredSNAT"-jMASQUERADE2.gateway发起请求,网关收到一个requestfrom对于客户端的请求,他会通过域名向basedao发起请求,格式为sts-basedao.basedao:30002。该请求将被发送到dns服务器进行解析,并返回一个实际的IP地址。当然,这个地址也是VIP。当创建一个pod时,会在pod中创建一个dns服务配置文件。nameserver代表dns服务器的IP地址,默认为集群中的kube-dns地址,search代表域名拼接规则。可以看到地址和kube-dns的VIP是一样的。接下来pod会向kube-dns的VIP发送请求,由于是在host上发送,所以会走OUTPUT链,然后进入KUBE-SERVICES-AOUTPUT-mcomment--comment"kubernetesserviceportals"-jKUBE-SERVICES如果满足KUBE-SERVICES的规则,则继续跳转到KUBE-SVC-ERIFXISQEP7F7OF4,然后再跳转到KUBE-SEP-ZT5TVM6PMFDFQAMO。-AKUBE-SERVICES-d10.96.0.10/32-ptcp-mcomment--comment"kube-system/kube-dns:dns-tcpclusterIP"-mtcp--dport53-jKUBE-SVC-ERIFXISQEP7F7OF4-AKUBE-SVC-ERIFXISQEP7F7OF4-mcomment--comment"kube-system/kube-dns:dns-tcp"-jKUBE-SEP-ZT5TVM6PMFDFQAMO最后将DNAT目标地址转发到dns的实际pod,最后返回dns给网关域名解析的ip。-AKUBE-SEP-ZT5TVM6PMFDFQAMO-ptcp-mcomment--comment"kube-system/kube-dns:dns-tcp"-mtcp-jDNAT--to-destination10.32.0.4:53whengatewaygetsrealIP,这个IP就是basedao的VIP,网关会使用这个VIP发起请求,依然会对应对应的iptables规则,完成VIP到IP的映射得到basedaopod的实际地址,发送数据包到的实际地址,这里就不说了,重复一下。总结由于Pod的IP地址不是持久化的,所以推荐使用ClusterIP或者域名来请求集群中的其他Pod,这两种方式都是通过Linux内核的iptables规则来实现的。使用iptables规则也有缺点。当集群中的服务pod过多时,每台宿主机对应的iptables规则就会增加,增加了网络的复杂度和追踪的难度,给后期运维带来一定的困难。参考文章《iptables详解》http://www.zsythink.net/archives/1199《k8s中文文档》https://v1-16.docs.kubernetes.io/zh/《深入剖析Kubernetes》极客时间