主题
实验 12:端口镜像与流量采样
交换机只把包发给目的端口,别的端口看不到。但排查问题时你经常需要旁听别人的流量。
OVS 提供三种手段,用途完全不同:
| 手段 | 复制什么 | 送给谁 | 适合 |
|---|---|---|---|
| Mirror | 完整报文 | 本机的某个端口 | 排障、入侵检测、合规抓包 |
| sFlow | 按比例抽样的报文头 | 远端采集器 | 长期流量画像、容量规划 |
| NetFlow/IPFIX | 流级别的统计(不含报文) | 远端采集器 | 计费、审计、异常检测 |
起环境
bash
cd labs/learn-ovs/10-mirror
./start.shtext
h1 .1 ─┐
h2 .2 ─┼── ovs1 br0
h3 ─┘ ← 抓包机,不配 IPh3 接在交换机上但不配地址,它只负责接收镜像来的帧。
先确认现在抓不到
一个终端抓包:
bash
docker exec clab-mirror-h3 timeout 6 tcpdump -i eth1 -n -l icmp另一个终端打流量:
bash
docker exec clab-mirror-h1 ping -c 3 192.168.10.2text
0 packets captured什么都没有。 交换机把包发给了 eth2(h2),不会发给 eth3。这是交换机该有的行为。
这就是交换机和 hub 的区别
早年的 hub 会把包广播给所有端口,抓包很容易。交换机按 MAC 表定向转发,所以想旁听必须显式配置镜像。
配端口镜像
bash
N="docker exec clab-mirror-ovs1"
$N ovs-vsctl \
-- --id=@eth1 get Port eth1 \
-- --id=@eth3 get Port eth3 \
-- --id=@m create Mirror name=mirror0 \
select-src-port=@eth1 select-dst-port=@eth1 output-port=@eth3 \
-- set Bridge br0 mirrors=@m这条命令值得拆开讲,它是 ovs-vsctl 里最容易写错的一类。
@ 符号是什么
Mirror 是 OVSDB 里的一张独立的表,通过 Bridge.mirrors 引用(实验 2 讲的 UUID 引用链)。
问题是:新建的 Mirror 记录的 UUID 要等创建之后才知道,但同一个事务里就要把它挂到 Bridge 上。ovs-vsctl 用 @名字 解决这个问题 —— 它是个事务内的临时变量。
text
--id=@eth1 get Port eth1 把 Port eth1 的 UUID 存进 @eth1
--id=@eth3 get Port eth3 把 Port eth3 的 UUID 存进 @eth3
--id=@m create Mirror ... 新建 Mirror,它的 UUID 存进 @m
(创建时引用 @eth1 和 @eth3)
set Bridge br0 mirrors=@m 把 @m 挂到 br0@ 符号必须先定义后使用
把 create Mirror 写在 get Port 前面会报:
text
ovs-vsctl: row id "@eth1" specified on "get" command
was used before it was defined先 get 拿 UUID,再 create 去引用。 顺序错了不会静默失败,会直接报这个错。
三个 select 字段
| 字段 | 含义 |
|---|---|
select-src-port | 镜像从这些端口进来的流量 |
select-dst-port | 镜像发往这些端口的流量 |
select-vlan | 镜像某个 VLAN 的全部流量 |
select_all=true | 镜像整个网桥的流量 |
select-src-port 和 select-dst-port 都设成 eth1,就是双向镜像 eth1 的流量。
只想看单方向就只设一个。
验证
bash
docker exec -d clab-mirror-h3 \
sh -c 'timeout 8 tcpdump -i eth1 -n -l icmp > /tmp/cap.txt 2>&1'
sleep 1
docker exec clab-mirror-h1 ping -c 4 -i 0.3 192.168.10.2
sleep 8
docker exec clab-mirror-h3 cat /tmp/cap.txttext
IP 192.168.10.1 > 192.168.10.2: ICMP echo request, id 77, seq 1, length 64
IP 192.168.10.2 > 192.168.10.1: ICMP echo reply, id 77, seq 1, length 64
IP 192.168.10.1 > 192.168.10.2: ICMP echo request, id 77, seq 2, length 64
IP 192.168.10.2 > 192.168.10.1: ICMP echo reply, id 77, seq 2, length 64
...
8 packets captured往返两个方向都抓到了。 h3 完全不参与通信,却看到了全部流量。
tcpdump 写文件要加 -l
默认情况下 tcpdump 输出到文件是块缓冲的,被 timeout 杀掉时缓冲区内容直接丢失,文件里只剩开头两行提示 —— 看起来像"什么都没抓到"。
-l 改成行缓冲,每抓到一个包就落盘。脚本里抓包一律加 -l。
看镜像的统计
bash
$N ovs-vsctl list Mirrortext
name : mirror0
output_port : c440e5e9-e7c4-4520-8c31-45f8f9b3f4c9
select_all : false
select_dst_port : [c37811f5-cc9e-4f8d-b187-3cedb4602b02]
select_src_port : [c37811f5-cc9e-4f8d-b187-3cedb4602b02]
snaplen : []
statistics : {tx_bytes=1372, tx_packets=14}statistics 是排查镜像问题的第一站:
tx_packets在涨 → 镜像在工作,问题在抓包侧tx_packets是 0 → 镜像没生效,检查select-*配置
两个实用选项
select_all:镜像整个网桥
bash
$N ovs-vsctl set Mirror mirror0 select_all=true \
select-src-port=[] select-dst-port=[]text
select_all : true
select_dst_port : []
select_src_port : []
statistics : {tx_bytes=4158, tx_packets=49}select_all 会翻倍流量
网桥上所有流量都复制一份到输出口。输出口的带宽必须撑得住整个网桥的总流量,否则会丢包 —— 而且丢的可能是业务包。
生产环境慎用,优先用 select-src-port 精确指定。
snaplen:只镜像报文头
bash
$N ovs-vsctl set Mirror mirror0 snaplen=64只复制每个包的前 64 字节。协议头够用了,载荷不要 —— 大幅降低镜像开销,也避免把敏感数据复制出去。
做入侵检测或协议分析时通常只需要头部,这个选项很实用。
镜像到 VLAN
除了输出到端口,还能输出到一个 VLAN:
bash
ovs-vsctl set Mirror mirror0 output-vlan=999 output-port=[]镜像的帧打上 VLAN 999 标签后在网桥里泛洪。远端的分析设备只要接在这个 VLAN 里就能收到 —— 这就是物理交换机上的 RSPAN。
output-port 和 output-vlan 只能设一个。
sFlow:抽样采集
Mirror 是本机的、全量的。要把流量信息送到远端采集器做长期分析,用 sFlow。
bash
$N ovs-vsctl -- --id=@s create sFlow \
agent=br0 target=\"127.0.0.1:6343\" \
header=128 sampling=64 polling=10 \
-- set Bridge br0 sflow=@stext
agent : br0
header : 128
polling : 10
sampling : 64
targets : ["127.0.0.1:6343"]| 字段 | 含义 |
|---|---|
target | 采集器地址,sFlow 标准端口 6343 |
sampling | 每 N 个包抽 1 个。64 表示 1/64 |
header | 每个抽样包发送前多少字节 |
polling | 每隔几秒上报一次接口计数 |
agent | 用哪个接口的地址作为上报者身份 |
sampling 是性能和精度的权衡。 值越小越精确但开销越大。生产环境通常 512 到 4096;万兆链路上用 64 会明显吃 CPU。
常用采集端:sflowtool、ntopng、Prometheus 的 sflow exporter。
NetFlow / IPFIX:流级统计
sFlow 送的是抽样的报文,NetFlow 送的是聚合后的流记录(五元组 + 包数 + 字节数 + 时间戳),不含报文内容。
bash
$N ovs-vsctl -- --id=@n create NetFlow \
targets=\"127.0.0.1:2055\" active-timeout=30 \
-- set Bridge br0 netflow=@ntext
active_timeout : 30
targets : ["127.0.0.1:2055"]active-timeout=30 表示长连接每 30 秒上报一次中间统计,不用等连接结束。
IPFIX 是 NetFlow v10 的标准化版本,配置类似(create IPFIX),支持更灵活的字段。
三者怎么选
text
想看到完整报文内容,本机分析 → Mirror
想长期画流量趋势,能接受抽样 → sFlow
想按连接计费/审计,不需要报文内容 → NetFlow / IPFIX清理
bash
$N ovs-vsctl clear Bridge br0 mirrors
$N ovs-vsctl clear Bridge br0 sflow
$N ovs-vsctl clear Bridge br0 netflow只清 Bridge 的引用会留下孤儿记录
clear Bridge br0 mirrors 只是解除引用。Mirror 记录本身会因为没有引用而被 OVSDB 自动回收(它是弱引用表),但保险起见可以确认:
bash
ovs-vsctl list Mirror # 应该是空的自动检查
bash
./verify.shtext
[通过] br0 上挂了 Mirror
[通过] 业务流量仍然正常
[通过] h3 抓到了 h1→h2 的 ICMP(镜像生效)
[通过] h3 也抓到了回程方向自测清单
- 为什么默认情况下 h3 抓不到 h1 和 h2 之间的流量?
ovs-vsctl里的@名字是什么?为什么必须先定义后使用?select-src-port和select-dst-port都设成同一个口,效果是什么?- 排查"镜像不生效"该先看哪个字段?
select_all=true有什么风险?- tcpdump 写文件时为什么要加
-l? - Mirror、sFlow、NetFlow 分别适合什么场景?
常见问题
抓不到镜像流量
先看镜像本身有没有工作:
bash
ovs-vsctl list Mirror | grep statisticstx_packets在涨 → 镜像正常,问题在抓包侧。检查抓包的接口对不对、有没有加-l、过滤条件是否太严tx_packets是 0 → 检查select-src-port/select-dst-port指的是不是你想监控的端口,以及Bridge.mirrors有没有挂上
配置镜像后业务丢包
输出口带宽不够。改用 snaplen 只镜像报文头,或者缩小 select-* 的范围。
报错 row id "@xxx" was used before it was defined
--id=@xxx get ... 必须写在引用它的命令之前。
镜像的输出口能同时跑业务吗
技术上可以,但不建议。镜像流量会和业务流量争带宽,而且抓包时会看到一堆无关的业务包。专门留一个口给抓包机。
sFlow 配了但采集器收不到
bash
ovs-vsctl list sFlow # 确认 targets 正确
ovs-vsctl get Bridge br0 sflow # 确认挂到了网桥上再检查 OVS 所在主机到采集器的 UDP 6343 是否可达。sFlow 是 UDP,丢了不会重传也不会报错。
小结
- 交换机定向转发,想旁听流量必须显式配置镜像
Mirror是 OVSDB 的独立表,用@名字在一个事务里完成"建记录 + 挂到 Bridge"@符号必须先定义后使用,顺序错了会直接报错select-src-port+select-dst-port设成同一个口 = 双向镜像Mirror.statistics.tx_packets是排查镜像问题的第一站select_all=true会把网桥总流量复制一份,注意输出口带宽snaplen只镜像报文头,大幅降低开销- 脚本里 tcpdump 写文件一定加
-l,否则缓冲区内容会丢 - Mirror 看报文(本机)、sFlow 看抽样趋势(远端)、NetFlow 看流统计(远端)
第 3 部分到此结束。下一部分进入 Overlay 网络:为什么 4094 个 VLAN 不够用,以及 VXLAN 怎么解决它。