zoukankan      html  css  js  c++  java
  • kubernetes-canal 主机访问 service 异常问题分析

    问题描述:

    使用 canal 来做 Kubernetes CNI network provider, 在一次使用部署了新版本的 Kubernetes 平台环境上出现从本机访问应用的 service 异常,出现超时,查 iptables 规则发现在 nat 表的 POSTROUTING 链中多出几条规则, POSTROUTING 链中规则如下所示:

    iptables -vnL POSTROUTING -t nat
    Chain POSTROUTING (policy ACCEPT 33 packets, 2588 bytes)
     pkts bytes target     prot opt in     out     source               destination
    1673K  121M cali-POSTROUTING  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:O3lYWMrLQYEMJtB5 */
    1673K  121M KUBE-POSTROUTING  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* kubernetes postrouting rules */
        0     0 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0
       18   936 RETURN     all  --  *      *      !192.168.64.0/20      192.168.66.0/24
     378K   26M RETURN     all  --  *      *       192.168.64.0/20      192.168.64.0/20
      294 15288 MASQUERADE  all  --  *      *       192.168.64.0/20     !224.0.0.0/4
        0     0 RETURN     all  --  *      *      !192.168.64.0/20      192.168.64.0/24
        1    52 MASQUERADE  all  --  *      *      !192.168.64.0/20      192.168.64.0/20
    

    其中 RETURN all -- * * !192.168.64.0/20 192.168.66.0/24 为多出来的规则。

    192.168.64.0/20 为集群 POD 网段,192.168.64.0/24 为本节点 PODCidr, 192.168.66.0/24 为 service 对应的 endpoint POD IP 所在节点的 PODCidr。

    从本机访问出去的流量不管目的地址是本节点还是跨节点都会匹配 OUTPUT 链,OUTPUT 链中有如下 hook 钩子,用来在 OUTPUT 链中实现 DNAT 功能。

    /* Before packet filtering, change destination */
    {
    	.hook	= iptable_nat_ipv4_local_fn,
    	.pf		= NFPROTO_IPV4,
    	.hooknum	= NF_INET_LOCAL_OUT,
    	.priority	= NF_IP_PRI_NAT_DST,
    },
    

    从本机访问 service ,OUTPUT 链中 DNAT 将 serivce IP + port 转换为 POD IP + port,从本机请求的报文走 DNAT 有如下函数处理流程:
    ip_local_out ----> iptable_nat_ipv4_local_fn -> nf_nat_ipv4_fn (match rule 并完成 DNAT )-> ip_route_me_harder(根据DNAT 后的目的地址重新查路由选择出接口) ---> dst_output

    返回流量有如下匹配,即目的 IP 不在集群 POD 网段,即做 SNAT:

    iptables -vnL  cali-nat-outgoing  -t nat
    Chain cali-nat-outgoing (1 references)
     pkts bytes target     prot opt in     out     source               destination
      587 46720 MASQUERADE  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:Wd76s91357Uv7N3v */ match-set cali4-masq-ipam-pools src ! match-set cali4-all-ipam-pools dst
    

    有了上述前提条件描述之后,正常的请求流程应该是:

    1. 本机请求 service 之后 DNAT 为 POD IP(192.168.66.0), 重新查路由出接口为 flannel.x vxlan 接口,
    2. POSTROUTING 做 SNAT,修改源 IP 为flannel.x 的 IP, 从 vxlan 接口送出去,调用设备驱动发送函数 vxlan_xmit,
    3. vxlan 封装后调用 ip_local_output 再重新查路由选择出接口... ...
    4. 目的节点收到 vxlan 报文并解封装送至上的 POD,并根据目的 IP 为请求端 flannel.x 的 IP 返回,送至本节点vxlan 接口,vxlan封装送到请求节点,
    5. 请求节点根据连接跟踪做 SNAT, 将 POD IP + port 转换为 service IP + port ... ...

    但是如果在步骤 2 中没有做 SNAT,在步骤 4 中直接根据上的 IP 将报文送至请求端,而此时的请求节点不做 SNAT,请求进程收到源 IP 与自己请求的目的 IP 不匹配的报文不处理,且自己预期的报文一直等待不至,直至请求超时。

    解决问题

    分析了问题产生原因之后,推测问题产生原因为节点规则清理不干净导致(原有的 kubernetes 节点未清理即加入到新 kubernetes 集群,本节点所在的 Kubernetes 有过得重装)
    flannel 本身有做清理工作,在 defer 函数中删除创建的 iptables 规则,但是由于对于进程退出信号未捕获,导致未执行 defer 函数规则未做回收。

    defer func() {
    	teardownIPTables(ipt, rules)
    }()
    

    作者针对 flannel 回收 iptables 规则的问题在 flannel github 社区提交了 PR,见[1]。

    参考链接

    [1] flannel recycle unused iptable rules pr. https://github.com/coreos/flannel/issues/986
    [2] 洞悉linux下的Netfilter&iptables:网络地址转换原理之DNAT. http://blog.chinaunix.net/uid-23069658-id-3210931.html

  • 相关阅读:
    try catch 和\或 finally 的用法
    postgresql与oracle对比
    今天遇到个let: not found
    NTLM相关
    【搜藏】net use命令拓展
    【shell进阶】字符串操作
    【网摘】网上邻居用户密码
    测试导航
    关系代数合并数据 left join
    真正的程序员
  • 原文地址:https://www.cnblogs.com/haoqingchuan/p/8940065.html
Copyright © 2011-2022 走看看