Linux 流量控制 (tc):實用指南
12 分鐘閱讀 - 2026年6月5日

使用 tc 在 Linux 上控制頻寬、優先處理流量,以及塑造入口和出口。真實伺服器的 HTB、IFB、DSCP 和 fq_codel 配置。
tc 的運作方式
每個 tc 設定皆由四個可變組件構成:
- qdisc(佇列紀律)。附著於網路介面的排程器,負責決定封包的入隊與出隊方式。
- Class。這是具有類別區分的 qdisc 內的子區分。可將其視為擁有自身速限的車道。
- Filter(篩選器)。檢查封包標頭(IP 位址、埠號、標記),並將每個封包分配至特定類別。
- Action(動作)。當封包符合條件時將執行的操作:轉發、丟棄、重定向。
這些元件共同構成一棵樹狀結構。封包從根 qdisc 進入,經過篩選器,透過 major:minor ,最終排入葉節點 qdisc 隊列以待傳輸。
若需比基於埠的匹配更複雜的處理,請先在 mangle 表中使用 iptables 或 nftables 標記封包,然後使用 fw filter tc 中的filter u32 規則來處理每種流量類型。
出站與入站
方向至關重要。核心可以對出站封包進行緩衝和延遲,這正是實現真正流量整形的關鍵。當您看到入站封包時,它們已經通過網路傳輸,因此除非您先將其重定向到 IFB 裝置,否則您只能對其進行流量監控(超過閾值時丟棄)。
| 功能 | 出站 | 入站 |
|---|---|---|
| 方向 | 出站 | 入站 |
| 流量整形 | 原生 | 需要 IFB |
| 流量監控 | 受支援 | 已支援 |
| 典型用途 | QoS、頻寬共享、流量調控 | 速率限制、基本 DDoS 緩解 |
您實際會使用的 qdisc
- HTB(分層令牌桶)。具類別。當您希望為每個服務保證最低頻寬,並能從其他類別借用未使用容量時,請使用此選項。
- TBF(Token Bucket Filter)。無類別。當您僅需將整個介面限制在單一速率時使用。
- fq_codel(公平佇列控制延遲)。 結合了每流公平性與主動佇列管理,以消除緩衝區膨脹。自 systemd 217 起,它已成為大多數 Linux 發行版的預設 qdisc,並在 RHEL 9 中預設搭載。請務必將其作為 HTB 類別下的葉節點 qdisc 附加,否則單一貪婪流量便可能佔用整個類別。
在 Linux 伺服器上設定 tc
tc 隨 iproute2 套件一併提供。在 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 為新介面的系統級預設 qdisc:
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) 虛擬介面,在此處,核心會將其視為出站流量,並允許您套用基於類別的 qdisc。
載入模組並啟用介面:
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進行 DSCP 比對時,請將數值向左移位 2 位元。DSCP EF (46) 將變為 0xb8。該遮罩 0xfc 將 6 位元 DSCP 位元與 2 位元 ECN 位元隔開。
針對伺服器工作負載的合理預設對應關係:
| 流量類型 | DSCP | TOS 十六進位 | 範例 |
|---|---|---|---|
| 互動式 | EF | 0xb8 | SSH、DNS、VoIP |
| 商務 | AF41 | 0x88 | HTTP、HTTPS、API |
| 批量 | CS1 | 0x20 | 備份、FTP、套件更新 |
| 盡力而為 | CS0 | 0x00 | 其他所有項目 |
在出站封包觸及您的 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請留意 dropped 和 overlimits 計數器。丟包表示佇列已飽和;超出限值則表示您已觸及類別上限,內核不得不延遲或捨棄流量。若要查看即時狀態:
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 至 leaf 類別,提高 burst |
若因設定錯誤導致系統無法存取,重置方法很簡單:
tc qdisc del dev eth0 roottc 無法憑空製造您原本沒有的頻寬,但在配置完善的上行鏈路上,這將決定系統是能維持可預測的效能,還是會在某個租戶啟動大流量傳輸的瞬間導致伺服器崩潰。若您需要原始頻寬並希望自由地進行流量整形,不妨看看 FDC 的專用伺服器。

Linux 流量控制 (tc):實用指南
使用 tc 在 Linux 上控制頻寬、優先處理流量,以及塑造入口和出口。真實伺服器的 HTB、IFB、DSCP 和 fq_codel 配置。
12 分鐘閱讀 - 2026年6月5日
為什麼擁有一個強大且不計費的 VPS 是很重要的?
7 分鐘閱讀 - 2025年5月9日