深夜3時、pagerから鳴り響くアラート音。
「APIサーバーのレスポンスが急激に悪化、一部でコネクション枯渇の兆候あり」
こういう修羅場で、君はまだ netstat を叩いていないだろうか?
「とりあえず netstat -an だ」と言って、巨大なリストがターミナルを流れていくのをボーッと眺める。あの、CPU使用率が跳ね上がり、結果が出てくるまでに何秒も待たされるもどかしい時間——。障害対応の現場において、その「数秒の待ち時間」は致命傷になり得る。
こんにちは。幾多のデータセンターの修羅場をくぐり抜けてきたNOCのシニアエンジニアだ。今日は、レガシーな netstat とお別れし、現代のLinuxインフラ運用において必須の武器である ss コマンドへの移行を進めるべき理由と、その実践的な使いこなし方について話をしよう。
—
なぜ私たちは netstat を捨て、ss を選ぶのか
1. 息をするように遅い netstat の正体
netstat(net-toolsパッケージに含まれる)がなぜ遅いのか。その理由は、それが参照する情報源にある。
レガシーな netstat は、カーネル内の情報を /proc/net/ 経由でテキストベースで読み取っている。数百、数千ものアクティブなコネクションがある高負荷なWebサーバーにおいて、毎秒 /proc 以下のテキストファイルをパースして構築する処理は、カーネルにとってもユーザー空間のプロセスにとっても重い負荷になる。
2. カーネル直結の超高速性をもたらす netlink ソケット
一方、今回解説する ss(iproute2パッケージ)は、Linuxカーネル空間とユーザー空間の間で効率的な通信を行う netlink ソケット を直接叩いている。
テキストをパースするオーバーヘッドがなく、カーネルがメモリ上に保持しているソケットの状態構造体をダイレクトに、かつ高速に取得する。数万のコネクションが張られたエッジサーバーであっても、ss なら一瞬で結果が返ってくる。この「速さ」こそが、障害時のトリアージにおいて何よりも勝る価値なのだ。
—
基礎から固める:ss の基本構文とオプション群
まずは、日常の運用監視やデバッグで頻繁に使う基本形を押さえよう。ss の構文は netstat と似ているようで、より洗練されている。
# 基本的な構文
ss [オプション] [フィルター]
実務で最もよく使うオプションの組み合わせをいくつか紹介しよう。
# すべてのTCPソケットを数値表現(名前解決をしない)で表示する
# ※DNSの逆引きでモタつくのを防ぐため、実務では '-n' は必須の作法だ。
ss -t -a -n
# 待ち受け(LISTEN)状態のポートだけを、プロセス名付きで高速リストアップ
# ※どのプロセスがどのポートを掴んでいるか一発でわかる(要root権限)
ss -t -l -p -n
# 確立済み(ESTABLISHED)のTCPコネクションだけを抽出し、統計情報を添える
ss -t -o state established
ここで重要なポイントは、netstat でおなじみだった -a(全件表示)、-t(TCP)、-u(UDP)に加え、-p(プロセス名表示)や -n(IP/ポートの数値化)といった直感的なフラグがそのまま使える点だ。
—
実務で直面するシナリオ別:ss フィルターの極意
ss の真骨頂は、その強力なフィルター機能にある。netstat の出力結果を grep や awk でゴリゴリ絞り込んでいた時代はもう終わりだ。ss 自体に条件を投げることで、カーネル側でフィルタリングされた最小限のデータだけを受け取ることができる。
シナリオA:特定の宛先IPやポートへのコネクションを即座に特定する
例えば、バックエンドのデータベースサーバー(IP: 192.168.10.50、ポート: 5432)との間で、コネクションが詰まっていないか確認したい場合。
# 特定の宛先IPとポートに対するTCPコネクションを抽出
ss -t -n dst 192.168.10.50:5432
もし特定の送信元(クライアント)からの接続状況を調べたいなら src を使う。
# 自ホスト側の特定ポート(例: 443)に着信しているコネクションを全表示
ss -t -n sport = :443
シナリオB:TIME_WAIT や CLOSE_WAIT の沼からの脱出
APIサーバーやリバースプロキシの運用で最も恐ろしいのが、TIME_WAITの過剰な蓄積や、アプリケーションのリークによる CLOSE_WAIT の嵐だ。これらも ss の state フィルターを使えば一網打尽にできる。
# TIME_WAIT状態のソケット数をカウントして現在の負荷を把握する
ss -t -s
# CLOSE_WAIT(アプリケーション側が切断処理を忘れている状態)をリストアップ
ss -t -n state close-wait
もし CLOSE_WAIT が大量発生しているログを見つけたら、それはネットワークの障害ではなく、アプリケーション層(Pythonの Requests や Node.js の axios、あるいはWebアプリのコード)でレスポンスのストリームを確実にクローズしていないことが原因だ。ss -t -l -p と組み合わせて、どのミドルウェアがそのソケットを握りしめているのか、即座に突き止めよう。
—
PythonやBashスクリプト連携:自動化とモニタリングへの応用
プロフェッショナルなインフラエンジニアは、手動でコマンドを叩くだけでなく、こうした診断ツールを自動化システムに組み込む。
例えば、現在の TIME_WAIT コネクション数が閾値を超えたらSlackにアラートを飛ばすような、軽量なPythonスクリプトの断片を書いてみよう。Pythonの subprocess を使って ss の出力をパースするのが最も確実で高速だ。
import subprocess
import sys
def check_time_wait_sockets(threshold=1000):
"""
ssコマンドを使用して現在のTIME_WAITソケット数を取得し、
閾値を超えている場合に警告を発報する関数
"""
# ssコマンドでTCPのTIME_WAIT状態のものを数値のみで抽出
cmd = ["ss", "-t", "-n", "state", "time-wait"]
try:
# コマンドを実行し、出力を取得(ヘッダー行を除外するため wc -l 相当の処理を行う)
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
lines = result.stdout.strip().split("\n")
# 出力が空(コネクションなし)の場合は0件とする
if len(lines) == 1 and lines[0] == "":
time_wait_count = 0
else:
# ヘッダー行を引くため、行数から1を引く
time_wait_count = len(lines) - 1
print(f"[INFO] 現在のTIME_WAITコネクション数: {time_wait_count}")
if time_wait_count > threshold:
print(f"[ALERT] 警告: TIME_WAITが閾値 ({threshold}) を突破しています!", file=sys.stderr)
# ここにSlack WebhookやPagerDutyへの通知処理を記述する
except subprocess.CalledProcessError as e:
print(f"[ERROR] ssコマンドの実行に失敗しました: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
check_time_wait_sockets(threshold=500)
このようなスクリプトを cron や監視エージェントから数分おきに回しても、カーネルに負荷をかけないのは ss が netlink を使っているからに他ならない。これが netstat だったら、高負荷時に監視スクリプト自体がサーバーの首を絞める原因になりかねなかったのだ。
—
シニアからの実践的なアドバイス
現場で若手エンジニアから「どのコマンドを覚えるべきですか?」と聞かれたら、私は迷わず ss を推す。
OSのディストリビューションによっては、デフォルトで net-tools がインストールされなくなっている環境も増えており、netstat は完全にレガシーの遺物となりつつある。
トラブルシューティングの基本は「ノイズを消し、ファクトを最速で掴むこと」だ。
名前解決をオフにする -n を忘れず、目的の状態(state)やポート(sport / dport)でピシャリとフィルタリングする。この習慣をつけるだけで、夜間障害の復旧時間は劇的に短縮されるはずだ。
さあ、次のインフラメンテの際には、古い癖を捨ててターミナルに ss と打ち込んでみてほしい。その圧倒的なレスポンスの速さに、きっと驚くはずだ。
コメント