🔬

Linux ネットワーク不安定性シミュレーション入門

に公開

1. はじめに

現代的な分散システムやマイクロサービスアーキテクチャでは複数のノードが非同期に連携しながら複雑に協調動作するケースが多く存在します。このような環境下ではネットワーク品質のわずかな変動がシステム全体に大きな影響を及ぼすことがあり、システム運用時に想定する最悪ケースの通信品質を再現したテスト環境を構築することが重要です。一般的に、完全に通信できなくなるような完全なネットワーク障害よりも、著しい遅延や確率的な瞬断が発生しながらも駆動し続けるような部分的な障害の方が、システム全体としての問題をより複雑で深刻化させる傾向があります。

ネットワークに不安定性 (遅延や瞬断など) が発生した場合、非同期システムでは次のような挙動が顕在化する可能性があります。

  • バックプレッシャーによる送信側キューの肥大化
  • スレッドプールの枯渇
  • クライアントの接続拒否
  • ネットワーク遅延に起因するノード故障の誤判定
  • Socket Timeout や Read Timeout の発動
  • エラー時処理、または再試行などのリカバリ機能の発動
  • ジッター (遅延時間のばらつき) によるイベント順序の逆転

この記事では Linux ネイティブのネットワーク制御ツールである tc (+ netem モジュール) と iptables を使い、テスト環境でネットワーク遅延や通信中の切断などの不安定性を再現する実践的な手順をハンズオン形式で解説します。

2. 前提と準備

対象: TCP/IP の基本的な知識を持ち、Linux のネイティブツールの扱いに慣れているエンジニア。

重要な注意点: 設定ミスや解除漏れによってシステムが意図しない状態に陥り、最悪の場合、元に戻せなくなるリスクを伴います。操作は隔離された環境で慎重に行ってください。

環境:

ハンズオン形式で進めるために、通信可能な 2 つのノードを次の環境で用意してください。

  1. 物理マシン上で動作する仮想化されていない Linux 環境、あるいは
  2. VirtualBox (Windows または MacOS) や KVM (Linux) などの完全仮想化で動作する Linux 環境

Docker コンテナはカーネルモジュールの操作にさまざまな制約があり、この記事で扱う tc の利用が困難になりがちですのでおすすめしません。

代表的な 2 種類の Linux ディストリビューションでの操作を説明するために、この記事では 2 つのノードに異なる Linux 環境を導入しています。自分の環境に合わせて該当するディストリビューションの説明を参照してください。

  • alpha-ub01: Ubuntu 24.04 環境のノード
  • beta-ce01: CentOS Stream 9 環境のノード (10 は VirtualBox で panic を起し動作しなかったため)

用意した双方のノードから ping を実行し、相互に通信できることを確認してください。

torao@alpha-ub01:~$ ping -c 3 beta-ce01
PING beta-ce01 (192.168.150.51) 56(84) bytes of data.
64 bytes from beta-ce01 (192.168.150.51): icmp_seq=1 ttl=64 time=0.701 ms
64 bytes from beta-ce01 (192.168.150.51): icmp_seq=2 ttl=64 time=0.725 ms
64 bytes from beta-ce01 (192.168.150.51): icmp_seq=3 ttl=64 time=0.832 ms

--- beta-ce01 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2074ms
rtt min/avg/max/mdev = 0.701/0.752/0.832/0.056 ms

上記の出力例は alpha-ub01 から beta-ce01 へ正常に ICMP パケットが送受信できていることを示しています。

2.1 テストツールのインストール

ここからは alpha-ub01 (Ubuntu 24.04) と beta-ce-01 (CentOS Stream 9) の説明を併記します。最初にそれぞれの環境でテストに必要なツールをインストールします。

torao@alpha-ub01:~$ sudo apt update &&
  sudo apt install -y iproute2 iptables iperf3
[torao@beta-ce01 ~]$ sudo dnf update &&
  sudo dnf install -y iproute iptables iperf3

構成を簡単にするため、ファイアウォールが設置されているならテスト終了までオフにします (重要: セキュリティ上のリスクを理解した上で行い、テスト終了後は必ず有効に戻してください)。

torao@alpha-ub01:~$ sudo ufw disable
[torao@beta-ce01 ~]$ sudo systemctl stop firewalld

ファイアウォールを有効にしたままテストを行う必要がある場合は、iperf3 の 5201 ポートなど、この記事の挙動確認で使用するプロトコルや各ポートの通信を許可するように設定を追加してください。

この状態で iperf3 を使って正常状態での TCP ベースの帯域速度を測定してみましょう。以下の例では alpha-ub01 でサーバを起動し beta-ce01 から接続しています。速度がネットワーク構成で想定するスペックに近ければ OK です (例えばギガビットイーサネット環境であれば 950Mbps 程度になるでしょう)。

Ubuntu 24.04 では iperf3 をインストールすると自動的に systemctl によって起動されるようになります。以下のコマンドで iperf3 がサーバとして起動しているか確認してください。

torao@alpha-ub01:~$ systemctl list-unit-files -t service | grep iperf3
iperf3.service                               enabled         enabled

それ以外の Linux や自動起動をオフにしている場合は、以下のように手動でサーバとして起動します。

torao@alpha-ub01:~$ iperf3 -s
-----------------------------------------------------------
Server listening on 5201 (test #1)
-----------------------------------------------------------

クライアントからは以下のように接続できます。これはクライアントからサーバ方向にデータを送信して計測していることに注意してください。

[torao@beta-ce01 ~]$ iperf3 -c alpha-ub01 -t 3
Connecting to host alpha-ub01, port 5201
[  5] local 192.168.150.51 port 57838 connected to 192.168.150.50 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec   227 MBytes  1.90 Gbits/sec  122    242 KBytes
[  5]   1.00-2.00   sec   227 MBytes  1.90 Gbits/sec  236    247 KBytes
[  5]   2.00-3.00   sec   222 MBytes  1.86 Gbits/sec  218    226 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-3.00   sec   676 MBytes  1.89 Gbits/sec  576             sender
[  5]   0.00-3.00   sec   674 MBytes  1.88 Gbits/sec                  receiver

iperf Done.

2.2 カーネルモジュールの確認とロード

netem やクラス/フィルター設定に必要なカーネルモジュールが読み込まれているかを確認し、不足していればロードします。

# netem モジュールがロードされているか確認
lsmod | grep -E 'sch_netem|sch_htb|cls_fw|cls_u32'

# ロードされていなければ以下のようにロードする
sudo modprobe sch_netem   # tc でパケット遅延、ロス、重複、リオーダーなどを注入する netem モジュール
sudo modprobe sch_htb     # 階層的な帯域制御を行う HTB (hierarchical token bucket) qdisc
sudo modprobe cls_fw      # ファイアウォールマークに基づいてトラフィックを分類するクラス
sudo modprobe cls_u32     # より複雑な条件でトラフィックを分類する汎用的なクラス

これらをロードすることで tc qdisc add ... コマンドが正常に動作します。

[!TIP]
使用するネットワークインターフェース (eth0enp0s3 など) に対して、パケットの集約 (GRO, LRO) と分割 (GSO, TSO) に関するハードウェアオフロードを無効化する必要があるかもしれません。これらが有効になっていると、ハードウェアがパケットの分割と集約を行うため tc netem の遅延や損失が個々のパケットに正確に反映されず、計測に影響を与える可能性があります。

# 現在のハードウェアオフロードの設定を確認 (例: enp0s3)
sudo ethtool -k enp0s3 | grep tcp-segmentation-offload

# TCP TSO を無効化
sudo ethtool -K enp0s3 tso off

3. 基本的なネットワーク遅延の注入と検証

次に Linux のトラフィック制御ツール tc (traffic control) とネットワークエミュレーション機能を提供する netem を使ってネットワークにパケット遅延を加え、その影響を pingiperf3 などのツールで確認します。遅延以外で tc を使って可能な動作、例えばパケット破損や帯域制限などは後にまとめています。

tc は Linux カーネルのトラフィック制御を設定するための強力なコマンドラインツールです。ネットワークインターフェースに対してパケットの送受信を制御し、さまざまな QoS やトラフィックシェーピングのルールを提供できます。

tc の基本的な概念として qdisc (キューイングディシプリン) があります。qdisc は、ネットワークインターフェースに出入りするパケットの待ち行列を管理し、どのようにパケットを処理(送信またはドロップ)するかを決定します。netem はこの qdisc の一種であり、パケットの遅延、損失、重複、破損などのネットワーク障害をエミュレートする機能を提供します。

3.1 netem (Network Emulator) の基本的な使い方

この記事では以下のように想定していますので、自分の環境に合わせて以下のホスト名などを読み替えてください。

ホスト名 IP アドレス ネットワークインターフェース
alpha-ub01 192.168.150.50 enp0s3
beta-ce01 192.168.150.51 enp0s3

デフォルト状態で enp0s3 は、遅延の影響を低減するためのアクティブキュー管理を行う fq_codel (Fair Queue CoDel) という qdisc が 1 つだけ設定されています。

torao@alpha-ub01:~$ sudo tc qdisc show dev enp0s3
qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64

3.1.1 特定の NIC から送信されるすべてのパケットに遅延を設定する

まず最も簡単な構成として、alpha-ub01enp0s3 インターフェースに qdisc を追加し、その NIC から送信されるすべてのパケットに 120 ミリ秒の送信遅延を加えます。この操作は既存の fq_codel qdisc を netem qdisc で置き換えます。

# netem qdisc を root としてセット
sudo tc qdisc add dev enp0s3 root netem delay 120ms

このコマンドの実行により enp0s3 に設定されていたデフォルトの qdisc が置き換えられます。

torao@alpha-ub01:~$ sudo tc qdisc show dev enp0s3
qdisc netem 8002: root refcnt 2 limit 1000 delay 120ms

この設定によって alpha-ub01 から enp0s3 を通して送信されるすべてのパケットに 120ms の遅延が加算されます。ping で確認すると RTT (往復遅延時間) が約 121 ミリ秒程度になっていることがわかるでしょう。ping のパケットは往復するため alpha-ub01beta-ce01 のどちら側で ping を実行してもこの遅延の影響を受けます。

[torao@beta-ce01 ~]$ ping alpha-ub01 -c 3
PING alpha-ub01 (192.168.150.50) 56(84) bytes of data.
64 バイト応答 送信元 alpha-ub01 (192.168.150.50): icmp_seq=1 ttl=64 時間=121 ミリ秒
64 バイト応答 送信元 alpha-ub01 (192.168.150.50): icmp_seq=2 ttl=64 時間=121 ミリ秒
64 バイト応答 送信元 alpha-ub01 (192.168.150.50): icmp_seq=3 ttl=64 時間=121 ミリ秒

--- alpha-ub01 ping 統計 ---
送信パケット数 3, 受信パケット数 3, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 120.684/120.967/121.341/0.275 ms

設定した netem qdisc を削除するには以下のコマンドを実行します。ping を動作させたままで行えば、ある時点から 120 ミリ秒の遅延がなくなっているのがわかるでしょう。

sudo tc qdisc del dev enp0s3 root

デフォルトの qdisc に戻っていることが確認できます。

torao@alpha-ub01:~$ sudo tc qdisc show dev enp0s3
qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64

3.1.2 特定の IP アドレス宛のパケットに遅延を設定する

tc filter を使用すると特定の IP アドレス宛のパケットに対してのみネットワーク障害を注入することができます。これはより現実的で複雑なネットワーク環境のシミュレーションを可能にします。

  • ネットワーク遅延の差異: ローカルネットワーク内のノードと地理的に離れた場所に存在するノードとの間の遅延差を再現する。
  • 外部サービスとの通信: 外部の API サービスやデータベースとの通信で不安定性の発生をシミュレーションする。
  • 段階的な影響調査: ネットワーク不安定性の注入範囲を部分的なノードから開始し徐々に拡大してゆくことで、システム全体への影響を段階的に調査する。

まず prio qdisc を追加し、その中の優先度バンド (ここでは band 3) に対して netem qdisc をぶら下げ、さらにその qdisc に filter で宛先 IP アドレスを一致させます。prio qdisc は複数の優先度を持つキューイング規律であり、ここでは異なる種類のトラフィックを異なるバンドに分類するために使用します。

例えば alpha-ub01 上の enp0s3 から beta-ce01 (192.168.150.51) 宛に送信されるパケットにのみ 120ms の遅延を追加するには以下のように設定します。

# 1. prio qdisc を root としてセット
sudo tc qdisc add dev enp0s3 root handle 1: prio

# 2. prio の band 3 (handle 1:3) に netem qdisc をセットして 120ms 遅延
sudo tc qdisc add dev enp0s3 parent 1:3 handle 30: netem delay 120ms

# 3. filter で IP アドレス 192.168.150.51 宛のパケットのみ band 3 に振り分け
sudo tc filter add dev enp0s3 protocol ip parent 1: prio 3 u32 match ip dst 192.168.150.51 flowid 1:3

ここで tc filter に設定する IP アドレスを 192.168.150.0/24 のようにネットワーク範囲で指定することもできます。複数のデータセンターをシミュレーションする場合などでは、各ノードの IP アドレスを所属するネットワークでシミュレーションして設定しておくと tc の設定が簡単になります。

設定を確認します。

torao@alpha-ub01:~$ tc qdisc show dev enp0s3
qdisc prio 1: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
qdisc netem 30: parent 1:3 limit 1000 delay 120ms

torao@alpha-ub01:~$ tc filter show dev enp0s3
filter parent 1: protocol ip pref 3 u32 chain 0
filter parent 1: protocol ip pref 3 u32 chain 0 fh 800: ht divisor 1
filter parent 1: protocol ip pref 3 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 *flowid 1:3 not_in_hw
  match c0a89633/ffffffff at 16

この設定も ping を用いて検証できます。alpha-ub01beta-ce01 間では約 120ms の遅延が発生しますが、それ以外のノードから alpha-ub01ping を送信しても遅延は発生しないはずです。

以下の手順は設定した順に削除しています。

# 1. まず宛先 IP アドレスを一致させていたフィルタのみを削除 (これで遅延は発生しなくなる)
sudo tc filter del dev enp0s3 protocol ip parent 1: prio 3

# 2. netem qdisc を削除
sudo tc qdisc del dev enp0s3 parent 1:3 handle 30:

# 3. prio qdisc を削除
sudo tc qdisc del dev enp0s3 root handle 1:

または root qdisc を削除すれば下層の qdisc や filter もまとめて削除されます。

# root qdisc を削除
sudo tc qdisc del dev enp0s3 root
torao@alpha-ub01:~$ tc qdisc show dev enp0s3
qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64

3.1.3 ポート単位またはより詳細な遅延を設定する

特定のポート番号 (例えば TCP の 80 ポート) での通信に対して遅延を設定するには、iptablestc を連携する必要があります。ここでは TCP の 5201 ポートを使用する iperf3 で遅延を観測します。

iperf3 はクライアントからサーバに対してペイロードを送信するため、クライアント側の beta-ce01 側で遅延を設定します (サーバ側に設置しただけでは ACK が遅延するだけです)。まず iptablesmangle テーブルの OUTPUT チェーンにルールを追加し、遅延を追加したい対象のパケット (宛先ポートが 5201 の TCP パケット) にマーク 1 を付けます。

sudo iptables -t mangle -A OUTPUT -p tcp --dport 5201 -j MARK --set-mark 1

mangle テーブルはパケットのヘッダ情報を変更するために使用されます。

次に、tc filter でマーク 1 の付いたパケットに対して遅延を適用します。

# 1. prio qdisc を root としてセット
sudo tc qdisc add dev enp0s3 root handle 1: prio

# 2. prio の band 3 (handle 1:3) に netem qdisc をセットして 120ms 遅延
sudo tc qdisc add dev enp0s3 parent 1:3 handle 30: netem delay 120ms

# 3. マーク 1 の付いたパケットを band 3 (flowid 1:3) に振り分け (fw はファイアウォールマークとのマッチングを表す)
sudo tc filter add dev enp0s3 protocol ip parent 1: prio 3 handle 1 fw flowid 1:3

上記の設定を beta-ce01 側で行い、alpha-ub01iperf3 -s -p 5202 も起動して beta-ce01 から帯域速度を計測すると、5201 ポートではビットレートが 50Mbps 程度まで低下していることがわかります。

[torao@beta-ce01 ~]$ iperf3 -c alpha-ub01 -p 5201 -t 3
Connecting to host alpha-ub01, port 5201
[  5] local 192.168.150.51 port 35916 connected to 192.168.150.50 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  5.29 MBytes  44.3 Mbits/sec   51    707 KBytes
[  5]   1.00-2.00   sec  6.25 MBytes  52.4 Mbits/sec    0    800 KBytes
[  5]   2.00-3.00   sec  6.25 MBytes  52.4 Mbits/sec    0    875 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-3.00   sec  17.8 MBytes  49.7 Mbits/sec   51             sender
[  5]   0.00-3.12   sec  15.1 MBytes  40.6 Mbits/sec                  receiver

iperf Done.

[torao@beta-ce01 ~]$ iperf3 -c alpha-ub01 -p 5202 -t 3
Connecting to host alpha-ub01, port 5202
[  5] local 192.168.150.51 port 51414 connected to 192.168.150.50 port 5202
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec   235 MBytes  1.97 Gbits/sec  221    297 KBytes
[  5]   1.00-2.00   sec   226 MBytes  1.90 Gbits/sec   85    242 KBytes
[  5]   2.00-3.00   sec   231 MBytes  1.94 Gbits/sec  199    212 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-3.00   sec   692 MBytes  1.94 Gbits/sec  505             sender
[  5]   0.00-3.00   sec   690 MBytes  1.93 Gbits/sec                  receiver

iperf Done.

この遅延を削除するには iptablestc filtertc qdisc の設定を順に削除します。

# 1. まず宛先 IP アドレスを一致させていたフィルタのみを削除 (これで遅延は発生しなくなる)
sudo tc filter del dev enp0s3 protocol ip parent 1: prio 3

# 2. netem qdisc を削除
sudo tc qdisc del dev enp0s3 parent 1:3 handle 30:

# 3. prio qdisc を削除
sudo tc qdisc del dev enp0s3 root handle 1:

# 4. ルール番号を確認
sudo iptables -t mangle -L OUTPUT --line-numbers

# 5. ルール番号を指定して削除 (例えば 5201 をマーク 1 とするルールが #1 にある場合)
sudo iptables -t mangle -D OUTPUT 1

または、他に mangle テーブルの OUTPUT チェーンに有効な設定が存在しないことが分っている場合に限り、以下のコマンドでチェーンをすべて削除できます。

sudo iptables -t mangle -F OUTPUT

この iptables と連携する方法の応用として、ポート番号以外にも多くの属性を組み合わせてパケットにマークを付けることができます。以下は iptables で使用できる代表的なパターンです (tc と連携可否や注意点については、個々の属性の組み合わせによって異なる場合があります)。

分類対象の属性 説明 iptables の一致例
ポート番号 TCP/UDP の送受信ポート -p tcp --dport 5201
IP アドレス/サブネット 送信元/宛先 IP やサブネット -s 10.0.0.0/24, -d 192.168.1.100
ネットワークインターフェース 出力インターフェース -o eth2 (Egress)
プロトコル TCP, UDP, ICMP など -p icmp, -p udp
接続状態 (conntrack) NEW, ESTABLISHED, RELATED 等 -m conntrack --ctstate NEW
DSCP/TOS QoS 用 DiffServ コードポイント -m dscp --dscp-class EF, -m tos --tos 0x10
TCP フラグ SYN, ACK, RST などのフラグ -p tcp --syn, -p tcp --tcp-flags RST RST
パケット長 パケットのバイト長 -m length --length 1500:65535
MAC アドレス 送信元または宛先の L2 MAC -m mac --mac-source AA:BB:CC:DD:EE:FF (PREROUTING チェーン)
時刻/曜日 特定の時間帯や曜日のみ適用 -m time --timestart 18:00 --timestop 20:00
UID/GID ローカル発信パケットのユーザー/グループ識別 -m owner --uid-owner 1001, -m owner --gid-owner 1001

4. パケット損失・重複・リオーダー・破損の注入と検証

ここまでの説明では netem を用いてパケット遅延 delay を追加してきました。しかし netem は他にもパケット損失、重複、リオーダー、破損といった、より複雑なネットワーク障害を注入することができます。

パケット障害の種類 netem オプションの例 挙動の確認方法
遅延 delay 50ms ping の RTT で確認。
損失 loss 0.2% ping 終了時のロス率 (packet loss) を確認。
重複 duplicate 5% ping のシーケンス番号が重複することを確認。
リオーダー reorder 5% ping のシーケンス番号の順序が逆転することを確認。
破損 corrupt 2% tcpdumpwireshark など。また暗黙的に破棄され損失として計上されることもある。
帯域制限 rate 64kbit iperf3 の Bitrate で確認。

詳細は tc-netem(8) を参照してください。

またこれらのパケット障害を組み合わせて、より複雑なネットワーク状況をエミュレーションすることもできます。以下の例では 100ms の遅延、5% のパケット損失、2% のパケット重複を同時に注入します。

sudo tc qdisc add dev enp0s3 root netem \
  delay 100ms \
  loss 5% \
  duplicate 2%

ただし、このような IP パケットレベルの障害は TCP のレイヤーで検知され再送や訂正が行われることが予想されます。これらの訂正動作は TCP 再送信 (TCP retransmission) の発生数の増加として観測されます。tcpdump -n -i enp0s3 'tcp and port 5201' -v | grep retrans のようなコマンドで TCP 再送信の数を監視すると影響を定量的に把握する手助けになります。

5. TCP 接続の確率的な障害

前章で説明した中でもパケット破損やリオーダーといった IP レベルでの細かな障害注入は、TCP プロトコルが持つ再送制御や順序制御の仕組みによって吸収されるため、アプリケーションが直接的な影響を受けにくい可能性があります。アプリケーションのネットワーク障害耐性をより直接的に評価するには、TCP レベルでの接続断や接続拒否といった、より影響の大きい障害のエミュレーションを優先する方が効果的です。

5.1 アクティブな TCP セッションの乱断

ここでは iptables を使って特定の条件に一致するアクティブな TCP セッションからの送信パケットを確率的に遮断し、強制的に接続をリセットする方法を紹介します。

以下のコマンドは iptablesOUTPUT チェーンにルールを追加し、送信元ポート 5201 を使用する確立済みの TCP セッションからの送信パケットを 5% の確率で遮断します。このとき、接続相手に TCP RST (Reset) パケットを送信して接続を強制的にリセットします。

sudo iptables -t filter -A OUTPUT \
  -p tcp --sport 5201 \
  -m statistic --mode random --probability 0.05 \
  -j REJECT --reject-with tcp-reset

この設定を削除するには -A-D に変更して実行します。

sudo iptables -t filter -D OUTPUT \
  -p tcp --sport 5201 \
  -m statistic --mode random --probability 0.05 \
  -j REJECT --reject-with tcp-reset

この挙動を確認してみましょう。iptables の設定を行った alpha-ub01iperf3 -s を起動し、beta-ce01 から alpha-ub01 の 5201 ポートへ iperf3 で接続すると、データ転送中にランダムに切断される様子が観測できます (運が悪いと接続時のハンドシェイクで遮断され無応答になることがあります)。

[torao@beta-ce01 ~]$ iperf3 -c alpha-ub01 -p 5201 -b 1M -t 60
Connecting to host alpha-ub01, port 5201
[  5] local 192.168.150.51 port 59390 connected to 192.168.150.50 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec   128 KBytes  1.05 Mbits/sec    0    109 KBytes
…
iperf3: error - unable to write to stream socket: Connection reset by peer

切断は送信パケット数に対して確率的に発生します。帯域速度が速すぎると一瞬で切断されてしまいますし、遅すぎるとなかなか切断されず観測に時間がかかります。私の環境では -b 1M で 1Mbps 程度の帯域制限を設定するとおおむね 10 秒以内に切断が発生して観測しやすくなりました。

5.2 ランダムな接続拒否

確立済みの接続を遮断するだけではなく、新しい接続の試行を意図的に拒否することも、アプリケーションの耐性を評価する上で重要です。ここでも iptables を使用して、特定のポートへの新しい TCP 接続を確率的に拒否する方法を紹介します。

以下のコマンドは filter テーブルの INPUT チェーンにルールを追加し、宛先ポートが 5201 の新しい TCP SYN パケット (TCP 接続の開始要求) を 50% の確率で拒否します。この場合、接続元に TCP RST (Reset) パケットを送信して接続を拒否したことを通知します。

sudo iptables -t filter -A INPUT \
  -p tcp --dport 5201 --syn \
  -m statistic --mode random --probability 0.5 \
  -j REJECT --reject-with tcp-reset

この設定を削除するには -A-D に変更して実行します。

sudo iptables -t filter -D INPUT \
  -p tcp --dport 5201 --syn \
  -m statistic --mode random --probability 0.5 \
  -j REJECT --reject-with tcp-reset

ここでも iperf3 を使って挙動を確認しましょう。iptables の設定を行った alpha-ub01iperf3 -s を起動して beta-ce01 から iperf3 の接続を試みると、約 50% の確率で接続が拒否されます。

[torao@beta-ce01 ~]$ iperf3 -c alpha-ub01 -p 5201 -t 3
Connecting to host alpha-ub01, port 5201
iperf3: error - unable to connect stream: Connection refused

6. まとめと今後の進展

この記事では、ネットワーク上に構築された現代的なシステムに制御可能なネットワークの不安定性を作り出し、アプリケーションの耐障害性を評価するための実践的な手法について解説しました。この方法は、地理分散された大規模システムから、帯域の貧弱なコンシューマ端末やデバイスを想定したアプリケーションのシステムテストに応用できるでしょう。

まとめると、この記事で紹介したネットワーク不安定性テストは、以下の 2 つを組み合わせることでより効率的に実施できます。

  • ネットワークのパケット遅延や帯域制限を tc netem で注入する。
  • TCP の接続拒否やアクティブなセッションの乱断を iptables で注入する。

ネットワークの不安定性は、大規模分散システムにおける避けられないリスクです。これらの手段によって、実際のデータセンター間やクラウド環境、公衆モバイル回線などで起こりうる様々なネットワークの不安定な状況を、制御されたテスト環境で再現することが可能です。

これにより、開発者はアプリケーションの潜在的な問題を早期に発見し、より堅牢で信頼性の高いシステムを構築できます。これはユーザに安定したサービスを提供することにつながるでしょう。

また、ここで解説している方法はしばしばアプリケーションに「不可解な挙動」を引き起こす可能性があります。しかし、注入した障害によって生じたその挙動の原因を想像し、考察することは、問題解決能力とシステム開発の理解を深めるための貴重な訓練となるでしょう。

次のステップ:

  • 実際の分散システム環境で実施: 可観測性の導入 (Prometheus, Grafana, OpenTelemetry)
  • 発生した問題のより深い問題分析のための手段: tcpdump や Wireshark を使ったパケットキャプチャとその分析。
  • 他のネットワークへ応用: Kubernetes や SD-WAN へ展開。
  • カオスエンジニアリングに関連する類似の商用ソリューション: Gremlin
GitHubで編集を提案

Discussion