EADDRNOTAVAILとは実際どういう意味か?
ログに Error: connect EADDRNOTAVAIL が表示される場合、通常はアプリケーションの動作速度がオペレーティングシステムの処理能力を上回っていることを意味します。平たく言えば、OSが新しい送信接続に割り当てるためのローカルポートを使い果たした状態です。このボトルネックは、技術的には**エフェメラルポートの枯渇(ephemeral port exhaustion)**と呼ばれます。
デフォルトでは、Linuxは送信トラフィック用に特定の範囲のポートを確保しています。アプリケーションがAPIコールやデータベースクエリを短時間に5,000回実行した場合、それらの接続は終了しても即座に消えるわけではありません。それらは TIME_WAIT 状態のまま60秒間残ります。古い接続が期限切れになる前に利用可能なプールを使い果たすと、アプリケーションは行き詰まってしまいます。
ステップ1:ポートの使用状況を診断する
むやみにシステム変数を変更し始めないでください。まず、TIME_WAIT ソケットが本当に原因であるかを確認します。トラフィックの多いサーバーでは、古い netstat よりもはるかに高速な ss ツールを使用してください。
# ソケット統計の概要を素早く取得する
ss -s
# TIME_WAIT 状態で停止している接続数を正確にカウントする
ss -tan | grep TIME-WAIT | wc -l
カウントが25,000前後、あるいはそれ以上であれば、確実に限界に達しています。このボリュームでは、カーネルは新しいリクエストを満たすのに十分な速さでポートを再利用することができません。
ステップ2:エフェメラルポートの範囲を拡張する
ほとんどのLinuxディストリビューションは、通常 32768 から 60999 という保守的な範囲で設定されています。これでは同時接続ポートは約28,000個しか確保できません。負荷の高いマイクロサービスでは、これらを数秒で使い果たしてしまう可能性があります。
次のコマンドで現在の制限を確認してください:
cat /proc/sys/net/ipv4/ip_local_port_range
これを解決するために、範囲を1024から65535まで広げることができます。これにより、容量は2倍以上の約64,000ポートになります。テストのために一時的に適用してみましょう:
sudo sysctl -w net.ipv4.ip_local_port_range="1024 65535"
再起動後もこの変更を維持するには、/etc/sysctl.conf に以下の行を追加します:
net.ipv4.ip_local_port_range = 1024 65535
ステップ3:安全なTCPソケット再利用を有効にする
範囲を広げることは役立ちますが、クローズされたソケットが60秒間ハングアップする問題は解決しません。プロトコルの観点から安全であれば、TIME_WAIT 状態のソケットを再利用するようカーネルに指示できます。これは高並列環境において画期的な解決策となります。
/etc/sysctl.conf にこの設定を追加します:
net.ipv4.tcp_tw_reuse = 1
次に、設定をリロードします:
sudo sysctl -p
注意: 古いチュートリアルで net.ipv4.tcp_tw_recycle を推奨しているものを見かけるかもしれません。これは避けてください。NATの背後にいるユーザー(モバイルユーザーやオフィスネットワークなど)の接続を切断してしまうため、Linuxカーネルのバージョン4.12以降で削除されました。tcp_tw_reuse を使用するようにしてください。
ステップ4:コード内の根本原因を修正する
インフラのチューニングは、多くの場合、一時しのぎに過ぎません。すべてのタスクに対して新しい接続を開いたり閉じたりしていると、CPUサイクルとネットワークリソースを浪費することになります。代わりに、**コネクションプーリング(Connection Pooling)**または Keep-Alive ヘッダーを実装すべきです。
- Node.js: 高ボリュームの場合はデフォルトのグローバルエージェントを使用しないでください。
keepAlive: trueおよびmaxSockets: Infinityを設定したカスタムのhttp.Agentを作成してください。 - データベース: クエリごとに接続・切断を行うのではなく、Postgresであれば PgBouncer のようなプーラーを使用するか、ORMに組み込まれたプーリングロジックを使用してください。
- Nginx: Nginxをリバースプロキシとして使用している場合は、
proxy_http_version 1.1;を使用し、Connectionヘッダーをクリアしてアップストリームのパイプを維持するようにしてください。
修正を確認する方法
カーネルを調整し、アプリケーションを更新したら、ソケット数を監視してください。ソケット数が安定し、エラーログが出ないことを確認します。
# TIME_WAIT 接続のカウントが1秒ごとに更新されるのを監視する
watch -n 1 "ss -tan | grep TIME-WAIT | wc -l"
ピーク時のトラフィックでも、数値が新しい上限である64,511を十分に下回っていれば、ボトルネックの解消に成功したことになります。
予防のためのプロのヒント
ネットワークの問題は、スケールに応じて変化することがよくあります。アプリケーションロジックが高速化すると、OSカーネルが新たな上限となります。複数のVPCを使用した複雑なアーキテクチャを構築する場合は、IPプランニングを注意深く行ってください。
私はCIDRブロックを計画する際、ToolCraftの Subnet Calculator を頻繁に使用します。これにより、IPの枯渇や範囲の重複なしに、数千のホストを処理できる十分な大きさのネットワークセグメントを確保できます。
極端なケースに備えて、最後に2つの微調整を検討してください:
- FIN-WAIT タイムアウトの短縮:
net.ipv4.tcp_fin_timeoutを60から15に下げます。これにより、OSはクローズされたソケットをより迅速に解放するようになります。 - IPエイリアシング: 1つのIPで不十分な場合は、ネットワークインターフェースに2つ目のIPを割り当てます。これにより、新たに64,000個のポートセットを利用できるようになります。

