在容器 veth 上用 tc netem 注入网络延迟,练习测量、定位与回滚

演练目标

环境准备

Linux 主机上的 Docker,内核支持 sch_netem。Docker Desktop 的 Linux VM 可能没有该模块,建议在 Linux 虚拟机中演练。全程只在 lab-client 容器的 eth0 上操作。

docker network create lab-net
docker run -d --name lab-server --network lab-net nginx:1.27-alpine
docker run -d --name lab-client --network lab-net --cap-add=NET_ADMIN \
  alpine:3.20 sleep 3600
docker exec lab-client apk add --no-cache iproute2 iputils curl
docker exec lab-client ping -c 3 lab-server

基线应只有不到 1 ms 的 RTT。如果基线已经很高,先排查主机负载和 Docker 网络,再继续注入。

注入故障

在客户端容器的出方向网卡上增加 200 ms 延迟和 50 ms 抖动:

docker exec lab-client tc qdisc add dev eth0 root netem delay 200ms 50ms distribution normal
docker exec lab-client tc qdisc show dev eth0

tc 只作用于 lab-client 的 eth0,不会影响宿主机或其他容器。绝对不要在宿主机主网卡、docker0 或物理网桥上执行 tc qdisc add,否则会影响整机网络。

观察现象

docker exec lab-client ping -c 5 lab-server
docker exec lab-client curl -s -o /dev/null \
  -w 'connect=%{time_connect}s total=%{time_total}s\n' http://lab-server
docker exec lab-client tc -s qdisc show dev eth0
docker exec lab-client ss -ti

预期 ping 的 RTT 约为 200 ms 上下并带抖动;curl 的 time_connect 明显升高,因为 TCP 握手报文被延迟;tc -s 显示 netem 队列的发送计数和 backlog;ss -ti 可看到连接 RTT 升高。

判断与定位

  1. 先比较注入前后的 ping 与 curl:ICMP 和 TCP 都慢,说明是链路层延迟;只有某个域名慢,优先查 DNS。
  2. tc -s qdisc 的 backlog 持续增长说明队列在堆积,可能同时存在带宽瓶颈;仅有延迟时 backlog 应很快排空。
  3. curl -w 拆分 time_namelookup、time_connect、time_starttransfer、time_total,判断慢在解析、握手、首字节还是传输。
  4. 容易误判:netem 加在客户端出方向时,只延迟请求,响应不一定被延迟,所以 RTT 不一定正好翻倍。另一个误判是把应用超时直接归因于网络,实际上可能是服务端处理慢或连接池耗尽。
  5. 生产环境不要用 ping 作为唯一证据,ICMP 可能被限速或丢弃,而 TCP 业务仍正常。

修复

实验中的“修复”就是删除 qdisc,恢复正常网络:

docker exec lab-client tc qdisc del dev eth0 root
docker exec lab-client ping -c 3 lab-server
docker exec lab-client curl -s -o /dev/null \
  -w 'connect=%{time_connect}s total=%{time_total}s\n' http://lab-server

真实故障中,按定位结果分别处理:跨地域链路延迟考虑就近接入或多活;带宽瓶颈做限速和扩容;丢包查链路和网卡错误计数;DNS 慢则优化解析链路和缓存。

清理与回滚

docker rm -f lab-client lab-server 2>/dev/null || true
docker network rm lab-net 2>/dev/null || true

容器删除后,注入的 qdisc 随网络命名空间一起消失,不会留在宿主机上。如果只删容器不删网络,用 docker network ls 确认 lab-net 已清理。

复盘

延伸阅读

读完自测

  1. 为什么网络延迟实验只能在容器或 veth 上做?
  2. ping 慢但 curl 的 time_starttransfer 正常,可能是什么情况?
  3. tc -s qdisc 中 backlog 持续增长说明什么?
答案
  1. 在宿主机主网卡加 qdisc 会影响所有进程和所有流量,可能中断远程连接;隔离的 netns 只影响实验容器。
  2. 可能是 ICMP 被限速或路径不同,而 TCP 业务路径正常;应结合 TCP 指标和实际请求判断。
  3. 发送队列持续堆积,通常说明出口带宽不足或下游接收慢,而不只是单纯延迟。