#server-performance

Linux トラフィック制御 (tc):実践ガイド

12分で読めます - 2026年6月5日

hero section cover

Linux上でtcを使用して、帯域幅を制御し、トラフィックに優先順位を付け、入出力トラフィックをシェイピングします。実稼働中のサーバー向けに、HTB、IFB、DSCP、およびfq_codelの設定例を紹介します。

Linux トラフィック制御 (tc):実践ガイド

Linuxの tc コマンドを使用すると、サーバーによるネットワークトラフィックの処理方法を直接制御できます。 サービスごとの帯域幅に上限を設定したり、大容量の転送が急増した際にも SSH などの対話型セッションの応答性を維持したり、単一のツールで送信および受信の両方のトラフィックをシェーピングしたりすることができます。このガイドでは、中核となる概念、HTB の実用的な設定、IFB によるインジェス・シェーピング、DSCP に基づく優先順位付け、および問題が発生した際のデバッグ方法について解説します。


 

tcの仕組み

すべての tc セットアップは、4つの構成要素から成り立っています:

  • qdisc(キューイング・ディシプリン)。ネットワークインターフェースに紐付けられたスケジューラです。パケットのキューへの投入およびキューからの取り出し方法を決定します。
  • クラス。クラスフル 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の設定

tciproute2 パッケージに含まれています。Debian および Ubuntu では、以下のコマンドでインストールしてください。 apt-get install iproute2でインストールします。RHELおよびその派生ディストリビューションでは、 yum install iprouteでインストールします。root 権限または sudo が必要です。

まず、正しいインターフェース名を確認してください。インターフェース名の指定ミスは、設定が何も反応しない最も一般的な原因です:

ip link show

そのインターフェースに現在何が設定されているか(リアルタイムのカウンタ情報を含む)を確認してください:

tc -s qdisc show dev eth0

新しい設定を適用する前に、既存のルート 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=bbr

SSH経由でリモートマシンを設定する際の安全対策として、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:20

IFB によるインバウンドトラフィックのシェーピング

パケットが到着する時点ですでに帯域幅が消費されているため、ネイティブにインバウンドトラフィックをシェーピングすることはできません。この問題を回避するには、インバウンドトラフィックを中間機能ブロック(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 1

DSCP によるトラフィックの優先順位付け

DSCP(Differentiated Services Code Point)は、TOSバイト内の6ビットの値でパケットにタグ付けを行うため、 tc フィルタは、ルールセット全体でポートを追跡するのではなく、タグに基づいて分類できるようになります。DSCPを照合する際は tc、値を2ビット左にシフトします。DSCP EF (46) は 0xb8となります。このマスクは 0xfc は、6 ビットの DSCP ビットを 2 ビットの ECN ビットから分離します。

サーバーのワークロードに適したデフォルトのマッピング:

トラフィックタイプDSCPTOS(16進数)
対話型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

監視とトラブルシューティング

頻繁に使用する3つのコマンド:

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 を追加して内部パラメータ(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 root

tc 持っていない帯域幅を作り出すことはできませんが、適切にプロビジョニングされたアップリンクがあれば、予測可能なパフォーマンスが得られるか、あるいは1人のテナントが大規模な転送を開始した瞬間にサーバーが機能不全に陥るかの分かれ目となります。生の帯域幅と、それを自由にシェイピングできる自由度が必要な場合は、FDCの専用サーバーをご検討ください。

ブログ

今週の特集

その他の記事
iperf3 チュートリアル:Linux および Windows でのネットワーク速度の測定
#bandwidth#server-performance

iperf3 チュートリアル:Linux および Windows でのネットワーク速度の測定

LinuxおよびWindowsで正確な結果を得るには、iperf3をインストールし、帯域幅テストを実行し、TCPバッファを調整してください。UDP、双方向、および10GbE以上のテストについて解説しています。

10分で読めます - 2026年5月7日

その他の記事
background image

ご質問またはカスタムソリューションが必要ですか?

icon

柔軟なオプション

icon

グローバル・リーチ

icon

即時展開

icon

柔軟なオプション

icon

グローバル・リーチ

icon

即時展開