【実務・中級編】 netstatコマンドの基本機能とネットワーク統計情報の収集 – トラブルシューティング&ネットワーク運用監視実践ガイド

深夜3時、静まり返ったNOCのフロアに、監視モニターの警告音が冷たく響き渡る。
「またか……。踏み台サーバーから外部APIへのコネクションが枯渇している」

Webアプリケーションのパフォーマンス低下や、マイクロサービス間の通信エラー。こうしたトラブルに直面したとき、君たちは真っ先に何を疑うだろうか?アプリケーションのログ、それともロードバランサーのメトリクス?
もちろんそれらも重要だ。だが、最終的にパケットが流れるOSのトランスポート層、その「足元」を見ずしてネットワークの真実には辿り着けない。

今回は、モダンな監視ツール全盛の今であっても、トラブルシューティングの現場で絶対に外すことのできないレガシーにして最強のツール、netstatコマンドについて語ろう。

—

1. なぜ今、netstatなのか?(レガシーツールの真価)

Linuxの世界では、後継であるssコマンドへの移行が進んで久しい。だが、長年数々の修羅場をくぐってきたエンジニアたちが、今でも脊髄反射のように叩くのはnetstatだ。
なぜなら、古くから存在するどのOS(Linuxはもちろん、AIXやSolaris、果てはWindowsまで)にも標準で備わっており、環境を選ばない普遍性があるからだ。

ネットワーク統計情報の収集において、netstatは単なる「接続確認ツール」ではない。カーネルが管理するソケットの生死、ルーティングの迷い、そしてインターフェースが泣き叫ぶパケットドロップの歴史を暴く、いわば「OSの健康診断書」なのだ。

RFCに裏打ちされたTCPの状態遷移

netstatが吐き出す出力の根底には、IETFが定めるTCPの仕様、すなわち RFC 793(Transmission Control Protocol)で定義された状態機械(State Machine)が存在する。
クライアントが SYN を送り、サーバーが SYN-ACK で返し、ESTABLISHED へと至るハンドシェイクのシーケンス。あるいは、コネクション切断時の TIME_WAIT や CLOSE_WAIT の滞留。

現場でよくある「APIサーバーが突然応答しなくなった」というインシデントの多くは、コードのバグによってソケットが適切に解放されず、CLOSE_WAIT が無限に増殖しているケースがほとんどだ。これを暴くために、僕らはnetstatの扉を叩く。

—

2. 実務で使うnetstat:基本コマンドとパラメーターの解剖

まずは、実務の現場で秒速で打つべき基本コマンドと、そのオプションの意味を整理しよう。
余計な名前解決(DNS逆引き)を省き、数千行の出力から真実を炙り出すための黄金の組み合わせだ。

# 全てのTCP/UDPソケットを、数値表現(IPとポート)かつプロセス名付きで一覧表示する
netstat -tulpn

このコマンドで使用している主要なパラメーターの意味は以下の通りだ。実務ではこの組み合わせを体に覚え込ませておいてほしい。

  • -t (--tcp): TCPコネクションのみを対象にする。
  • -u (--udp): UDPソケットのみを対象にする。
  • -l (--listening): 接続待ち(Listen状態)のサーバーソケットを表示する。
  • -p (--program): そのソケットを開いているPID(プロセスID)とプログラム名を併記する(要root権限)。
  • -n (--numeric): IPアドレスやポート番号を名前解決せず、数値のまま高速に表示する(DNSの逆引き遅延を防ぐため、現場では必須)。

—

3. 現場のユースケース:トラブルシューティングの実践

では、実際のWeb API開発やインフラ運用において、どのようにnetstatを活用するのか。具体的なシナリオを見ていこう。

シナリオA:APIクライアントからの「接続拒否(Connection Refused)」

自社のバックエンドAPIサーバーに対して、フロントエンドのNode.jsやPythonアプリからリクエストを送ったところ、突然 ECONNREFUSED が返るようになった。
サーバーが死んでいるのか、あるいはポートが空いていないのかを確かめる。

# 特定のポート(例: 8080番)でプロセスが正しくListenしているか確認する
netstat -an | grep 8080

もしここで何も出力されなければ、アプリケーションプロセスがクラッシュしているか、起動に失敗している。
逆に、LISTEN 状態であっても、接続元が限られている場合は注意が必要だ。ローカルループバック (127.0.0.1) だけでListenしていて、Dockerコンテナや別ホストからの通信を弾いているケース(バインドミスの典型)を、このコマンドが一発で見抜いてくれる。

シナリオB:TIME_WAIT 嵐とエフェメラルポートの枯渇

大量のHTTPリクエストを外部API(Web APIや外部SaaS)へ高速に投げ続けるバッチ処理を実装した際、突然処理が重くなり、次のようなエラーに直面したことはないだろうか?
「Cannot assign requested address」

これは、クライアント側から接続を切断した際に発生する TIME_WAIT 状態のソケットがOS内に溢れ返り、外向きの通信に使えるエフェメラルポート(一時ポート)が完全に枯渇している状態だ。現場では「TIME_WAIT 祭り」と呼んでいる。

現在のコネクションの偏りを統計的に確認するには、以下のコマンドが有効だ。

# TCPコネクションの状態ごとの件数を集計して降順でソートする
netstat -an | grep tcp | awk '{print $6}' | sort | uniq -c | sort -nr

出力例:

1245 ESTABLISHED
    892 TIME_WAIT
     12 LISTEN
      4 CLOSE_WAIT

この状態を見たら、アプリケーション側の設計を見直す必要がある。
HTTP/1.1であれば Keep-Alive を有効にしてコネクションを使い回すべきだし、Pythonの requests や Node.js の axios を使っているなら、セッション(コネクションプーリング)を張る実装に修正しなければならない。

以下に、Pythonの requests ライブラリでコネクションプールを適切に維持し、無駄な TIME_WAIT を発生させない実装例を示す。

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def create_robust_api_client():
    """
    コネクションプールとリトライロジックを備えたセッションを生成する。
    無駄なソケットの生成・破棄を防ぎ、TIME_WAITの枯渇を防ぐ実用的なパターン。
    """
    session = requests.Session()
    
    # リトライ戦略の設定(500番台エラーやネットワーク一時断に対応)
    retries = Retry(
        total=3,
        backoff_factor=0.5,
        status_forcelist=[500, 502, 503, 504]
    )
    
    # HTTPAdapterでプールサイズ(pool_maxsize)を明示的に定義
    adapter = HTTPAdapter(
        pool_maxsize=50,       # 同時接続プールの最大数
        pool_block=True,       # プールが枯渇した際にブロックして待機する
        max_retries=retries
    )
    
    session.mount("https://", adapter)
    session.mount("http://", adapter)
    
    return session

# 使用例
if __name__ == "__main__":
    client = create_robust_api_client()
    try:
        # 同一セッションを使い回すことでTCPハンドシェイクのオーバーヘッドを削減
        response = client.get("https://api.example.com/v1/data", timeout=5.0)
        response.raise_for_status()
        print(f"API Response: {response.json()}")
    except requests.exceptions.RequestException as e:
        print(f"API通信エラーが発生しました: {e}", file=sys.stderr)
    finally:
        client.close()

—

4. インターフェース統計情報の深掘り(パケットロッジの検知)

ソケットの状態だけでなく、netstatはネットワークカード(NIC)レベルの統計情報も教えてくれる。ハードウェアの故障、ケーブルの劣化、あるいはクラウド環境での帯域制限(スロットリング)に起因するパケットロスを見抜くには、-i オプションを使う。

# インターフェースごとの送受信パケット数、エラー、ドロップ数を一覧表示する
netstat -i

注目すべきは、出力結果の中にある RX-ERR, TX-ERR(受信/送信エラー)や、RX-DROP, TX-DROP(ドロップ数)の数値だ。
もし、正常に稼働しているはずのサーバーで RX-DROP が猛烈な勢いでカウントアップされていたら、それはOSのカーネルバッファ(受信キュー)が溢れ、パケットを処理しきれずに捨てている証拠だ。

このような現場に直面した場合、一時的な対策としてカーネルパラメータ(/etc/sysctl.conf)のチューニングが必要になる。

# /etc/sysctl.conf の設定例
# 受信キューの最大バックログ(未処理の接続要求を保持するキュー)を拡大する
net.core.somaxconn = 1024

# TIME_WAIT ソケットの早期再利用を有効にする(※環境により慎重に判断すること)
net.ipv4.tcp_tw_reuse = 1

設定を変更した後は、以下のコマンドで即座に反映させることを忘れないでほしい。

# sysctlの設定を即時反映させる
sudo sysctl -p

—

5. シニアエンジニアからの教訓

ネットワークのトラブルシューティングにおいて、最も恐ろしいのは「見えないものを見ようとせず、思い込みで設定ファイルを書き換えること」だ。

「APIが繋がらない」「重い」と感じたとき、感情的にコードをいじる前に、まずは落ち着いて netstat -tulpn を叩いてほしい。
今、何個のソケットがどこを向いて生きているのか。どのプロセスが通信を握りしめているのか。
OSは嘘をつかない。すべての真実は、パケットとソケットの統計情報という名のログに刻まれている。

レガシーと言われようとも、netstatはいつの時代も、泥臭く現場を支えるエンジニアの最も頼れる相棒なのだ。次回の障害対応の夜も、こいつの出番がないことを祈りつつ、万全の準備をしておこう。

コメント

タイトルとURLをコピーしました