はじめに
この記事に書いてあること
Azure Virtual Machine へ UDP 接続でデータを送るときに、IP フラグメンテーションが発生しないようにデータを送らないとデータが届かないことについてまとめました。
何が起きたか?そして何をしたか?
アメリカ東海岸にある Azure VM から東日本にある Azure VM まで、UDP でデータを転送する際のスループットの測定を実施中に、Azure Virtual Network の仕様に起因して通信が未達となりました。最終的に UDP パケットを生成するツール (iPerf3) 側でソケットバッファの長さを調整して対応しました。
ことの顛末
やろうとしていた実験
下記の図のように、Azure の東日本リージョンと米国東部リージョンとの間を仮想ネットワークのピアリング機能で接続し、米国東部リージョンにデプロイした Ubuntu 18.04 の仮想マシンから東日本リージョンの Ubuntu 18.04 の仮想マシンまでのデータ転送のスループットを iPerf3 を利用して測定するシナリオです。
発生したこと
以下の iPerf3 のコマンドで、サーバー側(受信側)を東日本リージョンに、クライアント側(送信側)を米国東部リージョンにて起動しました。
iperf3 -s
iperf3 -c 10.35.0.5 -u -b 0 --omit 1 --get-server-output
すると、クライアント側の出力でこんな結果が…。
tokawa@eastus:~$ iperf3 -c 10.35.0.5 -u -b 0 --omit 1 --get-server-output
Connecting to host 10.35.0.5, port 5201
[ 4] local 10.43.0.4 port 44851 connected to 10.35.0.5 port 5201
[ ID] Interval Transfer Bandwidth Total Datagrams
[ 4] 0.00-1.00 sec 881 MBytes 7.39 Gbits/sec 112740 (omitted)
[ 4] 0.00-1.00 sec 1.01 GBytes 8.70 Gbits/sec 132780
[ 4] 1.00-2.00 sec 1.01 GBytes 8.68 Gbits/sec 132390
[ 4] 2.00-3.00 sec 1.01 GBytes 8.68 Gbits/sec 132400
[ 4] 3.00-4.00 sec 1.01 GBytes 8.67 Gbits/sec 132360
[ 4] 4.00-5.00 sec 1.02 GBytes 8.72 Gbits/sec 133050
[ 4] 5.00-6.00 sec 1.02 GBytes 8.73 Gbits/sec 133200
[ 4] 6.00-7.00 sec 1.01 GBytes 8.71 Gbits/sec 132870
[ 4] 7.00-8.00 sec 1.02 GBytes 8.73 Gbits/sec 133160
[ 4] 8.00-9.00 sec 1.01 GBytes 8.71 Gbits/sec 132860
[ 4] 9.00-10.00 sec 1.01 GBytes 8.70 Gbits/sec 132760
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams
[ 4] 0.00-10.00 sec 10.1 GBytes 8.70 Gbits/sec 0.000 ms 18658/-90785 (0%)
[ 4] Sent -90785 datagrams
Server output:
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
Accepted connection from 10.43.0.4, port 38056
[ 5] local 10.35.0.5 port 5201 connected to 10.43.0.4 port 44851
[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams
[ 5] 0.00-1.00 sec 25.8 MBytes 216 Mbits/sec 0.098 ms 18658/21955 (85%) (omitted)
[ 5] 0.00-1.00 sec 0.00 Bytes 0.00 bits/sec 0.000 ms 0/0 (0%)
[ 5] 1.00-2.00 sec 0.00 Bytes 0.00 bits/sec 0.000 ms 0/0 (0%)
[ 5] 2.00-3.00 sec 0.00 Bytes 0.00 bits/sec 0.000 ms 0/0 (0%)
[ 5] 3.00-4.00 sec 0.00 Bytes 0.00 bits/sec 0.000 ms 0/0 (0%)
[ 5] 4.00-5.00 sec 0.00 Bytes 0.00 bits/sec 0.000 ms 0/0 (0%)
[ 5] 5.00-6.00 sec 0.00 Bytes 0.00 bits/sec 0.000 ms 0/0 (0%)
[ 5] 6.00-7.00 sec 0.00 Bytes 0.00 bits/sec 0.000 ms 0/0 (0%)
[ 5] 7.00-8.00 sec 0.00 Bytes 0.00 bits/sec 0.000 ms 0/0 (0%)
[ 5] 8.00-9.00 sec 0.00 Bytes 0.00 bits/sec 0.000 ms 0/0 (0%)
[ 5] 9.00-10.00 sec 0.00 Bytes 0.00 bits/sec 0.000 ms 0/0 (0%)
[ 5] 10.00-10.15 sec 0.00 Bytes 0.00 bits/sec 0.000 ms 0/0 (0%)
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams
[ 5] 0.00-10.15 sec 0.00 Bytes 0.00 bits/sec 0.000 ms 0/0 (0%)
iperf Done.
後半のセクションが、サーバー側で受信した結果ですが、Transfer と Bandwidth のところを見ると、0.00 Bytes ならびに 0.00bits/sec となっており、データが全く届いていなかったことがわかります。
なぜこんな結果になったのか
当初全くわからずだったのですけれども、Azure サポートのエース某氏に相談したところ、大きなヒントをもらうことが出来ました。
上記のドキュメントより「5. UDP でフラグメントが発生した場合」のところに記載のある内容が、大ヒントになっておりました。なるほど UDP 通信で IP fragmentation が発生している可能性があるかも…というわけで、おもむろに tcpdump でパケットを取得。すると予想通りの結果が確認できたわけです。
8KB の UDP パケットが 6 つに分割されて送信されており、また Frame #32 で送られているデータ以外は UDP ヘッダーすらついていないパケットとして送られていることがわかります。参照したブログの「TCP でも UDP でもない IP パケットとして届くことになりますが、Azure VNet は TCP / UDP / ICMP しかサポートしていません。」という条件にバッチリ該当してしまったことになりますね。
対応策
ここまでわかれば対応策の練りようはあります。パケットが fragmentation されないようにすればいいわけです。
Azure の仮想マシンならびに Azure 内のネットワークにおける MTU は 1500 byte ですので、そこから IP ヘッダーの 20 byte と UDP ヘッダーの 8byte を引いて、ペイロードが 1472 bytes になるようにすれば OK ということです!
というわけで、iPerf3 のオプションを一つ足して、以下のようにしてみます。
iperf3 -c 10.35.0.5 -u -b 0 --omit 1 --get-server-output -l 1472
こんな感じで、iPerf3 の -l オプションでバッファ長を調整できるようなので、これを使いました。
iperf3 -h
~~中略~~
-l, --len #[KMG] length of buffer to read or write
(default 128 KB for TCP, 8 KB for UDP)
結果
結果は以下の通りで、無事に UDP での通信が通るようになりました!
tokawa@eastus:~$ iperf3 -c 10.35.0.5 -u -b 0 --omit 1 --get-server-output -l 1472
Connecting to host 10.35.0.5, port 5201
[ 4] local 10.43.0.4 port 55681 connected to 10.35.0.5 port 5201
[ ID] Interval Transfer Bandwidth Total Datagrams
[ 4] 0.00-1.00 sec 447 MBytes 3.75 Gbits/sec 318700 (omitted)
[ 4] 0.00-1.00 sec 529 MBytes 4.44 Gbits/sec 376850
[ 4] 1.00-2.00 sec 529 MBytes 4.44 Gbits/sec 376980
[ 4] 2.00-3.00 sec 529 MBytes 4.44 Gbits/sec 376690
[ 4] 3.00-4.00 sec 529 MBytes 4.44 Gbits/sec 376620
[ 4] 4.00-5.00 sec 529 MBytes 4.44 Gbits/sec 376840
[ 4] 5.00-6.00 sec 529 MBytes 4.43 Gbits/sec 376560
[ 4] 6.00-7.00 sec 529 MBytes 4.44 Gbits/sec 377070
[ 4] 7.00-8.00 sec 529 MBytes 4.43 Gbits/sec 376590
[ 4] 8.00-9.00 sec 528 MBytes 4.43 Gbits/sec 376260
[ 4] 9.00-10.00 sec 529 MBytes 4.44 Gbits/sec 376900
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams
[ 4] 0.00-10.00 sec 5.16 GBytes 4.44 Gbits/sec 0.001 ms 175481/3767352 (4.7%)
[ 4] Sent 3767352 datagrams
Server output:
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
Accepted connection from 10.43.0.4, port 50088
[ 5] local 10.35.0.5 port 5201 connected to 10.43.0.4 port 55681
[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams
[ 5] 0.00-1.00 sec 350 MBytes 2.94 Gbits/sec 0.001 ms 11590/261006 (4.4%) (omitted)
[ 5] 0.00-1.00 sec 506 MBytes 4.24 Gbits/sec 0.001 ms 16644/376948 (4.4%)
[ 5] 1.00-2.00 sec 506 MBytes 4.25 Gbits/sec 0.001 ms 16000/376713 (4.2%)
[ 5] 2.00-3.00 sec 506 MBytes 4.24 Gbits/sec 0.001 ms 16559/376708 (4.4%)
[ 5] 3.00-4.00 sec 507 MBytes 4.25 Gbits/sec 0.001 ms 15697/376761 (4.2%)
[ 5] 4.00-5.00 sec 506 MBytes 4.25 Gbits/sec 0.001 ms 16156/376799 (4.3%)
[ 5] 5.00-6.00 sec 507 MBytes 4.25 Gbits/sec 0.001 ms 15312/376542 (4.1%)
[ 5] 6.00-7.00 sec 507 MBytes 4.25 Gbits/sec 0.001 ms 15849/377144 (4.2%)
[ 5] 7.00-8.00 sec 507 MBytes 4.25 Gbits/sec 0.001 ms 15251/376458 (4.1%)
[ 5] 8.00-9.00 sec 506 MBytes 4.25 Gbits/sec 0.001 ms 15832/376361 (4.2%)
[ 5] 9.00-10.00 sec 506 MBytes 4.24 Gbits/sec 0.001 ms 16529/376955 (4.4%)
[ 5] 10.00-10.15 sec 75.2 MBytes 4.12 Gbits/sec 0.001 ms 4062/57657 (7%)
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams
[ 5] 0.00-10.15 sec 0.00 Bytes 0.00 bits/sec 0.001 ms 163891/3825046 (4.3%)
iperf Done.
tcpdump の結果を見ても、ちゃんとすべてのパケットに UDP ヘッダーが付与された状態で送出されていることがわかります。
まとめ
ここまでの実験結果で、以下のことがわかりました。
- Azure の仮想マシンや仮想ネットワークは、フラグメント化されたパケットをサポートしていない
- UDP の場合は特に、ペイロードが 1472 bytes 以下になるように調整しないと UDP だけ通信が通らない原因にもなりうる
- iPerf3 を使って Azure VM のネットワークスループットを UDP で測るときには、
-l 1472
オプションをつけるべし
Azure の内部ネットワークのレイテンシーやスループットは結構優秀だと思いますので、IP fragmentation に気を付けつつ、うまく活用していただけたら良いかな…と思います。
オマケ
参考文献
TCP で米国東部→東日本のスループットを測ってみた
同じ環境で、以下のコマンドを使って TCP 20 並列接続のスループットを測ってみた結果も掲載しておきます。
tokawa@eastus:~$ iperf3 -c 10.35.0.5 -b 0 --omit 1 --get-server-output -P 20
TCP 接続での、米国東部→東日本へのトータルのスループットとは、1.70 Gbits/sec という結果になりました。秒間 200MB 超のデータを転送できるという結果ですので、大容量のデータを海外からいち早く安く日本に送ってくるような用途におススメできるかもしれません。
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth
[ 5] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 5] 0.00-10.17 sec 91.8 MBytes 75.7 Mbits/sec receiver
[ 7] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 7] 0.00-10.17 sec 97.7 MBytes 80.6 Mbits/sec receiver
[ 9] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 9] 0.00-10.17 sec 99.1 MBytes 81.7 Mbits/sec receiver
[ 11] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 11] 0.00-10.17 sec 76.8 MBytes 63.3 Mbits/sec receiver
[ 13] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 13] 0.00-10.17 sec 94.7 MBytes 78.1 Mbits/sec receiver
[ 15] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 15] 0.00-10.17 sec 88.0 MBytes 72.6 Mbits/sec receiver
[ 17] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 17] 0.00-10.17 sec 140 MBytes 115 Mbits/sec receiver
[ 19] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 19] 0.00-10.17 sec 161 MBytes 132 Mbits/sec receiver
[ 21] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 21] 0.00-10.17 sec 95.4 MBytes 78.7 Mbits/sec receiver
[ 23] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 23] 0.00-10.17 sec 84.5 MBytes 69.7 Mbits/sec receiver
[ 25] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 25] 0.00-10.17 sec 102 MBytes 83.9 Mbits/sec receiver
[ 27] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 27] 0.00-10.17 sec 94.5 MBytes 77.9 Mbits/sec receiver
[ 29] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 29] 0.00-10.17 sec 76.4 MBytes 63.0 Mbits/sec receiver
[ 31] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 31] 0.00-10.17 sec 74.8 MBytes 61.7 Mbits/sec receiver
[ 33] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 33] 0.00-10.17 sec 134 MBytes 111 Mbits/sec receiver
[ 35] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 35] 0.00-10.17 sec 77.7 MBytes 64.0 Mbits/sec receiver
[ 37] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 37] 0.00-10.17 sec 149 MBytes 123 Mbits/sec receiver
[ 39] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 39] 0.00-10.17 sec 75.0 MBytes 61.9 Mbits/sec receiver
[ 41] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 41] 0.00-10.17 sec 143 MBytes 118 Mbits/sec receiver
[ 43] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[ 43] 0.00-10.17 sec 102 MBytes 83.8 Mbits/sec receiver
[SUM] 0.00-10.17 sec 0.00 Bytes 0.00 bits/sec sender
[SUM] 0.00-10.17 sec 2.01 GBytes 1.70 Gbits/sec receiver