主题
Kubernetes Pod 网络抓包实战指南
生产环境排查 Pod 网络问题时,经常遇到业务容器镜像精简、没有 tcpdump 的尴尬情况。Kubernetes 的 Ephemeral Containers(临时调试容器)特性可以在不重启 Pod 的前提下,向运行中的 Pod 注入一个共享网络命名空间的调试容器,直接抓取业务容器进出的流量。本文以排查一个 order-service 服务的 HTTPS 连接异常(Connection reset)为例,完整介绍从注入临时容器、tcpdump 抓包、导出 pcap 文件到 Wireshark 分析的全流程。
适用对象:Kubernetes 1.23 及以上版本的集群(Ephemeral Containers 自 1.23 起为 Beta 默认开启,1.25 起 GA)。
一、环境准备与目标确认
1.1 设置环境变量
为避免重复输入,建议先设置环境变量:
bash
# 设置目标 Pod 和命名空间
pod="order-service-7d9f4b5c6-x8k2m"
namespace="default"
# 设置 debug 镜像(推荐 netshoot,内置 tcpdump)
debug_image="harbor.example.com/base/nicolaka/netshoot:v0.15"1.2 查找目标容器名称(关键步骤)
--target 参数必须指定为 Pod 内原有的业务容器名,否则临时容器无法与业务容器共享网络命名空间。
bash
# 方法1:查看 Pod 详情(最直观)
kubectl describe pod $pod -n $namespace
# 方法2:直接提取容器名(适合脚本自动化)
target_container=$(kubectl get pod $pod -n $namespace -o jsonpath='{.spec.containers[0].name}')
echo "目标容器: $target_container"在 kubectl describe 输出的 Containers: 段落中确认业务容器名称(本例为 order-service)。
二、注入临时 Debug 容器
2.1 执行注入命令
bash
kubectl debug -it $pod \
-n $namespace \
--image=$debug_image \
--target=$target_container \
--profile=general参数说明:
--target:指定共享网络命名空间的原容器。--profile=general:使用标准权限配置(推荐)。如需抓包权限受限,可改用--profile=sysadmin。- 系统会自动生成临时容器名(如
debugger-xxxxx)。
预期输出:
text
Targeting container "order-service". If you don't see processes from this container it may be because the container runtime doesn't support this feature.
Defaulting debug container name to debugger-gd8hd.请记录生成的临时容器名(如 debugger-gd8hd),后续下载抓包文件时需要用到。
2.2 进入 debug 容器的交互式 shell
bash
kubectl exec -it $pod \
-c debugger-gd8hd \
-n $namespace \
-- sh2.3 验证网络共享
进入容器后,执行 ip addr 或 netstat -tulpn,确认看到的网络接口和监听端口与目标业务容器一致。若看到的接口只有 loopback,说明 --target 未生效,需检查容器运行时是否支持 Ephemeral Containers 的网络共享。
三、抓包操作流程
3.1 抓包命令(针对 HTTPS/SSL)
在 debug 容器内执行:
bash
# 抓取特定目标 IP 的 443 端口流量
tcpdump -i eth0 \
"(host 203.0.113.20 or host 203.0.113.10) and port 443" \
-s 0 -w /tmp/ssl_capture.pcap参数说明:
-i eth0:监听主网络接口(Pod 内通常为 eth0)。-s 0:抓取完整数据包。默认只抓前 68 字节,分析 TLS 握手等应用层协议必须设为 0。-w:保存为 pcap 文件。注意临时容器退出后文件会丢失,抓完需及时导出。
3.2 触发复现与停止
- 保持抓包终端运行,让 tcpdump 持续抓包。
- 新开一个终端,触发业务请求(调用接口,复现 SSL 报错场景)。
- 返回抓包终端按
Ctrl + C停止抓包,tcpdump 会打印统计信息:
text
152 packets captured
152 packets received by filter
0 packets dropped by kernel- 执行
ls -lh /tmp/ssl_capture.pcap确认文件已生成且大小合理。
四、导出抓包文件到本地
4.1 确认临时容器名称
如果忘记了注入的容器名,可通过以下命令查询:
bash
# 查看 Pod 的临时容器定义
kubectl get pod $pod -n $namespace -o yaml | grep -A 10 ephemeralContainers在输出中查找 name: debugger-xxxxx。
4.2 复制文件到本地
bash
# 语法:kubectl cp <namespace>/<pod>:<容器内路径> <本地路径> -c <容器名>
kubectl cp $namespace/$pod:/tmp/ssl_capture.pcap \
./ssl_capture.pcap \
-c debugger-gd8hd常见报错处理:
- 如果报错
tar: removing leading '/',可忽略,这是 tar 的提示信息,文件通常已成功复制,本地ls -lh ssl_capture.pcap确认即可。 - 如果报错
tar not found(目标容器内没有 tar 命令),改用kubectl exec流式传输导出:
bash
kubectl exec $pod -n $namespace -c debugger-gd8hd \
-- cat /tmp/ssl_capture.pcap > ssl_capture.pcap使用流式导出时建议校验文件完整性(对比两端文件大小,或用 Wireshark 打开确认无截断)。
五、本地分析与故障排查
5.1 使用 Wireshark 分析
bash
wireshark ssl_capture.pcap关键显示过滤器:
| 过滤器 | 用途 |
|---|---|
tcp.flags.reset == 1 | 查看连接重置(RST)包 |
tls.handshake | 查看 TLS 握手过程(旧版 Wireshark 用 ssl.handshake) |
ip.addr == 203.0.113.10 | 过滤特定对端 IP |
tls.alert_message | 查看 TLS 协议级告警(旧版为 ssl.alert_message) |
5.2 连接重置(Connection reset)问题定位
针对客户端报 Connection reset 类异常,在 pcap 中重点关注三点:
- RST 包来源:过滤
tcp.flags.reset == 1,确认 RST 是客户端还是服务端(本例为203.0.113.10)发出的。若 RST 来自一个既非客户端也非服务端的中间 IP,需排查中间链路(防火墙、LB、安全设备)。 - TLS 握手阶段:查看
Client Hello和Server Hello是否成功交换。如果只有 Client Hello 没有响应,可能是对端或中间设备拒绝;如果 Server Hello 后立即 RST,需检查证书协商。 - Alert 报文:过滤
tls.alert_message查看是否有协议级错误,如bad_certificate、handshake_failure、protocol_version等,可定位到证书或 TLS 版本不兼容问题。
5.3 TLS 握手失败快速判断
| 抓包现象 | 可能原因 |
|---|---|
| 发出 Client Hello 后直接收到 RST | 对端端口未监听 TLS、中间设备拦截 |
| 收到 Alert: handshake_failure | 加密套件(Cipher Suite)不匹配 |
| 收到 Alert: bad_certificate / unknown_ca | 客户端不信任服务端证书链 |
| 收到 Alert: protocol_version | 两端 TLS 版本无交集(如服务端仅 TLS 1.3,客户端仅 TLS 1.2) |
| TCP 三次握手即失败(SYN 无响应或 SYN-ACK 后 RST) | 与 TLS 无关,属于网络连通性问题 |
六、清理资源
Ephemeral Container 无法单独删除,它会随 Pod 生命周期结束自动消失。退出 debug 终端后无需额外操作:
bash
# 退出 debug 容器 shell 即可,临时容器保留在 Pod 中但不再占用资源
exit
# 如需彻底移除临时容器,只能删除重建 Pod(生产环境慎用,确认业务可滚动重建后再执行)
kubectl delete pod $pod -n $namespace同时记得清理本地和容器内的 pcap 文件:抓包文件可能包含业务数据明文(HTTP 场景),分析完成后应及时删除。
附录 A:常用 tcpdump 过滤表达式速查
| 场景 | 表达式 |
|---|---|
| 指定主机 | host 203.0.113.10 |
| 指定端口 | port 443 |
| 源地址/目的地址 | src host 203.0.113.10 / dst host 203.0.113.10 |
| 源端口/目的端口 | src port 8080 / dst port 443 |
| 指定网段 | net 192.168.10.0/24 |
| 组合条件 | (host 203.0.113.10 or host 203.0.113.20) and port 443 |
| 排除某端口 | not port 22 |
| 只看 TCP 握手包 | `tcp[tcpflags] & (tcp-syn |
| 只看 RST 包 | tcp[tcpflags] & tcp-rst != 0 |
| DNS 查询 | udp port 53 |
| ICMP(ping) | icmp |
| HTTP 明文流量 | tcp port 80 |
其他常用参数:
| 参数 | 说明 |
|---|---|
-i any | 监听所有接口(排查多网卡场景) |
-n | 不做域名解析,直接显示 IP(建议总是加上) |
-nn | 同时禁用端口名解析(直接显示端口号) |
-c 1000 | 抓满 1000 个包自动停止 |
-C 100 -W 5 | 按 100MB 滚动切分文件,最多保留 5 个(防磁盘写满) |
-v / -vv / -vvv | 递增的详细输出 |
附录 B:生产环境抓包安全注意事项
| 事项 | 说明 |
|---|---|
| 权限控制 | kubectl debug 创建 Ephemeral Container 需要 pods/ephemeralcontainers 的 update 权限,生产环境应通过 RBAC 收敛到少数运维账号 |
| 数据敏感 | pcap 文件可能包含业务请求明文(HTTP、数据库协议等),禁止外发,分析完成后立即删除本地与容器内文件 |
| 磁盘占用 | 长时间抓包或全量抓包(无过滤条件)可能写满容器 tmpfs,务必加 -C/-W 限制文件大小,或加精确的过滤表达式 |
| 性能影响 | tcpdump 本身开销很小,但 -i any 全接口无过滤抓包在高流量节点上仍可能造成 CPU 抖动,优先精确过滤目标 IP/端口 |
| 变更留痕 | 生产集群执行抓包前建议走变更流程并通知业务方,抓包窗口尽量避开业务高峰 |
| 镜像合规 | debug 镜像建议推送到内部 Harbor(如 harbor.example.com)统一管控,避免临时拉取公网镜像失败或引入安全风险 |
故障速查
| 现象 | 排查方向 |
|---|---|
kubectl debug 报错 ephemeral containers 不支持 | 集群版本低于 1.23,或 apiserver 未开启 EphemeralContainers 特性门控(1.23 前需手动开启) |
| 进入 debug 容器后看不到业务容器的网络接口 | --target 指定的容器名错误,或容器运行时不支持网络命名空间共享 |
kubectl cp 报错 tar not found | 目标容器无 tar,改用 kubectl exec -- cat file > local 流式导出 |
| tcpdump 报错 permission denied | 临时容器缺少 NET_RAW/NET_ADMIN 能力,注入时使用 --profile=sysadmin |
| pcap 文件为 0 字节 | 过滤表达式过于严格无匹配流量,先去掉过滤条件验证接口是否有流量 |
| Wireshark 打开 pcap 报截断错误 | 流式导出时连接中断,重新导出并校验文件大小 |