#server-performance

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

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

hero section cover

使用 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 中作为默认配置提供。请务必将其作为叶节点 qdisc 附加在 HTB 类之下,否则单个贪婪流可能会独占整个类。

在 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)虚拟接口,在内核中,该接口将入站流量视为出站流量,从而允许您应用基于类的队列调度器(qdiscs)。

加载模块并启用该接口:

modprobe ifb numifbs=1
ip link set dev ifb0 up

在物理接口上添加一个入站 qdisc,并将所有流量重定向至 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已配置根 qdisctc 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 的专用服务器

博客

本周特色

更多文章
iperf3 教程:在 Linux 和 Windows 上测试网络速度
#bandwidth#server-performance

iperf3 教程:在 Linux 和 Windows 上测试网络速度

在 Linux 和 Windows 系统上安装 iperf3、运行带宽测试并调整 TCP 缓冲区,以获得准确结果。涵盖 UDP、双向及 10GbE+ 测试

10 分钟阅读 - 2026年5月7日

#server-performance

针对 Linux 服务器工作负载优化的调优配置文件

16 分钟阅读 - 2026年6月9日

更多文章