はじめに (対象読者・この記事でわかること)

Node.jsでWebアプリケーションを開発している際、サーバーを起動しようとしたら「Error: listen EADDRINUSE: address already in use」というエラーメッセージが出て、困った経験はありませんか? このエラーは、特定のポート番号がすでに別のプロセスによって使用されている場合に発生します。特に開発初期や頻繁にサーバーを再起動する場面で遭遇しやすく、初心者にとっては原因究明が難しい典型的なエラーの一つです。

この記事は、Node.js開発者の方、特に開発環境でサーバーが起動しないと悩んでいる方、そしてこのEADDRINUSEエラーメッセージを見たことがある方を対象にしています。この記事を読むことで、このエラーの根本的な原因を理解し、お使いのOSに応じた具体的な手順で問題のプロセスを特定、終了する方法を習得できます。さらに、エラーの再発を防ぐための根本的な解決策や回避策についても解説しますので、今後の開発がよりスムーズに進むようになるでしょう。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。 * Node.jsの基本的な実行方法(例: node app.js) * ターミナル(またはコマンドプロンプト)の基本的な操作 * ポート番号に関する基本的な知識

ポート競合の悪夢を乗り越える!EADDRINUSEエラーとは?

Error: listen EADDRINUSE: address already in use。このエラーメッセージは、直訳すると「アドレスがすでに使用中です」という意味になります。Node.jsアプリケーションが特定のポート番号(例: 30008080、記事のテーマである4000など)でリッスンしようとした際、そのポートが既に別のアプリケーションやプロセスによって占有されている場合に発生します。

なぜこのような状況が起こるのでしょうか?主な原因としては以下が挙げられます。

  1. サーバーの意図しない終了: 開発中にサーバーを停止せず、ターミナルを閉じたり、アプリケーションがクラッシュしたりした場合、バックグラウンドでプロセスが生き残り、ポートを占有し続けることがあります。
  2. 複数のアプリケーションの競合: 別のNode.jsアプリケーション、Webサーバー(Apache, Nginx)、データベース、他の開発ツールなどが、同じポートを使用するように設定されている場合があります。
  3. ホットリロードツールの不具合: nodemonなどの開発用ホットリロードツールを使用している際に、正常にプロセスが終了せず、新しいプロセスが立ち上がろうとした際に競合が発生することがあります。

このエラーは特に開発環境で頻繁に遭遇しますが、本番環境のVPSなどでデプロイ時に発生すると、サーバーが起動しない原因となり、運用に大きな影響を与える可能性があります。このエラーを迅速かつ正確に解決する能力は、開発者にとって非常に重要です。

ステップバイステップ!EADDRINUSEエラーの特定と解決

それでは、具体的にこの厄介なEADDRINUSEエラーを解決するための手順を見ていきましょう。OS別に詳細なコマンドと解説を提供します。

ステップ1: エラーメッセージを正しく理解する

まず、エラーメッセージから重要な情報を読み取ります。

Error: listen EADDRINUSE: address already in use :::4000
    at Server.setupListenHandle [as _listen2] (node:net:1819:16)
    at listenInCluster (node:net:1884:12)
    at Server.listen (node:net:1972:7)
    at Object.<anonymous> (/path/to/your/app.js:10:5)
    at Module._compile (node:internal/modules/cjs/loader:1256:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
    at Module.load (node:internal/modules/cjs/loader:1119:32)
    at Module._load (node:internal/modules/cjs/loader:960:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:23:47 {
  code: 'EADDRINUSE',
  errno: -98,
  syscall: 'listen',
  address: '::',
  port: 4000
}

このエラーメッセージで最も重要な部分は、address already in use :::4000です。これは、4000番ポートがすでに使用されていることを明確に示しています。port: 4000という部分も同様にポート番号を示しています。このポート番号をメモしておきましょう。

ステップ2: ポートを占有しているプロセスを特定する

次に、どのプロセスが指定されたポートを占有しているのかを特定します。OSによって使用するコマンドが異なります。

macOS / Linuxの場合

macOSやLinuxでは、lsofコマンドまたはnetstatコマンドが非常に有効です。

1. lsofコマンドを使用する

lsofは「list open files」の略で、システム上で開かれているファイルやネットワーク接続を一覧表示するコマンドです。ポートを指定して、そのポートを使用しているプロセスを特定できます。

Bash
lsof -i :4000
  • -iオプション: ネットワークファイルを表示します。
  • :4000: 4000番ポートを指定します。

実行例:

COMMAND   PID   USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
node    12345 kousukei   10u  IPv6 0xaaaaaaaaaaaaaaa      0t0  TCP *:4000 (LISTEN)

この出力から、PID(プロセスID)が12345COMMANDnodeであることがわかります。これがポート4000を占有しているプロセスです。

2. netstatコマンドとgrepコマンドを組み合わせる

netstatはネットワーク接続、ルーティングテーブルなどを表示するコマンドです。grepと組み合わせることで、特定のポートを使用しているプロセスを絞り込むことができます。

Bash
sudo netstat -tulnp | grep :4000
  • sudo: プロセス情報(特にPID)を表示するために管理者権限が必要な場合があります。
  • -t: TCP接続を表示
  • -u: UDP接続を表示
  • -l: リッスン状態のソケットのみ表示
  • -n: ホスト名やサービス名を数値で表示(解決しないため高速)
  • -p: プロセスID (PID) とプログラム名を表示
  • grep :4000: 出力の中から「:4000」を含む行をフィルタリングします。

実行例:

tcp6       0      0 :::4000                 :::*                    LISTEN      12345/node

この出力の最後の列に12345/nodeと表示されています。12345がPID、nodeがプロセス名です。

Windowsの場合

Windowsでは、netstatコマンドとfindstrコマンドを組み合わせてPIDを特定し、その後tasklistコマンドでプロセス名を確認します。

1. netstatコマンドとfindstrコマンドを組み合わせる

コマンドプロンプト(またはPowerShell)を開き、以下のコマンドを実行します。

Cmd
netstat -ano | findstr :4000
  • -a: すべてのアクティブなTCP接続とリッスンしているポートを表示します。
  • -n: アドレスとポート番号を数値形式で表示します。
  • -o: 各接続に関連付けられたプロセスID (PID) を表示します。
  • findstr :4000: 出力の中から「:4000」を含む行をフィルタリングします。

実行例:

  TCP    0.0.0.0:4000           0.0.0.0:0              LISTENING       12345
  TCP    [::]:4000              [::]:0                 LISTENING       12345

この出力の最後の列に12345というPIDが表示されています。

2. tasklistコマンドでプロセス名を確認する

特定したPID(例: 12345)がどのプロセスに属しているかを確認します。

Cmd
tasklist | findstr 12345

実行例:

node.exe                     12345 Console                    1     52,148 K

この出力から、PID 12345node.exeというプロセスであることがわかります。

ステップ3: 占有プロセスを終了する

ポートを占有しているプロセスを特定できたら、そのプロセスを終了します。強制終了はデータ損失やシステムの不安定化につながる可能性があるため、注意して行いましょう。ただし、開発中のNode.jsサーバーであれば、ほとんどの場合問題ありません。

macOS / Linuxの場合

killコマンドを使用して、特定のPIDを持つプロセスを終了します。

Bash
kill -9 12345 # 12345は特定したPIDに置き換えてください
  • kill: プロセスにシグナルを送るコマンドです。
  • -9: 強制終了(SIGKILLシグナル)を意味します。このシグナルはプロセスに終了を強制するため、通常は最後の手段として使われます。より穏やかな終了を試みる場合は-15(SIGTERM)を使用しますが、開発中の残存プロセスであれば-9で問題ないでしょう。

Windowsの場合

taskkillコマンドを使用して、特定のPIDを持つプロセスを終了します。

Cmd
taskkill /F /PID 12345 # 12345は特定したPIDに置き換えてください
  • taskkill: プロセスを終了するコマンドです。
  • /F: 強制的にプロセスを終了します。
  • /PID 12345: 終了したいプロセスのPIDを指定します。

プロセスを終了したら、再度Node.jsアプリケーションを起動してみてください。今度は正常に起動するはずです。

ハマった点やエラー解決

1. プロセスを特定できない、または終了できない場合

  • 権限の問題: lsofnetstat -ptasklistなどは、実行中のすべてのプロセス情報を取得するために管理者権限(root権限)が必要な場合があります。Mac/Linuxではコマンドの前にsudoをつけ、Windowsでは管理者としてコマンドプロンプト/PowerShellを実行してみてください。
  • ポートがすぐに解放される: 一部のプロセスは非常に短時間で終了するため、lsofなどを実行した瞬間にポートが解放されてしまうことがあります。その場合は、watchコマンド(Linux/Mac)を使って定期的に監視するか、エラーが頻発する根本原因を探る必要があります。
  • システムプロセス: 特定されたPIDがOSの重要なシステムプロセスだった場合、安易に終了してはいけません。再起動を検討するか、ポート番号を変更することを優先してください。

2. 根本的な解決策と再発防止策

一時的にプロセスを終了しても、同じ問題が再発する可能性があります。以下の方法で、根本的な解決や再発防止を図りましょう。

a. ポート番号を変更する 最も手軽で確実な方法です。アプリケーションが使用するポート番号を、他のアプリケーションと競合しない番号に変更します。環境変数や設定ファイル(.envなど)でポート番号を設定できるようにしておくと、変更が容易になります。

Javascript
// Node.jsアプリケーションでポートを設定する例 const PORT = process.env.PORT || 4000; // 環境変数PORTがあればそれを使用、なければ4000 app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); });

b. 開発サーバーの自動再起動ツールを活用する nodemonなどのツールを使用すると、コードの変更を検知して自動的にサーバーを再起動してくれます。これらのツールは、適切に設定されていれば、古いプロセスを終了してから新しいプロセスを立ち上げるため、EADDRINUSEエラーの発生を防ぎやすくなります。

Bash
# nodemonのインストール npm install -g nodemon # nodemonを使ってアプリケーションを起動 nodemon app.js

c. 開発フローの見直し サーバーを停止する際は、Ctrl+Cなどで適切に終了する習慣をつけましょう。ターミナルをそのまま閉じるのではなく、プロセスが終了するのを待つことが重要です。

d. Dockerなどのコンテナ環境を活用する Dockerコンテナは、それぞれが独立したネットワーク空間を持つため、コンテナ内でのポート競合は少なくなります。ただし、ホストマシンとコンテナ間のポートマッピングを行う際には、ホスト側のポートがすでに使用されていないか注意が必要です。

Bash
# Dockerでのポートマッピング例 docker run -p 4000:4000 my-node-app

この例では、ホストの4000番ポートをコンテナの4000番ポートにマッピングしています。ホストの4000番ポートが空いている必要があります。

e. CI/CD環境での注意 CI/CDパイプラインでEADDRINUSEエラーが発生する場合は、ビルドエージェント上で前回のビルドプロセスが適切に終了しているかを確認する必要があります。ジョブの開始時にポートを解放するスクリプトを追加するなどの対策が有効です。

解決策

これらのステップとヒントに従うことで、EADDRINUSEエラーを迅速に解決し、開発・デプロイプロセスをスムーズに進めることができるでしょう。根本的な解決策を導入することで、再発のリスクを大幅に減らすことが可能です。

まとめ

本記事では、Node.jsアプリケーションの開発において頻繁に遭遇するError: listen EADDRINUSE: address already in useエラーの原因、特定方法、そして具体的な解決策について詳細に解説しました。

  • 要点1: EADDRINUSEエラーは、指定されたポートがすでに他のプロセスによって占有されているために発生します。
  • 要点2: macOS/Linuxではlsof -i :PORTsudo netstat -tulnp | grep :PORT、Windowsではnetstat -ano | findstr :PORTコマンドを使って、ポートを占有しているプロセスのPIDを特定できます。
  • 要点3: 特定したPIDを使って、macOS/Linuxではkill -9 PID、Windowsではtaskkill /F /PID PIDコマンドでプロセスを強制終了することで問題を解決できます。
  • 要点4: 根本的な解決策としては、ポート番号の変更、nodemonなどの開発ツールの活用、Dockerコンテナの利用、そして開発フローの見直しが有効です。

この記事を通して、開発中に突如現れるこの厄介なエラーに惑わされることなく、原因を迅速に特定し、適切な手順で問題を解決できるようになることで、スムーズな開発環境を維持できるようになることを願っています。

今後は、Node.jsアプリケーションのデプロイ時のベストプラクティスや、Docker環境におけるより高度なポート管理戦略など、発展的な内容についても記事にする予定です。

参考資料