tcpdump —— 命令行抓包
一句话定义
tcpdump 直接从内核网卡 / 接口抓原始网络包,按过滤条件显示或写文件。它是网络问题最终诊断工具——iptables / curl / ping 都看着没问题但还是不通时,tcpdump 给你"流量到底有没有到这里"的权威答案。
典型场景
- pod 连不上 service:在 service 的 pod 抓 → 看包到没到
- pod 间 ping 不通:在两端抓、对比
- iptables 规则是否生效:抓抓那条规则前后接口
- 应用 timeout:抓看 TCP 握手 / 重传
- 看 K8s overlay 的真实包(外层 IP / 内层 IP)
装
apt install -y tcpdump # Debian/Ubuntu
yum install -y tcpdump # CentOS/RHEL
需要 root 跑(要 raw socket 权限)。
五个核心 flag
tcpdump -i eth0 # 抓 eth0
tcpdump -i any # 抓所有接口
tcpdump -nn # 不解析 IP / 端口名
tcpdump -w out.pcap # 写到 pcap 文件
tcpdump -r out.pcap # 读 pcap 文件回放
记住套路:tcpdump -i <iface> -nn -vv 'filter' -c 100
| flag | 含义 |
|---|---|
-i <iface> | 指定接口(必加);any = 所有 |
-nn | 不解析 IP 和端口(必加,否则慢) |
-v / -vv / -vvv | 详细程度 |
-c N | 抓 N 个包就退出 |
-s 0 | 不截断(抓完整包) |
-w file.pcap | 写文件(长期抓 / 复杂分析必用) |
-r file.pcap | 读文件 |
-A | 打印 ASCII(HTTP 等明文协议好用) |
-X | 同时打印 hex + ASCII |
-e | 显示链路层(MAC 地址) |
-Q in/out/inout | 只看入向 / 出向 / 都看 |
过滤表达式
tcpdump 用 BPF 过滤语法。可以按 host / port / protocol / 网段过滤。
按主机 / 端口
tcpdump -i any -nn 'host 10.0.24.28' # 跟某主机相关
tcpdump -i any -nn 'src host 10.0.24.28' # 源是这个
tcpdump -i any -nn 'dst host 10.0.24.28' # 目的是这个
tcpdump -i any -nn 'port 80' # 80 端口
tcpdump -i any -nn 'src port 22' # 源端口 22
tcpdump -i any -nn 'dst port 6443' # apiserver 端口
tcpdump -i any -nn 'host 10.0.24.28 and port 6443' # AND
tcpdump -i any -nn 'host 10.0.24.28 or host 10.0.24.29' # OR
tcpdump -i any -nn 'not port 22' # 反向
按协议
tcpdump -i any -nn 'icmp' # 只看 ping
tcpdump -i any -nn 'tcp'
tcpdump -i any -nn 'udp port 53' # DNS 流量
tcpdump -i any -nn 'tcp port 80 and host www.example.com'
按 TCP flags(高级)
tcpdump -i any -nn 'tcp[tcpflags] & tcp-syn != 0' # 只看 SYN(建连尝试)
tcpdump -i any -nn 'tcp[tcpflags] & (tcp-syn|tcp-ack) != 0' # SYN 或 ACK
tcpdump -i any -nn 'tcp[tcpflags] & tcp-rst != 0' # 看 RST(被拒)
按网段
tcpdump -i any -nn 'net 10.0.24.0/24' # 集群内网
tcpdump -i any -nn 'src net 10.244.0.0/16' # pod CIDR 源
输出怎么读
14:23:45.123456 IP 10.0.24.28.43210 > 10.0.24.29.6443: Flags [S], seq 1, win 64240, length 0
14:23:45.124000 IP 10.0.24.29.6443 > 10.0.24.28.43210: Flags [S.], seq 1, ack 2, win 65535, length 0
14:23:45.124500 IP 10.0.24.28.43210 > 10.0.24.29.6443: Flags [.], ack 2, win 64240, length 0
14:23:45.125000 IP 10.0.24.28.43210 > 10.0.24.29.6443: Flags [P.], seq 1:100, ack 2, length 99: ...
读法:
时间戳IP源.端口>目的.端口: Flags [...] ...- Flags:
S= SYN(建连).= ACKP= PSH(有数据要立即上送)F= FIN(正常关闭)R= RST(异常关闭/拒连)
seq/ack—— TCP 序号length—— 载荷字节数
三次握手:SYN → SYN-ACK ([S.]) → ACK ([.])。 四次挥手:FIN → ACK → FIN → ACK。
信号速查
| 看到 | 含义 |
|---|---|
只有 [S],无 [S.] 回应 | 包没到对端 / 对端没监听 / 防火墙挡 |
[S.] 但客户端没 [.] | 客户端崩了 / 网络往返路径异常 |
[R] 频繁 | 对端拒连(端口没监听 / 防火墙 REJECT) |
| 大量重传(相同 seq) | 网络丢包 |
length 0 全是空 ACK | 正常 keep-alive |
K8s 排错黄金套路
套路 1:节点之间 pod 网络通不通
# 在源节点 m1(pod IP 10.244.0.5)抓
ssh m1 'tcpdump -i any -nn host 10.244.1.5'
# 在目的节点 m2(pod IP 10.244.1.5)抓
ssh m2 'tcpdump -i any -nn host 10.244.0.5'
# 让两端同时抓
ssh m1 'tcpdump -i any -nn -c 20 host 10.244.1.5' &
ssh m2 'tcpdump -i any -nn -c 20 host 10.244.0.5' &
# 触发流量
kubectl exec pod-on-m1 -- curl http://10.244.1.5:8080
判断方向:
- m1 抓到 SYN 出,m2 没抓到 → 包丢在路上(CNI / 路由问题)
- m2 抓到 SYN 进、没 SYN-ACK 出 → 包到了但 pod 没监听 / 防火墙挡
- m2 SYN-ACK 出、m1 没收到 → 回包丢
套路 2:HTTP 看明文(K8s service mesh 调试)
tcpdump -i any -nn -A 'tcp port 8080' -s 0
# -A 显示 ASCII,-s 0 不截断 → 看到完整 HTTP 请求 / 响应
但 HTTPS / TLS 看不了内容(加密)。要看 HTTPS 内容用别的方法(SSLKEYLOGFILE + Wireshark)。
套路 3:进入 pod 网络命名空间抓包
pod 里没装 tcpdump?在节点上抓那个 pod 的 netns:
# 找到 pod 容器 PID
PID=$(crictl inspect $(crictl ps -q --name my-pod) | jq '.info.pid')
# 进 netns 抓 pod 视角的包
nsenter -t $PID -n tcpdump -i any -nn -c 50
或者抓 host 侧的 veth(每个 pod 在节点上有一头 vethXXXX):
# 找 pod 的 veth
nsenter -t $PID -n ip link
# 3: eth0@if23 ← pod 内 eth0 指向 host ifindex 23
ip link | grep '@if'
# vethABCD@if3 ...
# 那个 host 侧 ifindex 23 的就是
tcpdump -i vethABCD -nn
套路 4:抓 DNS 看 CoreDNS 行为
tcpdump -i any -nn 'udp port 53' -A
# 看到 query name / response 内容
# 在某 pod 节点上抓
ssh m4 'tcpdump -i any -nn udp port 53 -c 20'
# 然后让 pod 解析
kubectl exec my-pod -- nslookup kubernetes.default
排查"pod 解析慢":能在节点上看到 query 发出但没回应?哪个 CoreDNS pod 收到?
套路 5:抓 K8s overlay 的内外层
CNI 用 VXLAN(flannel / weave)/ IPIP(calico)等 overlay:
tcpdump -i eth0 -nn 'udp port 4789' # VXLAN
tcpdump -i eth0 -nn 'proto 4' # IPIP
# 看完整 payload
tcpdump -i eth0 -nn -e -s 0 'udp port 4789' -A
外层是节点 IP(10.0.24.x),解封装后内层是 pod IP(10.244.x.x)。tcpdump 能显示两层(要新版 tcpdump 才解析 VXLAN)。
写文件 + Wireshark 离线分析
复杂分析在本机用 Wireshark 看,不在 ssh 终端硬看:
# 在节点上抓 5 分钟(或 1000 个包)
tcpdump -i any -nn -w /tmp/trace.pcap -c 1000 'host 10.0.24.28 and port 6443'
# 或者:
timeout 300 tcpdump -i any -nn -w /tmp/trace.pcap 'host 10.0.24.28'
# 拷回本机
scp m1:/tmp/trace.pcap .
# 本机 Wireshark
open trace.pcap # macOS
wireshark trace.pcap # Linux
Wireshark 比命令行强大百倍:协议解析、follow TCP stream、I/O 图表、专家提示等。
滚动 / 切片抓包
长时间抓避免单文件太大:
tcpdump -i any -w /tmp/cap-%Y%m%d-%H%M.pcap -G 60 -C 100 -W 5 'host 10.0.24.28'
| flag | 含义 |
|---|---|
-G 60 | 每 60 秒切一个文件 |
-C 100 | 单个文件 100 MB 上限 |
-W 5 | 滚动 5 个文件后覆盖(环形) |
适合"问题不定时发生,长跑等它出现"。
性能 / 接口选择
-i any 的代价
-i any 比 -i <具体接口> 慢且会重复(同一个包可能在 eth0、cni0、vethXXXX 都被抓一次)。
排错明确接口时指定单一接口:
tcpdump -i eth0 # 比 -i any 快
-s 0 抓完整包
老版 tcpdump 默认 -s 96 只抓前 96 字节(snap length)。看 HTTP 内容时不够:
tcpdump -i eth0 -nn -A -s 0
新版默认 65535(够大),但养成 -s 0 习惯保险。
-Q 控制方向(节省噪音)
tcpdump -i eth0 -nn -Q in 'tcp port 80' # 只看入向
tcpdump -i eth0 -nn -Q out 'tcp port 80' # 只看出向
常见踩坑
坑 1:忘加 -nn,tcpdump 卡死
tcpdump -i eth0 host m1
# 卡在那里
-n 不解析 IP / 端口名(默认会反向 DNS);-nn 完全不解析。永远加 -nn。
坑 2:过滤表达式没引号、被 shell 吃了
tcpdump -i eth0 host 10.0.24.28 and port 6443
# 报错或意外行为,因为 shell 把 and 当独立命令
用单引号包:
tcpdump -i eth0 -nn 'host 10.0.24.28 and port 6443'
坑 3:在错的接口抓
tcpdump -i eth0 'host 10.244.1.5' # K8s pod IP
# 什么也抓不到
pod 流量可能走 cni0、flannel.1 或 vethXXXX,不一定经 eth0。不确定就用 -i any 然后再缩小范围。
坑 4:抓 K8s overlay 的内层 IP 抓不到
tcpdump -i eth0 -nn 'host 10.244.1.5' # pod IP
# 抓不到 —— 因为节点间是 VXLAN 封装,外层是节点 IP
抓 overlay 内层要在解封装后的接口(cni0 / flannel.1)抓,或抓 VXLAN 端口 udp port 4789。
坑 5:抓完留下大文件不清
tcpdump -i eth0 -w /tmp/cap.pcap # 跑了一小时
ls -lh /tmp/cap.pcap
# 5.2 GB
抓包很容易撑爆节点磁盘。用 -C -W 切片 + 环形覆盖、或者 -c 限制包数。
坑 6:包数显示 0 packets captured
tcpdump -i any -nn 'host 10.0.24.28' -c 10
# 0 packets captured, 0 packets received by filter
可能:
- 真的没有流量
- 过滤表达式写错(语法对、但匹配不到)
- 抓错接口
去掉过滤先看有没有流量:tcpdump -i any -nn -c 10。
坑 7:root 才能跑
$ tcpdump
tcpdump: ... permission denied
普通用户没 cap:sudo tcpdump ... 或 ssh root。
或者给 tcpdump 设 capability(不推荐生产):
setcap 'cap_net_raw,cap_net_admin=eip' /usr/sbin/tcpdump
坑 8:在 K8s pod 里抓没权限
kubectl exec my-pod -- tcpdump -i eth0
# permission denied
pod 通常不带 NET_ADMIN capability。两种做法:
- 用
kubectl debug起带特权的 sidecar:kubectl debug my-pod -it --image=nicolaka/netshoot --target=app - 在节点上抓 pod netns(见上面套路 3)
替代 / 关联工具
| 工具 | 比 tcpdump |
|---|---|
tshark | Wireshark 命令行版,协议解析更强 |
ngrep | 像 grep 那样在流量里搜字符串 |
iftop | 实时看哪些连接占带宽 |
nethogs | 按进程看流量 |
bpftrace / bcc | eBPF 探针更细粒度 |
cilium monitor | Cilium 集群里看 eBPF datapath 包 |
但 tcpdump 是最通用、最可靠的——任何 Linux 系统装一下立刻能用。