Skip to main content

第24章:ポート競合を永久に起こさないコツ📛🔒

この章はズバリ、「ポート番号のケンカ(3000取り合い問題😇)」を構造で根絶する回です! 結論から言うと、勝ちパターンはこれ👇

  • 外に出すポートは“入口”だけ(基本 80/443) 🚪✨
  • 中のサービス(DB/Redis/内部APIなど)は外に出さない 🧊
  • どうしてもホストから触りたい時は“例外ルール”で安全に 🧯

この章のゴール🎯

  • 「このポートもう使われてる😡」を二度と起こさない
  • DBやRedisをうっかり外に晒さない(地味に超重要)🧨
  • 複数プロジェクトを同時に立ち上げても、ポートが衝突しない🥳

1) まず“ポート競合”の正体を1分で理解🧠💡

Dockerでよくある地獄はこれ👇

  • Aプロジェクトが localhost:3000 を使う
  • Bプロジェクトも localhost:3000 を使う
  • すると どっちかが起動できない(先に掴んだ者勝ち)⚔️

ここで大事なのは:

  • 衝突するのは「ホスト側のポート」(Windowsの localhost:3000 の方)
  • コンテナの中は別世界なので、コンテナ内の3000同士は衝突しない 🌍

2) 永久解決の“設計ルール”はこれだけ✅✨

ルールA:ports: を書くのは「入口」だけ🚪

**リバースプロキシ(Caddy/Traefik/Nginx)**だけが 80/443 を握る。 それ以外は、原則 ports: を消す!✂️

なお、ポート公開(publish)は「やると危険が増える」前提で注意書きが公式にもあります。ホストIPを 127.0.0.1 に縛ると「自分のPCからだけアクセス」にできます。(Docker Documentation)

ルールB:DB/Redis/Queueは “外に出さない” が標準🧊🧱

  • DBを外に出すと、セキュリティ的に事故りやすい 🔥
  • しかもポート競合の温床(5432/6379など)😇

ルールC:例外は「ローカル限定 + 一時的」にする🧯

  • 127.0.0.1 に縛って公開(外から来れない)
  • もしくは ランダムポートに逃がす(競合しづらい)🎲

3) 入口だけ公開の完成イメージ図🗺️✨

[ブラウザ] 
↓ http://app1.localhost / http://api.localhost
[リバースプロキシ] ←←(ここだけ 80/443 を公開)

[app1] [api] [admin] [db] [redis]
(ここから下は “外に出さない世界”)
  • 外に見せるのは「入口の80/443」だけ

  • 中は サービス名で通信(ComposeのネットワークDNS)🌐

    • Composeは同一ネットワーク上で、サービス名で到達できるのが基本です。(Docker Documentation)

4) “ダメな例” → “勝ち構成”に直す🛠️✨

❌ ダメな例:全部 ports: で外に出す(競合・事故の元)

services:
web:
build: ./web
ports:
- "3000:3000"

api:
build: ./api
ports:
- "8787:8787"

db:
image: postgres:16
ports:
- "5432:5432" # ← これが事故りやすい😇

✅ 勝ち構成:外に出すのは proxy の 80/443 だけ

services:
proxy:
image: caddy:2
ports:
- "80:80"
- "443:443"
networks:
- edge
- internal

web:
build: ./web
networks:
- internal
# ports: は書かない!

api:
build: ./api
networks:
- internal
# ports: は書かない!

db:
image: postgres:16
networks:
- internal
# ports: は絶対に書かない(基本)🧊

networks:
edge:
internal:

ポイント🌟

  • proxyだけが外界と接してる
  • web/api/db は “internal ネットワーク”に閉じ込める🔐
  • これで プロジェクトが何個増えてもポート競合しにくい 🥳

5) 「でもDBをGUIツールから触りたい…」問題の解き方🍪🧯

ここ、めちゃくちゃ現実的に困りますよね😅 解決は3パターンあります👇(おすすめ順)


パターン①:ホストに出さずに exec で触る(最強)💪🐳

docker compose exec db psql -U postgres
  • ポート競合ゼロ
  • 外部公開ゼロ
  • 慣れるとこれが一番ラク😋

パターン②:公開するけど “127.0.0.1 限定” に縛る🔒

db:
image: postgres:16
ports:
- "127.0.0.1:5432:5432"
  • 127.0.0.1 に縛ると、自分のPCからだけアクセス (「公開は危険」「localhost縛りで限定可能」は公式の説明あり)(Docker Documentation)

⚠️ただし:

  • 5432は他プロジェクトとも被りやすいので、競合はまだ起こり得る 😇

パターン③:公開するけど “ランダムポート” にして逃がす🎲✨

ホスト側ポートを空欄にすると、Dockerが空いてるポートを割り当てます(競合回避に強い)🕊️ EXPOSE を自動でランダム公開する考え方も公式で紹介されています。(Docker Documentation)

Compose例(短縮形)👇

db:
image: postgres:16
ports:
- "127.0.0.1::5432" # ← ホスト側が空欄=自動割り当て

割り当て確認👇

docker compose port db 5432
  • これで ポート競合しにくい 🙌
  • ただし毎回変わるので、ツール設定は“都度確認”になる 🥹

6) “長い構文”でキレイに書く(読みやすさUP)📚✨

「127.0.0.1 に縛る」「published/target を明示」みたいな時は、長い構文が便利です👇 Compose仕様にも host_ip / published / target が載っています。(GitHub)

db:
image: postgres:16
ports:
- name: postgres-local
target: 5432
published: "15432"
host_ip: 127.0.0.1
protocol: tcp
mode: host

7) よくあるミス集(即死回避)🧯📕

ミス1:DBを 0.0.0.0:5432 で公開してた😱

  • “自分だけのつもり”でも、環境次第で外から届くことがあります
  • まずは 127.0.0.1縛り、できれば 公開しない が基本🧊 (公開は危険、localhost縛りで限定可能:公式)(Docker Documentation)

ミス2:expose: を書いたからホストから入れると思った🤔

  • expose:コンテナ間で見せるだけ(ホストには公開しない)という位置づけで語られます(Stack Overflow)
  • ただし実運用では「同一ネットワークなら普通にサービス名で到達できる」ので、expose: は“説明用”になることも多いです(なくても動く場面が多い)🌿

ミス3:複数Composeを起動したら名前解決が混乱した🌀

  • Composeのネットワーク名は「プロジェクト名」由来になります(ディレクトリ名等)(Docker Documentation)
  • 入口を共通化して複数プロジェクトを共存させるなら、前章でやった external network 設計が効いてきます🧵✨

8) AIに投げる例(コピペで使える)🤖💬

例1:ポート公開を“入口だけ”に整理してもらう

次のdocker composeを「proxyだけが80/443をportsで公開」し、
web/api/db/redisは外に出さない構成に書き換えて。
内部通信は同一networkでサービス名解決する前提で。
あわせて、dbをホストから触りたい時のために
(1) execで入る方法 (2) 127.0.0.1縛り (3) ランダムポート
の3案も提示して。

例2:いま起きてる競合の原因特定

Windowsで docker compose up したら
"bind: address already in use" が出た。
原因になりやすい ports 設定の見方と、
衝突しない設計(入口だけ公開)に直す手順を説明して。

9) ミニ課題🎓✨(15分で“永久解決”が体に入る)

課題A:DBの ports: を消してもアプリが動くのを確認🧪

  1. db:ports: を削除
  2. docker compose up -d
  3. API→DB接続が生きてるか確認(アプリ動作でOK)

課題B:DBに入りたい時は exec で入る🐳

docker compose exec db psql -U postgres

課題C:どうしてもGUIで触りたい場合は“例外ルール”を追加🔒

  • 127.0.0.1:15432:5432 にして、固定だけど安全寄りにする
  • もしくは 127.0.0.1::5432ランダム割り当てにして競合回避🎲

まとめ:この章の“合言葉”📣✨

  • portsは入口だけ! 🚪
  • DB/Redisは外に出さない! 🧊
  • 例外は 127.0.0.1 縛り or ランダムポート! 🔒🎲

次の章(ローカルHTTPS)に進むと、さらに「本番っぽい入口運用」に近づいて気持ちよくなります😆🔐