『詳解 システム・パフォーマンス 第2版』を読む:第10章 ネットワークの全容

Progress 8 / 13
目次

はじめに

[!NOTE] この記事は、筆者が鈍器本を読みながら書き殴った個人的な読書メモをAIに食べさせ、ブログ用に読みやすく再構成させたものです。

CPU、OS、メモリと分析の基礎を固めてきましたが、今回は分散システムやマイクロサービスアーキテクチャにおいて最も非難の的になりやすい「10章 ネットワーク」です。

インフラからアプリケーションまで、システム全体のチューニングにおいてネットワークのパフォーマンスは絶対に避けて通れません。ネットワークは本質的に複雑で、輻輳やパケットドロップといった見えない問題が多いため、何かあると「遅いのは全部ネットワークのせいだ」という思考停止に陥りがちです。

今回は、教科書的なOSI参照モデルのお勉強は思い切ってスキップし、本番環境で「謎の通信遅延」や「接続エラー」に直面した際に、真っ先に疑うべきTCPの仕様と、最新の可観測性ツールを使ったサバイバル術だけをチートシート化しました。


1. レイテンシの真犯人「TTFB」と「RTT」を切り分ける

ネットワークが遅いと感じたとき、それが「純粋な回線の遅延」なのか、「サーバー上のアプリの処理遅延」なのかを切り分けることが分析の第一歩になります。

  • RTT(ラウンドトリップ時間): パケットがネットワークの端から端までを往復する純粋な時間です。ICMPエコー(ping)などで計測でき、リモートホスト側の処理時間はほとんど含まれません。
  • TTFB(TCPファーストバイトレイテンシ): TCP接続が確立してから、クライアントが「最初のデータ」を受け取るまでの時間です。ここには、サーバー側のCPUスケジューリング待ちや、アプリケーションが応答を計算するための時間(Think Time)がガッツリ含まれています

「TTFBが遅いからネットワークの問題だ」と短絡的に決めつけると、実は単にアプリケーションのスレッドが詰まっていただけだった、という少し恥ずかしいオチになりかねません。


2. 接続が静かに死ぬ「TCPバックログ」の溢れ

負荷のバースト時に「接続に異常に時間がかかる」「つながらない」という場合、OSが管理する「TCPバックログキュー」が溢れてパケットが密かに捨てられている可能性が高いです。

  • TCPの接続確立(3ウェイハンドシェイク)の裏には、OSが管理する2つのキューが存在します。まだ確立していない接続のための「SYNバックログ」と、アプリが accept() するのを待つ「リスンバックログ」です。
  • アプリの処理が追いつかずリスンバックログが溢れると、OSは新たなSYNパケットを黙ってドロップ(破棄)します。するとクライアントはタイムアウトを待って再送を試みるため、数千ミリ秒という絶望的な接続レイテンシがペナルティとして追加されてしまいます。シンプルに地獄ですね。

netstat -snstat などでSYN再送出数やlisten queueのオーバーランが記録されていないか監視することが非常に重要になります。


3. マイクロサービスを殺す「TIME_WAIT」とポート枯渇

サーバー間で短命なTCP接続を大量に繰り返すと、「TIME_WAIT」状態のソケットが溜まりすぎて新しい接続ができなくなるスケーラビリティの罠があります。

  • TCPセッションを完全に閉じた後、遅れて届いたパケットが新しい接続に混入するのを防ぐため、OSはそのポートを一定時間(通常60秒など)「TIME_WAIT」状態でロックします。
  • データベースや別のAPIに高い頻度で接続・切断を繰り返すと、利用可能なエフェメラルポート(約6万個)をあっという間に使い果たしてしまい、新しい接続がクラッシュ(ポート枯渇)してしまいます。

これを防ぐには、アプリ側でコネクションプールを使って接続を使い回すか、OS側で tcp_tw_reuse パラメータを有効にしてTIME_WAITセッションを安全に再利用する設定が必須になります。


4. 謎の遅延を生む「Nagle」と「遅延ACK」の競合

小さなデータをやり取りする通信において、TCPの「良かれと思って」の最適化機能同士が喧嘩をして、数百ミリ秒の謎の遅延を引き起こすことがあります。

  • Nagleアルゴリズム: 小さなパケットによるネットワークの無駄遣いを防ぐため、送信データをある程度まとめてから送り出そうとする賢い仕組みです。
  • 遅延ACK: 受信側が、複数のACK(確認応答)をまとめて返すために、最大500ミリ秒ほどACKの送信をわざと遅らせる仕組みです。

送信側が「もっとデータが来るまで待とう(Nagle)」とし、受信側が「ACKを返すのを遅らせよう(遅延ACK)」とすると、お見合い状態になって強烈な遅延が発生してしまいます。HTTPなどの細かい通信では、ソケットオプションで TCP_NODELAY を指定し、Nagleアルゴリズムを無効化するのが定石ですね。


5. クラウドの救世主「BBR」輻輳制御アルゴリズム

パケットが落ちるような信頼性の低いネットワーク(クラウド環境やインターネットなど)において、TCPの「輻輳制御アルゴリズム」の設定はパフォーマンスを劇的に左右します。

  • CUBIC(現在のLinuxのデフォルト): パケット消失を「ネットワークが混雑しているサイン」と判断して送信スピードを急激に落としてしまうため、パケットロスが起きやすい環境ではスループットが伸び悩みます。
  • BBR: Googleが開発したアルゴリズムで、パケットロスではなく「ネットワークの実際の帯域幅とRTT」を計測して送信ペースを決めます。Netflixのクラウドでは、BBRを採用したことで、パケットが激しく消失する状況でのスループットが3倍も向上したそうです。

sysctl net.ipv4.tcp_congestion_control で現在のアルゴリズムを確認し、パケットドロップが避けられない環境であれば、BBRへの切り替えを検討するのが現代のクラウドチューニングの定石ですね。


6. 「とりあえず tcpdump」からの卒業

ネットワーク分析といえばパケットキャプチャ(スニッフィング)が有名ですが、本番環境での実行はCPUとストレージに破壊的なオーバーヘッドをもたらすため、基本的には最後の手段とすべきです。

現代のLinuxでは、パケットの中身まで見なくても、次のような低負荷なツールで十分な分析が可能になっています。

  • ss -tiepm: オープンされているソケットの情報を表示するツールです。TCP再送タイムアウト値や輻輳ウィンドウのサイズなどが一撃でわかる神ツールです。
  • tcplife (BCC): TCPセッションの確立から終了までをトレースし、PIDやライフスパン、転送バイト数を表示します。パケットごとではなく「状態遷移」だけをトレースするため、本番環境でロガーとして常時実行できるほどオーバーヘッドが低く、非常に強力です。

おわりに

この章を読むことで、ネットワークのパフォーマンス障害は「回線の太さ」だけで決まるわけではなく、OSのキュー、バッファサイズ、そしてTCPの高度なアルゴリズムが複雑に絡み合って起きていることが痛いほどわかります。

次回の分散システムのパフォーマンス・チューニングにおいても、TTFBと純粋なネットワークレイテンシを明確に切り分け、sstcplife といった最新の可観測性ツールを駆使して、TCPバックログの溢れやTIME_WAITのポート枯渇といった「OSレベルのネットワークの悲鳴」を確実に見逃さないようにしていきたいと思います!