メインコンテンツまでスキップ

第55章:ポート系トラブル(見えない/繋がらない)🚪❌

この章は、「ブラウザで見えない😵」「別サービスから繋がらない😵‍💫」みたいな “ポート周りの詰まり” を、最短ルートで切り分けて直す 練習をします💪✨ (ログは第52章でやったので、ここは “ネットワークのどこで止まってるか” に全集中!🔥)


0) まず結論:ポートが見えるまでの“道”を分解する🧩🗺️

ポート問題は、だいたいこの4段のどこかで詰まってます👇

  1. アプリが起動してる?(プロセス)🏃‍♂️
  2. アプリはどこで待ち受けしてる?(listen先)👂
  3. コンテナ→ホストに公開できてる?(publish / ports)📣
  4. アクセス先が合ってる?(localhost / host IP / 別PC)🎯

イメージ👇

[ブラウザ] ──(Windowsのlocalhost:3000)──> [Dockerの公開ポート] ──> [コンテナ:3000] ──> [Nodeアプリ]
▲ ▲ ▲
③ publish ④ 宛先 ② listen先

Dockerのポート公開(publish)の概念は公式ドキュメントでも整理されています。(Docker Documentation)


1) 最速で切り分ける「5点セット」✅⚡

トラブル時、順番を固定すると強いです😎 (上から順に潰すだけ)

✅① コンテナは生きてる?(落ちてない?)

docker compose ps
  • Up ならOK👌
  • Exited / Restarting なら、まず第53章(終了コード)に戻るのが近道🔁😵

✅② ポート公開できてる?(ホスト側の入口がある?)

docker compose port api 3000

ここで 0.0.0.0:3000 とか 127.0.0.1:3000 が出たら「公開自体はある」🙆‍♂️ 何も出ないなら、compose.ymlports: が怪しい👀


✅③ Windows側でポート被ってない?(入口が塞がれてない?)

PowerShellでOK👇

netstat -ano | findstr :3000
  • 何か出る=誰かが3000を使ってる 可能性大🔥
  • そのPIDを見て犯人特定👇
tasklist /FI "PID eq 12345"

✅④ コンテナ内では繋がる?(アプリは動いてる?)

docker compose exec api sh
## alpine系なら: ash

中で👇

## 待ち受け確認(どのIPでlistenしてるかが重要!)
ss -lntp | grep 3000 || netstat -lntp | grep 3000

## 自分自身へ疎通(まずは127.0.0.1)
curl -i http://127.0.0.1:3000/health
  • コンテナ内で curl が通るなら、アプリは生きてる🙆‍♀️
  • 通らないなら、アプリ側の起動・PORT指定が怪しい🧨

✅⑤ アプリは 0.0.0.0 で待ってる?(超重要)📌

Dockerで外から見せたいサーバは、基本 0.0.0.0 でlistenが安全です🛟 localhost(127.0.0.1)で待つと、“コンテナの外” から入れない 事故が起きがち😇(典型)(Medium)


2) よくある原因ランキングTOP7 🏆😵‍💫

  1. アプリが localhost でしかlistenしてない(最頻出)🥇
  2. ports: が無い / 間違ってる(公開してない)🥈
  3. ホスト側ポート被り(別アプリが使用中)🥉
  4. アクセス先の勘違い(localhost / 127.0.0.1 / 別PCのIP)🎯
  5. Windows Firewall / セキュリティソフトがブロック🛡️
  6. LANからアクセスしたいのに、Docker Desktop+WSL2の都合で届かない📡
  7. IPv6/localhost解決が絡んで混乱(まれ)🌀

3) ハンズオン①:わざと「見えない」を作って直す😈🔧

Todo API(例)に /health がある前提で進めます🌱

3-1) わざと壊す(localhost待ち受け)🧨

src/server.ts のlistenをこうしてみてください👇

import express from "express";

const app = express();
const port = Number(process.env.PORT ?? 3000);

app.get("/health", (_req, res) => res.json({ ok: true }));

// ❌ わざと:localhost(127.0.0.1)で待つ
app.listen(port, "127.0.0.1", () => {
console.log(`listening on 127.0.0.1:${port}`);
});

compose.yml は普通に👇(例)

services:
api:
build: .
ports:
- "3000:3000"
environment:
- PORT=3000

起動👇

docker compose up --build

で、Windowsブラウザから👇

  • http://localhost:3000/health見えない(または接続できない)😵

3-2) 切り分け(“中では通る”を確認)🔍

docker compose exec api sh
curl -i http://127.0.0.1:3000/health
ss -lntp | grep 3000 || netstat -lntp | grep 3000

ここで、待ち受けがだいたいこう見えます👇

  • 127.0.0.1:3000 でLISTEN 👂

つまり… コンテナの外から入ってきた通信が、アプリまで届かない って状態です🚪❌


3-3) 直す(0.0.0.0待ち受け)✅🎉

こう直す👇

app.listen(port, "0.0.0.0", () => {
console.log(`listening on 0.0.0.0:${port}`);
});

再ビルド&起動👇

docker compose up --build

今度は👇

  • http://localhost:3000/health → 見える!👀✨

4) ハンズオン②:ポートマッピング間違い & ポート被りを直す🧯

4-1) 間違い例:ホスト3000 → コンテナ3001(ズレ)😇

ports:
- "3000:3001" # ❌

これだと ホストは3000 だけど、コンテナ側は3001 に投げるのでズレます🎯💥

確認👇

docker compose port api 3000
docker compose port api 3001

直す👇

ports:
- "3000:3000" # ✅

4-2) ポート被り:3000がすでに使われてる🔥

エラー例:bind: address already in use みたいなやつ😵

PowerShell👇

netstat -ano | findstr :3000
tasklist /FI "PID eq 12345"

対処は2択👇

  • A:犯人を止める(別のNode/別のDocker/VS Codeの古いデバッグなど)
  • B:ホスト側だけずらす(開発ではこれが楽)
ports:
- "3001:3000" # ✅ ホスト3001で開く

ブラウザは http://localhost:3001/health に変更🎯


5) Composeの ports: は「短い書き方」と「長い書き方」がある🧾✨

長い書き方は、target/published/host_ip/protocol など細かく指定できます。(Docker ドキュメント)

例:ローカルPCだけから触れればOKなら(外に晒さない)こう👇

ports:
- target: 3000
published: 3000
host_ip: 127.0.0.1
protocol: tcp
  • host_ip: 127.0.0.1 にすると、基本 自分のPC内だけで完結しやすい🛡️
  • 逆に、LANからも触りたいなら host_ip を外す(= 0.0.0.0扱い)方向が多いです📡(ただし次の注意あり)

6) 「別PC/スマホ(LAN)からも見たい!」時の注意📱🖥️📡

ここ、2025〜2026あたりで混乱が増えがちです😵‍💫 Docker Desktopは コンテナ・VM・ホスト間の通信を独自にルーティングしていて、見え方が環境で変わります。(Docker Documentation)

さらに、Microsoft のWSL2にはネットワークモード(NAT/ミラーなど)の話があり、ミラーモードでは localhost で相互接続できる旨が説明されています。(Microsoft Learn) 一方で、「Docker Desktop側がそのまま期待通りにLAN公開できるか」は別問題になりやすく、アップデート後に “localhostはOKだけどホストIP/LAN IPはダメ” という報告もあります。(GitHub)

まず試す順番(安全ルート)✅

  1. 自分のPC内で localhost で見えるか(ここが土台)
  2. portshost_ip を縛っていないか確認
  3. Windows Firewallで受信が落ちてないか確認🛡️
  4. それでもLANがダメなら、“WSL2/Docker Desktopのネットワーク都合” を疑う(この段階で深追いしすぎないのがコツ)🧠

LANテストが必要なら、開発では トンネル系(Dev Tunnels / ngrok系) や、クラウドの検証環境を使うほうが早いことも多いです🚀 (ここは沼りやすいので、必要になったら“目的(誰がどこからアクセス?)”で最短手段を選ぶのがおすすめ👍)


7) コンテナ→ホストへ繋ぎたい時の定番:host.docker.internal 🏠🔁

「コンテナから、ホストで動いてるAPI/DBにアクセスしたい」時は、Docker Desktopでは host.docker.internal がよく使われます(開発用途)。(Docker ドキュメント) ※本番環境で常に使えるものではない点も明記されています。(Docker ドキュメント)


8) AIに投げると強い「質問テンプレ」🤖🧠✨

GitHub Copilot でも、OpenAI 系のAI拡張でも、この形で投げると当たりやすいです🎯

テンプレ①:どこで詰まってるか判定してもらう🔍

Dockerのポートが見えません。どの層が怪しいか、順番に切り分け手順を出して。

- 目的: Windowsブラウザから http://localhost:3000 にアクセスしたい
- docker compose ps:
(貼る)
- docker compose port api 3000:
(貼る)
- Windows: netstat -ano | findstr :3000 の結果:
(貼る)
- コンテナ内: ss -lntp | grep 3000 の結果:
(貼る)
- curl http://127.0.0.1:3000/health の結果:
(貼る)

テンプレ②:compose.yml の ports を長い書き方に直す🧾

このports設定を long syntax に変換して。ローカルだけ(127.0.0.1)公開にしたい。
(現状のcompose.ymlを貼る)

9) ミニまとめ(この章のゴール)🏁🎉

  • ポート問題は 4段(起動 / listen / publish / 宛先) に分解すると勝てる🧩
  • コンテナ内でcurlが通るのに外で見えない → だいたい 0.0.0.0 待ち受けか ports が犯人👂📣
  • Windowsは ポート被り が多いので netstat の習慣が超効く🪟🔧
  • LAN公開は沼りやすいので、目的次第でトンネル/クラウド検証も視野📡🚀

おまけ:1分セルフテスト⏱️📝

  1. コンテナ内で curl 127.0.0.1:3000 は通るのに、ホストから通らない。まず疑うのは? → listen先(localhost待ち) or ports公開 🚪❌

  2. ports: "3001:3000" のとき、ブラウザで開くのは? → localhost:3001 🎯


次の第56章は「依存関係トラブル(モジュールが無い/バージョン違い)」📦💥で、また別の“詰まりの王”を潰しにいきます😆🔥