tcとNetEmでネットワークカオスを再現:レイテンシ・パケットロス・ジッター下における分散システムの検証

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

背景と導入の意義

ローカルの開発・ステージング環境は、しばしば現実離れした良好な状態を見せます。サービスは2ミリ秒で応答し、レプリケーションの遅延はゼロ、ネットワークタイムアウトなど理論上の机上論にすら思えます。しかし、本番のマルチリージョンなクラウドクラスタにデプロイした瞬間、厳しい現実に直面することになります。

ピーク負荷時にはデータセンター間のリンクが逼迫してパケットがドロップし、突発的なレイテンシスパイクが連鎖的なタイムアウトを引き起こします。Raftクラスタはパニックを起こして終わりのないリーダー選出を繰り返し、スレッドプールは無応答の下流サービスを待ち続けて枯渇します。何の問題もないローカルループバックだけでテストしている限り、サーキットブレーカーやリトライポリシーは単なる理論値に過ぎず、深夜3時の障害を待つだけの時限爆弾となってしまいます。

ブラックフライデーの最中にAWSのトランジットゲートウェイが不安定になるのを待つ必要はありません。Linuxカーネルレベルで制御されたカオスを直接注入できます。tc (Traffic Control) と netem (Network Emulator) のキューイング規則(qdisc)を組み合わせることで、不安定な接続、パケットロス、ジッターをオンデマンドで自在に再現可能です。

インストール

Linuxには、カーネルネイティブのネットワークトラフィック制御機能が組み込まれています。これらは、ほぼすべての最新ディストリビューションに標準で含まれる iproute2 パッケージ内の tc コマンドで管理します。

Debian、Ubuntu、およびその派生ディストリビューションの場合:

sudo apt-get update
sudo apt-get install -y iproute2

RHEL、Rocky Linux、Fedoraの場合:

sudo dnf install -y iproute

次に、カーネルモジュール sch_netem がメモリにロードされていることを確認します:

sudo modprobe sch_netem
lsmod | grep sch_netem

ルーティング規則を変更する前に、ip -br link を実行して対象のインターフェース名を確認してください。eth0 や ens3 などの物理アダプタ、または docker0 や cni0 などのコンテナ用ブリッジインターフェースを確認します。

設定方法

トラフィック制御は、送信(egress)トラフィックに対してキューイング規則(qdisc)を適用します。重要な点として、NetEmは指定したインターフェースから送出されるパケットのみを制御します。受信リクエストの遅延をシミュレートしたい場合は、クライアント側でこれらのルールを適用するか、中継ゲートウェイインターフェースを経由してトラフィックをルーティングしてください。

1. 固定遅延および変動遅延(ジッター)の導入

ベースラインとなる遅延を設定した netem qdiscを追加することで、マイクロサービス間の大西洋横断レベルのラウンドトリップ遅延をシミュレートします。100msの遅延を付加するには以下を実行します:

sudo tc qdisc add dev eth0 root netem delay 100ms

現実のネットワーク接続で、遅延がミリ秒単位で完全に一定になることは稀です。ジッター(揺らぎ)を導入することで、パケット間のばらつきと相関関係を再現できます。たとえば、平均100msの遅延に対し、±20msの一様分布の変動を与え、各パケットの遅延が直前のパケットと25%相関するように設定するには次のようにします:

sudo tc qdisc change dev eth0 root netem delay 100ms 20ms 25%

インターネットのトラフィックは通常、単純な一様分布ではなくベルカーブ(正規分布)を描きます。WANトラフィックをより正確に再現するには、正規(ガウス)分布を適用します:

sudo tc qdisc change dev eth0 root netem delay 100ms 20ms distribution normal

2. パケットロスとパケット破損のシミュレーション

帯域が飽和したトップオブラック(ToR)スイッチやノイズの多い無線回線では、頻繁にフレームがドロップします。カーネルによって送信フレームの5%をランダムに破棄させるには以下のように設定します:

sudo tc qdisc change dev eth0 root netem loss 5%

パケットロスは通常、ルーターのバッファがオーバーフローした際にバースト状に発生します。これは相関率(correlation)を指定することでモデル化できます。50%の相関を設定した場合、1つのパケットがドロップすると、後続のパケットも同様にドロップする確率が実質的に倍増します:

sudo tc qdisc change dev eth0 root netem loss 5% 50%

故障しかけたネットワークカードや不良ケーブルによって、ビット反転やフレームの重複が発生することもあります。こうしたエッジケースは、カスタムバイナリプロトコルやデシリアライズ処理に潜む微妙なバグを露呈させることがよくあります:

# 1ビット反転により、0.2%のパケットをランダムに破損させる
sudo tc qdisc change dev eth0 root netem corrupt 0.2%

# 送信パケットの1%を重複させる
sudo tc qdisc change dev eth0 root netem duplicate 1%

3. 複数の障害条件の組み合わせ

実際のネットワーク障害は複合的です。電波状況の悪いモバイル回線や混雑したVPNトンネルでは、遅延、ジッター、パケットドロップが同時に発生します。NetEmでは、これらのパラメータを1つのルールにまとめてチェーン指定できます:

sudo tc qdisc change dev eth0 root netem delay 150ms 30ms loss 3% duplicate 1%

この単一のコマンドで、過酷なモバイルネットワーク環境を再現できます。gRPCのデッドライン制御、Redisの接続プール、Kafkaのリトライバックオフが、下流のワーカーへリトライストームを浴びせることなく、適切にフェイルバックできるかを検証するのに最適なストレステストになります。

4. 設定の安全なリセット

テスト完了後は、必ずカオス実験の設定をクリーンアップしてください。rootのqdiscを削除すると、インターフェースは即座にカーネルデフォルトのキューイング規則(通常は fq_codel または pfifo_fast)へと復元されます:

sudo tc qdisc del dev eth0 root

検証とモニタリング

カオスルールが正常に動作していると思い込んではいけません。Linuxカーネル層とネットワーククライアント層の双方で動作を検証しましょう。

設定されたルールの確認

インターフェースの状態を照会してアクティブなキューイング規則を確認し、パラメータが正しくロードされているか検証します:

tc qdisc show dev eth0

以下のような出力が表示されるはずです:

qdisc netem 8001: root refcnt 2 limit 1000 delay 100ms loss 5%

Pingによる動作検証

対象ホストに向けてピアノードやテストクライアントから20個のICMP echoパケットを送信し、設定した遅延とドロップ率を検証します:

ping -c 20 192.168.1.50

ping実行結果の末尾にある統計サマリーを確認します:

20 packets transmitted, 19 received, 5% packet loss, time 19024ms
rtt min/avg/max/mdev = 99.842/101.314/104.120/1.240 ms

pingのサマリーから、設定通り約100msのラウンドトリップ遅延が付加され、意図した5%のパケットロスが正確に発生していることが確認できます。

リアルタイムなインターフェース統計の監視

アプリケーションレベルの負荷テストを実行しながら、リアルタイムのドロップ数やスループットカウンタを追跡します:

tc -s qdisc show dev eth0

dropped と overlimits のカウンタに注目してください。アプリケーションがトラフィックを流すにつれてこれらの数値が増加していれば、NetEmがフレームをアクティブにインターセプトしてドロップしていることの証明になります。

こうした軽量な tc netem によるアサーションを自動化されたCI/ステージングパイプラインに組み込むことで、本番環境で深刻な障害(Sev-1)を引き起こす前に、脆弱なタイムアウト設計や制御不能なコネクションリークを確実に検知できるようになります。

Share: