はじめに
自宅サーバーやWebアプリの運用をしていると、原因を知っていればすぐ解決できるのに、知らないと延々とハマってしまう「あるある」なエラーがいくつもあります。本記事では、そうした小粒だけれど覚えておくと役立つトラブルシューティングの知識をまとめました。
EADDRINUSE:アドレスは既に使用中
Node.jsやその他のサーバープロセスを起動しようとしたときに、次のようなエラーに遭遇することがあります。
Error: listen EADDRINUSE: address already in use 0.0.0.0:3000
原因
このエラーは、指定したポート番号を既に別のプロセスが使用しているために、新しいプロセスがそのポートで待ち受け(listen)を開始できないことを意味します。よくある原因は次の通りです。
- 同じアプリケーションのプロセスを、停止し忘れたまま再度起動しようとした
- systemd等でサービス化して常駐させているアプリを、誤って手動でも起動してしまい二重起動になった
- デバッグ用に手動起動したプロセスが、コンソールを閉じた後もバックグラウンドで残り続けていた
調査方法
ssコマンド(netstatの後継)を使うと、指定したポートを実際に使用しているプロセスを特定できます。
ss -tlnp | grep :3000
-
-t:TCPソケットを対象にする -
-l:待ち受け(LISTEN)状態のソケットのみ表示する -
-n:ポート番号などを名前解決せず数値のまま表示する -
-p:ソケットを使用しているプロセス名・PIDを表示する
該当するプロセスのPIDが分かれば、kill <PID>で停止してから、改めて正しい方法(systemd管理下ならサービスの再起動コマンド等)で起動し直します。
シェルの&と&&の違い
見た目が似ているため打ち間違えやすいのが、シェルの&(1つ)と&&(2つ)です。意味はまったく異なります。
| 演算子 | 意味 |
|---|---|
command & |
コマンドをバックグラウンドで実行する。シェルはコマンドの終了を待たずにすぐ次の入力を受け付ける |
command1 && command2 |
command1が成功した場合のみcommand2を実行する(条件付き連続実行) |
# 例1: apt updateをバックグラウンド化しつつ、直後にapt upgradeも実行してしまう(意図しない動作になりやすい)
apt update & apt upgrade -y
# 例2: apt updateが成功した場合のみapt upgradeを実行する(通常こちらが意図した動作)
apt update && apt upgrade -y
&を使うと2つのコマンドの出力が入り混じって表示され、状況が把握しづらくなります。単に「前のコマンドが終わってから次を実行したい」だけであれば&&を使うのが基本です。
ps aux | grepが空振りする
フォアグラウンドで起動していたプロセス(例:ターミナルで直接npm run devを実行した状態)は、そのターミナル(コンソール)を閉じると一緒に終了します。そのため、後からps aux | grep nodeのように確認しても、grepコマンド自身の行しかヒットしない、ということがあります。
$ ps aux | grep node
user 12345 0.0 0.0 6400 736 pts/0 S+ 10:00 0:00 grep node
これは「プロセスが実際に動いていない」ことを意味しているだけなので、killする対象を探す必要はなく、改めて起動コマンドを実行すればよい、というだけの話です。フォアグラウンド起動と常駐化(systemd化等)の違いを理解しておくと混乱しにくくなります。
HSTSキャッシュによる意図しないHTTPS強制
HSTS(HTTP Strict Transport Security) は、一度httpsでアクセスしたサイトに対して「今後は必ずhttpsで接続する」ことをブラウザに記憶させるセキュリティ機能です。サーバーがStrict-Transport-Securityレスポンスヘッダーを返すことで有効になります。
これは便利な機能ですが、開発中には落とし穴になることがあります。過去に一度でも対象ドメインへhttpsでアクセスしたことがあるブラウザでは、その後http://と入力しても自動的にhttps://へ転送されてしまいます。もしhttps側にSSL証明書が設定されていない環境(例:開発中のローカル環境)にアクセスしようとすると、接続エラーになり原因が分かりにくくなります。
Chromeの場合、chrome://net-internals/#hstsを開き、該当ドメインを指定して「Delete domain security policies」を実行することで、記憶されたHSTS設定を削除できます。
生のバリデーションエラーがそのまま画面に出てしまう
WebフレームワークによるAPIは、入力値が不正な場合に422 Unprocessable Entityのようなステータスコードとともに、詳細なエラー内容をJSON形式で返すことがよくあります。
{
"detail": [
{"type": "value_error", "loc": ["body", "email"], "msg": "value is not a valid email address"}
]
}
フロントエンド側でこのdetailを文字列としてしか想定していないと、JSON.stringify()等でこの生のJSON・英語の内部メッセージがそのまま画面に表示されてしまうことがあります。利用者向けの画面では、エラーの形式(配列かどうか等)を判定した上で、フィールド名やエラー種別から分かりやすい文言に変換する共通処理を用意しておくのが望ましい設計です。
まとめ
| 現象 | 原因 | 調査・対処 |
|---|---|---|
| EADDRINUSE | ポートを既に別プロセスが使用中 |
ss -tlnp | grep <ポート>でプロセスを特定し停止 |
&と&&の混同 |
バックグラウンド実行と条件付き連続実行の意味の違い | 連続実行したいだけなら&&を使う |
ps aux | grepが空振り |
フォアグラウンドプロセスはコンソールを閉じると終了する | 単に再起動すればよい、killは不要 |
| HTTPSへの意図しない転送 | HSTSによりブラウザが過去のhttps接続を記憶している | ブラウザのHSTS設定を削除する |
| バリデーションエラーの生JSON表示 | フロントエンドがエラー形式を想定していない | エラー形式を判定して利用者向け文言に変換する |