Linux 流量控制 (tc):实用指南

12 分钟阅读 - 2026年6月5日

hero section cover
目录
  • Linux 流量控制 (tc):实用指南
  • tc 的工作原理
  • 在 Linux 服务器上配置 tc
  • 使用 HTB 对出站流量进行整形
  • 使用 IFB 整形入站流量
  • 使用 DSCP 进行流量优先级排序
  • 监控与故障排除
分享

使用 tc 在 Linux 上控制带宽、确定流量优先级以及塑造入口和出口。为实际服务器配置 HTB、IFB、DSCP 和 fq_codel。

Linux 流量控制 (tc):实用指南

Linux 的 tc 命令可让您直接控制服务器处理网络流量的方式。 您可以限制每个服务的带宽,在批量传输激增时保持 SSH 等交互会话的响应性,并使用单一工具对出站和入站流量进行整形。本指南涵盖了核心概念、可运行的 HTB 设置、使用 IFB 进行入站流量整形、基于 DSCP 的优先级排序,以及出现故障时的调试方法。


 

tc 的工作原理

每个 tc 配置均由四个动态组件构成:

  • qdisc(队列纪律)。这是连接到网络接口的调度器,负责决定数据包如何入队和出队。
  • Class)。这是基于类的 qdisc 内部的子划分。可以将其视为一条拥有自身限速的车道。
  • Filter过滤器)。检查数据包头(IP 地址、端口、标记)并将每个数据包分配到相应的类中。
  • Action(动作)。数据包匹配后将执行的操作:转发、丢弃、重定向。

这些组件构成了一棵树。数据包从根 qdisc 进入,经过过滤器,由 major:minor 句柄分入不同类,最终在叶节点 qdisc 排队等待传输。

对于比基于端口的匹配更复杂的场景,请在mangle表中使用iptablesnftables对数据包进行标记,然后使用 fw filter tc 中的过滤器,根据标记进行分类。其可扩展性远优于为每种流量类型单独链式连接原始 u32 规则。

出站与入站

方向很重要。内核可以缓冲和延迟出站数据包,这正是实现真正流量整形的关键。当你看到入站数据包时,它们已经通过网络传输,因此除非你先将其重定向到 IFB 设备,否则只能对其进行流量监管(超过阈值时丢弃)。

功能出站入站
方向出站入站
流量整形原生需要IFB
流量监管受支持支持
典型用途QoS、带宽共享、速率控制速率限制、基础DDoS缓解

您实际会用到的 qdisc

  • HTB(分层令牌桶)。支持类管理。当您希望为每个服务保证最低带宽,同时能够从其他类中借用未使用的带宽时,请使用它。
  • TBF(令牌桶过滤器)。无类。当您只需将整个接口的速率限制在单一值时使用。
  • fq_codel(公平队列控制延迟)。 结合了按流公平性与主动队列管理,以消除缓冲区膨胀。自 systemd 217 以来,它已成为大多数 Linux 发行版的默认 qdisc,并在 RHEL 9 中作为默认配置提供。请务必将其作为 HTB 类下的叶节点 qdisc 进行挂载,否则单个贪婪的流量流可能会独占整个类。

在 Linux 服务器上配置 tc

tciproute2 软件包一同提供。在 Debian 和 Ubuntu 上,请使用以下命令进行安装 apt-get install iproute2。在 RHEL 及其衍生系统上, yum install iproute。您需要 root 或 sudo 权限。

首先确认正确的接口名称。接口命名错误是配置文件无响应的最常见原因:

ip link show

检查接口上的现有数据,包括实时计数器:

tc -s qdisc show dev eth0

在应用新配置前清除任何现有的 root qdisc,以避免 RTNETLINK answers: File exists 错误:

tc qdisc del dev eth0 root 2>/dev/null || true

如果您是在更新现有规则而非从头开始,请使用 replace 代替 add 以实现原子替换。

TSO 和 GSO 等硬件卸载功能会以干扰流量整形的方式对数据包进行打包。请在需要整形的接口上将其关闭:

sudo ethtool -K eth0 tso off gso off

fq_codel 作为新接口的全局默认队列调度器:

sysctl -w net.core.default_qdisc=fq_codel

对于繁忙的服务器,建议将其与 BBR 拥塞控制算法(内核 4.9 及以上)配合使用。BBR 能在不增加队列长度的情况下保持高吞吐量:

sysctl -w net.ipv4.tcp_congestion_control=bbr

若通过 SSH 配置远程服务器,请养成以下安全习惯:打开第二个会话,并将 tc qdisc del dev eth0 root 准备好粘贴。一条错误的过滤规则可能会让你瞬间被锁在系统之外。

使用 HTB 对出站流量进行整形

HTB 允许您为每项服务设定保证的最低值(rate)和上限(ceil)。未使用的带宽将按优先级顺序分配给需要它的服务。以下是一个针对 1 Gbps 上行链路的有效三层配置。

创建根 HTB 队列调度器。 default 30 会将任何未分类的数据包发送至 class 1:30 ,而非允许其绕过您的规则:

tc qdisc add dev eth0 root handle 1: htb default 30

将总吞吐量限制在 900 Mbps。始终将整形值设定在实际链路容量的略低水平,否则队列会在您无法控制的上游路由器或调制解调器上形成:

tc class add dev eth0 parent 1: classid 1:1 htb rate 900mbit ceil 900mbit

定义服务层级。优先级较低的 prio 级别的服务优先获得未使用的带宽:

# High priority: web and API traffic
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 500mbit ceil 900mbit prio 1
 
# Medium priority: database replication
tc class add dev eth0 parent 1:1 classid 1:20 htb rate 300mbit ceil 900mbit prio 2
 
# Low priority: bulk and backup traffic
tc class add dev eth0 parent 1:1 classid 1:30 htb rate 100mbit ceil 900mbit prio 3

fq_codel 作为每个类别的叶节点 qdisc,以防止单个流量流独占其层级:

tc qdisc add dev eth0 parent 1:10 handle 10: fq_codel
tc qdisc add dev eth0 parent 1:20 handle 20: fq_codel
tc qdisc add dev eth0 parent 1:30 handle 30: fq_codel

现在对流量进行分类。对于简单的端口匹配, u32 是最快的:

tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 \
  match ip dport 443 0xffff flowid 1:10

对于任何需要状态跟踪的情况,请在 iptables 中添加标记,并通过该标记进行匹配 fw:

iptables -t mangle -A OUTPUT -p tcp --dport 5432 -j MARK --set-mark 2
tc filter add dev eth0 protocol ip parent 1:0 prio 2 handle 2 fw flowid 1:20

使用 IFB 整形入站流量

您无法原生对入站流量进行整形,因为当数据包到达时,它已经占用了您的带宽。解决方法是将入站流量重定向到中间功能块(IFB)虚拟接口,在内核中,该接口会被视为出站接口,从而允许您应用基于类的队列调度器。

加载模块并启用接口:

modprobe ifb numifbs=1
ip link set dev ifb0 up

在物理接口上添加一个入站队列调度器,并将所有流量重定向至 ifb0:

tc qdisc add dev eth0 ingress handle ffff:
tc filter add dev eth0 parent ffff: protocol all u32 \
  match u32 0 0 action mirred egress redirect dev ifb0

从此时起, ifb0 该接口的行为与其他接口无异。请像处理出站流量一样,向其应用您的 HTB 树:

tc qdisc add dev ifb0 root handle 1: htb default 30
tc class add dev ifb0 parent 1: classid 1:1 htb rate 900mbit ceil 900mbit
tc class add dev ifb0 parent 1:1 classid 1:10 htb rate 500mbit ceil 900mbit prio 1

使用 DSCP 进行流量优先级排序

DSCP(差异化服务代码点)会在 TOS 字节中使用 6 位值对数据包进行标记,因此您的 tc 过滤器可以按标签进行分类,而非在规则集中遍历端口。在匹配 DSCP 时 tc,请将该值向左移 2 位。DSCP EF (46) 将变为 0xb8。该掩码 0xfc 将 6 位 DSCP 位与 2 位 ECN 位分离。

针对服务器工作负载的合理默认映射:

流量类型DSCPTOS 十六进制示例
交互式EF0xb8SSH、DNS、VoIP
企业AF410x88HTTP、HTTPS、API
批量CS10x20备份、FTP、软件包更新
尽力而为CS00x00其他所有

在出站数据包到达过滤器之前,先在 iptables 中对其进行标记 tc 过滤器之前,在 iptables 中为其添加标签:

iptables -t mangle -A OUTPUT -p tcp --dport 22 -j DSCP --set-dscp 46

然后在 tc 并将其路由到正确的 HTB 类:

# EF (SSH, VoIP) goes to the high-priority class
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 \
  match ip tos 0xb8 0xfc flowid 1:10
 
# AF41 (web traffic) goes to the medium class
tc filter add dev eth0 protocol ip parent 1:0 prio 2 u32 \
  match ip tos 0x88 0xfc flowid 1:20
 
# CS1 (bulk) goes to the low-priority class
tc filter add dev eth0 protocol ip parent 1:0 prio 3 u32 \
  match ip tos 0x20 0xfc flowid 1:30

监控与故障排除

您将经常使用的三个命令:

tc -s qdisc show dev eth0
tc -s class show dev eth0
tc -s filter show dev eth0

关注 droppedoverlimits 计数器。丢包意味着队列已饱和;超限则表示您触及了类上限,内核不得不延迟或丢弃流量。实时查看请用:

watch -n 1 'tc -s class show dev eth0'

添加 -d 以查看内部参数(目标、间隔、量子),并 -j 若需将数据管道传输至指标堆栈,请添加以获取 JSON 输出。配合 ss -tin 可查看 TCP 层的 RTT 估计值和重传情况。

大多数故障可归纳为以下几种:

症状可能原因解决方法
RTNETLINK answers: File exists根 qdisc 已配置tc qdisc del dev eth0 root 首先
规则生效但流量未受限接口错误,或 TSO/GSO 仍处于启用状态请通过 ip link show,使用 ethtool -K
过滤器从未匹配端口/IP语法错误或子网掩码对齐问题添加一个计数器操作,并在 tc -s filter show
重启后规则消失配置仅驻留于内存中封装在脚本中,并通过 systemd 或 NetworkManager 分发器调用
优先级流量延迟过高没有叶节点 qdisc,或突发值过低fq_codel 连接到叶节点类,提高 burst

如果您因配置错误而无法访问,重置方法很简单:

tc qdisc del dev eth0 root

tc 无法凭空制造本不存在的带宽,但在配置完善的上行链路上,这决定了性能是否可预测,还是服务器会在某个租户启动大文件传输的瞬间崩溃。如果您需要原始带宽并希望自由地按需进行流量整形,不妨关注 FDC 的专用服务器

博客

本周特色

更多文章
Linux 流量控制 (tc):实用指南

Linux 流量控制 (tc):实用指南

使用 tc 在 Linux 上控制带宽、确定流量优先级以及塑造入口和出口。为实际服务器配置 HTB、IFB、DSCP 和 fq_codel。

12 分钟阅读 - 2026年6月5日

为什么必须拥有功能强大且不计量的 VPS

7 分钟阅读 - 2025年5月9日

更多文章