【実務・中級編】 netstatコマンドによるTCP/UDPソケットの状態一覧表示(-anオプション) – トラブルシューティング&ネットワーク運用監視実践ガイド

おう、諸君。NOCの古株として、今日は皆に「netstat」コマンド、特に-anオプションを使ったTCP/UDPソケットの状態確認について、実体験を交えて語ってやろう。Web APIの設計やインフラ運用に携わる諸君らにとって、これはまさに「現場の相棒」とも言えるコマンドだ。机上の空論ではなく、パケットが飛び交う現実に根差した話をするぜ。

伝説のコマンド「netstat」と -an オプション:ソケット状態を可視化する魔法

Web APIを開発していると、クライアントからのリクエストがちゃんとサーバーに届いているか、あるいはサーバーからのレスポンスがクライアントに返っているか、心配になることがあるだろう。インフラ運用担当者なら、サービスが正常に稼働しているか、予期せぬ通信が発生していないか、常に目を光らせなければならない。そんな時、君たちの頼れる相棒となるのがnetstatコマンドだ。

特に-anオプションを組み合わせると、システム上の全てのTCPおよびUDPソケットの状態を、IPアドレスとポート番号と共に一覧表示してくれる。これは、まるでネットワークの「カルテ」を眺めるようなものだ。

なぜ -an なのか?

netstatコマンドには様々なオプションがあるが、なぜ-anが特別なのか。

  • -a (all): これは、LISTEN状態(待ち受け状態)のソケットだけでなく、確立された接続(ESTABLISHED)、切断処理中の接続(CLOSE_WAIT, TIME_WAIT)など、全てのソケット状態を表示してくれる。これがないと、アクティブな通信しか見えず、問題の根本原因を見逃す可能性がある。
  • -n (numeric): これがないと、netstatはホスト名やサービス名を名前解決しようとする。これがまた、DNSサーバーの応答を待つことになり、コマンドの実行が遅くなるし、場合によっては名前解決に失敗して本来見たい情報が見えなくなることもある。-nオプションを付けることで、IPアドレスとポート番号を数値のまま表示してくれる。これは、障害発生時に一刻を争う状況では非常に重要だ。迷わず-nを付けよう。

つまり、-anオプションは、「全てのソケットを、名前解決せずに数値で表示しろ!」という、現場のエンジニアが求める「迅速かつ網羅的な情報」をピンポイントで提供してくれるわけだ。

TCP/UDPソケットの状態:RFCの向こう側

さて、-anオプションで表示される「状態」だが、いくつか代表的なものを見ていこう。これらはTCPのRFC(例えばRFC 793)で定義されているものだが、現場ではもっと生々しい挙動として現れる。

  • LISTEN: これは、サーバーアプリケーションが特定のポートで「クライアントからの接続を待っている」状態だ。Webサーバーならポート80や443、APIサーバーなら指定されたポートがこの状態になっているはずだ。
  • 実務でのチェックポイント: APIサーバーが起動しているか、指定ポートで正しく待ち受けているかを確認する最初のステップだ。もしLISTEN状態になっていなければ、アプリケーションが起動していないか、ポートが他のプロセスに占有されている可能性がある。
  • ESTABLISHED: クライアントとサーバーの間でTCP接続が正常に確立され、通信が可能な状態だ。APIリクエストの送受信が行われている、まさに「やり取り」が行われている状態と言える。
  • 実務でのチェックポイント: 正常に動作しているAPI通信の状態を把握するのに役立つ。大量のESTABLISHED接続は、サービスが活況な証拠でもあるが、リソース不足の兆候である可能性も。
  • TIME_WAIT: TCP接続が正常にクローズされた後、一定期間(通常は2MSL: Maximum Segment Lifetimeの2倍)ソケットを保持する状態だ。これは、まだネットワーク上に遅延して届く可能性のあるパケットを処理するため、または、相手からのACKが届かなかった場合のために、接続を「保留」しておくための状態。
  • 実務でのチェックポイント: もしTIME_WAIT状態のソケットが異常に多く、かつ長時間滞留している場合、それは「コネクションハングアップ」や「リソース枯渇」の兆候かもしれない。大量の短命な接続が頻繁に発生するようなAPIでは、この状態を気にする必要がある。
  • CLOSE_WAIT: 相手側(クライアント)が接続切断を要求し、こちらはその切断要求を受け取ったが、まだアプリケーション側が接続を閉じる処理を完了していない状態。
  • 実務でのチェックポイント: これは「サーバー側が接続を閉じきれていない」ことを示唆する。アプリケーションのバグや、リソース(ファイルディスクリプタなど)の枯渇が原因であることが多い。この状態が多発している場合は、サーバーサイドのアプリケーションコードや、OSレベルでのリソース設定を見直す必要がある。

現場で役立つ netstat -an の活用例

では、具体的なシチュエーションで、netstat -anコマンドがどう役立つのか見ていこう。

例1:APIサーバーが応答しない!

クライアントからAPIサーバーにリクエストを送っても、まったく応答がない。そんな時、まずサーバーにログインして、このコマンドを実行する。

# サーバーにSSHでログインし、実行
netstat -an

出力結果の一部を例として見てみよう。

Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      # 8080ポートで待ち受け中
tcp        0      0 192.168.1.100:8080      192.168.1.50:54321      ESTABLISHED # クライアントIP 192.168.1.50 からの接続がある
tcp        0      0 192.168.1.100:8080      192.168.1.60:54322      ESTABLISHED # 別のクライアントからの接続
tcp        0      0 192.168.1.100:8080      192.168.1.70:54323      CLOSE_WAIT  # クライアントは切断要求したが、サーバーが応答できていない?
udp        0      0 0.0.0.0:5353            0.0.0.0:*               *           # UDP通信 (mDNSなど)
  • 0.0.0.0:8080 が LISTEN になっているなら、アプリケーションは起動している。
  • ESTABLISHED の接続があれば、一部のクライアントは接続できているようだ。
  • もし、CLOSE_WAIT の状態が多ければ、サーバー側のアプリケーションに問題がある可能性が高い。
  • あるいは、LISTEN になっていない場合は、アプリケーションが起動していないか、ポートが別のプロセスに奪われている。

例2:特定のIPアドレスやポートからの通信を絞り込む

全てのソケットを見るのも良いが、特定のクライアントからの通信や、特定のポートだけを調べたい場合もあるだろう。grepコマンドと組み合わせるのが定石だ。

例えば、192.168.1.50 というIPアドレスからの通信だけを調べたい場合:

netstat -an | grep '192.168.1.50'

APIサーバーが使用しているポート 8080 だけを調べたい場合:

netstat -an | grep ':8080'

出力例:

tcp        0      0 192.168.1.100:8080      192.168.1.50:54321      ESTABLISHED
tcp        0      0 192.168.1.50:54321      192.168.1.100:8080      ESTABLISHED

このように、関連する通信を素早く特定できる。

コード例との連携:Fetch API, curl, Python

netstatコマンドは、あくまで「サーバー側の状態」を見るためのものだ。しかし、クライアント側からAPIを叩くコードや、サーバーサイドでAPIクライアントを実装するコードと連携させることで、より包括的なトラブルシューティングが可能になる。

クライアントサイド: JavaScript (Fetch API)

WebブラウザからAPIを叩く際のJavaScriptコードだ。

async function callMyApi() {
  const apiUrl = 'http://your-api-server.com:8080/v1/status'; // 実際にアクセスするAPIのエンドポイント
  const requestOptions = {
    method: 'GET', // HTTPメソッド (GET, POST, PUT, DELETEなど)
    headers: {
      'Content-Type': 'application/json', // リクエストボディの形式を指定
      'X-My-Custom-Header': 'some-value' // カスタムヘッダーの例
    },
    // body: JSON.stringify({ key: 'value' }), // POSTリクエストなどでボディを送る場合
    // signal: AbortSignal.timeout(5000) // タイムアウト設定 (ブラウザによってはサポート)
  };

  try {
    console.log(`Sending request to ${apiUrl}...`);
    const response = await fetch(apiUrl, requestOptions);

    if (!response.ok) {
      // HTTPステータスコードが200-299以外の場合
      console.error(`HTTP error! status: ${response.status}, statusText: ${response.statusText}`);
      // サーバー側でCLOSE_WAITが増えている可能性を疑う
      return;
    }

    const data = await response.json(); // レスポンスボディをJSONとしてパース
    console.log('API response received:', data);
    // ここで、netstat -an の ESTABLISHED 状態が正常に通信できているか確認できる

  } catch (error) {
    // ネットワークエラーやタイムアウトなど
    console.error('Error calling API:', error);
    // netstat -an で、LISTEN 状態になっているか、ESTABLISHED になっていないかの確認が有効
  }
}

// 関数を実行
callMyApi();

このコードを実行した時、netstat -anでESTABLISHED状態の接続が確認できれば、ネットワークレベルでは通信できている証拠だ。もしfetchでエラーが出ているのにESTABLISHEDになっているなら、アプリケーションレベルでのエラー(例: 500 Internal Server Error)の可能性が高い。

コマンドラインツール: curl

開発者やインフラ担当者がよく使うcurlコマンドも、APIテストに必須だ。

# GETリクエストを送信し、詳細なヘッダー情報も表示
curl -v -X GET http://your-api-server.com:8080/v1/status

# POSTリクエストを送信し、JSONボディを指定
curl -v -X POST \
  -H "Content-Type: application/json" \
  -d '{"name": "test", "value": 123}' \
  http://your-api-server.com:8080/v1/items

-vオプションを付けると、TCP接続の確立からTLSハンドシェイク(HTTPSの場合)、HTTPリクエスト/レスポンスヘッダーのやり取りまで、詳細に表示される。これも、netstat -anで確認できるESTABLISHED状態と合わせて見ると、通信経路のどこで問題が発生しているかのヒントになる。

サーバーサイド: Python

PythonでAPIクライアントを実装する場合の例だ。

import requests
import json

api_url = 'http://your-api-server.com:8080/v1/data' # APIのエンドポイント
headers = {
    'Content-Type': 'application/json',
    'X-API-Key': 'your-secret-key' # APIキーなどの認証情報
}
payload = {
    'query': 'example',
    'limit': 10
}

try:
    # GETリクエストの例
    print(f"Sending GET request to {api_url}...")
    response = requests.get(api_url, headers=headers, timeout=10) # 10秒のタイムアウトを設定

    # POSTリクエストの例
    # print(f"Sending POST request to {api_url}...")
    # response = requests.post(api_url, headers=headers, data=json.dumps(payload), timeout=10)

    response.raise_for_status() # HTTPエラーが発生した場合に例外を発生させる

    # レスポンスが正常な場合
    print(f"Request successful. Status code: {response.status_code}")
    data = response.json()
    print("Response data:", data)
    # ここでも netstat -an の ESTABLISHED 状態と照らし合わせる

except requests.exceptions.Timeout:
    print("Error: Request timed out.")
    # netstat -an で LISTEN 状態になっているか、ESTABLISHED が確立されないか確認
except requests.exceptions.ConnectionError as e:
    print(f"Error: Could not connect to the server. {e}")
    # netstat -an で LISTEN 状態になっていない、またはネットワーク設定を確認
except requests.exceptions.HTTPError as e:
    print(f"Error: HTTP error occurred. {e}")
    # サーバー側のアプリケーションエラーの可能性。netstat -an で CLOSE_WAIT などがないか確認
except Exception as e:
    print(f"An unexpected error occurred: {e}")

Pythonのrequestsライブラリも、タイムアウト設定やHTTPエラーハンドリングが充実している。これらのエラーが発生した際に、netstat -anでサーバー側のソケット状態を確認する、という流れは、まさに現場でよくあるデバッグパターンだ。

設定ファイルの記述例

APIサーバーとして動作するアプリケーションの設定ファイルや、リバースプロキシ(Nginxなど)の設定ファイルで、ポート番号を定義する場面は多いだろう。

Webサーバー(例:Node.js/Express)

const express = require('express');
const app = express();
const port = 8080; // ここでポート番号を定義

app.use(express.json()); // JSONリクエストボディをパースするためのミドルウェア

// ルートエンドポイント
app.get('/', (req, res) => {
  res.send('Hello World!');
});

// APIエンドポイントの例
app.get('/api/v1/status', (req, res) => {
  res.json({ status: 'ok', message: 'Service is running' });
});

// サーバーを起動
app.listen(port, () => {
  console.log(`API server listening at http://localhost:${port}`);
  // この行が出力されたら、netstat -an で 0.0.0.0:${port} が LISTEN になっているはず
});

リバースプロキシ(例:Nginx)

NginxでAPIサーバー(例:localhost:8080で動いている)へのリクエストを api.yourdomain.com に転送する場合。

server {
    listen 80; # HTTPリクエストを待ち受けるポート
    server_name api.yourdomain.com; # このドメインへのリクエストを処理

    location / {
        proxy_pass http://localhost:8080; # バックエンドのAPIサーバーへ転送
        proxy_set_header Host $host; # クライアントのホスト名をバックエンドに伝える
        proxy_set_header X-Real-IP $remote_addr; # クライアントのIPアドレスを伝える
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # 必要に応じてSSL設定を追加
    # listen 443 ssl;
    # ssl_certificate /etc/nginx/ssl/yourdomain.com.crt;
    # ssl_certificate_key /etc/nginx/ssl/yourdomain.com.key;
}

これらの設定ファイルで定義されたポートが、netstat -anの出力で正しくLISTEN状態になっているかを確認することが、運用の基本中の基本だ。

まとめ:現場で頼れる「netstat -an」

「netstat -an」コマンドは、ネットワークの「今」を映し出す鏡だ。
APIが応答しない、サービスが遅い、予期せぬ通信が発生している…そんな時、このコマンドは君に多くのヒントを与えてくれる。

  • LISTEN: サービスは起動しているか?
  • ESTABLISHED: 正常に通信できているか? 接続数は多すぎないか?
  • CLOSE_WAIT: サーバー側で接続を閉じきれていない問題はないか?
  • TIME_WAIT: 接続のクローズ処理で問題はないか?

これらの状態を、IPアドレスとポート番号で数値として把握することが、迅速な原因特定への第一歩だ。
諸君も、この「伝説のコマンド」を使いこなし、日々の開発や運用をよりスムーズに進めてほしい。何かわからないことがあれば、いつでも聞きに来たまえ。

コメント

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