netstat卒業: witrで「このポートなぜ動く?」を1コマンド解決

Replace manual netstat workflows with witr’s single-command answers

* 本ページはプロモーションが含まれています

はじめに:深夜の「このポート何だっけ?」問題 

サーバー運用をしていると、深夜にアラートを見て SSH した先で「このポート何が開いてるんだっけ?」という状況に何度も遭遇します。
これまでは netstat / ss / lsof / nmap / systemctl / docker ps を組み合わせて、人力でそのプロセスの正体と起源を推理していました。
最近、witr (Why is this running) という CLI ツールを導入したところ、「プロセスがなぜ存在しているのか」まで 1 コマンドで説明してくれるようになり、調査フローがかなり変わりました。

https://github.com/pranshuparmar/witr


従来フロー:Debian・Docker・macOS での調査 

Debian(systemd サーバー) 

例えば、本番の Debian サーバーで 8080 番ポートが開いているのを見つけたときは、だいたいこんな流れでした。

# ポートと PID の確認
ss -ltnp | grep 8080

# PID からプロセス名を確認
ps aux | grep 12345

# systemd 管理かどうかを確認
systemctl status my-app.service

このあと、「どのユニットから起動されているか」「実行ユーザー」「起動オプション」などを、systemctl や journalctl、サービスの設定ファイルを見ながら手で繋ぎ合わせていました。

Docker 多めの環境 

Docker が絡むと、さらに 1 ステップ増えます。

# ホスト側のポートだけ分かっている状態
ss -ltnp | grep 5000

# プロセスが docker-proxy / containerd っぽい → コンテナを調べる
docker ps
docker ps --format 'table {{.Names}}\t{{.Ports}}'

# コンテナ内のポート状況を見る
docker exec -it my-app ss -ltnp

「ホストの 0.0.0.0:5000 → docker-proxy → コンテナ my-app → コンテナ内のアプリ」といった因果関係を、自分で追いかける必要がありました。

macOS(開発マシン) 

開発用 macOS でも、似たようなことをやっていました。

# LISTEN しているポート一覧
lsof -iTCP -sTCP:LISTEN

# 気になるポートだけ掘る
lsof -i:3000

しばらく触っていないプロジェクトの dev サーバーが残っていると、「これどのディレクトリの Node だったっけ?」と、さらに ps / cwd / シェルの履歴まで遡ることもありました。


witr の導入とインストール 

https://github.com/pranshuparmar/witr

Debian(Linux) 

curl -fsSL https://raw.githubusercontent.com/pranshuparmar/witr/main/install.sh -o install.sh
chmod +x install.sh
sudo ./install.sh

スクリプトが OS(linux / darwin / freebsd)とアーキテクチャ(amd64 / arm64)を自動判定し、最新バイナリと man ページを /usr/local/bin/usr/local/share/man に配置してくれます。

手動で入れたい場合は、こんな感じでも入れられます。

OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
[ "$ARCH" = "x86_64" ] && ARCH="amd64"
[ "$ARCH" = "aarch64" ] && ARCH="arm64"

curl -fsSL "https://github.com/pranshuparmar/witr/releases/latest/download/witr-${OS}-${ARCH}" -o witr
chmod +x witr
sudo mv witr /usr/local/bin/witr

macOS(Homebrew) 

brew install witr

Homebrew 環境なら一行で導入できるので、開発用 mac とサーバーの両方に同じコマンドが入るのも便利でした。


実例1:Debian 本番での「この 5000 番何?」事件 

ある晩、ALB のヘルスチェックが一部失敗しているという通知を受けて、本番 Debian に入って確認したときの話です。

1. いつものように ss から入る 

ss -ltnp | grep 5000

出力(イメージ)はこんな感じでした。

LISTEN 0      4096          0.0.0.0:5000       0.0.0.0:*    users:(("app-worker",pid=12345,fd=7))

app-worker ってプロセスが 5000 番で LISTEN しているのは分かったけど、systemd のどのユニットだったっけ…?」といういつもの状態になります。

2. witr を使って「なぜ動いているか」を聞いてみる 

ここで、最近入れた witr を試してみました。

witr --port 5000

マスクしたイメージですが、出力はこんな雰囲気です。

Port 5000 is bound by PID 12345 (app-worker) running as appuser
└─ Started by systemd unit app-worker.service
   └─ WantedBy multi-user.target
      └─ Enabled via /etc/systemd/system/app-worker.service

Summary:
  Port 5000 → app-worker.service (systemd) → multi-user.target
  This process exists because the app-worker service is enabled and started at boot.

この 1 回の実行で、

  • どの PID / ユーザーがポートを掴んでいるか
  • それがどの systemd ユニットか
  • そのユニットがどの target からぶら下がっているか
  • 起動理由(ブート時に enable 済みなのか、手動 start なのか)

といった「因果関係」まで、一気に読める形で返してくれました。

3. 要約だけ欲しいとき 

障害対応中に長い説明まではいらない場合は、ショートモードがちょうど良かったです。

witr --port 5000 --short

出力イメージ:

Port 5000 → app-worker (PID 12345) started by systemd unit app-worker.service (enabled at boot)

「これは systemd 管理の常駐ワーカーだから、ALB の設定を見直すべき」とすぐ判断できたのがかなり助かりました。


実例2:Docker だらけの検証環境でのポート迷子 

次は、Docker コンテナが大量に動いている検証サーバーの例です。

1. ホストで怪しいポートを見つける 

あるとき、ログに「5001 番ポートに対する unknown client からのアクセス」が出ていて、サーバー側を確認しました。

ss -ltnp | grep 5001

出力イメージ:

LISTEN 0      4096      0.0.0.0:5001       0.0.0.0:*    users:(("docker-proxy",pid=23456,fd=4))

「あ〜 docker-proxy だな…このポート、どのコンテナだっけ?」となります。

2. witr で一気にコンテナの正体まで辿る 

witr --port 5001

出力イメージ:

Port 5001 is bound by PID 23456 (docker-proxy) on the host
└─ This proxies to container port 8000 in container web-frontend-1
   └─ Container web-frontend-1 (image registry.example.com/web-frontend:2025-12-01)
      └─ Started by docker-compose.service (systemd) from /srv/projects/frontend/docker-compose.yml

Summary:
  Port 5001 → docker-proxy → container web-frontend-1 → docker-compose.service
  This exists because docker-compose.service is running and published port 5001:8000.

普段なら、

  • docker-proxy の PID から docker が怪しいと推測
  • docker ps を眺めて、ポート欄から対応するコンテナを探す
  • docker inspect で起動コマンドや compose ファイルを確認
  • 必要なら systemd の docker-compose ユニットまで辿る

という 3〜4 ステップを踏んでいたところを、1 コマンドで「ポート → docker-proxy → コンテナ → compose → systemd ユニット」という因果チェーンまで説明してくれたのが印象的でした。

コンテナ内をさらに深掘りしたい場合は、コンテナの中にも witr を入れておいて、

docker exec -it web-frontend-1 bash
witr --port 8000

のように、コンテナ内プロセスの起源まで見に行くこともできます。


実例3:macOS ローカルでの「3000 番誰問題」 

最後に、開発用 macOS の話です。

1. しばらく触っていない dev サーバーが残っていた 

ある日、別プロジェクトで 3000 番を使おうとしたところ、「すでに使用中」と言われました。

lsof -i:3000

出力イメージ:

node    98765  myuser   21u  IPv4 0x1234567890abcdef      0t0  TCP *:hbci (LISTEN)

Node で何かが動いていることは分かりますが、「これどのプロジェクトの dev サーバーだっけ?」となります。

2. witr に聞いてみる 

witr --port 3000

出力イメージ(macOS 対応版):

Port 3000 is bound by PID 98765 (node) running as myuser
└─ Started from interactive shell session:
   └─ /bin/zsh -l
      └─ Working directory: /Users/myuser/dev/old-project
      └─ Command: node_modules/.bin/next dev

Summary:
  Port 3000 → node (Next.js dev server) started manually in /Users/myuser/dev/old-project

「昔の Next.js プロジェクトの dev サーバーが /old-project 配下で残っている」ことが一目瞭然になり、そのシェルを閉じるか ctrl+c するだけで片付きました。
cwd や起動コマンドまで含めて「なぜ動いているか」を説明してくれるので、macOS でもかなり便利に使えています。


まとめ:状態を見るツールから、理由を説明するツールへ 

  • ss / netstat / lsof / nmap / systemctl / docker ps などの従来ツールは、「何がどこで動いているか」という状態を知るのに非常に優秀です。
  • 一方で、「そのプロセスがなぜ存在しているのか」「どの仕組みが責任を持っているのか」は、複数ツールの出力を自分で突き合わせて推理する必要がありました。
  • witr は、ポート番号・PID・プロセス名など、どこから入っても最終的に “Why is this running?” に答える形で因果チェーンをテキスト化してくれるので、Debian の本番サーバーでも Docker だらけの検証環境でも macOS の開発マシンでも、「とりあえず witr を 1 回叩いてから考える」というのが自分の新しい標準フローになりつつあります。

See also