言語を切り替える
テーマを切り替える

Docker ポートマッピング:「port already allocated」で金曜の夜を台無しにしない

金曜日の夜 7 時半。帰る準備をしていたら、突然プロダクトマネージャーからメッセージが届きます。「テスト環境をサクッとデプロイしてくれない? 明日の朝、クライアントにデモするんだ。」

仕方ない。ターミナルを開き、おなじみの docker run コマンドを打ち込みます。

docker run -d -p 8080:80 nginx

Enter。画面に赤いエラーメッセージが表示されます。

Error response from daemon: driver failed programming external connectivity on endpoint romantic_euler:
Bind for 0.0.0.0:8080 failed: port is already allocated.

心臓が一瞬止まりそうになります。8080 が埋まってる? 誰が? なぜ? どうすればいい?

この光景、見覚えがありませんか? Docker ユーザーの少なくとも半分は、このエラーの前で頭を抱えたことがあるはずです。さらに困るのは、ただコンテナを起動したいだけなのに、ポートの排查、プロセスの確認、ファイアウォール設定の確認と、5 分で終わるはずの作業が 30 分のトラブルシューティングに変わってしまうことです。

ポートマッピングとは一体何か?

正直なところ、ポートマッピングは難しそうに聞こえますが、理屈はそれほど複雑ではありません。

Docker コンテナを「マンションの部屋」だと思ってください。部屋の中には独自の「部屋番号(ポート)」があります。例えば Nginx はデフォルトで 80 番をリッスンします。しかし、このマンションは独立した建物で、外の人は中の部屋番号を知りませんし、入ることもできません。

ポートマッピングは、マンションの入口に「呼び出し転送機」を置くようなものです。外から 3000 番を呼び出すと、自動的に 80 番の部屋に繋がります。これが -p 3000:80 の役割です。ホストの 3000 番ポートを、コンテナの 80 番ポートにマッピングします。

書式はシンプルです:-p ホストポート:コンテナポート。最初はよく順番を間違えましたが、「外から中へ(外:中)」という合言葉で覚えています。

-p と -P の違いは?

この 2 つ、紛らわしいですよね。-p(小文字)は手動指定モード。あなたが決めたポートをマッピングします。-P(大文字)はお任せモードです。Docker がコンテナの公開ポートを、ホスト側のランダムな高位ポート(通常 32768〜61000 の間)に自動マッピングします。

-P を使ったときは、docker psdocker port でどのポートに割り当てられたか確認する必要があります。

docker run -d -P nginx
docker port <container_id>

出力例:

80/tcp -> 0.0.0.0:32768

これはコンテナの 80 番がホストの 32768 番に繋がったことを意味します。便利ですが、ポート番号が固定されないので、本番環境ではあまり使いません。

ポートが占有されていたとき:犯人を見つける 3 つの手順

冒頭のシナリオに戻りましょう。ポートが埋まっています。どうしますか?

第一の手:Docker 自身が犯人か確認する

コンテナを再起動しようとして、古いコンテナが完全に停止せずポートを握っているケースがよくあります。まず docker ps -a でゾンビコンテナを探しましょう。

docker ps -a | grep 8080

見つけたら、削除します。

docker rm -f <container_id>

第二の手:ホスト上のどのプロセスが使っているか確認する

Docker じゃなければ、ホスト上の別プロセスです。OS によって確認コマンドが異なります。

Linux / Mac の場合:

# 方法1:lsof
sudo lsof -i :8080

# 方法2:netstat
sudo netstat -tulnp | grep 8080

# 方法3:ss(より高速)
sudo ss -tulnp | grep 8080

出力例:

COMMAND  PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
node    1234  odensu   21u  IPv4  0x1234      0t0  TCP *:8080 (LISTEN)

PID は 1234、Node のプロセスです。次のどちらかを選べます。

  1. 不要なら停止する:kill -9 1234
  2. 必要なら、Docker 側のポートを変える

Windows の場合は少し手順が増えます。

# ポート確認
netstat -ano | findstr :8080

# 出力例:
# TCP    0.0.0.0:8080    0.0.0.0:0    LISTENING    1234

# プロセス確認
tasklist | findstr 1234

# プロセス停止
taskkill /PID 1234 /F

第三の手:別のポートを使う

実は、ここまで大げさに調べなくても済むことが多いです。8080 がダメなら 8081 でいい。

docker run -d -p 8081:80 nginx

あるいは、Docker に選ばせる方法もあります。

docker run -d -p 0:80 nginx

ホストポートに 0 を指定すると、Docker が空いているポートを自動で割り当てます。その後 docker ps で確認すれば OK です。

複数ポートマッピングと IP バインド

複数ポートをマッピングする方法

1 つのコンテナで複数ポートを公開したい場合があります。フルスタックアプリなら、フロントエンド 3000、バックエンド 8000、データベース 5432 といった構成です。

docker run -d \
  -p 3000:3000 \
  -p 8000:8000 \
  -p 5432:5432 \
  my-fullstack-app

-p パラメータを並べて書くだけです。

もう 1 つのテクニックとして、ポート範囲のマッピングもあります。

docker run -d -p 8000-8010:8000-8010 my-app

ホストの 8000〜8010 をコンテナの対応ポートにマッピングします。ただ、管理が混乱しやすいので、私はあまり使いません。

特定の IP アドレスにバインドする

デフォルトでは、Docker は 0.0.0.0 にバインドします。すべてのネットワークインターフェースからアクセス可能という意味です。本機からだけアクセスさせたい場合は、127.0.0.1 を指定します。

docker run -d -p 127.0.0.1:8080:80 nginx

これで外部ネットワークからはアクセスできず、ホスト自身からのみ接続できます。サーバーに複数の NIC がある場合、特定 IP にバインドすることもできます。

docker run -d -p 192.168.1.100:8080:80 nginx

「マッピングしたのに、なぜアクセスできない?」

これが最も多い質問です。ポートマッピングは正常に見え、docker ps でも成功しているのに、ブラウザからは繋がらない。私が踏んだ落とし穴をいくつか紹介します。

落とし穴 1:コンテナ内のサービスが 0.0.0.0 をリッスンしていない

これが最も見落とされやすい原因です。多くのアプリはデフォルトで 127.0.0.1(localhost)だけをリッスンします。コンテナ内部からの接続しか受け付けず、外部リクエストは届きません。

Node.js アプリの例:

// 誤った書き方
app.listen(3000, 'localhost');  // 127.0.0.1 のみリッスン

// 正しい書き方
app.listen(3000, '0.0.0.0');    // すべてのネットワークインターフェースをリッスン

Python Flask も同様です。

# 誤り
app.run(host='127.0.0.1')

# 正しい
app.run(host='0.0.0.0')

確認方法:コンテナに入って確認します。

docker exec -it <container_id> netstat -tulnp

127.0.0.1:3000 と表示されていて、0.0.0.0:3000:::3000 でなければ、これが原因です。

落とし穴 2:ファイアウォールがブロックしている

Linux サーバーでは、ファイアウォール(firewalld や ufw)がポートを塞いでいることがあります。CentOS 上で、Docker のポートマッピングは正しいのに外部からアクセスできない、という経験があります。

ファイアウォールの状態を確認します。

# CentOS/RHEL
sudo firewall-cmd --list-all

# Ubuntu/Debian
sudo ufw status

ファイアウォールが有効なら、ポートを許可します。

# CentOS/RHEL
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload

# Ubuntu/Debian
sudo ufw allow 8080/tcp

環境が安全だと確信できる場合(開発マシンなど)は、一時的にファイアウォールを止めてテストすることもできます。

# CentOS/RHEL
sudo systemctl stop firewalld

# Ubuntu/Debian
sudo ufw disable

ただし、本番環境では絶対にやらないでください。

落とし穴 3:クラウドのセキュリティグループが未設定

Alibaba Cloud、AWS、Tencent Cloud などのクラウドサーバーを使っている場合、OS のファイアウォールとは別に「セキュリティグループ」があります。これはクラウド基盤レベルのファイアウォールで、OS 側より優先度が高いです。

Alibaba Cloud ECS を初めて使ったとき、OS のファイアウォールを止め、Docker 設定も正しいのにアクセスできませんでした。原因はセキュリティグループのインバウンドルールが未設定だったことでした。

対処法:クラウドコンソールでセキュリティグループ設定を開き、必要なポートのインバウンドルールを追加します。各クラウドの操作は少し異なりますが、考え方は同じです。

落とし穴 4:Docker ネットワークモードが合っていない

Docker には bridge(デフォルト)、host、none、container などのネットワークモードがあります。--network host を使うと、ポートマッピングパラメータは無効になります。コンテナがホストのネットワークスタックを直接使うからです。

# この場合 -p パラメータは無視される
docker run -d --network host -p 8080:80 nginx

host モードでは、コンテナ内のサービスがリッスンするポートがそのままホストのポートになり、マッピングは不要です。パフォーマンスは最良ですが、ポート競合のリスクがあります。

クイック排查フロー

ポートが通らない問題に直面したら、私はだいたいこの順番で確認します。

  1. docker ps — ポートマッピング設定が正しいか確認
  2. docker logs <container_id> — コンテナログで起動失敗がないか確認
  3. docker exec -it <container_id> netstat -tulnp — コンテナ内サービスが 0.0.0.0 をリッスンしているか確認
  4. curl localhost:8080 — ホスト上でテストし、ネットワーク問題を切り分け
  5. システムのファイアウォールルールを確認
  6. クラウドプロバイダーのセキュリティグループ設定を確認

このフローに沿って進めれば、ほとんどの問題は見つかります。

ポートマッピングはパフォーマンスを落とすか?

正直に言うと、落とします。ただし、影響の大きさは状況次第です。

Docker のポートマッピングは iptables(Linux)または userland proxy(クロスプラットフォーム互換モード)に基づいています。パケットがポートマッピングを通るたびに転送ロジックが走るので、多少のオーバーヘッドは避けられません。

ab(Apache Bench)で Nginx コンテナを簡単にベンチマークした結果:

  • コンテナ IP に直接アクセス(ポートマッピングなし):約 50,000 リクエスト/秒
  • ポートマッピング経由でアクセス:約 45,000 リクエスト/秒

約 10% の差です。多くのアプリでは許容範囲ですが、パフォーマンスに極端に敏感なサービス(高頻度トレーディング、ゲームサーバーなど)では最適化を検討する価値があります。

最適化案 1:host ネットワークモードを使う

先ほど触れた --network host モードでは、コンテナがホストのネットワークスタックを直接使うため、ポートマッピングのオーバーヘッドがありません。

docker run -d --network host nginx

パフォーマンスは最良ですが、2 つの代償があります。

  1. コンテナポートとホストポートが競合する可能性がある
  2. ネットワーク分離によるセキュリティが失われる

本番環境では慎重に使ってください。

最適化案 2:userland proxy を無効化する

Docker はデフォルトで iptables と userland proxy の両方を使います。後者は Go で書かれたプロキシで、互換性は高いもののパフォーマンスは劣ります。iptables が使える Linux 環境なら、無効化できます。

/etc/docker/daemon.json を編集します。

{
  "userland-proxy": false
}

Docker を再起動します。

sudo systemctl restart docker

オーバーヘッドは多少減りますが、劇的な改善ではありません(5% 前後程度)。

最適化案 3:不要なポートマッピングを減らす

他のコンテナからだけ使うサービスは、ホストに公開する必要がありません。データベースコンテナがアプリコンテナからだけアクセスされるなら、ポートをマッピングしない方がよいです。

# ポートをマッピングせず、Docker ネットワーク内だけで通信
docker run -d --name postgres --network mynet postgres

# アプリコンテナが DB に接続(コンテナ名経由)
docker run -d --name app --network mynet -p 3000:3000 my-app

コンテナ間は Docker ネットワーク経由で通信する方が、ポートマッピングより高速です。

いつパフォーマンスを気にすべきか?

正直、多くのシーンではポートマッピングのオーバーヘッドは無視できます。本当に気にすべきは次の 3 点です。

  1. アプリ本体のボトルネック(DB クエリ、コードロジック)
  2. コンテナのリソース制限(CPU、メモリ)
  3. ディスク I/O とネットワーク帯域

ポートマッピングのオーバーヘッドは、通常は優先度が低いです。QPS が数万に達するまでは、先にアプリコードを最適化しましょう。

おわりに

記事冒頭のシナリオに戻りましょう。金曜日の夜 7 時半、プロダクトマネージャーからテスト環境のデプロイ依頼が来た。今なら次のことがわかっています。

「port already allocated」が出たら、まず docker ps -a で古いコンテナが残っていないか確認し、lsofnetstat でホストのポート占有を調べる。それでもダメならポートを変えるか、Docker に自動割り当てさせる(-p 0:80)。

マッピングしたのにアクセスできない場合は、この順番で確認します。コンテナ内サービスのリッスンアドレス → ホストのファイアウォール → クラウドのセキュリティグループ → Docker ネットワークモード。十中八九、原因はここにあります。

ポートマッピングは Docker で最も基本的でありながら、最もトラブルになりやすい部分です。原理と排查方法を理解すれば、ここに無駄な時間を使うことはなくなります。

次にポート問題に遭遇しても、慌てる必要はありません。深呼吸して、フローに沿って確認すれば、必ず解決できます。そうすれば定時で帰れて、金曜の夜を楽しめます。

この記事をブックマークしておくのもおすすめです。またポート問題に直面したとき、開けばかなりの時間を節約できます。私自身、そうしています。

Docker ポートマッピング完全排查フロー

ポート占有の排查からパフォーマンス最適化まで、Docker ポートマッピングのあらゆる悩みを体系的に解決

⏱️ 目安時間: 15 分

  1. 1

    ステップ1: ポートマッピング構文とよくあるエラーを理解する

    ポートマッピング構文:
    • -p host_port:container_port(指定ポートをマッピング、例:-p 8080:80)
    • -p container_port(ランダムマッピング、例:-p 80)
    • -P(公開されているすべてのポートをマッピング)
    • --publish-all(-P と同等)

    よくあるエラー:
    • ポートが既に使用されています(Error starting userland proxy: Bind for 0.0.0.0:8080 failed: port is already allocated)
    • コンテナが起動できず、ポート占有の排查が必要になります
  2. 2

    ステップ2: ポート占有の排查と解決策

    排查方法:
    • lsof -i :8080 でポート占有を確認
    • netstat -tuln | grep 8080 でポート状態を確認
    • docker ps でコンテナのポートマッピングを確認
    • docker port container-name でコンテナのポートを確認

    解決策:
    • ポートを占有しているプロセスを停止(kill -9 PID)
    • ポートを占有しているコンテナを停止(docker stop container-name)
    • ポートマッピングを変更(-p 8081:80)
    • 動的ポート(ホスト側ポートを指定せず、Docker に自動割り当てさせる)
  3. 3

    ステップ3: ベストプラクティスとパフォーマンス最適化

    ベストプラクティス:
    • docker-compose でポートマッピングを管理する
    • ポート範囲を設定して競合を避ける
    • 定期的にポート占有状況を確認する
    • ポートスキャンツールで利用可能なポートを確認する

    パフォーマンス最適化:
    • ポートマッピング自体のオーバーヘッドは非常に小さい
    • パフォーマンス問題が出た場合は、アプリ本体のボトルネック(DB クエリ、コードロジック)を確認する
    • コンテナのリソース制限(CPU、メモリ)
    • ディスク I/O とネットワーク帯域

    ポートマッピングは Docker で最も基本的でありながら、最もトラブルになりやすい部分です。原理と排查方法を理解すれば、ここに無駄な時間を使うことはなくなります。

FAQ

Docker ポートマッピングでよくあるエラーは?
よくあるエラー:ポートが既に使用されています(Error starting userland proxy: Bind for 0.0.0.0:8080 failed: port is already allocated)。コンテナが起動できず、ポート占有の排查が必要です。

排查方法:
• lsof -i :8080 でポート占有を確認
• netstat -tuln | grep 8080 でポート状態を確認
• docker ps でコンテナのポートマッピングを確認
• docker port container-name でコンテナのポートを確認
ポート占有問題をどう排查する?
排查方法:
• lsof -i :8080 でポート占有を確認(lsof -i :8080)
• netstat -tuln | grep 8080 でポート状態を確認
• docker ps でコンテナのポートマッピングを確認
• docker port container-name でコンテナのポートを確認

解決策:
• ポートを占有しているプロセスを停止(kill -9 PID)
• ポートを占有しているコンテナを停止(docker stop container-name)
• ポートマッピングを変更(-p 8081:80)
• 動的ポート(ホスト側ポートを指定せず、Docker に自動割り当てさせる)
Docker ポートマッピングの構文は?
ポートマッピング構文:
• -p host_port:container_port(指定ポートをマッピング、例:-p 8080:80)
• -p container_port(ランダムマッピング、例:-p 80)
• -P(公開されているすべてのポートをマッピング)
• --publish-all(-P と同等)

例:
• docker run -d -p 8080:80 nginx(8080 をコンテナの 80 番ポートにマッピング)
• docker run -d -p 80 nginx(コンテナの 80 番ポートにランダムマッピング)
• docker run -d -P nginx(公開されているすべてのポートをマッピング)
ポートマッピングのベストプラクティスは?
ベストプラクティス:
• docker-compose でポートマッピングを管理する
• ポート範囲を設定して競合を避ける
• 定期的にポート占有状況を確認する
• ポートスキャンツールで利用可能なポートを確認する

パフォーマンス最適化:
• ポートマッピング自体のオーバーヘッドは非常に小さい
• パフォーマンス問題が出た場合は、アプリ本体のボトルネック(DB クエリ、コードロジック)を確認する
• コンテナのリソース制限(CPU、メモリ)
• ディスク I/O とネットワーク帯域

ポートマッピングは Docker で最も基本的でありながら、最もトラブルになりやすい部分です。原理と排查方法を理解すれば、ここに無駄な時間を使うことはなくなります。

4分で読めます · 公開日: 2025年12月17日 · 更新日: 2026年6月8日

関連記事

コメント

GitHubアカウントでログインしてコメントできます