K8s 客户端与 Pod 连接跟踪:各模式原理与排查命令
适用版本:Kubernetes 1.23+(conntrack bypass 参数有所变化),Calico/Cilium/IPVS
⚠️:有些地方需要验证
一、先搞懂一个核心问题
外部客户端与集群内 Pod 建立连接时,ss/netstat 看不到完整信息,原因是 "两边都不在同一层网络":
客户端所在网络,与 Pod 网络通常不在同一二层
Pod 的连接不绑定宿主机端口,ss 无法感知
真正看到连接状态的入口是 conntrack(走了 NAT 的情况)或 各模式自己的连接表
二、快速对照表
模式 |
走 conntrack? |
客户端能否看到 Pod IP |
查看命令 |
|---|---|---|---|
iptables |
✅ 必走 |
❌ 被 SNAT 隐藏 |
|
IPVS + NAT/SNAT |
✅ 走 |
部分隐藏 |
|
IPVS + Full-NAT (conntrack bypass) |
❌ 绕过 |
❌ 双向 SNAT |
|
Cilium eBPF socket LB |
❌ 绕过 |
✅ 真实 IP 直通 |
|
三、iptables 模式
3.1 原理
Client(10.0.0.5:任意端口)
│
▼
NodeIP:NodePort(192.168.1.100:30080)
│
▼ kube-proxy 创建的 iptables DNAT 规则
│ prerouting → KUBE-SVC-xxxxx → KUBE-SEP-xxxxx
│
▼ [conntrack 记录 DNAT]
PodIP:PodPort(10.244.1.20:8080) ← 源 IP 被 SNAT 为 NodeIP
│
▼ [conntrack 回程匹配 SNAT]
返回包 → 自动还原为 ClientIP:端口
关键点:
iptables 在 PREROUTING / OUTPUT 做 DNAT(目的地址转换)
在 POSTROUTING 做 SNAT(源地址伪装)
所有转换记录在 conntrack 表 中
出去和回来的包都经过 conntrack 查询匹配
3.2 查看连接的完整方案
# 在 Node 上查看所有活跃连接(最常用)
conntrack -L -p tcp -n
# 查看访问某个 NodePort 的所有连接
conntrack -L -p tcp --dport 30080 -n
# 查看某个客户端 IP 的所有连接记录
conntrack -L -p tcp -s 10.0.0.5 -n
# 查看访问某个 ClusterIP 的连接
conntrack -L -p tcp -d 10.244.0.1 -n
# 统计当前 conntrack 表条目数量
conntrack -C
# 查看 conntrack 表容量上限(满了会导致丢包!)
cat /proc/sys/net/netfilter/nf_conntrack_max
cat /proc/sys/net/netfilter/nf_conntrack_count
# 如果有 conntrack_WARN 日志,说明连接数逼近上限
dmesg | grep conntrack
3.3 conntrack 查看结果解读
tcp 6 115 ESTABLISHED src=10.244.0.5 dst=10.244.1.20 sport=45678 dport=8080 src=10.244.1.20 dst=10.0.0.5 sport=8080 dport=45678 [ASSURED]
src/dst= 客户端视角(原始请求方向)src/dst(第二组)= Pod 视角(经过 DNAT+SNAT 后的方向)[ASSURED]= 连接正常,双向有流量[UNREPLIED]= 只发过 SYN,未确认或已断开
3.4 iptables 规则查看(了解谁在动)
# 查看 KUBE-SERVICES 链(所有 Service 入口)
iptables -t nat -L KUBE-SERVICES -n --line
# 查看某个 Service 的 DNAT 规则
iptables -t nat -L KUBE-SVC-xxxxx -n -v
# 查看 SNAT(伪装)规则
iptables -t nat -L KUBE-POSTROUTING -n -v
四、IPVS 模式
4.1 两种子模式:是否绕过 conntrack
IPVS 本身是内核中的负载均衡模块(基于哈希表,性能远优于 iptables 链式匹配)。 kube-proxy IPVS 模式分为两种情况:
4.1.1 IPVS + conntrack(默认 / Masquerade 模式)
Client → NodeIP:NodePort → IPVS(DNAT) → Pod
│
conntrack(SNAT) → 回程匹配
kube-proxy 参数
--masquerade-all=true或逐 Service 设置--masquerade-bitIPVS 只做第一次 DNAT 分配
回程流量由 conntrack 处理 SNAT
conntrack 中可见完整连接
# 查看 IPVS Service 定义
ipvsadm -ln
# 查看 IPVS 连接的 session 表(IPVS 自己维护的连接)
ipvsadm -lnc
# 查看统计
ipvsadm -ln --stats
# 查看经过 IPVS 端口的 conntrack 条目
conntrack -L -p tcp --dport <NodePort> -n
4.1.2 IPVS + Full-NAT(conntrack bypass 模式)
从 Kubernetes 1.23 版本起,支持 --ipvs-conntrack-bypass 参数(实验性):
Client → NodeIP:NodePort
│
IPVS(DNAT+SNAT) ← IPVS 同时做源目转换,不调用 conntrack
│
PodIP:PodPort
│
直接回包(IPVS SNAT 还原源 IP)
关键点:
IPVS 在转发时同时做 source NAT,Pod 直接用原始 Client IP 发回响应
不需要 conntrack 介入回程处理
conntrack 表中看不到 Service→Pod 这段的连接
性能更好,避免了 conntrack 的性能开销
# 查看 IPVS session 表(核心手段)
ipvsadm -lnc
# 仍可以看到客户端到 NodePort 入口的连接
ss -ant | grep :<NodePort>
# Pod 侧:exec 进入 Pod 查看
kubectl exec -it <pod> -- ss -ant
# tcpdump 在主机网卡抓包验证
tcpdump -i eth0 host <ClientIP> and tcp port <ServicePort> -nn
# 查看 IPVS 统计确认流量经过
ipvsadm -ln --stats | grep <ServiceIP>
4.2 IPVS 模式连接状态解读
tcp 00:57:09 ESTABLISHED 10.244.0.5:45678 10.244.1.20:8080
这是 ipvsadm -lnc 的输出:
左边是客户端 IP(未经过 NAT 的原始 IP)
右边是 Pod IP(经过 IPVS DNAT 后的目标)
ESTABLISHED 说明连接活跃
五、Cilium eBPF 模式
5.1 原理
Cilium 完全替代 kube-proxy,使用 eBPF sockmap/sockhash 在 socket 层做负载均衡:
Client Pod → ClusterIP
│
eBPF Socket LB(内核侧拦截 connect/bind)
│
直接路由到目标 Pod(无 NAT)
│
Pod 直接回包给 Client(真实 IP)
关键点:
零 NAT:客户端 IP 和 Pod IP 端到端保持原样
零 conntrack:数据包路径完全不经过 conntrack
在 socket 创建时就完成了负载均衡选择(比 iptables/IPVS 更早)
性能最高,支持 FALLBACK(部分流量降级到 iptables)
5.2 查看连接的完整方案
方式一:Cilium agent 命令(最推荐)
# 进入 Cilium agent Pod
kubectl exec -it -n kube-system <cilium-pod> -- /bin/bash
# 查看所有 BPF socket 映射(类似 conntrack 表)
cilium-dbg bpf sock list
# 查看 Service → backend 的负载均衡映射
cilium-dbg bpf lb list
# 查看端点(所有 Pod)
cilium-dbg endpoint list
# 查看 conntrack 表(Cilium 自己的,如果不走 conntrack 则为空)
cilium-dbg bpf ct list
# 查看详细状态
cilium-dbg status --verbose
方式二:直接用 ss(Pod 内)
# 进入 Pod,直接用 ss 查看,这个是最直观的
kubectl exec -it <namespace>/<pod> -- ss -ant
# 对端就能看到真实 IP 了
# State Recv-Q Send-Q Local Address:Port Peer Address:Port
# ESTAB 0 0 10.244.0.5:45678 10.244.1.20:8080
# ↑ 客户端 IP(真实)
方式三:Hubble 可观测性(最强大)
# 查看 Pod 发出的所有流量
kubectl exec -it -n kube-system <cilium-pod> -- \
hubble observe --from pod default/nginx-pod --to-port 80
# 查看特定 IP 的流量
kubectl exec -it -n kube-system <cilium-pod> -- \
hubble observe --ip 10.244.1.20
# 查看某个 Service 的所有流量
kubectl exec -it -n kube-system <cilium-pod> -- \
hubble observe --to service default/my-service
方式四:宿主机上查看(需 privileged)
# 在宿主机上查看 BPF socket map(需要 Cilium CLI)
cilium bpf sock list
# 查看 BPF conntrack(如果有)
cilium bpf ct list
# tcpdump 抓包验证(两端 IP 都能看到)
tcpdump -i cilium_host host <ClientIP> and host <PodIP> -nn
5.3 Cilium 模式下的 conntrack
# 在 Cilium 替换 kube-proxy 后,conntrack 通常为空
conntrack -L -p tcp -n | wc -l
# 如果仍看到条目,说明有些流量降级走了 iptables
conntrack -L -p tcp -n | grep <ServiceIP>
# Cilium 状态确认
cilium-dbg status | grep -i conntrack
cilium-dbg status | grep -i kubeProxyReplacement
六、实战排查流程图
发现连接问题(访问慢 / 连接不上 / 丢包)
│
▼
这是什么类型的流量?
│
├─ ClusterIP(集群内部)→ 查看 Pod 内的 ss
│
├─ NodePort(外部访问)
│ │
│ ▼
│ 什么代理模式?
│ │
│ ├─ iptables → conntrack -L -p tcp --dport <NodePort>
│ │
│ ├─ IPVS → ipvsadm -lnc + conntrack -L
│ │
│ └─ Cilium → cilium-dbg bpf sock list + hubble observe
│
└─ LoadBalancer(云商入口)→ 查云商控制台 + NodePort 手段
七、通用排查命令速查表
场景 |
推荐命令 |
|---|---|
任意模式下,查看 Pod 内部连接 |
|
iptables 模式,查看 Service 连接 |
|
IPVS 模式,查看 Session |
|
Cilium 模式,查看 Socket |
|
Cilium 模式,追踪流量 |
|
任意模式,抓包验证 |
|
查看 conntrack 表大小 |
|
查看 iptables 规则 |
|
查看 IPVS 规则 |
|
确认当前 kube-proxy 模式 |
|
八、各模式连接生命周期对比
阶段 |
iptables |
IPVS+conntrack |
IPVS+FullNAT |
Cilium |
|---|---|---|---|---|
入口匹配 |
iptables PREROUTING |
IPVS |
IPVS |
eBPF sockmap |
目的转换 |
conntrack DNAT |
IPVS DNAT |
IPVS DNAT+SNAT |
无(直接路由) |
源转换 |
iptables SNAT |
conntrack SNAT |
IPVS SNAT |
无 |
回程匹配 |
conntrack |
conntrack |
IPVS 自己处理 |
无 |
conntrack 表 |
✅ 有记录 |
✅ 有记录 |
❌ 无记录 |
❌ 无记录 |
Pod 看到的客户端 IP |
NodeIP(被 SNAT) |
NodeIP(被 SNAT) |
原始 ClientIP |
原始 ClientIP |
性能 |
⭐⭐ |
⭐⭐⭐ |
⭐⭐⭐⭐ |
⭐⭐⭐⭐⭐ |