Linuxでのネットワーク障害シミュレーション
1. 概要
このチュートリアルでは、Linuxでのいくつかのネットワーク障害のシミュレーションを見ていきます。 特に、tcコマンドラインとnetemキューイングの分野を使用してシミュレーションを実行します。
2. ネットワークトラフィック制御
ネットワークトラフィック制御は、システムのネットワークトラフィック特性を管理する方法です。 具体的には、Linuxカーネルネットワークスタック内のパケットのキューイング特性を操作することにより、主にトラフィックを制御します。
つまり、パケットがネットワークインターフェイスに送信される前に、スケジューラはパケットをエンキューします。 次に、さまざまなルールとロジックを適用して、キューイングの動作を変更できます。 その結果、これらのキューを構成することにより、ネットワークトラフィックの特性に影響を与える可能性があります。
一般に、ネットワークトラフィックの制御に関しては、次の4つの側面があります。
- シェーピング
- スケジューリング
- 分類
- ポリシング
2.1. シェーピング
ネットワークトラフィック制御では、シェーピングとは、トラフィック転送速度を操作して目的の速度を達成することを指します。 たとえば、シェーピングによってインターフェイスの帯域幅を制限できます。
さらに、パケットが配信される前にパケットを遅延させることも、シェーピングの一形態です。
2.2. スケジューリング
スケジューラーは、パケットがキューにある間にパケットを再配置できます。 つまり、キューを並べ替えることで、重要なパケットを優先することができます。 結果として、これはユーザーにサービス品質を実装する方法を提供します。
2.3. 分類
ネットワークトラフィック制御での分類とは、プロパティに従ってパケットをグループ化することです。 たとえば、宛先または送信元に基づいてパケットをクラスにグループ化できます。
次に、さまざまな制御メカニズムをさまざまなクラスに接続できるため、さまざまな種類のパケットの処理をよりきめ細かく制御できます。
2.4. ポリシング
ポリシングは、パケットが次のステップに進むことを許可または停止するネットワークトラフィック制御のメカニズムです。 ポリシングの例の1つは、特定のリモートホスト宛てのパケットをドロップすることです。
3. キューイング規律
キューイングディシプリン(または略してqdisc)は、パケットキューのスケジューリングを管理するスケジューラーです。 ネットワークトラフィックを制御するのはqdiscを介してです。 qdiscsは、さらにクラスフルまたはクラスレスqdiscに分類されます。
クラスフルqdiscは、classesを使用してルールの階層を形成できます。 これらのクラスを通じて、さまざまなルールと動作を適用できます。 したがって、さまざまなルールの下でさまざまなクラスのパケットを送信する可能性があります。
一方、 classlessqdiscにはクラスの概念がありません。 言い換えると、クラスレスqdiscsのルールは、それらを通過するすべてのパケットに適用されます—それらは無差別です。
このチュートリアルでは、クラスレスqdiscに焦点を当てます。 特に、 netem クラスレスqdiscを検討します。これは、さまざまな一般的なネットワーク障害モードをシミュレートするのに十分です。
4. tc
tc は、Linuxのトラフィック制御コマンドラインツールです。 具体的には、 tc コマンドを使用して、Linuxカーネルネットワークスタックのトラフィック制御設定を構成します。
4.1. インストール
最近のLinuxディストリビューションのほとんどには、すでにtcコマンドが付属しています。 コマンドがシステムに存在しない場合は、コマンドを含むパッケージをインストールすることで取得できます。
DebianベースのLinuxでは、apt-getを使用してiproute2パッケージをインストールすることにより、tcコマンドを取得できます。
$ sudo apt-get update
$ sudo apt-get install -y iproute2
一方、RHELベースのLinux(CentOSなど)の yum パッケージマネージャーを使用して、パッケージiproute-tcパッケージをインストールできます。
$ sudo yum update
$ sudo yum install -y iproute-tc
インストールが完了したら、 tc -help を実行して、tcコマンドが使用可能であることを確認できます。
$ sudo tc -help
Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }
tc [-force] -batch filename
where OBJECT := { qdisc | class | filter | chain |
action | monitor | exec }
OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[aw] |
-o[neline] | -j[son] | -p[retty] | -c[olor]
-b[atch] [filename] | -n[etns] name | -N[umeric] |
-nm | -nam[es] | { -cf | -conf } path }
ヘルプページが表示されたら、インストールが成功したことを確認できます。
5. Qdiscsの一覧表示
システム上のすべてのネットワークインターフェイスに適用されているqdiscsを表示するには、tc qdiscshowを実行します。
$ sudo tc qdisc show
qdisc noqueue 0: dev lo root refcnt 2
qdisc noqueue 0: dev eth0 root refcnt 2
qdisc noqueue 0: dev eth1 root refcnt 2
出力から、システムに3つのネットワークインターフェイスがあることがわかります。 さらに、それらすべてに noqueueqdiscが接続されています。
noqueue qdiscは、クラス、スケジューラ、ポリシング、またはレート制限のない単純なqdiscです。 つまり、パケットを受信するとすぐに送信します。
また、コマンドの最後にインターフェース名を指定することで、1つのインターフェースの情報を表示することもできます。 たとえば、インターフェイスeth0の情報のみを表示するには次のようにします。
$ sudo tc qdisc show dev eth0
qdisc noqueue 0: dev eth0 root refcnt 2
6. 固定遅延のシミュレーション
たとえば、eth0を通過するパケットに100msの固定遅延を追加できます。
$ sudo tc qdisc add dev eth0 root netem delay 100ms
$ sudo tc qdisc list
qdisc noqueue 0: dev lo root refcnt 2
qdisc netem 8003: dev eth0 root refcnt 2 limit 1000 delay 100.0ms
qdisc noqueue 0: dev eth1 root refcnt 2
上記のコマンドは、 netem delayオプションを指定します。 さらに、100msの固定遅延を指定しました。
次に、 tc qdisc add コマンドは、遅延ルールをeth0インターフェイスのルートレベルにアタッチするだけです。 この記事ではクラスフルqdiscを取り上げていないため、いつでもqdiscをインターフェイスのrootレベルに追加できます。
qdiscの動作を確認するために、ping実験を実行してみましょう。 特に、google.comにpingを実行して読み取り値を取得しようとします。 次に、読み取り値を比較できます。
ping -c 5 google.com
PING google.com (172.217.27.238) 56(84) bytes of data.
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=1 ttl=37 time=8.21 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=2 ttl=37 time=10.4 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=3 ttl=37 time=9.63 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=4 ttl=37 time=11.2 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=5 ttl=37 time=8.33 ms
--- google.com ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4008ms
rtt min/avg/max/mdev = 8.210/9.560/11.248/1.171 ms
ルールを適用する前に、各pingリクエストが完了するまでに約9.56ミリ秒かかります。
このセクションで前述したコマンドを使用して100msの固定遅延を追加すると、完了時間がおよそ100ms増加することがわかります。
ping -c 5 google.com
PING google.com (172.217.27.238) 56(84) bytes of data.
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=1 ttl=37 time=127 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=2 ttl=37 time=112 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=3 ttl=37 time=109 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=4 ttl=37 time=111 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=5 ttl=37 time=111 ms
--- google.com ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4007ms
rtt min/avg/max/mdev = 109.429/114.275/127.325/6.579 ms
実験が終了したら、 tc qdiscdeleteコマンドを使用してインターフェイスからqdiscを削除します。
インターフェイスeth0に接続したqdiscを削除しましょう。
$ sudo tc qdisc delete dev eth0 root
$ sudo tc qdisc show eth0
qdisc noqueue 0: dev eth0 root refcnt 2
7. 正規分布の遅延のシミュレーション
固定遅延の他に、 netem は、分布に従って遅延をシミュレートする可能性も提供します。 特に、netem遅延オプションを介して正規分布する遅延をシミュレートできます。
これを実現するために、分布の平均と標準偏差をそれぞれ表す2つの引数を指定します。
netem delay <mean> <standard deviation> distribution <distribution name>
標準偏差値を指定し、分布引数を省略すると、netemはデフォルトで正規分布を使用します。
たとえば、平均が100ミリ秒、標準偏差が50ミリ秒の正規分布の遅延をシミュレートできます。
$ sudo tc qdisc add dev eth0 root netem delay 100ms 50ms distribution normal
新しい構成を確認するために、ping実験を再度実行できます。 統計実験の実行に関する1つの注意点は、実行ごとにより多くのデータを収集する必要があるということです。 これは、取得した統計値がより正確になるようにするためです。
google.com を240回pingして、ラウンドトリップ時間の統計を取得しましょう。
$ ping -c 240 -q google.com
PING google.com (216.58.196.14) 56(84) bytes of data.
--- google.com ping statistics ---
240 packets transmitted, 240 received, 0% packet loss, time 239363ms
rtt min/avg/max/mdev = 9.256/113.478/238.708/50.648 ms
要約から、240のICMP要求が完了するのに平均113.48msかかることがわかります。 約10msの実際の完了時間を考慮に入れると、結果は私たちの予想通りです。
さらに、記録された50.65msの標準偏差は、実際に構成した標準偏差値に近いものです。
8. パケット損失のシミュレーション
netemの損失オプションを使用すると、ネットワークパケットのランダムなドロップをシミュレートできます。 たとえば、キューが30%の確率でランダムにパケットをドロップするシナリオをシミュレートできます。
$ sudo tc qdisc add dev eth0 root netem loss 30%
ping 実験を再度実行して、動作を確認してみましょう。
$ ping -q -c 60 google.com
PING google.com (172.217.27.238) 56(84) bytes of data.
--- google.com ping statistics ---
60 packets transmitted, 42 received, 30% packet loss, time 63590ms
rtt min/avg/max/mdev = 8.015/11.345/23.986/2.988 ms
出力から、実験によるパケット損失率は30%であることがわかります。 これは、インターフェイスを構成したときとまったく同じです。
実際には、パケット損失は通常、純粋な偶然ではなく、一連のパケットで発生しています。 この相関関係を説明するために、最後の引数として相関関係のパーセンテージ値を指定できます。
$ sudo tc qdisc add dev eth0 root netem loss 30% 50%
上記のコマンドでは、受信したパケットを30% ofドロップするようにqdiscを構成しています。 さらに、50 % o fの次のパケットがドロップされる確率は、前のパケットに対して生成された確率に依存します。
9. パケット複製のシミュレーション
netemのduplicateオプションを使用して、パケットをランダムに複製するようにqdiscを構成できます。 たとえば、eth0で50% cのハンスを使用してパケットの複製をシミュレートできます。
$ sudo tc qdisc add dev eth0 root duplicate 50%
実際の動作を確認するには、 ping google.com:
$ ping -c 2 google.com
PING google.com (142.250.199.46) 56(84) bytes of data.
64 bytes from 142.250.199.46 (142.250.199.46): icmp_seq=1 ttl=37 time=7.48 ms
64 bytes from 142.250.199.46 (142.250.199.46): icmp_seq=1 ttl=37 time=7.51 ms (DUP!)
64 bytes from 142.250.199.46 (142.250.199.46): icmp_seq=2 ttl=37 time=8.51 ms
--- google.com ping statistics ---
2 packets transmitted, 2 received, +1 duplicates, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 7.484/7.834/8.506/0.475 ms
出力からわかるように、ICMPシーケンス1に対して重複したICMP要求があります。
10. パケット破損のシミュレーション
次に、 netem を使用して、パケットにデータ破損をランダムに注入できます。 具体的には、corruptオプションを使用して行うことができます。
破損オプションが指定されている場合、netemは、設定されたパーセンテージに従って、パケットにシングルビットエラーをランダムに導入します。
たとえば、30% cのパケット破損の可能性を導入できます。
$ sudo tc qdisc add dev eth0 root netem corrupt 30%
ここで、 ping コマンドを実行すると、破損したパケットが原因で約30%のパケット損失が発生するはずです。
$ ping -q -c 240 google.com
PING google.com (172.217.174.174) 56(84) bytes of data.
--- google.com ping statistics ---
240 packets transmitted, 165 received, 31.25% packet loss, time 241364ms
rtt min/avg/max/mdev = 7.392/9.126/29.282/1.968 ms
11. 転送速度の制限
netem qdiscは、制限オプションを介して転送速度制限をサポートします。 たとえば、インターフェイスeth0のネットワーク転送速度を10Mbitに制限できます。
$ sudo tc qdisc add dev eth0 root netem rate 10Mbit
iperf コマンドを使用して、構成をテストするための実験を行ってみましょう。 iperf コマンドは、主にネットワーク負荷テストの目的で使用されるLinuxのコマンドです。
172.18.0.3のリモートホストでiperfを実行すると、帯域幅の容量が33.4ギガビットであることがわかります。
$ iperf3 -c 172.18.0.3 -p 8080
Connecting to host 172.18.0.3, port 8080
[ 5] local 172.18.0.2 port 50164 connected to 172.18.0.3 port 8080
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 3.36 GBytes 28.9 Gbits/sec 0 1.33 MBytes
[ 5] 1.00-2.00 sec 3.86 GBytes 33.1 Gbits/sec 0 1.33 MBytes
[ 5] 2.00-3.00 sec 3.94 GBytes 33.9 Gbits/sec 0 1.33 MBytes
[ 5] 3.00-4.00 sec 3.97 GBytes 34.1 Gbits/sec 0 1.33 MBytes
[ 5] 4.00-5.00 sec 3.92 GBytes 33.7 Gbits/sec 0 1.33 MBytes
[ 5] 5.00-6.00 sec 4.01 GBytes 34.5 Gbits/sec 0 1.33 MBytes
[ 5] 6.00-7.00 sec 4.02 GBytes 34.5 Gbits/sec 0 1.33 MBytes
^C[ 5] 7.00-7.70 sec 2.82 GBytes 34.5 Gbits/sec 0 1.33 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-7.70 sec 29.9 GBytes 33.4 Gbits/sec 0 sender
[ 5] 0.00-7.70 sec 0.00 Bytes 0.00 bits/sec receiver
構成をアクティブ化すると、netemはそのインターフェイスの転送速度を10Mビットに制限します。 その結果、同じホストで同じ iperf コマンドを実行すると、転送速度が低下します。
$ iperf3 -c 172.18.0.3 -p 8080
Connecting to host 172.18.0.3, port 8080
[ 5] local 172.18.0.2 port 50172 connected to 172.18.0.3 port 8080
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 2.29 MBytes 19.2 Mbits/sec 0 239 KBytes
[ 5] 1.00-2.00 sec 1.06 MBytes 8.86 Mbits/sec 0 297 KBytes
[ 5] 2.00-3.00 sec 1.30 MBytes 10.9 Mbits/sec 0 355 KBytes
[ 5] 3.00-4.00 sec 1.55 MBytes 13.0 Mbits/sec 0 414 KBytes
[ 5] 4.00-5.00 sec 1.80 MBytes 15.1 Mbits/sec 0 472 KBytes
[ 5] 5.00-6.00 sec 1018 KBytes 8.34 Mbits/sec 0 530 KBytes
[ 5] 6.00-7.00 sec 1.06 MBytes 8.86 Mbits/sec 0 588 KBytes
[ 5] 7.00-8.00 sec 2.42 MBytes 20.3 Mbits/sec 0 648 KBytes
[ 5] 8.00-9.00 sec 1.25 MBytes 10.5 Mbits/sec 0 706 KBytes
[ 5] 9.00-10.00 sec 0.00 Bytes 0.00 bits/sec 0 764 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 13.7 MBytes 11.5 Mbits/sec 0 sender
[ 5] 0.00-10.65 sec 12.1 MBytes 9.56 Mbits/sec receiver
12. 概要
このチュートリアルでは、Linuxのtcコマンドについて説明しました。
まず、ネットワークトラフィック制御の紹介テキストから始めました。 さらに、クラスレスqdiscsとクラスフルqdiscsの違いについても見てきました。
次に、 netemqdiscのみを確認しました。 特に、 netemqdiscを使用していくつかの種類のネットワーク障害をシミュレートする方法を学びました。 たとえば、delayオプションを使用してパケット遅延をシミュレートする方法を示しました。 さらに、distributionを使用して正規分布の遅延をシミュレートする方法を見てきました。
さらに、パケットキューに障害を挿入することを目的とした損失、重複、および破損オプションを確認しました。
最後に、limitオプションを使用して帯域幅を制限する方法を学びました。