![](https://img-blog.csdnimg.cn/img_convert/151c9d92cbb31a40a8852612babb38bf.png)
当前的集群中给2个apache pod注册了一个service,这个地址是10.152.183.151,
![](https://img-blog.csdnimg.cn/img_convert/7d5dd9feb12dae19f34a83b8b613cdc8.png)
在ubuntu的pod中测试这个ip是可以通信的,it work来源于本机的pod,多访问几次发现会随机的把请求定向到本机的pod或者远端的pod。
当前节点上所有pod、网桥、flannel都是10.1.62这个网段,
![](https://img-blog.csdnimg.cn/img_convert/b93df5f961a2b2d12ea6915aaee7dbda.png)
10.211.55.12是对外的ip地址,
![](https://img-blog.csdnimg.cn/img_convert/a3af565c54df7ab3813629db61846986.png)
当前node上是没有这个service的10.152.183这个网段的,
![](https://img-blog.csdnimg.cn/img_convert/c94f67fc71ba0e64b1178ced42984659.png)
这个ip地址也是ping不通的,
![](https://img-blog.csdnimg.cn/img_convert/ff007669a869eb07ee578742c67f22ca.png)
查看apiserver的配置信息,发现配置的这个网段和apache service ip的网段是同一个,这个网段包括ip都是由k8s虚拟的进行控制,这个ip不是实际存在的。
![](https://img-blog.csdnimg.cn/img_convert/f9cae7a17a5f8e2b3184d59042f7ba53.png)
k8s通过iptables实现service,iptables分为表和列,表代表可以完成特定功能的一组步骤,列是每个步骤;
为了实现service,k8s用到了iptables中的nat表和filter表,基本上都是nat表在工作,
图中橘黄色是和nat表相关的步骤,首先外部报文请求进来之后,如果访问本机进程,会先走PREROUTING步骤,它属于nat表 ,它来决定需不需要做DNAT(NAT是网络地址转换,D是目标),即要不要做目标地址转换,这个地方可以自行决定,如果转换了之后,不在本机的话,可以直接走FORWARD这个步骤,然后到nat的POSTROUTING就直接出去了;如果和当前的进程相关,就会进入filter表的INPUT这个步骤,当本机的进程有报文输出的话,会先走nat这张表的OUTPUT步骤,在这个步骤中可能再去做一个DNAT来决定这个报文的目的地址是原封不动的还是要做一些改动,然后再经过filter的OUTPUT链再走POSTROUTING就出去了,在POSTROUTING的时候还可以做一个SNAT(源网络地址的NAT),决定这个报文出去的ip地址是不是需要变化,源网络地址就是别人可以看到的这个地址,表示报文从哪里来的。
上图中有2个地方可以做DNAT,有一个地方可以做SNAT。
![](https://img-blog.csdnimg.cn/img_convert/9540e5e81a19690fa54399ae15534697.png)
当在curl这个service的时候,这个service有一个ip地址,
![](https://img-blog.csdnimg.cn/img_convert/4ce30defb934d9b8f4c567d446bb599d.png)
151这个ip地址实际上是不存在的,当pod使用curl访问这个ip地址的时候,
![](https://img-blog.csdnimg.cn/img_convert/1e307e514d46d08399f4d2db79316bd5.png)
它就会到OUTPUT这个地方来,OUTPUT这个地方,k8s在iptables里面插入了一些指令,当它发现是指向某一个service的时候,通过DNAT去把这个报文的目标ip转换成实际提供这个服务的pod ip,然后再把这个报文路由出去;同理当从外部访问某一个service的时候,它也通过第一个DNAT(图中左上角的那个),把service这个不存在的ip替换掉,替换成一个具体的pod的ip,然后再经过路由,路由到相应的pod上,要么是本地要么是别的机器,再来去回应这个service的请求。
看下curl经历的过程
![](https://img-blog.csdnimg.cn/img_convert/76a529ec78ed17490a5826c87d78d117.png)
在pod中的本地进程curl对应着图中的这一链路,
![](https://img-blog.csdnimg.cn/img_convert/89dd5cc18f7069a9589b9f22583829c3.png)
![](https://img-blog.csdnimg.cn/img_convert/dde5e8fb2e06e2a4973e2540d41bad28.png)
看下nat这张表的OUTPUT这个链,在OUTPUT链中有2个target,和service最相关的就是kube-service,
kube-service也是一个链(步骤),这个是OUTPUT链的下一步,也就是说k8s会在iptables插入新的链来解决这个问题,在OUTPUT这个标准的步骤中,所有的源和目的地址都会进入这个步骤,
![](https://img-blog.csdnimg.cn/img_convert/9de247b0e0f1e183f510b602e5d63538.png)
这个是k8s插入的新的链(新的步骤), 查看这个链的内容,
![](https://img-blog.csdnimg.cn/img_convert/d5910af1970be62db845d989662eb5dc.png)
![](https://img-blog.csdnimg.cn/img_convert/cf753cc36a62c40b363d157bf435314a.png)
都跟service相关,
![](https://img-blog.csdnimg.cn/img_convert/9f4ed09408535747b3c084f2265ab8a2.png)
筛选和apache service相关的,
创建的这个apache service其实就是k8s在kube-service这个链中插入了2条与apache service相关的信息,这两条又是新的链,其中KUBE-MARK-MASQ的主要作用是给ip的这个报文加个标记,加标记之后,未来会在PREROUTING做snat用。
![](https://img-blog.csdnimg.cn/img_convert/62b104d41ccb01eb2a3a34ce72036a0b.png)
KUBE-SVCxxxx的内容是2个KUBE-SEPxxxx,SEP就是service endpoint,有50%的概率走第一个endpoint,
![](https://img-blog.csdnimg.cn/img_convert/64ae04682a03f54c8bfb22398d587563.png)
另外50%走第二个endpoint,
![](https://img-blog.csdnimg.cn/img_convert/055b7b7ece9771f3344a317da4dd3586.png)
查看这个SEP,发现有一个IP地址10.1.62.174,这个endpoint做了一个dnat,也就是说它在报文当中把apache的service ip改掉了,本来是service目标ip地址改成了实际的pod的ip地址10.1.62.174;
![](https://img-blog.csdnimg.cn/img_convert/90122b1f0db0d057e809f44eef073224.png)
另外的一个endpoint也是一个dnat,也是将apache service的ip改成了10.1.71.22即另外一个pod的ip;
![](https://img-blog.csdnimg.cn/img_convert/95d657da68e2a986f88f5aada7e35467.png)
通过iptables里的这个kube-service的规则的路由概率,把访问apache service的ip包,平均的分发到了2个不同的pod上,
![](https://img-blog.csdnimg.cn/img_convert/8fbfba90e9c8187182ef4ca7761cb211.png)
pod用了flannel的地址空间,就会通过flannel的网络设备,如果需要的话,就会转发到另外的node上,从而就路由到了最终的pod上。
![](https://img-blog.csdnimg.cn/img_convert/6b83ace7aa06ecc037833901a0096ae1.png)
这个其实就是做了一个mark,打了一个16进制的标签,这个标签会在POSTROUTING步骤中做一个snat
![](https://img-blog.csdnimg.cn/img_convert/48e032fb1727cd1ce934ad56ef45f72d.png)
看下nat表的POSTROUTING,
![](https://img-blog.csdnimg.cn/img_convert/407f74da83f6ae38737dbd7ae8ecfd79.png)
这里有一个KUBE-POSTROUTING,这也是k8s自己插入的,如果没有打那个16进制的标签,就走这个
![](https://img-blog.csdnimg.cn/img_convert/a2dce3e6da335e4ebc21af4f7742b1a9.png)
,否则就走这个
![](https://img-blog.csdnimg.cn/img_convert/cdc42986a285c52cfa4cf441705d7bfc.png)
。
MASQUERADE是一个snat,只不过它是动态的基于当前使用的是哪个网络接口对外分发的报文,就把这个报文的源地址改成网络设备的地址。
为什么要做这个事情?
因为在这个ubuntu的pod中,curl这个apache的service的时候,这个service的目标地址通过刚才的介绍已经被修改了,但它的源地址依然是这个pod ip的地址,
![](https://img-blog.csdnimg.cn/img_convert/8ce62ce8fbbdf69fb2221b1d0c0def13.png)
实际上这个源地址是通过eth虚拟网络设备分配的,这个地址在k8s集群中是不可见的,在pod中可见,所以就需要把这个报文的源地址改成在集群中可见的地址,比如说出口网络设备的地址;
filter和service的流程是类似的,当然k8s的service还有loadbalancer、nodeport机制,k8s都是通过在iptables中动态的插入规则来实现的。