tc限速规则在pod网络中不生效,因其默认作用于宿主机物理网卡(如eth0),而pod出向流量经veth对和cni0桥接后才到达该接口;真正生效位置是pod对应的veth设备,需进入pod网络命名空间后对eth0(pod内主网卡)配置tbf限速。

tc 限速规则为什么在 Pod 网络里不生效
因为默认的 tc 命令作用在宿主机网卡(如 eth0),而 Pod 出向流量通常不经过该接口——它先走 veth 对、再经 docker0 或 cni0 桥接,最后才到物理网卡。直接在 eth0 上加 qdisc,对 Pod egress 流量是“看不见”的。
真正起作用的位置,是每个 Pod 对应的 veth 设备(比如 vethabc123),但这个设备在宿主机上不可见,除非你进入 Pod 的网络命名空间。
- 用
ip link show在宿主机上只能看到veth*名字,对应关系需通过crictl inspect或nsenter查找 - 必须在 Pod 的 netns 内执行
tc qdisc add,否则规则挂载失败或无效 - 容器重启后
veth设备重建,规则自动丢失,无法靠宿主机脚本持久化
如何在 Pod netns 中正确添加 tc 限速规则
核心是:进命名空间 → 找出口设备 → 加 qdisc → 绑定 filter(可选)。Pod 内部通常没有 tc 命令,所以得用宿主机的 nsenter + tc 组合。
典型流程(以 containerd 为例):
- 查 Pod 的 PID:
crictl inspect <pod-id> | grep pid</pod-id>,拿到"pid": 12345 - 进 netns:
nsenter -t 12345 -n tc qdisc show确认当前无规则 - 加限速(例如限 1Mbps):
nsenter -t 12345 -n tc qdisc add dev eth0 root tbf rate 1mbit burst 32kbit latency 400ms -
eth0是 Pod 内看到的主网卡名,不是宿主机的eth0;tbf比htb更简单稳定,适合单速率限速
为什么用 tbf 而不是 htb 或 sfq
tbf(Token Bucket Filter)是单速率整形器,行为确定、开销低、无队列调度逻辑,在 Pod egress 限速这种“只压峰值、不保优先级”的场景下更可靠。
-
htb需要 class + filter 多层配置,容易因 classid 冲突或 filter 匹配失败导致规则静默失效 -
sfq是排队算法,不提供限速能力,单独用它等于没限速 -
tbf的burst参数必须设(哪怕很小),否则 Linux 内核会拒绝加载;latency控制最大排队延迟,设太小会导致丢包,太大则缓冲膨胀 - 实测中,
burst小于rate / 8 * 0.1(即 100ms 窗口数据量)时,HTTP 小包响应延迟明显升高
Kubernetes 中自动化部署 tc 规则的风险点
有人写 DaemonSet 在节点启动时扫所有 Pod 并批量注入 tc,这看似省事,实际埋了三个硬伤:
- Pod 启动快于 DaemonSet 容器就绪,导致规则漏加;尤其 InitContainer 场景下,netns 可能还没完全 setup 完
- 不同 CNI 插件(Calico / Cilium / Flannel)对
veth命名和 netns 挂载方式不同,nsenter路径可能失效 - Cilium 启用 eBPF 替代 kube-proxy 时,部分流量绕过 netfilter,
tc在eth0上的规则可能被跳过 - 如果 Pod 使用 hostNetwork,
tc必须加在宿主机接口而非 netns,混用逻辑极易出错
真正可控的做法,是把 tc 命令塞进容器启动脚本(如 entrypoint.sh),并在 sleep 1 && tc qdisc add... 前加 netns 就绪检测,而不是依赖外部协调。










