ping、curl 和 tc -s 观察延迟、握手时间和队列状态。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 升高。
ping 与 curl:ICMP 和 TCP 都慢,说明是链路层延迟;只有某个域名慢,优先查 DNS。tc -s qdisc 的 backlog 持续增长说明队列在堆积,可能同时存在带宽瓶颈;仅有延迟时 backlog 应很快排空。curl -w 拆分 time_namelookup、time_connect、time_starttransfer、time_total,判断慢在解析、握手、首字节还是传输。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 已清理。
tc 会影响所有流量。ping 慢但 curl 的 time_starttransfer 正常,可能是什么情况?tc -s qdisc 中 backlog 持续增长说明什么?