这次又是 bridge-nf-call-iptables 惹的祸...

news/2024/5/18 15:33:51 标签: 网络, kubernetes, curl, bbs, wireshark


使用 kubernetes 遇到最多的 70%问题都可以归于网络问题,最近发现如果内核参数: bridge-nf-call-iptables设置不当的话会影响 kubernetes 中 Node 节点上的 Pod 通过 ClusterIP 去访问同 Node上的其它 pod 时会有超时现象,复盘记录一下排查的前因后因。

问题现象

集群环境为 K8s v1.15.9,cni 指定了 flannel-vxlan 跟 portmap, kube-proxy 使用 mode 为 ipvs

问题现象是,某个 Node 节点上的 pod 通过 service 访问其它服务时,有些能通,有些不通,不通的都提示 timeout

异常的请求:

curl -v http://panorama-v2-frontend-service.spring-prod.svc.cluster.local:8080
* Rebuilt URL to: http://panorama-v2-frontend-service.spring-prod.svc.cluster.local:8080/
*   Trying 10.233.53.172...
* TCP_NODELAY set
# 这里一直等待直到超时

这个节点上的很多 pod 都存在这个问题, 其它节点未发现异常.

正常请求:

curl -v http://panorama-v2-frontend-service.spring-prod.svc.cluster.local:8080
* Rebuilt URL to: http://panorama-v2-frontend-service.spring-prod.svc.cluster.local:8080/
*   Trying 10.233.53.172...
* TCP_NODELAY set
* Connected to panorama-v2-frontend-service.spring-prod.svc.cluster.local (10.233.53.172) port 8080 (#0)
> GET / HTTP/1.1
> Host: panorama-v2-frontend-service.spring-prod.svc.cluster.local:8080
> User-Agent: curl/7.52.1
> Accept: */*
>
< HTTP/1.1 200 OK

排查过程

先对异常 Node 进行排查,因为其它节点是正常的,通过对异常 Node 的网络、kube-proxy、 iptables、ipvs 规则、kubelet 等排查后未发现有可疑的地方,就暂时排除嫌疑了.

第二个可疑的是 DNS, coreDNS 负责对 service 解析成 ClusterIP, 在出问题的 Node 上经过多次测试均能正确解析,coreDNS 排除嫌疑

可疑的地方都筛了一遍,无果, 那就只能再从现象来发现看看有没有相同点.

首先,访问路径为: Service – > ClusterIP – > PodIP

对 service 访问异常,coreDNS 已经被排除了,那先绕过 service,直接使用 ClusterIP 访问呢? 测试后现象依旧

那再绕过 ClusterIP,直接使用 PodIP 呢? Bingo,之前会出问题的访问都是正常的了.

那么问题就出在 CluterIP – > PodIP 上, 那么又有以下可能:

  1. ClusterIP 没有正确转发到 PodIP 上可能导致超时

    1. 如果正确转发,响应没有返回也可能导致超时

    第一种可能性很容易排查,之前已经确认了 ipvs 规则、iptables 规则都是没有问题的,且通过 ClusterIP 发起的请求可以到达 PodIP 上, 基本就排除了可能性一

    另外,对比正常跟异常请求会发现,异常的请求原 Pod 跟目标 pod 都是在同一个 Node 上,而正常的请求则处于不同的 Node,会是这个影响吗?

    上面的可能性二,只能祭出抓包神器了tcpdump, 通过抓包发现(抓包过程见文未)会发现请求中出现了Reset

    那么问题转换一下: 为什么相同 Node 上 podA 通过 service/ClusterIP 访问 PodB 响应会不返回呢,而通过 PodIP 访问就没问题?

    补充一句就是,相同 Node 上的 pod 相互访问是不需要经过 Flannel 的,因此 Flannel 可以排除嫌疑

    so, 问题在哪?

    回到 tcpdump 的抓包数据, 可以发现,响应的数据没有按照请求的路径返回,嗯,Interesting

    罪魁祸首

    不管是 iptables 还是 ipvs 模式,Kubernetes 中访问 service 都会进行 DNAT,将原本访问 ClusterIP:Port 的数据包 DNAT 成 service 的某个 Endpoint (PodIP:Port),然后内核将连接信息插入 conntrack 表以记录连接,目的端回包的时候内核从 conntrack 表匹配连接并SNAT,这样原路返回形成一个完整的连接链路.

    从 tcpdump 看到请求被 reset 了, 没错, bridge-nf-call-iptables(如果是 ipv6 的话则是net.bridge.bridge-nf-call-ip6tables)参数

    但是不对,这个参数 linux 默认开启的呢?难道是有人修改了么?

    使用命令查看该参数是否开启:

    $ cat /proc/sys/net/bridge/bridge-nf-call-iptables
    # 0
    

    返回 0,说明确实没有开启(后来被证实是被同事修改了),那这个参数是如何影响的返回路径的呢?

    那就不得不说linux bridge

    虽然 CNI 使用的是 flannel, 但 flannel 封装的也是 linux bridge,linux bridge 是虚拟的二层转发设备,而 iptables conntrack 是在三层上,所以如果直接访问同一网桥内的地址(ip 同一网段),就会直接走二层转发,不经过 conntrack:

    结合上面的图来看,同 Node 通过 service 访问 pod 的访问路径如下:

    1. PodA 访问 service, 经过 coreDNS 解析成 Cluster IP,不是网桥内的地址(ClusterIP 一般跟 PodIP 不在一个网段),走 Conntrack,进行 DNAT,将 ClusterIP 转换成 PodIP:Port

    1. DNAT 后发现是要转发到了同节点上的 PodB,PodB 回包时发现目的 IP(此时是 PodA 的 IP) 在同一网桥上(PodA 与 PodB 的 IP 段一致),就直接走二层转发了,不会去调 conntrack,这样就导致回包时没有原路返回

    没有返回包就导致请求方一直等直到超时退出.

    这样也解释了为何访问在其它节点的应用的 ClusterIP 没有问题,因为目标 PodIP 与源 PodIP 不在同一个网段上,肯定要走 conntrack.

    问题解决

    总述,开启参数后问题解决

    $ echo "net.bridge.bridge-nf-call-iptables=1" >> /etc/sysctl.conf
    $ echo "net.bridge.bridge-nf-call-ip6tables=1" >> /etc/sysctl.conf
    
    $ sysctl -p /etc/sysctl.conf
    

    linux conntrack

    关于 conntrack 其实也是个值得好好研究一番的知识点, 各个发行版都有工具可以看到 conntrack 里的记录,格式如下:

    $ conntrack -L
    tcp      6 119 SYN_SENT src=10.224.1.34 dst=10.233.53.172 sport=56916 dport=8080 [UNREPLIED] src=10.224.1.56 dst=10.224.1.34 sport=8080 dport=56916 mark=0 use=1
    

    那个著名的DNS 5s timeout[1]的问题就跟 conntrack 机制有关,由于篇幅有限,就不在这里展开.

    tcpdump

    在容器中的抓包命令

    $ tcpdump -vvv host 10.224.1.34 or 10.233.53.172 or 10.224.1.56
    

    其中的三个 ip 分别对应 podA IP, podB 的 ClusterIP, podB 的 PodIP

    这里由于篇幅的关系,只保存有关键信息,同时使用注释是作者加入的,方便理解.

    对于异常请求的 tcpdump,如下:

    # podA 请求 PodB
    panorama-frontend-deploy-c8f6fd4b6-52tvf.45954 > panorama-v2-frontend-service.spring-prod.svc.cluster.local.8080: Flags [S], cksum 0x4cc5 (incorrect -> 0xba1b), seq 1108986852, win 28200, options [mss 1410,sackOK,TS val 1345430037 ecr 0,nop,wscale 7], length 0
    # 10-224-1-56是PodB的podIP, 这里省略了解析过程,可以看到返回数据给PodA
    10-224-1-56.panorama-v2-frontend-service.spring-prod.svc.cluster.local.8080 > panorama-frontend-deploy-c8f6fd4b6-52tvf.45954: Flags [S.], cksum 0x1848 (incorrect -> 0x99ac), seq 3860576650, ack 1108986853, win 27960, options [mss 1410,sackOK,TS val 2444502128 ecr 1345430037,nop,wscale 7], length 0
    # 重点: podA直接reset了请求.
    panorama-frontend-deploy-c8f6fd4b6-52tvf.45954 > 10-224-1-56.panorama-v2-frontend-service.spring-prod.svc.cluster.local.8080: Flags [R], cksum 0xb6b5 (correct), seq 1108986853, win 0, length 0
    

    最后会发现 PodA 给 PodB 发送了个R Flags, 也就是reset, 就是因为当 PodB 返回握手确认给到 PodA,PodA 根本不认识这个请求,所以直接给 reset 掉了, 三手握手都没有建立,this is why!

    而对于net.bridge.bridge-nf-call-iptables=1的正常请求的 tcpdump 如下:

    # 能看到正常的三次握手, 这里省略
    
    # 开启传输数据
    panorama-frontend-deploy-c8f6fd4b6-52tvf.36434 > panorama-v2-frontend-service.spring-prod.svc.cluster.local.8080: Flags [P.], cksum 0x4d3c (incorrect -> 0x6f84), seq 1:128, ack 1, win 221, options [nop,nop,TS val 1346139372 ecr 2445211463], length 127: HTTP, length: 127
        GET / HTTP/1.1
        Host: panorama-v2-frontend-service.spring-prod.svc.cluster.local:8080
        User-Agent: curl/7.52.1
        Accept: */*
    
    panorama-v2-frontend-service.spring-prod.svc.cluster.local.8080 > panorama-frontend-deploy-c8f6fd4b6-52tvf.36434: Flags [.], cksum 0x4cbd (incorrect -> 0xe8a6), seq 1, ack 128, win 219, options [nop,nop,TS val 2445211463 ecr 1346139372], length 0
    
    panorama-v2-frontend-service.spring-prod.svc.cluster.local.8080 > panorama-frontend-deploy-c8f6fd4b6-52tvf.36434: Flags [P.], cksum 0x4dac (incorrect -> 0x0421), seq 1:240, ack 128, win 219, options [nop,nop,TS val 2445211463 ecr 1346139372], length 239: HTTP, length: 239
        HTTP/1.1 200 OK
        Server: nginx/1.17.1
        Date: Wed, 18 Aug 2021 15:10:17 GMT
        Content-Type: text/html
        Content-Length: 1540
        Last-Modified: Fri, 09 Jul 2021 06:36:53 GMT
        Connection: keep-alive
        ETag: "60e7ee85-604"
        Accept-Ranges: bytes
    

    相信这个请求路径还是很清晰的,就不再啰嗦.

    结语

    禁用net.bridge.bridge-nf-call-ip6tables这个参数当然也有好外,那就是考虑同网段的 IP 访问没必要走 conntrack,一定程度有助于性能。

    kubernetes 的官方文档[2]明确提及 Node 节点上需要开启这个参数,不然碰到各种诡异的现象也只是时间问题,所以还是不要随意调整。

    以防后患的话可以对该参数是否开启进行监控,防止被人误修改。

    参考文章

    • https://izsk.me/2020/06/10/Kubernetes-coredns-5s-timeout

    • https://wiki.libvirt.org/page/Net.bridge.bridge-nf-call_and_sysctl.conf

    • https://cloud.tencent.com/developer/article/1761367

    • https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/

    • https://imroc.cc/post/202105/why-enable-bridge-nf-call-iptables/

    • https://github.com/kubernetes/kubernetes/issues/85422#issuecomment-714629138

    • https://www.stackrox.com/post/2020/01/kubernetes-networking-demystified/

    • https://blog.csdn.net/u010278923/article/details/81735970

    引用链接

    [1]

    DNS 5s timeout: https://izsk.me/2020/06/10/Kubernetes-coredns-5s-timeout/

    [2]

    官方文档: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/

    原文链接:https://izsk.me/2021/08/18/Kubernetes-bridge-nf-call-iptables/


    你可能还喜欢

    点击下方图片即可阅读

    送书福利 | 云原生数据中心网络

    云原生是一种信仰 ????

    关注公众号

    后台回复◉k8s◉获取史上最方便快捷的 Kubernetes 高可用部署工具,只需一条命令,连 ssh 都不需要!

    点击 "阅读原文" 获取更好的阅读体验!

    发现朋友圈变“安静”了吗?


http://www.niftyadmin.cn/n/1749915.html

相关文章

HIVE笔记(表的常见操作 )

2019独角兽企业重金招聘Python工程师标准>>> 创建库 CREATE DATABASE DATABASENAME&#xff1b; 创建表 CREATE [EXTERNAL] TABLE TABLENAME (FIELD FIELDTYPE,......) [PARITITIONED BY (FIELD TYPE)] [ROW FORMAT ..] //DELIMITED FIELDS TERMI…

平均 3000-20000 块不等,有空接外包私活的入群!

知乎上这个被浏览了948,816次的问题&#xff0c;看得我真是抓心挠肝&#xff1a;都2021年了&#xff0c;居然还有兄弟不知道接外包赚外快的活路吗&#xff1f;&#xff01;根据自己的时间安排自由接活、开发方式可以选远程还是驻地&#xff0c;最后&#xff0c;只要你的项目够扎…

Java中获取系统时间

经常要计算系统的性能&#xff0c;因此需要在算法运行前后获取一下系统时间作个对比。 //获取纳秒差值 long t1 System.nanoTime(); // TODO: 运行程序 long t2 System.nanoTime(); System.out.println((t2-t1));//获取毫秒差值 long t1 System.currentTimeMillis(); // TO…

以 Serverless 的方式实现 Kubernetes 日志告警

本文转载自公众号「KubeSphere 云原生」作者简介&#xff1a;方阗&#xff0c;OpenFunction 开源社区 Maintainer。当我们将容器的日志收集到消息服务器之后&#xff0c;我们该如何处理这些日志&#xff1f;部署一个专用的日志处理工作负载可能会耗费多余的成本&#xff0c;而当…

Weblogic问题收集

2019独角兽企业重金招聘Python工程师标准>>> 1.Weblogic92异常处理——BEA-101020 http://blog.csdn.net/fei1502816/article/details/23940195 转载于:https://my.oschina.net/wallenheng/blog/515187

直播预告 | Kubernetes 上的图数据库——本周四晚八点

云原生直播间第 3 期直播&#xff0c;敬请期待&#xff01;直播预告本周四晚八点&#xff0c;将由 VEsoft inc.&#xff08;欧若数网&#xff09;开发者布道师古思为&#xff0c;为大家带来线上的分享。本次分享将介绍 K8s 上的图数据库 Nebula Graph(NG) 的演进、实现&#xf…

sprint在scrum中到底是什么?

很多聊技术的人会聊敏捷&#xff0c;聊敏捷似乎避免不了scrum&#xff0c;而scrum中经常出现的sprint这个东西&#xff0c;那么sprint在scrum中到底是什么呢&#xff1f; 有人也许会说sprint就是是一个迭代&#xff08;iteration&#xff09;&#xff0c;一个开发周期嘛&#x…

RabbitMQ+HAProxy

原本打算直接用nginx反向代理&#xff0c;发现不好用&#xff0c;默认不支持长连接&#xff0c;见很多推荐HAProxy的&#xff0c;就试试吧~ wget http://haproxy.1wt.eu/download/1.4/src/haproxy-1.4.21.tar.gz tar -zxvf haproxy-1.4.21.tar.gz cd haproxy-1.4.21 make TARG…