0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Docker Composeで複数コンテナを組む基本

0
Last updated at Posted at 2026-10-06

はじめに

Webアプリとデータベースを Docker Compose で動かすときに押さえておきたい3つのテーマを整理しました。

  1. 起動順と待ち合わせ(depends_on / healthcheck)
  2. 設定の外出し(.env と env_file)
  3. ネットワークとボリューム

題材は、店舗の注文を管理するアプリです。コンテナは db(PostgreSQL)、web(Web API)、worker(定期ジョブ)、migrate(テーブルを作って終了する初期化処理)の4つです。

第1章 起動順と待ち合わせ

depends_on は「コンテナの起動」しか待たない

docker compose up -d
  │
  ├─ db を起動 ───────────────────────────────┐
  │    healthcheck: pg_isready を5秒ごとに実行  │ 状態: starting
  │                                           ▼ 状態: healthy(接続を受け付けられる)
  │
  ├─ web / worker      depends_on: - db
  │    → db コンテナが「起動した」ことだけを待つ
  │
  └─ migrate           depends_on: db: condition: service_healthy
       → db が healthy になるまで待ってから起動する

depends_on: - db は起動順の指定で、中のPostgreSQLが接続を受け付けられるかまでは見ません。接続できるまで待たせたいときは、db に healthcheck を付け、待つ側に condition: service_healthy を書きます。

healthcheck:
  test: ["CMD", "pg_isready", "-U", "postgres"]
  interval: 5s        # 5秒ごとに確かめる
  timeout: 3s         # 1回の確認が3秒で終わらなければ失敗と数える
  retries: 12         # 12回続けて失敗したら unhealthy と判定する
  start_period: 90s   # 起動から90秒間は、失敗しても回数に数えない

retries は回数です。start_period が無いと、約60秒(5秒 × 12回)で起動しなければ unhealthy になり、待っていたコンテナは起動されません。start_period の間に成功すればその時点で healthy になるので、長めに取っても起動は遅くなりません。

healthy 待ちが必要なのは「1回で終わる処理」だけ

depends_on が効くのは起動時だけで、運用中にDBが止まった場合は何もしません。そのため、常駐プロセスはどのみち「接続に失敗したらログを残し、次の処理でもう一度試す」作りにしておく必要があります。そう作れば起動直後の接続失敗にも同じ処理で対応できるので、healthy 待ちは要りません。

接続に失敗したら healthy 待ち
web・worker(常駐) 次の処理でもう一度試す 不要
migrate(1回で終了) 次がない。失敗がそのまま結果になる 必要

第2章 設定の外出し

環境変数は2段階で使われる

docker compose up -d
  │
  │ 【段階1】Compose が docker-compose.yml を組み立てる
  │   .env を自動で読み、${...} の部分を値で置き換える
  │
  │ 【段階2】コンテナを起動し、環境変数を渡す
  │   env_file に並べたファイルの中身を、コンテナの環境変数にする
  ▼
 コンテナの中: os.environ["DATABASE_URL"] で読める
ファイル 誰が読むか 何に使うか
.env Compose(段階1) ${...} の置き換え
.env.development など コンテナ(段階2) アプリの設定
.env.example 人間 変数の一覧(ダミー値)。Gitで管理するのはこれだけ

${...} の書き方

${ENVIRONMENT}                  # そのまま置き換える。未設定なら空文字
${ENVIRONMENT:-development}     # 未設定なら development を使う
${POSTGRES_PASSWORD:?メッセージ}  # 未設定なら、メッセージを出して止める

パスワードには :? を使います。:- で既定値を置くと、その値が docker-compose.yml ごとGitに入り、設定し忘れても起動してしまいます。

環境の切り替えを1か所にまとめる

env_file:
  - .env                                  # 共通の設定(先に読む)
  - .env.${ENVIRONMENT:-development}      # 環境ごとの設定(後に読む → 同じ名前ならこちらが勝つ)

.env の ENVIRONMENT を1行変えるだけで、開発と本番を切り替えられます。

第3章 ネットワークとボリューム

【コンテナ → コンテナ】ports は不要
  web ── "db:5432" ──▶ Docker内のDNS(サービス名をIPに変換)──▶ db

【ホスト → コンテナ】ports が必要
  ホスト ── 127.0.0.1:5432 ──▶ ports: "127.0.0.1:5432:5432" ──▶ db:5432

【データの保存先】volumes
  db  /var/lib/postgresql/data ──▶ 名前付きボリューム(Dockerが管理する置き場)
  web /workspace               ──▶ ホストのプロジェクトフォルダ(バインドマウント)

接続先はサービス名で書く

同じネットワークにいるコンテナには、サービス名で接続できます。コンテナの中の localhost はそのコンテナ自身を指すため、DBの接続先は localhost ではなく db です。IPアドレスはDockerが自動で割り振り、作り直すと変わるので、直接書きません。

$ docker compose exec web pg_isready -h db -p 5432
db:5432 - accepting connections

$ docker compose exec web pg_isready -h localhost -p 5432
localhost:5432 - no response

ports はホストから入るための入口

ports はホストからコンテナへ入るための設定で、コンテナどうしの通信には使いません。"127.0.0.1:5432:5432" と書くと、ホスト自身からだけ接続を受け付け、LAN側からは接続できません。

名前付きボリュームとバインドマウント

名前付きボリューム バインドマウント
書き方の例 pgdata:/var/lib/postgresql/data .:/workspace
実体の場所 Dockerが管理する置き場 ホストの指定したフォルダそのもの
向いているもの DBのデータ(プログラムだけが触る) ソースコード(人が編集し、Gitで管理する)
消える条件 docker compose down -v ホストでフォルダを消したとき

バインドマウントしたソースを編集すると、コンテナの中のファイルもすぐに変わります。ただし、動いているプロセスは古いコードを読み込んだままなので、再起動が必要です。

完成形

はじめに挙げた4つのコンテナで組んだ例です。web・worker・migrate は同じイメージを使い、command と待ち方だけを変えています。

docker-compose.yml
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: shop
      POSTGRES_DB: shop
      POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?POSTGRES_PASSWORD を .env に設定してください}"
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "shop", "-d", "shop"]
      interval: 5s
      timeout: 3s
      retries: 12
      start_period: 90s
    volumes:
      - pgdata:/var/lib/postgresql/data
    ports:
      - "127.0.0.1:5432:5432"
    restart: unless-stopped
    networks:
      - backend-net

  web:
    build: .
    working_dir: /workspace
    command: ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
    env_file:
      - .env.${ENVIRONMENT:-development}
    depends_on:
      - db                          # 常駐するので起動順だけ指定する
    volumes:
      - .:/workspace
    ports:
      - "127.0.0.1:8000:8000"
    restart: unless-stopped
    networks:
      - backend-net

  worker:
    build: .
    working_dir: /workspace
    command: ["python", "-m", "app.worker"]
    env_file:
      - .env.${ENVIRONMENT:-development}
    depends_on:
      - db                          # 常駐するので起動順だけ指定する
    volumes:
      - .:/workspace
    restart: unless-stopped
    networks:
      - backend-net

  migrate:
    build: .
    working_dir: /workspace
    command: ["python", "-m", "app.migrate"]
    env_file:
      - .env.${ENVIRONMENT:-development}
    depends_on:
      db:
        condition: service_healthy  # 1回で終わるので、DBが接続を受け付けるまで待つ
    volumes:
      - .:/workspace
    restart: "no"                   # 終了したら再起動しない
    networks:
      - backend-net

networks:
  backend-net:

volumes:
  pgdata:
.env(Gitに入れない)
ENVIRONMENT=development
POSTGRES_PASSWORD=change-me
.env.development(Gitに入れない)
DATABASE_URL=postgresql://shop:change-me@db:5432/shop

付録 よく使うコマンド

コマンド 何をするか
docker compose up -d 全サービスをバックグラウンドで起動する
docker compose restart web 指定したサービスだけ再起動する
docker compose down コンテナとネットワークを削除する。ボリュームは残る
docker compose down -v ボリュームも削除する。DBの中身が消える
docker compose ps コンテナの状態(healthy など)を一覧する
docker compose logs -f --tail 50 web 末尾50行からログを流し続ける
docker compose exec web bash コンテナに入ってシェルを開く
docker compose exec db psql -U shop -d shop DBに入ってSQLを打つ
docker compose config -q 書式が正しいかだけを確かめる
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?