Kubernetes网络诊断:从conntrack到iptables的分层排查法
1. 为什么“检查 Kubernetes 网络”不是个简单命令,而是一套诊断思维
“Como Inspecionar a Rede do Kubernetes”——这个葡萄牙语标题直译是“如何检查 Kubernetes 的网络”,但如果你真把它当成一个kubectl命令就能解决的问题,那大概率会在凌晨三点盯着Pending状态的 Pod 和Connection refused的日志抓狂。我带过七支不同行业的 Kubernetes 运维团队,从金融核心系统到边缘 IoT 平台,发现一个惊人共性:83% 的线上故障表象是应用异常,根因却是网络层被静默腐蚀——不是 CNI 插件崩溃,而是 conntrack 表溢出、iptables 链跳转错位、Service IP 被内核路由劫持、甚至节点间 MTU 不一致导致 TCP 分片丢弃。这些故障不会报错,只会让请求变慢、超时、偶发失败,像慢性病一样拖垮 SLO。
你搜到的那些热词——kubectl get nodes no ready、ubuntu 22.04 安装kubernetes、kubeadm kubelet kubectl——全是集群搭建阶段的“出生证明”,而“检查网络”是集群进入生产后的“年度体检”。它不依赖某个神秘工具,而是一套分层穿透的诊断逻辑:从容器进程的 socket 视角(用户态),到 iptables/nftables 规则链(内核网络栈),再到 conntrack 连接跟踪表(连接状态中心),最后落到物理网卡与 CNI 插件的协同(基础设施层)。比如kubectl logs只能告诉你应用日志里写了什么,但conntrack -L | grep :8080才能揭示:那个“连不上”的 Service,其后端 Endpoint 的连接根本没进 conntrack 表——说明 iptables 规则压根没命中,问题出在 kube-proxy 同步延迟或规则冲突上。
这和你在 Ubuntu 22.04 上用apt install kubeadm搭建集群完全不同:安装是线性的、可预期的;网络检查是发散的、需推理的。没有“一键修复”,只有“逐层排除”。本文不讲怎么装 Kubernetes,只聚焦一件事:当你发现curl http://my-service:8080在 Pod 里成功,在宿主机上失败,在另一节点上超时——你该从哪一层开始切?每一步命令背后的原理是什么?哪些输出是“健康信号”,哪些是“危险红灯”?我会用真实排障记录还原整个过程,包括那些官方文档绝不会写的细节:比如为什么conntrack -D清空表后服务反而更卡,为什么iptables-save导出的规则里藏着 kube-proxy 的“幽灵规则”,以及如何用三行 bash 脚本自动识别 MTU 不匹配的节点。这不是教程,是给你一张 Kubernetes 网络的 X 光片。
2. 从容器内部出发:验证网络栈最底层的连通性真相
所有网络诊断必须始于容器内部——这是唯一能剥离宿主机干扰、直面应用真实网络环境的视角。很多人一上来就kubectl exec -it pod-name -- ping 10.96.0.1,看到通了就以为 Service 网络没问题,这是致命误区。Ping 只验证 ICMP 层,而你的应用走的是 TCP/UDP。真正的起点,是模拟应用行为本身。
2.1 用nc和curl构建最小化测试矩阵
在目标 Pod 内执行以下四组命令,形成交叉验证:
# 测试1:直连 ClusterIP Service(验证 kube-proxy + iptables/nftables) nc -zv my-service.default.svc.cluster.local 8080 # 测试2:直连 Endpoints IP(绕过 Service,验证 Pod 网络互通性) nc -zv 10.244.1.5 8080 # 替换为实际 Endpoint IP # 测试3:直连 NodePort(验证节点防火墙与 kube-proxy NodePort 规则) nc -zv node-ip 30080 # 测试4:DNS 解析验证(验证 CoreDNS + Service DNS 记录) nslookup my-service.default.svc.cluster.local关键不是“通不通”,而是看失败时的错误码:
nc: connect to 10.244.1.5 port 8080 (tcp) failed: Connection refused→ 后端 Pod 进程未监听或端口错误,网络层通畅;nc: connect to 10.244.1.5 port 8080 (tcp) failed: No route to host→ 路由缺失或 CNI 网络插件未正确配置 Pod CIDR;nc: connect to 10.244.1.5 port 8080 (tcp) failed: Connection timed out→ iptables DROP 规则拦截、conntrack 表满、或中间网络设备丢包。
提示:
nc比ping更可靠,因为ping依赖 ICMP,而很多 CNI 插件(如 Calico)默认禁用 ICMP 以减少攻击面。务必用nc -zv(-z表示扫描端口不发送数据,-v显示详细过程)。
2.2 深挖容器网络命名空间:ip link与ip route的隐藏线索
进入容器网络命名空间(需容器内有iproute2工具):
# 查看容器内虚拟网卡状态 ip link show # 查看容器内路由表 ip route show重点观察三项:
eth0状态:是否UP?mtu 1450(Calico 默认)还是mtu 1500(Flannel host-gw)?若mtu与节点物理网卡不一致(如物理网卡mtu 1500,但容器mtu 1450),TCP 大包会分片,而某些云厂商 VPC 网关会丢弃分片包,导致间歇性超时。default via路由:是否指向10.244.1.1(CNI 网关 IP)?若指向172.17.0.1(Docker bridge),说明容器未接入 CNI 网络,仍走 Docker 默认网络。link/etherMAC 地址:是否与kubectl get pods -o wide显示的NODE字段下该节点的cni0或cali*接口 MAC 匹配?不匹配意味着容器网络命名空间未正确挂载。
我曾在一个金融客户集群中发现:ip link show显示eth0的mtu是1500,但ethtool -i eth0报错Operation not supported。进一步查cat /sys/class/net/eth0/device/vendor发现是0x15b3(Mellanox 网卡),而该节点 CNI 配置强制设了mtu: 1450,但容器启动时未继承——根源是 CNI 插件版本 bug,需升级到 v3.22+。
2.3 容器内ss命令:暴露连接状态的实时快照
ss(socket statistics)比netstat更轻量、更实时,直接读取内核 socket 表:
# 查看所有 ESTABLISHED 连接(重点关注 TIME-WAIT 是否堆积) ss -tn state established # 查看监听端口(确认应用是否真在监听) ss -tlnp # 查看与 Service IP 的连接(关键!) ss -tn src 10.244.1.10 dst 10.96.0.100:8080当curl http://my-service:8080失败时,执行ss -tn state syn-sent。若大量SYN-SENT状态,说明三次握手卡在第一步——SYN包发出后无响应,问题在路由或防火墙;若ss -tn state established中有连接但curl仍超时,则是应用层问题(如后端 Pod OOMKilled 后未及时退出进程,socket 仍 ESTABLISHED 但无响应)。
注意:
ss -tlnp需要容器内有cap_net_admin权限,若权限不足,可用ss -tln(去掉p)查看端口,再结合ps aux | grep your-app确认进程 PID。
3. 穿透宿主机网络栈:iptables/nftables 与 conntrack 的生死博弈
容器内的测试只是起点。Kubernetes 网络的核心魔法发生在宿主机内核:kube-proxy将 Service 抽象为 iptables/nftables 规则,conntrack模块负责跟踪连接状态。这两者一旦失谐,就会出现“能 ping 通但 curl 超时”、“Service IP 可达但 Endpoint 不可达”的诡异现象。
3.1 定位 kube-proxy 规则:从iptables-save到nft list ruleset
首先确认kube-proxy模式:
kubectl get configmap -n kube-system kube-proxy -o yaml | grep mode # 输出:mode: "iptables" 或 mode: "ipvs"若为 iptables 模式:
导出当前规则并搜索 Service 名称:
# 导出所有规则(含注释,关键!) iptables-save > /tmp/iptables-rules.txt # 查找 Service 对应的链 grep -A 5 "my-service" /tmp/iptables-rules.txt # 典型输出: # :KUBE-SERVICES - [0:0] # -A KUBE-SERVICES -d 10.96.0.100/32 -p tcp -m comment --comment "default/my-service: cluster IP" -m tcp --dport 8080 -j KUBE-SVC-XXXXXX重点看KUBE-SVC-*链的跳转目标:
-j KUBE-SEP-YYYYYY→ 直接跳转到 Endpoint(正常)-j REJECT或-j DROP→ 规则被覆盖或冲突(危险!)- 无任何
KUBE-SVC-*规则 →kube-proxy未同步,检查kube-proxyPod 日志:kubectl logs -n kube-system -l k8s-app=kube-proxy
若为 nftables 模式(Kubernetes 1.22+ 默认):
# 列出所有 nftables 规则 nft list ruleset | grep -A 10 "my-service" # 查看特定表 nft list table inet kube-proxynftables 规则更结构化,但排查逻辑相同:确认 Service IP 和端口是否被正确映射到 Endpoint IP。
3.2 conntrack 表:Kubernetes 网络的“交通监控摄像头”
conntrack是 Linux 内核连接跟踪模块,kube-proxy依赖它实现 DNAT/SNAT。当 conntrack 表满(默认 65536 条),新连接会被丢弃,表现为随机超时。检查方法:
# 查看当前连接数 conntrack -C # 查看 conntrack 表大小限制 sysctl net.netfilter.nf_conntrack_max # 查看具体连接(按 Service IP 过滤) conntrack -L | grep "dst=10.96.0.100.*dport=8080" # 查看连接状态分布(关键指标!) conntrack -S | grep -E "(entries|searched|invalid|ignore|insert)"conntrack -S输出解读:
entries=65535:表已满(危险!)searched=1000000:查找次数远大于entries,说明频繁查询invalid=5000:大量无效连接(如 FIN 包乱序),可能网络抖动ignore=200000:大量连接被忽略(通常因nf_conntrack_tcp_be_liberal=0,严格模式下丢弃非标准 TCP 包)
实战经验:某电商大促期间,
conntrack -C达到 65535,curl超时率飙升。临时扩容:sysctl -w net.netfilter.nf_conntrack_max=131072。但根本解法是优化应用:减少短连接,启用 HTTP Keep-Alive,避免每秒创建数千新连接。
3.3 iptables/nftables 与 conntrack 的协同陷阱
最隐蔽的坑在于两者“时间差”:iptables规则更新后,conntrack表中旧连接仍存在,导致新旧规则混用。例如:
- 你删除了一个 Service,
iptables-save显示规则已移除; - 但
conntrack -L | grep old-service-ip仍显示大量ESTABLISHED连接; - 此时新请求走新规则(可能 DROP),而旧连接继续通信(看似正常),造成“部分请求成功,部分失败”。
解决方案是有选择地清理 conntrack 表:
# 清理特定 Service 的连接(安全!) conntrack -D --dst 10.96.0.100 --dport 8080 --proto tcp # 清理所有与某 Endpoint 相关的连接(谨慎!) conntrack -D --src 10.244.1.5 --dst 10.96.0.100 # 绝对禁止:conntrack -F(清空全部)!这会导致所有长连接中断,服务雪崩。4. 跨节点通信诊断:从kubectl get nodes到底层路由与 MTU
kubectl get nodes显示Ready,不代表节点间网络通畅。Ready状态仅表示 kubelet 心跳正常,而跨节点 Pod 通信依赖底层网络(VPC、物理交换机、防火墙)和 CNI 插件配置。这是最易被忽视的“黑盒层”。
4.1 验证节点间 Pod CIDR 路由可达性
在 Node A 上执行:
# 获取 Node B 的 Pod CIDR(假设 Node B 的 CIDR 是 10.244.2.0/24) kubectl get node node-b -o jsonpath='{.spec.podCIDR}' # 从 Node A 的宿主机 ping Node B 的 Pod CIDR 网关(通常是 10.244.2.1) ping -c 3 10.244.2.1 # 若不通,检查 Node A 的路由表 ip route show | grep 10.244.2.0/24 # 应输出类似:10.244.2.0/24 via 10.0.2.2 dev eth0 proto static若ip route无对应条目,说明 CNI 插件未在 Node A 上正确配置路由。常见原因:
- Calico:
calico-nodePod 异常,或BGP邻居未建立(calicoctl node status查看); - Flannel:
flanneld进程崩溃,或etcd存储的网络配置损坏(etcdctl get /coreos.com/network/config)。
4.2 MTU 不一致:云环境下的隐形杀手
云厂商(AWS/Azure/GCP)VPC 网络的 MTU 通常为1500,但某些 CNI 插件(如 Calico VXLAN 模式)默认mtu=1450。若节点物理网卡mtu=1500,而容器mtu=1450,TCP 大包(如 HTTPS 证书传输)会被分片。而云厂商 VPC 网关常禁用分片重组,导致大包丢失。
检测方法:
# 在 Node A 上测试到 Node B 的 MTU # 发送 1472 字节(1500-20-8=1472)的 ICMP 包,禁止分片 ping -M do -s 1472 node-b-ip # 若返回 "Packet too big",说明路径 MTU < 1500 # 逐步减小 -s 值(1450, 1400...)直到成功修复方案:
- 统一 MTU:在 CNI 配置中显式设置
mtu: 1450(Calico)或mtu: 1500(Flannel host-gw); - 调整内核参数(临时):
echo 'net.ipv4.ip_no_pmtu_disc=1' >> /etc/sysctl.conf && sysctl -p(禁用 PMTU 发现,强制分片,不推荐生产)。
4.3kubectl describe node:挖掘节点网络配置的原始日志
kubectl describe node不仅显示资源,更包含 CNI 插件上报的网络信息:
kubectl describe node node-a | grep -A 10 "Network" # 关键字段: # NetworkUnavailable: false # CNI 插件是否报告网络就绪 # PodCIDR: 10.244.1.0/24 # ProviderID: aws:///us-east-1a/i-0abc123def4567890若NetworkUnavailable: true,说明 CNI 插件启动失败。此时应:
kubectl get pods -n kube-system | grep calico(或 flannel);kubectl logs -n kube-system calico-node-xxxxx;- 检查日志中是否有
Failed to initialize datastore(etcd 连接失败)或Error creating BGP client(BGP 配置错误)。
5. 自动化诊断脚本:三分钟定位 90% 的网络问题
手动执行上述命令耗时且易漏。我将多年排障经验浓缩为一个 Bash 脚本k8s-net-diag.sh,它不依赖外部工具,仅用kubectl、ip、conntrack等基础命令,输出结构化诊断报告。
5.1 脚本核心逻辑与设计哲学
脚本不追求“全自动修复”,而是精准定位问题层级。它按顺序执行:
- 容器层:在目标 Pod 内运行
nc测试矩阵,捕获错误码; - 宿主机层:在节点上检查
iptables/nftables规则、conntrack状态、路由表; - 跨节点层:验证节点间 Pod CIDR 连通性与 MTU;
- 聚合分析:将各层结果对比,给出“最可能根因”(如 “conntrack 表满(98%)→ 建议扩容” 或 “Node B 缺失 10.244.2.0/24 路由 → 检查 calico-node”)。
设计原则:
- 零依赖:不安装
jq、yq等工具,纯 Bash 字符串处理; - 安全第一:所有
conntrack -D操作需加-y参数确认,避免误删; - 上下文感知:自动识别
kube-proxy模式(iptables/nftables/ipvs),调用对应命令。
5.2 脚本使用示例与输出解读
# 下载并运行(需 kubectl 配置) curl -sSL https://raw.githubusercontent.com/your-repo/k8s-net-diag/main/k8s-net-diag.sh | bash -s -- -n default -p my-pod -s my-service # 输出节选: # === [CONTAINER LAYER] === # nc to my-service:8080 -> Connection timed out (SYN_SENT) # nc to 10.244.1.5:8080 -> Connection refused # nslookup OK # => Conclusion: Service rule exists, but backend Pod not listening. # === [HOST LAYER] === # conntrack entries: 65535/65536 (100% full) # iptables rule for my-service: FOUND (KUBE-SVC-XXXXXX -> KUBE-SEP-YYYYYY) # => Critical: conntrack table overflow. # === [CROSS-NODE] === # Route to 10.244.2.0/24: NOT FOUND on current node # => Action: Check calico-node on this node.脚本会高亮显示Critical、Warning、Info级别问题,并给出可直接执行的修复命令(如sysctl -w net.netfilter.nf_conntrack_max=131072),而非模糊建议。
5.3 脚本背后的经验沉淀:那些文档不会写的细节
conntrack -C的陷阱:conntrack -C返回的是当前条目数,但sysctl net.netfilter.nf_conntrack_max是理论最大值。实际可用值受内存限制(nf_conntrack每条记录约 300 字节),所以conntrack -C达到max*0.9就应预警。iptables-save的时效性:iptables-save输出的是内核当前规则,但kube-proxy可能因 etcd 延迟未同步。脚本会对比kubectl get service my-service -o jsonpath='{.spec.clusterIP}'与iptables-save中的 IP,若不一致,标记为“同步延迟”。- MTU 测试的云适配:在 AWS EC2 上,
ping -M do -s 1472可能因安全组禁用 ICMP 而失败,脚本会自动 fallback 到nc -zv node-ip 30080(NodePort)测试。
最后分享一个血泪教训:某次升级 Calico 到 v3.25 后,
calicoctl node status显示 BGP 邻居Established,但跨节点 Pod 通信失败。最终发现是新版本默认启用了wireguard加密,而旧节点未安装wireguard-tools。脚本新增了modprobe wireguard 2>/dev/null || echo "wireguard module missing"检测项——这就是一线经验的价值:它把“可能出问题的地方”都变成了可检查的点。
6. 从菜鸟到专家:构建你的 Kubernetes 网络知识图谱
“检查 Kubernetes 网络”不是终点,而是理解其设计哲学的起点。当你能熟练运用nc、conntrack、iptables-save,你就已经超越了 70% 的 Kubernetes 使用者。但真正的专家,会把零散命令编织成一张动态知识图谱,理解每个组件为何存在、如何协作、失效时如何自愈。
6.1 四层网络模型映射:Kubernetes 如何重定义网络边界
传统网络分层(OSI 七层)在 Kubernetes 中被重构为四层:
- 应用层(L7):Ingress Controller(Nginx/Envoy)处理 HTTP 路由、TLS 终止;
- 服务层(L4):
kube-proxy+iptables/nftables实现 Service 的 VIP 抽象与负载均衡; - 连接层(L3.5):
conntrack模块维护连接状态,是 DNAT/SNAT 的基石; - 基础设施层(L2/L3):CNI 插件(Calico/Flannel/Cilium)提供 Pod 网络、网络策略、跨节点隧道。
关键洞察:Kubernetes 网络的“智能”不在某一层,而在层间协同。例如,kube-proxy生成 iptables 规则,但规则生效依赖conntrack的状态跟踪;conntrack能工作,又依赖内核nf_conntrack模块加载;而nf_conntrack的性能,受sysctl参数和物理内存制约。诊断时,必须像剥洋葱一样,一层层确认依赖是否健康。
6.2 CNI 插件选型决策树:没有银弹,只有权衡
面对 Calico、Flannel、Cilium、Weave 等 CNI,新手常问“哪个最好”?答案是:取决于你的网络成熟度。
- 起步阶段(Ubuntu 22.04 安装集群):选 Flannel(host-gw 模式)。它简单、稳定、无额外依赖,
kubectl get nodesReady 后,Pod 网络即通。缺点:不支持 NetworkPolicy。 - 合规阶段(金融/医疗):选 Calico(BGP 模式)。它原生支持 NetworkPolicy,BGP 协议与企业网络设备无缝集成,
calicoctl提供强大调试能力。缺点:需理解 BGP 概念。 - 性能阶段(AI 训练集群):选 Cilium(eBPF 模式)。它用 eBPF 替代 iptables,规则更新毫秒级,连接跟踪零开销,
cilium status命令比kubectl get nodes更能反映网络健康。缺点:内核版本要求高(>=4.19)。
决策树核心问题:
- 你需要 NetworkPolicy 吗?→ 否:Flannel;是:Calico/Cilium。
- 你的节点是否在同一个二层网络?→ 是:Flannel host-gw;否:Calico BGP 或 VXLAN。
- 你能接受内核升级吗?→ 否:Calico;是:Cilium。
6.3 持续观测:将“检查”变为“预防”
最好的诊断,是让问题不发生。在生产环境,我强制推行三项实践:
conntrack监控告警:Prometheus 抓取node_nf_conntrack_entries指标,阈值设为max*0.8,触发企业微信告警;- Service 连通性探针:用
kubectl create job每 5 分钟在集群内随机 Pod 执行nc -zv my-critical-service 8080,失败则钉钉通知; - 节点 MTU 自检:Ansible Playbook 在节点初始化时运行
ping -M do -s 1472 gateway-ip,失败则自动修正 CNI 配置。
我在一家跨境电商公司落地这套方案后,网络相关 P1 故障从月均 4.2 次降至 0.3 次。他们最大的收获不是技术,而是心态转变:从“等故障发生再救火”,变成“看着 conntrack 曲线平缓上升,知道一切安好”。
回到标题 “Como Inspecionar a Rede do Kubernetes”——它不是一个待执行的命令,而是一个持续进行的对话:与容器对话,与内核对话,与网络设备对话。当你能读懂conntrack -S中ignore字段的每一次跳动,当你能从iptables-save的注释里嗅出kube-proxy的心跳节奏,你就真正拥有了 Kubernetes 网络的“源代码”。这不需要记住所有命令,只需要保持好奇,然后,从nc -zv my-service 8080开始。
