第13章:サービス間通信(Composeネットワーク)を理解する🕸️📡
この章でやることはシンプルです👇 「コンテナ同士は “サービス名” でつながる」 を体に入れます💪✨
1) まずは超ざっくり結論🎯
- Composeは、起動すると デフォルトのネットワークを1つ作って そこに全サービスを参加させます。
そして各サービスは サービス名(例:
db)で名前解決できる ようになります。(Docker Documentation) - サービス間通信で使うのは CONTAINER_PORT(コンテナ側ポート) です。 HOST_PORT(ホスト側ポート) は “外から入るため” のもの。(Docker Documentation)
つまり: ✅ API→DB は
db:5432✅ API→Redis はredis:6379✅ 自分のPC→API はlocalhost:xxxx(公開してるときだけ) って感じです😊
2) なぜ「サービス名」でつながるの?🧠🔍
Composeのネットワークでは、
services:に書いた キー(サービス名) が- DNS名(ホスト名)として使える
という設計になってます。(Docker Documentation)
たとえば services: db: があれば、同じネットワークの他サービスからは db で到達可能 です👍
3) ここが一番ハマる:localhost の罠🪤😇
コンテナの中で localhost と書いたら、それは…
「そのコンテナ自身」 です😵💫
だからAPIコンテナからDBに繋ぐのに localhost:5432 と書くと、
“APIコンテナの中の5432” を探しにいってコケます💥
✅ 正しくは db:5432(サービス名+コンテナ側ポート)です。(Docker Documentation)
4) いったん最小構成で手を動かす✋🐳
4-1) compose.yaml(通信の要点だけ)📄✨
ポイント:
- APIだけ外に出したいから
ports: - DB/Redisは “中だけ” なら
expose:でOK(=ホストには出さない)(Docker Documentation)
services:
api:
build: ./api
ports:
- "3000:3000"
environment:
# ✅ “db” はサービス名
DATABASE_URL: "postgres://app:app@db:5432/appdb"
# ✅ “redis” もサービス名
REDIS_URL: "redis://redis:6379"
depends_on:
- db
- redis
db:
image: postgres:18
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_DB: appdb
expose:
- "5432"
redis:
image: redis:7
expose:
- "6379"
💡 ports: は「ホスト↔コンテナのポートマッピング」です。(Docker Documentation)
💡 expose: は「ネットワーク内で見える入口(ただしホストには公開しない)」です。(Docker Documentation)
4-2) API側(TypeScript)で “ホスト名は db” を使う🧩🧑💻
// api/src/db.ts
import pg from "pg";
const { Client } = pg;
export async function connectDb() {
const url = process.env.DATABASE_URL!;
const client = new Client({ connectionString: url });
await client.connect();
const r = await client.query("select now() as now");
await client.end();
return r.rows[0].now;
}
「db ってどこ?」は、ComposeネットワークのDNSが解決してくれます。(Docker Documentation)
5) “見える化” 実験:DNSで引けてるか確認する🔎🧪
5-1) 起動
docker compose up -d
docker compose ps
5-2) APIコンテナ内から db の名前解決を試す(NodeでOK)🧠✨
docker compose exec api node -e "require('dns').lookup('db', console.log)"
docker compose exec api node -e "require('dns').lookup('redis', console.log)"
IPが返ってくれば サービス名→IP が動いてます🎉
(IPは変わることがあるけど、名前 db は変わらない のが大事!)(Docker Documentation)
6) HOST_PORT と CONTAINER_PORT を一撃で整理🧷📌
例:もしDBをこう公開したら…
db:
ports:
- "8001:5432"
- コンテナ同士(API→DB) は
db:5432(CONTAINER_PORT)(Docker Documentation) - 自分のPC→DB は
localhost:8001(HOST_PORT)
この “二重ポート” が混乱ポイントです😂
7) ちょい実戦:ネットワークを分けて「見せたくない」を作る🛡️🧱
「APIは外に見せるけど、DBは裏側だけ」ってやりたいとき、 ネットワークを front / back に分けると気持ちよく整理できます😎
Composeは複数ネットワークを定義して、サービスごとに参加ネットワークを指定できます。(Docker Documentation)
services:
api:
build: ./api
ports:
- "3000:3000"
networks: [front, back]
db:
image: postgres:18
networks: [back]
networks:
front: {}
back: {}
これでDBは front側から見えない(=分離)になります🧼✨
8) トラブルシュート集(ここだけ見れば勝てる)🧯😺
A) getaddrinfo ENOTFOUND db(dbが見つからん)
ありがち原因👇
- サービス名が違う(
dbじゃなくてpostgresだった、とか) - 同じComposeプロジェクト/同じネットワークにいない(ネットワーク分けたのに
apiがbackに入ってない等)(Docker Documentation)
B) ECONNREFUSED db:5432(見つかるけど拒否)
- DBコンテナが起動直後でまだ準備できてない(次章の “待ち合わせ” で解決が王道です⏳🩺)
C) 「PCからDBに繋がらない」
ports:を書いてない(=外には出てない)- あるいは公開してても、ホスト側は HOST_PORT を使う必要がある(例:
localhost:8001)(Docker Documentation)
D) セキュリティ的に「公開したくない…」😱
ポート公開は “外にも開く” ので注意です⚠️
必要なときだけ 127.0.0.1 バインドにするのが安全寄りです。(Docker Documentation)
例:
ports:
- "127.0.0.1:8001:5432"
9) AIに頼るときの「良い聞き方」🤖🧠
コピペ用プロンプト👇(短くて効きます✨)
- 「Composeで
apiからdb(Postgres) に繋ぐ。サービス名で接続する前提でDATABASE_URLを作って」 - 「
portsとexposeの使い分けを、ホスト接続とサービス間通信の観点で説明して。設定例も出して」
10) この章のまとめ✅🎉
- Composeは デフォルトネットワーク を作り、サービスは 名前で発見できる(Docker Documentation)
- サービス間通信は サービス名 + CONTAINER_PORT(例:
db:5432)(Docker Documentation) - 外から入るのは
ports、内だけならexposeがスッキリ(Docker Documentation) localhostの意味が “場所によって変わる” のが最大の罠🪤😇
次の第14章(待ち合わせ:healthcheck / depends_on)に入ると、 「DBが起きる前にAPIが落ちる😵」がかなり減って、体験が一段よくなりますよ〜⏳🩺✨