• ClusterIP的iptables模型


    总体流程先参考下面这张图
    (侵删)

    K8S-Service-ClusterIP

    ClusterIP模式的流量主要来自于集群内部,所以PREROUTING部分可以先忽略不看,我们从OUTPUT链开始看起。

    上面图片简单总结一下就是:所有发出去的请求先经过OUTPUT链,然后被KUBE-SERVICES链拦截处理,一部分不是集群内部的请求(! -s 10.244.0.0/16)会被发送到KUBE-MASK-MARQ链打上标记。再经过KUBE-SVC-链做负载均衡,而后到达真正后端所对应的KUBE-SEP链,KUBE-SEP链会先把源地址是自身IP的报文送去KUBE-MASK-MARQ链打上标记,然后对报文做DNAT转换。最后报文被送给POSTROUTING链,依据报文有没有被KUBE-MASK-MARQ链标记来确定是否对报文做SNAT转换。

    下面我们通过查看Iptables规则,一步一步解析里面的详细过程。

    先看一下master节点的iptables的OUTPUT链。

    [root@master ~]#iptables -t nat -S OUTPUT
    -P OUTPUT ACCEPT
    -A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
    -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
    

    由上面可以看出,所有经过OUTPUT链的报文需要先经过KUBE-SERVICES链处理。

    我们继续查看一下KUBE-SERVICES链。

    [root@master homework]#iptables -t nat -S KUBE-SERVICES
    -N KUBE-SERVICES
    -A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.96.0.10/32 -p udp -m comment --comment "kube-system/kube-dns:dns cluster IP" -m udp --dport 53 -j KUBE-MARK-MASQ
    -A KUBE-SERVICES -d 10.96.0.10/32 -p udp -m comment --comment "kube-system/kube-dns:dns cluster IP" -m udp --dport 53 -j KUBE-SVC-TCOU7JCQXEZGVUNU
    -A KUBE-SERVICES ! -s 10.244.0.0/16 -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-MARK-MASQ
    -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-SERVICES ! -s 10.244.0.0/16 -d 10.96.0.1/32 -p tcp -m comment --comment "default/kubernetes:https cluster IP" -m tcp --dport 443 -j KUBE-MARK-MASQ
    -A KUBE-SERVICES -d 10.96.0.1/32 -p tcp -m comment --comment "default/kubernetes:https cluster IP" -m tcp --dport 443 -j KUBE-SVC-NPX46M4PTMTKRN6Y
    -A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.109.56.98/32 -p tcp -m comment --comment "default/svc-wp:http cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
    -A KUBE-SERVICES -d 10.109.56.98/32 -p tcp -m comment --comment "default/svc-wp:http cluster IP" -m tcp --dport 80 -j KUBE-SVC-WF4SMZZJD7RVRLEC
    -A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.97.113.239/32 -p tcp -m comment --comment "default/svc-demo:http cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
    -A KUBE-SERVICES -d 10.97.113.239/32 -p tcp -m comment --comment "default/svc-demo:http cluster IP" -m tcp --dport 80 -j KUBE-SVC-3K5T3MDPJQWLFXU2
    -A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.98.48.225/32 -p tcp -m comment --comment "default/external-mysql:mysql cluster IP" -m tcp --dport 3306 -j KUBE-MARK-MASQ
    -A KUBE-SERVICES -d 10.98.48.225/32 -p tcp -m comment --comment "default/external-mysql:mysql cluster IP" -m tcp --dport 3306 -j KUBE-SVC-RMOXSRCCJ27QSW5A
    -A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.96.0.10/32 -p tcp -m comment --comment "kube-system/kube-dns:metrics cluster IP" -m tcp --dport 9153 -j KUBE-MARK-MASQ
    -A KUBE-SERVICES -d 10.96.0.10/32 -p tcp -m comment --comment "kube-system/kube-dns:metrics cluster IP" -m tcp --dport 9153 -j KUBE-SVC-JD5MR3NA4I4DYORP
    -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
    

    这里会发现有很多规则,其实,每个Seriver在这条链里都会生成两条规则。

    这里过滤一下,查看其中一个Service即可,这里选择svc-demo。

    [root@master ~]#iptables -t nat -S KUBE-SERVICES | grep svc-demo
    -A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.97.113.239/32 -p tcp -m comment --comment "default/svc-demo:http cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
    -A KUBE-SERVICES -d 10.97.113.239/32 -p tcp -m comment --comment "default/svc-demo:http cluster IP" -m tcp --dport 80 -j KUBE-SVC-3K5T3MDPJQWLFXU2
    

    第一条链:如果源地址不是10.244.0.0/16(集群内Pod网段地址),而目标地址是10.97.113.239/32(本Service地址)的,跳转到KUBE-MARK-MASQ链。

    第二条链:如果目标地址是10.97.113.239/32(本Service地址)的,跳转到KUBE-SVC-3K5T3MDPJQWLFXU2链。

    这里从KUBE-SVC-3K5T3MDPJQWLFXU2链的名称来看,一下就能猜出是HASH的结果,Service文件内容不一样,生成KUBE-SVC链的名称就不一样。

    先来查看下KUBE-MARK-MASQ链。

    [root@master ~]#iptables -t nat -S KUBE-MARK-MASQ
    -N KUBE-MARK-MASQ
    -A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000
    

    可以看出KUBE-MARK-MASQ链仅仅只是对报文打了个标记,本身明没有继续处理报文,所以打完标记后,又会跳回到原来的KUBE-SERVICES链,继续向下匹配,因此,所有的报文其实都要经过KUBE-SERVICES链的第二条规则,区别在于如果源地址不是集群内Pod网段地址的报文会被KUBE-MARK-MASQ链打上一个16进制的标记。标记的作用是:在最后报文出去的时候经过POSTROUTING链,会依据标记的有无来确定是否对报文做SNAT转换。

    然后再来看一下KUBE-SVC-3K5T3MDPJQWLFXU2链。

    [root@master ~]#iptables -t nat -S KUBE-SVC-3K5T3MDPJQWLFXU2
    -N KUBE-SVC-3K5T3MDPJQWLFXU2
    -A KUBE-SVC-3K5T3MDPJQWLFXU2 -m comment --comment "default/svc-demo:http" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-Z5GFFHZQEMOZPZUT
    -A KUBE-SVC-3K5T3MDPJQWLFXU2 -m comment --comment "default/svc-demo:http" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-H77X75P5C2QIH26Z
    -A KUBE-SVC-3K5T3MDPJQWLFXU2 -m comment --comment "default/svc-demo:http" -j KUBE-SEP-6KXJAK22WDIAXLZY
    

    KUBE-SVC链其实是用来对后端Pod做负载均衡的一个链,从上面可以看到,每条链里都有 -m statistic --mode random --probability 0.33333333349 这样类似的一段,statistic是用来进行报文数量收集的一个模块,这里使用random模式,然后指定权重为0.33333333349。因为我们一共在Service中创建了三个Pod,所以第一个Pod的流量比例为三分之一,当第一个Pod承载的流量到达三分之一后,剩余流量的二分之一由第二个Pod承担,最后剩下的流量由最后一个Pod承担。

    通过KUBE-SVC链随机选择后,会跳转到KUBE-SEP链,也就是真正Service后端的Pod对应的链,这里以匹配到第一条链为例,继续查看对应的KUBE-SEP-Z5GFFHZQEMOZPZUT。

    [root@master ~]#iptables -t nat -S KUBE-SEP-Z5GFFHZQEMOZPZUT
    -N KUBE-SEP-Z5GFFHZQEMOZPZUT
    -A KUBE-SEP-Z5GFFHZQEMOZPZUT -s 10.244.1.29/32 -m comment --comment "default/svc-demo:http" -j KUBE-MARK-MASQ
    -A KUBE-SEP-Z5GFFHZQEMOZPZUT -p tcp -m comment --comment "default/svc-demo:http" -m tcp -j DNAT --to-destination 10.244.1.29:80
    

    可以看到这里也有两条链。

    第一条链:如果源地址是10.244.1.29/32(Pod对应的自身Ip),跳转到KUBE-MARK-MASQ链去打个标记。

    第二条链:对匹配到的报文做DNAT转换。

    这里解释一下第一条链,当集群内的某个Pod请求自身对应的Service后,结果又被调转回自己。这样的报文无法被处理,所以为了防止这种情况,需要给这种报文也打上标记,最后经由POSTROUTING出去的时候,对其做SNAT转换。

    最后,我们查看下POSTROUTING链

    [root@master ~]#iptables -t nat -S POSTROUTING
    -P POSTROUTING ACCEPT
    -A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING
    -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
    -A POSTROUTING -s 10.244.0.0/16 -d 10.244.0.0/16 -j RETURN
    -A POSTROUTING -s 10.244.0.0/16 ! -d 224.0.0.0/4 -j MASQUERADE
    -A POSTROUTING ! -s 10.244.0.0/16 -d 10.244.0.0/24 -j RETURN
    -A POSTROUTING ! -s 10.244.0.0/16 -d 10.244.0.0/16 -j MASQUERADE
    

    和OUTPUT链一样,所有经过POSTROUTING的,都要先经过KUBE-POSTROUTING链。

    而KUBE-POSTROUTING链就是专门用来对那些被打过标记的请求做SNAT转换的。

    这里先看下这里各条链都做了什么:

    第一条链:跳转到KUBE-POSTROUTING链。

    第二条链:如果源地址是172.17.0.0/16(docker默认网段),目标地址不是docker0网段,做SNAT转换。这里对应的情况是:docker网段容器对外进行通信,所以要做SNAT伪装。

    第三条链:如果源地址是10.244.0.0/16(集群内Pod网段地址),目标地址也是10.244.0.0/16(集群内Pod网段地址)的,执行Return动作,而POSTROUTING为主链,Policy为ACCEPT,所以最终执行ACCEPT。这里对应的情况是:pod于pod之间的通信,所以直接放行。

    第四条链:如果源地址是10.244.0.0/16(集群内Pod网段地址),目标地址不是10.244.0.0/16(集群内Pod网段地址)的,做SNAT转换。这里对应的情况是:pod对集群外部进行通信,所以要做SNAT伪装。

    第五条链:如果源地址不是10.244.0.0/16(集群内Pod网段地址),目标地址是10.244.0.0/24(集群内Pod网段地址)的,执行Return动作,原因同第三条链,所以最终执行ACCEPT。这里对应的情况是:本机的其他服务要访问本机的pod。

    第六条链:如果源地址不是10.244.0.0/16(集群内Pod网段地址),目标地址是10.244.0.0/16(集群内Pod网段地址)的,做SNAT转换。这里对应的情况是:本机的其他服务要访问其他节点的pod,要做SNAT伪装。

    最后查看下KUBE-POSTROUTING链。

    [root@master ~]#iptables -t nat -S KUBE-POSTROUTING
    -N KUBE-POSTROUTING
    -A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
    -A KUBE-POSTROUTING -j MARK --set-xmark 0x4000/0x0
    -A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE
    

    第一条链:如果没有被打标记,就跳转回POSTROUTING去。

    第二条链:重新打个标记。

    第三条链:做动态SNAT地址伪装。

    感兴趣的话可以试着抓几个包看看。

  • 相关阅读:
    《张艺谋这个人》较真
    《智能》是真智能
    《解密小米之互联网下的商业奇迹》
    《三毛。。。。》烂漫
    《盛典―― 诺奖之行》
    常用iOS、Mac框架和库及常用中文开发博客
    《人脸识别与人体动作识别技术及应用》
    《程序员第二步从程序员到项目经理》
    《信息安全导论》
    [leetCode]141.环形链表
  • 原文地址:https://www.cnblogs.com/wuvikr/p/14270430.html
Copyright © 2020-2023  润新知