K8s中Pod的服务发现

Go语言精选

共 5707字,需浏览 12分钟

 ·

2021-02-15 22:34


Pod是怎么找到请求的Pod的,pod之间又是怎么负载均衡的(iptables模式下)





01

iptables知识



想知道这个问题,首先需要了解一些iptables知识。


五条链


Iptables是linux内核集成的IP信息过滤规则,负责将发往主机的网络包进行分发,转换等。当客户端请求服务器的某个服务时,请求信息会先通过网卡进入服务器内核,这时iptables会对包进行过滤,决定这些包是发往用户态的服务进程或是转发出去到别的主机。而决定这些路径的方式在iptables中称为链,刚进入内核的请求流会经过PREROUTING链,根据路由规则判断是是不是发往本机请求,是则走INPUT链进入本机用户态进程,否则会走FORWARD链并匹配对应的规则最后流出本机;如果是本机发出的请求会走OUTPUT链并进一步到POSTROUTINE链流出本机,或转发到其他机器或回复信息给客户端。




总结上述几条链:


  • PREROUTINE:流入本机路由前

  • POSTROUTINE:流出本机路由前

  • FORWARD:转发路径

  • OUTPUT:由本机用户程序发出的

  • INPUT:发送至本机用户程序的


两个动作


SNAT


源地址转换,是指将报文发送方的ip地址转换,这样当相应方回复请求时,回复的是发送方的地址。


示例


client发送请求给server时,需要经过gateway,如果gateway不对包进行源地址转换(SNAT),发往server的网络包携带的源地址依然是clientserver会对该源地址响应,但client并不识别server的地址,会导致该条请求出现错误。



DNAT


目标地址转换,是将报文的目标地址转换,起到请求转发到别的目的地的作用。







02

k8s基础知识    


需要了解k8s中的几种IP类型。


虚拟IP


虚IP(下文称VIP)有ClusterIP(即serviceIP),是集群自己生成的,ping不通,并和PodIP不处于同一网段,避免请求发生混乱。当创建一个service时,k8s会为该service指派一个IP地址,并会被集群中的所有kube-proxy观察到,kube-proxy从而会安装一系列的iptables规则到宿主机,kube-dns也会相应的插入一条域名解析IP的规则。请求到来的时候,如果符合规则,iptables会将VIP转化为实际的IP并使用。



实际IP


实IP分别有PodIP等,该IP是由CNI插件分配的,在k8s集群启动时候,需要安装CNI插件,通常是一个DaemonSet控制器控制,保证每台节点都有该进程。他的作用是在集群内部产生一套网络,并给每个pod插上网线,保证pod与节点,pod与pod是互通的。



Pod之间通信的方式可以通过实际的PodIP,但是该IP会随着pod的变化而变化,不适合用该方式,也可以通过ClusterIP的方式通信,比较稳定,但是不容易被记住,还可以通过svc.ns这种域名的格式,该方法请求kube-dns域名解析得到域名对应的IP






03

案例学习    



接下来根据一个案例探讨一下。


案例介绍


  • 集群中有两个gateway-pod,service类型为NodePort,暴露端口给集群外部,接收外界请求并负载均衡,并将请求转发到下游pod。


  • 有一个basedao pod,负责接收gateway pod发来的请求并响应,对应的service名称是sts-basedao,命名空间名称是basedao。


  • Kube-dns pod将域名解析成IP地址,并返回给发送方。


查看一下各个pod的IP,该IP是实际的IP。

[centos@guozhao-50 ~]$ kubectl get pod --all-namespaces -owideNAMESPACE     NAME                                IP                    basedao       sts-basedao-0                      10.34.0.2gateway       sts-gateway-59d5fdbc54-4d6kv       10.40.0.0gateway       sts-gateway-59d5fdbc54-dmncq       10.34.0.1 kube-system   coredns-6c76c8bb89-kztxk           10.32.0.4


查看一下三种pod对应service的IP,可以看到gateway service代理的30001端口映射到了外部的31001端口。


[centos@guozhao-50 ~]$ kubectl get svc --all-namespacesNAMESPACE     NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                  AGEbasedao       svc-basedao            ClusterIP   10.111.143.223           30003/TCP                70mgateway       svc-gateway            NodePort    10.102.103.1             30001:31001/TCP          70mkube-system   kube-dns               ClusterIP   10.96.0.10               53/UDP,53/TCP,9153/TCP   14d



client发起请求


通过iptables-save命令可以查看到所有的iptables规则


当client发起请求时,会请求服务器对应的端口,对应NodePort端口,我们来查看对应的iptables规则,会经过PREROUTING,跳转到KUBE-SERVICES规则

-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES



在KUBE-SERVICE中找到符合的规则,--dst-type LOCAL 代表本地,会继续跳转到KUBE-NODEPORTS

-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS



该请求会跳转到KUBE-MARK-MASQ给请求打一个标签,并跳转到下一条规则KUBE-SVC-4QJQIYWH2AWK2TSA。

-A KUBE-NODEPORTS -p tcp -m comment --comment "gateway/svc-gateway:gateway-port" -m tcp --dport 31001 -j KUBE-MARK-MASQ-A KUBE-NODEPORTS -p tcp -m comment --comment "gateway/svc-gateway:gateway-port" -m tcp --dport 31001 -j KUBE-SVC-4QJQIYWH2AWK2TSA


跟随上条规则有两条规则与其对应,--probability 0.50000000000代表有0.5的概率进入上面的规则,如果没有进入上面规则,就会进入下面的规则,负载均衡也就是在iptables层面实现的。

-A KUBE-SVC-4QJQIYWH2AWK2TSA -m comment --comment "gateway/svc-gateway:gateway-port" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-XEQTQPKSTZECBET2-A KUBE-SVC-4QJQIYWH2AWK2TSA -m comment --comment "gateway/svc-gateway:gateway-port" -j KUBE-SEP-OCFEUGRRJNZZEOHZ



分别查看两个规则对应的下一条规则,该规则指定了目标地址,执行了DNAT操作进行目标地址转发,分别对应了两个pod实例的实际IP地址,请求会跟随这条规则转发到实际的gateway。

-A KUBE-SEP-XEQTQPKSTZECBET2 -p tcp -m comment --comment "gateway/svc-gateway:gateway-port" -m tcp -j DNAT --to-destination 10.34.0.1:30001-A KUBE-SEP-OCFEUGRRJNZZEOHZ -p tcp -m comment --comment "gateway/svc-gateway:gateway-port" -m tcp -j DNAT --to-destination 10.40.0.0:30001



在最终发出请求之前会走POSTROUTING链,会对该请求做一个SNAT操作,MASQUERADE代表执行了SNAT操作修改了请求源地址。

-A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE


gateway发起请求


此时gateway收到了来自client的请求,他会向basedao发起请求,请求方式是通过域名即sts-basedao.basedao:30002格式。该请求会发送给dns服务器进行解析返回一个实际IP地址,当然,该地址也是一个VIP。


在pod被创建时候,就会在pod中创建dns服务配置文件,nameserver代表dns服务器的IP地址,默认是集群中的kube-dns地址,search代表域名拼接规则。可以看出,该地址和kube-dns的VIP是相同的。



接下来该pod会发出请求到kube-dns的VIP


由于是在主机上发出,会走OUTPUT链,进而进入到KUBE-SERVICES

-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES



满足KUBE-SERVICES的该条规则,继续跳转到KUBE-SVC-ERIFXISQEP7F7OF4,再次跳转到KUBE-SEP-ZT5TVM6PMFDFQAMO。

-A KUBE-SERVICES -d 10.96.0.10/32 -p tcp -m comment --comment "kube-system/kube-dns:dns-tcp cluster IP" -m tcp --dport 53 -j KUBE-SVC-ERIFXISQEP7F7OF4-A KUBE-SVC-ERIFXISQEP7F7OF4 -m comment --comment "kube-system/kube-dns:dns-tcp" -j KUBE-SEP-ZT5TVM6PMFDFQAMO



最终进行DNAT目标地址转发到dns实际的pod,最终dns返回给gateway由域名解析到的ip。

-A KUBE-SEP-ZT5TVM6PMFDFQAMO -p tcp -m comment --comment "kube-system/kube-dns:dns-tcp" -m tcp -j DNAT --to-destination 10.32.0.4:53


当gateway得到真实的IP,该IP就是basedao的VIP,gateway会使用该VIP发起请求,依然会对应到相应的iptables规则,完成VIP到IP的映射得到basedao pod的实际地址,并向实际地址发送数据包,这里便不再赘述。





04

总结    


由于Pod的IP地址不是持久的,所以尽量建议采用ClusterIP或者域名的方式请求集群中的其他pod,而这两种方式是通过Linux内核的iptables规则实现的。使用iptables规则也有缺点,当集群中有过多的服务Pod,相应的各台宿主机iptables规则就会增加,这增加了网络的复杂度与跟踪难度,会给后期运维带来一定的困难。






05

参考文章    



  • 《iptables详解》http://www.zsythink.net/archives/1199

  • 《k8s中文文档》https://v1-16.docs.kubernetes.io/zh/

  • 《深入剖析Kubernetes》极客时间



推荐阅读


福利

我为大家整理了一份从入门到进阶的Go学习资料礼包,包含学习建议:入门看什么,进阶看什么。关注公众号 「polarisxu」,回复 ebook 获取;还可以回复「进群」,和数万 Gopher 交流学习。

浏览 26
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报