第55章:ポート系トラブル(見えない/繋がらない)🚪❌
この章は、「ブラウザで見えない😵」「別サービスから繋がらない😵💫」みたいな “ポート周りの詰まり” を、最短ルートで切り分けて直す 練習をします💪✨ (ログは第52章でやったので、ここは “ネットワークのどこで止まってるか” に全集中!🔥)
0) まず結論:ポートが見えるまでの“道”を分解する🧩🗺️
ポート問題は、だいたいこの4段のどこかで詰まってます👇
- アプリが起動してる?(プロセス)🏃♂️
- アプリはどこで待ち受けしてる?(listen先)👂
- コンテナ→ホストに公開できてる?(publish / ports)📣
- アクセス先が合ってる?(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.yml の ports: が怪しい👀
✅③ 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 🏆😵💫
- アプリが
localhostでしかlistenしてない(最頻出)🥇 ports:が無い / 間違ってる(公開してない)🥈- ホスト側ポート被り(別アプリが使用中)🥉
- アクセス先の勘違い(
localhost/127.0.0.1/ 別PCのIP)🎯 - Windows Firewall / セキュリティソフトがブロック🛡️
- LANからアクセスしたいのに、Docker Desktop+WSL2の都合で届かない📡
- 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)
まず試す順番(安全ルート)✅
- 自分のPC内で
localhostで見えるか(ここが土台) portsでhost_ipを縛っていないか確認- Windows Firewallで受信が落ちてないか確認🛡️
- それでも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分セルフテスト⏱️📝
-
コンテナ内で
curl 127.0.0.1:3000は通るのに、ホストから通らない。まず疑うのは? → listen先(localhost待ち) or ports公開 🚪❌ -
ports: "3001:3000"のとき、ブラウザで開くのは? →localhost:3001🎯
次の第56章は「依存関係トラブル(モジュールが無い/バージョン違い)」📦💥で、また別の“詰まりの王”を潰しにいきます😆🔥