深夜のNOCルーム。静かに唸る空調の音と、無数のサーバーラックが放つ微かなLEDの明かりだけが、この戦場の景色だ。
「おい、APIゲートウェイのコネクションプールが枯渇した! 応答が返ってこないぞ!」
若手エンジニアの焦る声が響く。数万、数百万のトラフィックが渦巻く大規模データセンターにおいて、こうした障害は前触れもなく牙を剥く。
こんな時、古いエンジニアが真っ先に叩くコマンドは何だと思う? netstat? いや、時代遅れの骨董品をいつまでも使っている暇はない。今の現場で我々が頼るべき相棒は、カーネル空間のソケット情報をダイレクトかつ高速に引き剥がす ss コマンドだ。
今回は、数々の修羅場をくぐり抜けてきた私が、ss コマンドの高度なフィルタリングと状態別ソケット抽出を駆使し、地獄のような障害現場を秒速で切り抜けるための実践知を伝授しよう。
—
なぜ netstat を捨てて ss を使うべきなのか
かつては netstat がネットワークデバッグの王様だった。しかし、考えてみてほしい。netstat は /proc/net からテキストベースで情報を取得している。数万、数十万のコネクションが張られた現代の大規模Webインフラやマイクロサービス環境において、テキストパースを伴う netstat は、CPUを無駄に食い潰し、結果が出る頃には障害が拡大しているという最悪の事態を引き起こす。
一方、ss(Socket Statistics)は、Linuxカーネルの inet_diag というダイレクトなカーネルインターフェースを叩く。これにより、数万件のソケット情報があろうが一瞬でメモリ上から抽出される。この「圧倒的な速度」こそが、秒単位の復旧が求められるNOCの現場で ss が選ばれる理由だ。
—
現場で即座に使える ss フィルタリングの極意
ss の真骨頂は、その強力なフィルタ表現にある。TCPの状態(state)やポート番号(ウドレッサ、スポート)、アドレスを指定して、ノイズを完全に削ぎ落としたピンポイントの調査が可能だ。
ここでは、実務で頻繁に遭遇するユースケースごとに、具体的なコマンドと内部の動きを解説する。
1. 接続確立済み(ESTAB)のHTTP/HTTPSソケットを秒速で炙り出す
APIサーバーの負荷が高いとき、まず確認すべきはどのクライアントとどの程度のセッションが維持されているかだ。以下のコマンドを見てほしい。
# ESTABLISHED状態かつ、ローカルポートがHTTP(80)またはHTTPS(443)のソケットを抽出
ss -t -n state established '( sport = :http or sport = :https )'
-t: TCPソケットのみを対象にする。-n: IPアドレスやポート番号を名前解決せず、数値のままで高速に表示する(大規模環境での名前解決のタイムアウトを防ぐため、これは鉄則だ)。state established: 3ウェイハンドシェイクが完了し、データ送受信が可能な状態のソケットに絞り込む。( sport = :http or sport = :https ): サービス側のポート(ローカルポート)が 80 または 443 のものだけにフィルタリングする。/etc/servicesに定義されたサービス名は:をつけて指定できる。
このコマンド一発で、現在アクティブに外部と通信しているコネクションの一覧が手に入る。もし特定のIPから異常な数のコネクションが張られていれば、次のステップへ進む。
2. 特定のIPアドレスやネットワークからの接続をピンポイントで追う
DDoS攻撃や、特定のマイクロサービスからのコネクションリークを疑う場面では、宛先や送信元のIPアドレスで絞り込む。
# 特定のクライアントIP (192.168.10.50) とのコネクションを抽出
ss -t -n dst 192.168.10.50
# 特定のサブネット (10.0.0.0/16) からの入力をすべてキャッチ
ss -t -n src 10.0.0.0/16
dst(宛先)や src(送信元)の後にIPやCIDRを書くだけだ。ss は内部でCIDRマスクも高速に処理してくれるため、巨大なログファイルから grep で苦悶する必要は一切ない。
—
実践:APIゲートウェイのコネクション枯渇をデバッグする
では、もう少し実践的なシナリオを想定しよう。
あなたが設計・運用するAPIゲートウェイに対し、クライアント側(例えばフロントエンドのNext.jsサーバーやモバイルアプリ)から「API呼び出しがタイムアウトする」というアラートが上がった。
サーバーにSSHでログインし、まずはTCPの状態別カウンターを確認する。
# すべてのTCPソケットの状態サマリーを表示
ss -s
出力結果の中に TIME-WAIT が異常な数でたまっていたり、CLOSE-WAIT が放置されている場合、アプリケーション層(Pythonの requests や Node.js、あるいはWebサーバーの設定)に問題がある。
特に厄介なのが CLOSE-WAIT だ。これは「リモート側から切断要求(FIN)を受け取ったが、こちらのアプリケーションがまだソケットを閉じきれていない(close() システムコールを呼んでいない)」状態を意味する。
以下のコマンドで、どのプロセスがその CLOSE-WAIT ソケットを握りしめているかを暴く。
# CLOSE-WAIT状態のソケットをプロセス情報(-p)付きで強制抽出
sudo ss -t -l -p state close-wait
*(※ -l はリスニングですが、確立済みソケットの詳細なプロセス紐付けには sudo ss -t -a -p のように全状態(-a)を対象にするのが実務では一般的です)*
実際にプロセスID(PID)とプログラム名が特定できたら、そのプロセスがどのような挙動をしているか、コードレベルで確認する必要がある。例えば、Pythonの requests ライブラリでコネクションプールやセッション管理を誤っている場合、以下のようなコードが原因でソケットがリークすることがある。
import requests
from requests.adapters import HTTPAdapter
def call_buggy_api(target_url):
# 【アンチパターン】毎回セッションを破棄せず、レスポンスのストリームを閉じ忘れる例
# これが数千回走ると、TIME-WAITやCLOSE-WAITが爆発し、カーネルのファイルディスクリプタが枯渇する。
response = requests.get(target_url)
return response.json()
# 【正しい実装例】セッションを再利用し、適切にコンテキストマネージャーやクローズ処理を行う
def call_robust_api(target_url):
session = requests.Session()
# コネクションプールの最大数を適切に絞る
adapter = HTTPAdapter(pool_connections=10, pool_maxsize=20)
session.mount('https://', adapter)
try:
with session.get(target_url, timeout=5) as response:
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
# ログに詳細を吐き出して適切にハンドリング
print(f"API呼び出しに失敗しました: {e}")
return None
インフラエンジニアであっても、アプリケーションがどのようにネットワークリソースを消費しているか(あるいは垂れ流しているか)のコードリーディングができなければ、根本的な障害解決にはたどり着けない。
—
高度なフィルタ演算子を使いこなす
ss のフィルタは、論理演算子を組み合わせることで、さらに鋭利なメスに変貌する。
| 演算子 | 意味 | 記述例 |
| :— | :— | :— |
| and または , | 論理積(かつ) | state established and sport = :http |
| or | 論理和(または) | sport = :http or sport = :https |
| not または ! | 否定(以外の) | not sport = :ssh |
例えば、「SSH接続(ポート22)以外の、確立されたすべてのTCPコネクションを、宛先ポート80番または443番に絞って抽出したい」という場合は、以下のように記述する。
ss -t -n state established '( sport = :http or sport = :https ) and not sport = :ssh'
この柔軟性があれば、複雑に入り組んだマイクロサービスの通信経路であっても、ノイズを完全に排除して「今、何が起きているのか」の真実だけを画面に映し出すことができる。
—
シニアからの現場の教訓
障害対応において最も恐ろしいのは、パニックになって「手当たり次第に再起動する」という愚行だ。再起動は一時的な延命にはなるが、根本原因(Root Cause)を闇に葬り去る。次に同じ障害が起きたとき、あなたはまた冷や汗をかくことになる。
ss コマンドを使ったソケットの状態分析は、OSのカーネルが今まさに何をしようとしているのか、その「心の声」を聞く作業に他ならない。
- コネクションが溢れているなら
stateでどこに滞留しているかを見極めろ。 - 応答がないなら
sportやdstでトラフィックの偏りを暴け。 - 最終的には、それを引き起こしているアプリケーションのコードや設定(KeepAliveのタイムアウト値やリバースプロキシのバッファ設定など)にメスを入れろ。
ネットワークとシステムは嘘をつかない。すべてはパケットとソケットの挙動という「ログ」に刻まれている。
次にアラートが鳴り響いたとき、深呼吸をひとつして、ターミナルに ss と打ち込んでみてほしい。そこには、混沌とした障害を紐解くための確かな道筋が必ず見えてくるはずだ。
コメント