Skip to content

Port Exhaustion

TCPのポートを使い切ったことはあるだろうか。

大規模サービスの運用では珍しい話ではない。ポートは65535が上限だが、実際にはエフェメラルポートの範囲が決まっていて、使えるのはさらに少ない。データストアへのコネクション数は設計段階で計算できる。ワーカー数×接続先の数。これは想定内だ。問題はポートの開放にある。

TCPはコネクションを閉じた後、TIME_WAITという状態で一定時間ポートを保持する。同じ送信元ポートと宛先の組み合わせで新しいコネクションが張られたとき、前のコネクションの遅延パケットと混同しないためだ。デフォルトで60秒。その間ポートは使えない。秒間数百のリクエストを捌いていると、60秒で数万のポートがTIME_WAITに沈む。

当時の定番の対処がtcp_tw_recycleだった。TIME_WAIT状態のポートを再利用する。これで解決、のはずだった。NATが絡むと壊れる。NATの背後にいる複数のクライアントからのパケットが同じIPに見えるので、タイムスタンプの整合性が崩れて接続が拒否される。ロードバランサの裏で有効にして痛い目を見た人間は少なくないだろう。

そしてLinux 4.12でtcp_tw_recycleは廃止された。カーネルのアップデートで、それまで頼っていたパラメータが消える。sysctlの設定を引き継いでいたつもりが、次のカーネルでは存在しないパラメータになっている。インフラの地層は、こうして静かに変わっていく。

いまではコンテナやマネージドサービスの裏に隠れて、ポートの枯渇を意識する機会は減った。個人開発ではまず踏まない。Unixドメインソケットで繋いでいればポートの概念すらない。localhostで動かしている限り、65535という数字の小ささを知る機会がない。知らなくても動く。でも知らないまま本番のトラフィックを浴びると、計算が合わなくなる瞬間が来る。