Pod 内没有 tcpdump?不改镜像也能抓包。本文从 Pod 网络命名空间原理讲起,给出在宿主机用 nsenter 进入 Pod netns 并用 tcpdump 抓包的完整流程、常用过滤表达式、文件滚动策略与一键脚本,帮助快速定位 DNS、连接超时、重传等常见网络故障。
线上网络故障最难的地方在于:日志、指标、事件都"看起来没问题",但请求就是超时、偶发失败或延迟飙升。这个时候最直接的证据只有一个:数据包。
现实又很尴尬:很多业务镜像精简到只剩运行所需,容器里没有 tcpdump、也不允许临时安装工具。更别说一些只读文件系统或强约束环境,想"进容器抓一下包"几乎不可能。
这篇文章给出一个在生产中很常用、侵入性很低的方法:在宿主机上进入 Pod 的网络命名空间,然后直接运行 tcpdump 抓包。不改镜像,不重启 Pod,抓到的包也更接近真实网络路径。
1. 思路:为什么 nsenter 能在宿主机抓到 Pod 的包?
Pod 的网络视图(网卡、IP、路由、iptables 规则)都存在于一个 Linux 网络命名空间(netns)里。Kubernetes 用"沙箱/暂停容器"(通常叫 pause,在 CRI 里叫 pod sandbox)来承载这个 netns,Pod 内的业务容器共享它。
nsenter 的作用是:让你在宿主机上"切换进"某个进程的命名空间。拿到 pod sandbox 的 PID 后,执行:
bash
sudo nsenter -t <sandbox-pid> -n -- <command>
你运行的 <command>(比如 ip a、tcpdump)看到的就是 Pod 的网络环境 ,抓到的就是 Pod 网卡上的真实流量。
bash
nsenter [options] [program [arguments]]
options:
-t, --target pid:指定目标进程的 PID(必选)
-m, --mount:进入挂载命名空间
-u, --uts:进入 UTS 命名空间(主机名和域名)
-i, --ipc:进入 IPC 命名空间(进程间通信)
-n, --net:进入网络命名空间(我们最常用的)
-p, --pid:进入 PID 命名空间
-U, --user:进入用户命名空间
-r, --root:设置根目录
-w, --wd:设置工作目录
2. 抓包前的准备与边界
在开始前先明确几个前提:
- 你需要能登录到 Pod 所在的节点(或有等效的节点运维入口)。
- 一般需要 root 权限(或具备
CAP_SYS_ADMIN/CAP_NET_ADMIN的能力),因此命令通常会加sudo。 - 抓包会接触到业务明文/密文数据,属于高敏操作。要控制时间、范围与文件留存。
3. 三步抓包:定位节点 → 找到 sandbox PID → nsenter + tcpdump
3.1 定位 Pod 在哪个节点
bash
kubectl get pod -n <namespace> <pod-name> -o wide
输出里会看到 NODE 字段。接下来登录该节点(SSH / 运维平台 / 跳板机等)。
3.2 获取 Pod sandbox 的 PID(按运行时选择)
优先推荐走 CRI(containerd、CRI-O 统一适用)。Docker 作为运行时的集群较少见,但仍给出方法。
方法 A:containerd / CRI-O(推荐)
- 找到 Pod 的 sandbox ID:
bash
sudo crictl pods --name <pod-name> --namespace <namespace> -q
- 从 sandbox 信息中拿 PID:
bash
sudo crictl inspectp <pod-sandbox-id> | jq -r '.info.pid'
如果节点没有 jq,你也可以先把输出保存到文件再处理,或者临时用简单的文本匹配(不够严谨,但在应急场景能用):
bash
sudo crictl inspectp <pod-sandbox-id> | grep -oE '"pid":[[:space:]]*[0-9]+' | head -n 1 | grep -oE '[0-9]+'
方法 B:Docker(如果你的集群还在用)
通常 pause 容器名字里会带 k8s_POD_。先找出 pause 容器 ID,再取 PID:
bash
sudo docker ps --filter "name=k8s_POD_<pod-name>_<namespace>" -q
sudo docker inspect -f '{{.State.Pid}}' <container-id>
3.3 进入 netns 并抓包
先进入 netns 验证网络视图是否正确:
bash
sudo nsenter -t <sandbox-pid> -n -- ip a
sudo nsenter -t <sandbox-pid> -n -- ip r
确认后开始抓包。一个通用且安全的起点是:不做域名解析、抓全包长度、落盘为 pcap:
bash
sudo nsenter -t <sandbox-pid> -n -- tcpdump -i any -nn -s 0 -w /tmp/pod.pcap
抓到足够证据后 Ctrl+C 停止,再把文件下载到本地用 Wireshark 分析:
bash
sudo chmod 644 /tmp/pod.pcap
4. tcpdump 真正常用的部分:参数与过滤表达式
抓包效率和"可用性"主要靠两件事:选对参数 + 写对过滤条件。
4.1 常用参数速查
-i <iface>:指定网卡,any表示所有网卡(排障时很省心)-nn:不做 DNS 解析、不把端口转成服务名(避免误导与额外开销)-s 0:抓完整包(默认 snaplen 可能截断)-vv:更详细的协议解码-c <n>:抓 n 个包就停,适合快速取样-w <file.pcap>:写入文件(强烈推荐,后续分析更方便)-A/-X:以 ASCII / HEX+ASCII 打印 payload(只适合少量包,且注意敏感信息)
4.2 过滤表达式:从"能用"到"好用"
过滤表达式是 BPF 语法,最常用的就是围绕 host/net/port/proto 组合:
bash
# 只看 DNS(UDP 53)
tcpdump -i any -nn udp port 53
# 只看访问某个上游 IP 的 443
tcpdump -i any -nn host 10.0.0.10 and tcp port 443
# 只看对外请求(目的地址)
tcpdump -i any -nn dst host 10.0.0.10
# 看一个网段的流量
tcpdump -i any -nn net 10.0.0.0/16
组合条件时,建议显式写括号,避免优先级踩坑:
bash
tcpdump -i any -nn '((udp port 53) or (tcp port 443)) and host 10.0.0.10'
4.3 文件滚动:避免把磁盘写爆
生产抓包一定要考虑落盘控制。两个常用策略:
按大小滚动(每个文件 50MB,最多 10 个):
bash
tcpdump -i any -nn -s 0 -C 50 -W 10 -w /tmp/pod.pcap
输出文件会按序号滚动,例如 /tmp/pod.pcap0、/tmp/pod.pcap1。
按时间切片(每 60 秒一个文件,最多 30 个):
bash
tcpdump -i any -nn -s 0 -G 60 -W 30 -w /tmp/pod-%Y%m%d%H%M%S.pcap
5. 典型排障场景:抓什么、怎么看
这里给几个"上手就能用"的抓包目标。
5.1 DNS 解析慢 / 偶发 NXDOMAIN
抓 DNS:
bash
sudo nsenter -t <sandbox-pid> -n -- tcpdump -i any -nn udp port 53 -w /tmp/dns.pcap
在 Wireshark 里重点看:
- 同一个查询是否反复重试(可能是丢包或上游无响应)
- 响应码(NOERROR/NXDOMAIN/SERVFAIL)
- 是否命中意外的 DNS 服务器 IP(可能是 /etc/resolv.conf 或 CoreDNS 转发配置问题)


5.2 连接超时 / 偶发 5xx(怀疑重传、握手异常)
抓某个上游的 TCP 443:
bash
sudo nsenter -t <sandbox-pid> -n -- tcpdump -i any -nn host <upstream-ip> and tcp port 443 -w /tmp/tls.pcap
在 Wireshark 里可以直接过滤:
text
tcp.analysis.retransmission || tcp.analysis.lost_segment || tcp.analysis.out_of_order
如果大量出现重传/乱序,通常要继续沿链路检查:节点到上游的路径、网络策略、SNAT、MTU、以及 CNI/iptables 规则是否异常。

5.3 Service 访问异常:先抓 Pod 侧,再抓 Node 侧
有些问题只在"经过 Service / NAT / 网络策略"时出现,抓包建议分两层做:
- Pod 侧:进 netns 抓
eth0/any,确认应用到底有没有发请求、有没有收到响应 - Node 侧:在宿主机抓 CNI 相关网卡,确认包有没有离开节点、是否被 NAT/策略影响
Pod 侧(示例:抓某个 Service 的 ClusterIP:Port):
bash
sudo nsenter -t <sandbox-pid> -n -- tcpdump -i any -nn host <service-cluster-ip> and port <port> -w /tmp/svc.pcap
Node 侧先看有哪些候选网卡(不同 CNI 名字不一样):
bash
ip link
ip addr
常见的网卡/前缀包括:cni0、flannel.1、cali*(Calico)、weave(Weave)、以及各种 veth*。选到合适的网卡后再抓:
bash
sudo tcpdump -i <node-iface> -nn -s 0 host <pod-ip> -w /tmp/node.pcap
下面脚本做三件事:定位节点(仅展示信息)、获取 sandbox PID、在 netns 内执行 tcpdump。把它放在节点上执行即可。
bash
#!/usr/bin/env bash
set -euo pipefail
info() { echo -e "\033[32m[INFO] $1\033[0m"; }
error() { echo -e "\033[31m[ERROR] $1\033[0m" >&2; }
if [ $# -lt 1 ]; then
error "用法: $0 <pod-name> [namespace] [tcpdump-args...]"
exit 1
fi
POD_NAME=$1
NAMESPACE=${2:-default}
for cmd in kubectl nsenter tcpdump; do
command -v "$cmd" >/dev/null 2>&1 || { error "缺少命令: $cmd"; exit 1; }
done
NODE=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.nodeName}' 2>/dev/null || true)
[ -n "$NODE" ] && info "Pod 节点: $NODE"
CONTAINER_PID=""
if command -v crictl >/dev/null 2>&1; then
POD_ID=$(crictl pods --name "$POD_NAME" --namespace "$NAMESPACE" -q 2>/dev/null || true)
if [ -n "$POD_ID" ]; then
if command -v jq >/dev/null 2>&1; then
CONTAINER_PID=$(crictl inspectp "$POD_ID" | jq -r '.info.pid' 2>/dev/null || true)
else
CONTAINER_PID=$(crictl inspectp "$POD_ID" | grep -oE '"pid":[[:space:]]*[0-9]+' | head -n 1 | grep -oE '[0-9]+' || true)
fi
fi
fi
if [ -z "$CONTAINER_PID" ] && command -v docker >/dev/null 2>&1; then
CID=$(docker ps --filter "name=k8s_POD_${POD_NAME}_${NAMESPACE}" -q 2>/dev/null | head -n 1 || true)
[ -n "$CID" ] && CONTAINER_PID=$(docker inspect -f '{{.State.Pid}}' "$CID" 2>/dev/null || true)
fi
if [ -z "$CONTAINER_PID" ]; then
error "未找到 Pod sandbox PID,请确认在正确节点且运行时可用(crictl/docker)"
exit 1
fi
info "Sandbox PID: $CONTAINER_PID"
if [ $# -gt 2 ]; then
shift 2
info "执行: tcpdump $*"
nsenter -t "$CONTAINER_PID" -n -- tcpdump "$@"
else
info "进入交互式 netns(退出用 Ctrl+D)"
nsenter -t "$CONTAINER_PID" -n -- /bin/bash
fi
使用示例:
bash
chmod +x e_net.sh
# 进入 netns 手动探索
sudo ./e_net.sh demo-pod-xxx demo
# 直接抓 DNS 到文件
sudo ./e_net.sh demo-pod-xxx demo -i any -nn -s 0 udp port 53 -w /tmp/dns.pcap

7. 安全与经验:抓包时别踩的坑
- 抓包文件里可能包含 Cookie、Token、甚至明文口令。拷贝与留存要按安全规范走,用完及时删除。
- 抓包尽量加过滤条件与时间/大小限制,避免对 CPU/磁盘造成压力。
- 遇到
tcpdump: eth0: No such device,先在 netns 里ip a看真实网卡名,再决定抓eth0还是any。 nsenter报/proc/<pid>/ns/net不存在,多半是 PID 已退出或拿错 PID,重新获取 sandbox PID。
8. 结论
不改镜像、直接在宿主机抓 Pod 包的关键只有一句话:找到 Pod sandbox PID,用 nsenter 进入 netns,在里面运行 tcpdump。
1、找到 coredns 所在的Node节点
2、在Node节点,查询 coredns 的pause 容器的PID
bash
#获取POD_ID
sudo crictl pods | grep coredns
# 获取 POD_ID 后,查其 PID
sudo crictl inspectp <POD_ID> | jq '.info.pid'
3、使用 nsenter 进入网络命名空间并测试 DNS
bash
sudo nsenter -t 12345(PID) -n
#抓包DNS
tcpdump -ni any udp and port 53 -w udp_dns.pcap
#抓包DNS和HTTP、HTTPS的连接
tcpdump -ni any "(udp port 53) or (tcp port 53) or (tcp port 80) or (tcp port 443)" -w /tmp/all_traffic.pcap