なぜ「広大な帯域」を使い切れないのか? iperf3とTCPウィンドウで紐解くネットワークの深淵
深夜のデータセンター。監視画面に並ぶ真っ赤なアラートを横目に、ふと自問自答したことはないだろうか。「10Gbpsの物理リンクがあるのに、なぜアプリのスループットは1Gbpsも出ないのか?」と。
多くのエンジニアは、まず ping で疎通を確認し、traceroute で経路を追い、curl でHTTPステータスを叩く。だが、それで解決しない「見えないボトルネック」の正体は、往々にしてTCPスタックの深層にある。今日は、現場のトラブルシューティングにおける最後の砦、iperf3 を使った帯域測定と、そのパフォーマンスを左右する「TCPウィンドウサイズ」の極意について話そう。
なぜ「帯域」は教科書通りにいかないのか
ネットワークの理論値と実測値の乖離を生む最大の要因は、BDP(Bandwidth Delay Product:帯域遅延積)にある。
TCPは信頼性を確保するために、受信側が「ここまで受け取ったよ(ACK)」と教えるまで、送信側は次のパケットを送り出すのを待つ仕組みがある。このとき、ACKが戻ってくるまでの間にパイプライン(ネットワーク上)に存在できるデータ量が、TCPウィンドウサイズの上限で決まってしまうのだ。
もし、物理帯域が太くても、OSのデフォルト設定であるウィンドウサイズが小さいままだと、パケットの往復時間(RTT)が長いネットワークでは、送信側は「待ちぼうけ」を食らい続けることになる。これが「帯域はあるのに速度が出ない」最大の理由だ。
iperf3による「真の限界」の測定
まずは iperf3 を使って、現在の環境でどれだけのデータが流せるかを定量化しよう。
1. サーバー側の準備
サーバー側でリッスンを開始する。
# サーバー側:5201番ポートでiperf3を待機
iperf3 -s
2. クライアント側での測定
ここで重要なのが -w オプションだ。これを使って、明示的にTCPウィンドウサイズを指定し、スループットがどう変化するかを追いかける。
# クライアント側:ウィンドウサイズを256KBに固定して測定
iperf3 -c <サーバーIP> -w 256K -t 10
もし、-w を指定しない場合と指定した場合でスループットに劇的な差が出るなら、それはOSの自動チューニング(tcp_rmem / tcp_wmem)が現在のレイテンシ環境に追いついていない証拠だ。
実務で直面する「遅いAPI」をどうデバッグするか
インフラエンジニアだけでなく、Web APIを設計する開発者も、この挙動を知っておく必要がある。例えば、大きなJSONレスポンスを返すAPIを作成した際、curl で計測すると異常に遅いケースがある。
# ヘッダー情報を取得し、応答までの時間を確認
curl -w "Connect: %{time_connect} TTFB: %{time_starttransfer} Total: %{time_total}\n" -o /dev/null -s https://api.example.com/data
もし time_starttransfer は早いが time_total が極端に長いなら、それはアプリケーションではなく、転送中のTCPセッションがウィンドウ制御で詰まっている可能性が高い。Pythonで簡易的なクライアントを書き、ソケットレベルでバッファをいじって再現テストを行うのも一つの手だ。
import socket
# ソケットを作成
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# TCPウィンドウサイズ(受信バッファ)を強制的に変更する例
# OSの制限を超える場合は、カーネルパラメータの変更が必要になる
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1024 * 1024) # 1MBに設定
# あとは通常のHTTPリクエストを投げる
# ...
シニアエンジニアからの処方箋
現場でこの問題を解決する際、いきなりアプリケーションコードを書き換えるのは悪手だ。以下の手順でボトルネックを切り分けよう。
1. RTTの確認: ping での往復時間を測る。RTTが100msを超えてくると、ウィンドウサイズの影響は指数関数的に効いてくる。
2. パケットロス率の確認: netstat -s や ss -ti を実行し、再送(retransmission)が発生していないか確認する。ロスがある環境でウィンドウサイズを無理に大きくすると、逆に再送パケットが溢れ、スループットは死ぬ。
3. カーネルパラメータのチューニング: Linuxであれば /etc/sysctl.conf を確認する。
# 推奨される一時的な設定例(広帯域・高レイテンシ環境向け)
# 受信バッファの最大値を16MBに引き上げる
net.core.rmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
最後に:数字の裏側にある「流れ」を想像せよ
ネットワークは生き物だ。コマンド一つでデータは流れるが、その裏ではパケットが光の速度で海を越え、ルーターのバッファで押し合いへし合いしている。
iperf3 の数値は単なる結果ではない。それは、君が設計したアーキテクチャが、物理的な限界とどう折り合いをつけているかを示す「通信の鼓動」そのものだ。教科書を閉じて、実際のトラフィックを眺めてみてほしい。なぜここで止まるのか、なぜここで加速するのか。その「なぜ」を突き詰めた先に、真のインフラエンジニアへの道がある。
トラブルシューティングを楽しもう。現場からは以上だ。
コメント