Linuxトラフィック制御(tc):実践ガイド
12分で読めます - 2026年6月5日

tcを使用して、Linux上で帯域幅を制御し、トラフィックを優先順位付けし、入口と出口を形成する。実サーバーのHTB、IFB、DSCP、fq_codelコンフィグ。
tcの仕組み
すべての tc セットアップは、4つの構成要素から成り立っています:
- qdisc(キューイング・ディシプリン)。ネットワークインターフェースに紐づくスケジューラ。パケットのキューへの追加(enqueue)とキューからの取り出し(dequeue)の方法を決定します。
- Class。クラスフル qdisc 内の細分化。独自の速度制限を持つ車線のようなものと考えてください。
- Filter。パケットヘッダー(IPアドレス、ポート、マーク)を検査し、各パケットをクラスに割り当てます。
- アクション。パケットが条件に一致した際に実行される処理:転送、破棄、リダイレクト。
これらはツリー構造を形成します。パケットはルートqdiscから入り、フィルタを通過し、 major:minor ハンドルによってクラスに分類され、最終的にリーフQdiscのキューに入れられて送信されます。
ポートベースのマッチングよりも複雑な処理を行う場合は、iptables または nftables の mangle テーブルでパケットにマークを付け、その後 fw filter tc を使用してマークごとに分類します。これは、すべてのトラフィックタイプに対して生の u32 ルールをチェーンするよりも、はるかに優れたスケーラビリティを発揮します。
送信(Egress)と受信(Ingress)
方向は重要です。カーネルは送信パケットをバッファリングして遅延させることができ、これが真のシェーピングを可能にします。受信パケットは、あなたがそれを確認する時点ですでにネットワークを経由して到着しているため、最初に IFB デバイスにリダイレクトしない限り、それらをポリシング(しきい値を超えたものをドロップ)することしかできません。
| 機能 | 送信 | イングレスの |
|---|---|---|
| 方向 | 送信 | インバウンド |
| シェーピング | ネイティブ | IFBが必要 |
| ポリシング | サポート対象 | サポート対象 |
| 一般的な用途 | QoS、帯域幅の共有、ペーシング | レート制限、基本的なDDoS対策 |
実際に使用するqdisc
- HTB (Hierarchical Token Bucket)。クラスあり。サービスごとに保証された最小帯域幅を確保しつつ、他のクラスから未使用の容量を借りられる機能が必要な場合に使用します。
- TBF (Token Bucket Filter)。クラスレス。インターフェース全体を単一のレートに制限したい場合に使用します。
- fq_codel (Fair Queuing Controlled Delay)。 フローごとの公平性とアクティブキュー管理を組み合わせ、バッファブロートを解消します。systemd 217以降、ほとんどのLinuxディストリビューションでデフォルトのqdiscとなっており、RHEL 9ではデフォルトで搭載されています。必ずHTBクラスの下位にあるリーフqdiscとして配置してください。そうしないと、1つの貪欲なフローがクラス全体を占有してしまう可能性があります。
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新しいインターフェースのシステム全体のデフォルトqdiscとして fq_codel を新しいインターフェースのシステム全体のデフォルトqdiscとして設定します:
sysctl -w net.core.default_qdisc=fq_codel負荷の高いサーバーでは、BBR輻輳制御アルゴリズム(カーネル4.9以降)と組み合わせて使用してください。BBRはキューを膨らませることなくスループットを高く維持します:
sysctl -w net.ipv4.tcp_congestion_control=bbrSSH経由でリモートマシンを設定する際の安全対策として、2つ目のセッションを開き、 tc qdisc del dev eth0 root 貼り付けられるように準備しておくことです。誤ったフィルタルールは、即座に接続を遮断してしまう可能性があります。
HTB によるアウトバウンドトラフィックのシェーピング
HTBを使用すると、各サービスに対して保証された下限値(rate)と上限(ceil)を設定できます。未使用の帯域幅は、優先順位に従ってそれを必要とするトラフィックに割り当てられます。以下は、1 Gbpsのアップリンク向けの動作する3層構成の例です。
ルートHTB qdiscを作成します。 default 30 は、分類されていないパケットをクラス 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各クラスのリーフqdiscとして 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:20IFB を使用したインバウンドトラフィックのシェーピング
パケットが到着する時点ですでに帯域幅が消費されているため、ネイティブにインバウンドトラフィックをシェーピングすることはできません。その回避策として、インバウンドトラフィックを中間機能ブロック(IFB)仮想インターフェースにリダイレクトします。これにより、カーネルはそれをアウトバウンドとして扱い、クラスベースのqdiscを適用できるようになります。
モジュールをロードし、インターフェースを起動します:
modprobe ifb numifbs=1
ip link set dev ifb0 up物理インターフェースにingress 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 1DSCP によるトラフィックの優先順位付け
DSCP(Differentiated Services Code Point)は、TOSバイト内の6ビット値でパケットにタグ付けを行うため、 tc フィルタは、ルールセット全体でポートを追跡するのではなく、タグに基づいて分類できます。DSCPを照合する際は tc、値を2ビット左にシフトします。DSCP EF (46) は 0xb8となります。このマスクは 0xfc は、6 ビットの DSCP ビットを 2 ビットの ECN ビットから分離します。
サーバーワークロード向けの適切なデフォルトのマッピング:
| トラフィックタイプ | DSCP | TOS 16進数 | 例 |
|---|---|---|---|
| 対話型 | EF | 0xb8 | SSH、DNS、VoIP |
| ビジネス | AF41 | 0x88 | HTTP、HTTPS、API |
| Bulk | CS1 | 0x20 | バックアップ、FTP、パッケージの更新 |
| ベストエフォート | CS0 | 0x00 | その他すべて |
アウトバウンドパケットがフィルタに到達する前に、iptables でタグ付けする tc フィルターに到達する前にタグを付ける:
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監視とトラブルシューティング
常に使用する3つのコマンド:
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 を追加して内部パラメータ(target、interval、quantum)を確認し、 -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 roottc 持っていない帯域幅を作り出すことはできませんが、適切にプロビジョニングされたアップリンクであれば、予測可能なパフォーマンスと、1人のテナントが大規模な転送を開始した瞬間に機能不全に陥るサーバーとの差を決定づけます。生の帯域幅と、それを自由にシェイピングする自由度が必要な場合は、FDCの専用サーバーをご検討ください。

Linuxトラフィック制御(tc):実践ガイド
tcを使用して、Linux上で帯域幅を制御し、トラフィックを優先順位付けし、入口と出口を形成する。実サーバーのHTB、IFB、DSCP、fq_codelコンフィグ。
12分で読めます - 2026年6月5日
パワフルで無制限のVPSが重要な理由
7分で読めます - 2025年5月9日

ご質問またはカスタムソリューションが必要ですか?
柔軟なオプション
グローバル・リーチ
即時展開
柔軟なオプション
グローバル・リーチ
即時展開