Docker Compose

この講座では、Docker Composeを使った複数コンテナの管理について学びます。

  • Docker Composeの概要とメリット
  • compose.yamlファイルの書き方(サービス・ポート・環境変数・ボリューム)
  • サービスの起動順序とヘルスチェック
  • ネットワークの設定
  • docker compose up / downによる一括管理

1. Docker Composeとは

1.1 Docker Composeの概要

Docker Composeは、複数のコンテナで構成されるアプリケーションを、1つの設定ファイル(compose.yaml)で定義し、まとめて管理するためのツールです。

前の講座のハンズオンでは、3つのコンテナを起動するために、それぞれ長いコマンドを実行する必要がありました。Docker Composeを使用すると、これらの設定をYAMLファイルに記述し、1つのコマンドで全てのコンテナを起動できます。

1.2 Docker Composeのメリット

Docker Composeを使用することで、以下のメリットが得られます。

設定の可視化と共有

コンテナの構成がcompose.yamlファイルに明文化されるため、どのようなコンテナがどのような設定で動いているかが一目でわかります。このファイルをチームで共有することで、全員が同じ環境を再現できます。

簡単な起動・停止

複数のコンテナをdocker compose upコマンドで一括起動、docker compose downで一括停止できます。個別にコマンドを実行する必要がなくなり、操作ミスも減少します。

依存関係の管理

コンテナ間の起動順序や依存関係を定義できます。例えば、「データベースが起動してからアプリケーションを起動する」といった順序制御が可能です。

環境の再現性

設定ファイルをバージョン管理することで、いつでも同じ環境を再現できます。開発環境、テスト環境、ステージング環境などで、一貫した構成を維持できます。

2. compose.yamlの基本構文

Docker Composeの設定は、YAML形式で記述します。ファイル名はcompose.yaml(またはdocker-compose.yml)を使用します。

💡 ポイント
Docker Compose V2(現在の標準)ではcompose.yamlが推奨ファイル名です。インターネット上の古いチュートリアルではdocker-compose.ymlが使われていることがありますが、どちらでも動作します。この講座では推奨されるcompose.yamlを使用します。Docker公式のHow Compose worksにも「The default path for a Compose file is compose.yaml (preferred) or compose.yml that is placed in the working directory.」と明記されています。

ここでは、シンプルな構成から段階的に機能を追加していく形で、compose.yamlの書き方を学んでいきます。

2.1 シンプルなサービスの定義

まずは、1つのコンテナだけを起動する最もシンプルな例から見ていきましょう。

services:
  web:
    image: nginx:alpine

この設定ファイルは、以下のdocker container runコマンドと同等の動作をします。

docker container run nginx:alpine

servicesセクションは、Docker Composeで起動するコンテナを定義する場所です。webはサービス名で、任意の名前を付けることができます。imageは、コンテナのベースとなるDockerイメージを指定します。

Dockerfileからイメージをビルドする場合は、imageの代わりにbuildを使用します。

services:
  api:
    build: ./api

この設定は、以下のDockerコマンドを順番に実行するのと同等です。

docker image build -t api-image ./api
docker container run api-image

./apiディレクトリにあるDockerfileを使用してイメージをビルドし、そのイメージからコンテナを起動します。docker compose upを実行すると、これらの処理が自動的に行われます。

2.2 複数のサービスを定義する

Docker Composeの真価は、複数のコンテナを同時に管理できることにあります。servicesセクションに複数のサービスを記述することで、複数のコンテナを一括で起動できます。

services:
  db:
    image: mysql:8.0

  api:
    build: ./api

  web:
    image: nginx:alpine

この設定では、3つのコンテナ(データベース、API、Webサーバ)が定義されています。docker compose upコマンドを1回実行するだけで、3つのコンテナがすべて起動します。

Dockerコマンドで同じことを行う場合、以下の複数のコマンドを実行する必要があります。

docker image build -t api-image ./api
docker container run -d -e MYSQL_ROOT_PASSWORD=rootpassword mysql:8.0
docker container run -d api-image
docker container run -d nginx:alpine

Docker Composeを使うことで、これらのコマンドを1つのdocker compose upで置き換えられます。

💡 ポイント
Docker Composeは、同じプロジェクト内のサービス間で自動的にネットワークを作成し、サービス名でお互いに通信できるようにします。例えば、apiサービスからdbサービスへは、ホスト名dbで接続できます。

2.3 ポート・環境変数・ボリュームの設定

実際のアプリケーションでは、ポートの公開や環境変数の設定、データの永続化が必要です。これらの設定は、各サービスの下に追加していきます。

ports(ポートマッピング)

portsは、ホストマシンのポートとコンテナのポートをマッピングします。これにより、ホストマシンからコンテナ内のアプリケーションにアクセスできるようになります。

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"

この設定は、以下のDockerコマンドと同等です。

docker container run -p 8080:80 nginx:alpine

ホストの8080番ポートにアクセスすると、コンテナの80番ポートに転送されます。

複数のポートをマッピングすることもできます。

    ports:
      - "8080:80"
      - "8443:443"

environment(環境変数)

environmentは、コンテナに環境変数を渡します。データベースの接続情報やアプリケーションの設定値など、外部から注入したい値を指定します。

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: appdb
      MYSQL_USER: appuser
      MYSQL_PASSWORD: apppassword

この設定は、以下のDockerコマンドと同等です。

docker container run \
  -e MYSQL_ROOT_PASSWORD=rootpassword \
  -e MYSQL_DATABASE=appdb \
  -e MYSQL_USER=appuser \
  -e MYSQL_PASSWORD=apppassword \
  mysql:8.0

環境変数は、リスト形式で記述することもできます。

    environment:
      - MYSQL_ROOT_PASSWORD=rootpassword
      - MYSQL_DATABASE=appdb

どちらの形式でも動作は同じですが、キーと値を明確に分離できるマップ形式(最初の例)が読みやすいでしょう。

volumes(ボリューム・バインドマウント)

volumesは、データの永続化やホストとのファイル共有を設定します。

名前付きボリュームは、Dockerが管理する永続的なデータ領域です。データベースのデータなど、コンテナを削除しても残しておきたいデータに使用します。

services:
  db:
    image: mysql:8.0
    volumes:
      - db-data:/var/lib/mysql

volumes:
  db-data:

この設定は、以下のDockerコマンドと同等です。

docker volume create db-data
docker container run -v db-data:/var/lib/mysql mysql:8.0

サービスのvolumesで使用する名前付きボリュームは、ファイル末尾のvolumesセクション(トップレベル)で宣言する必要があります。Docker Composeは、トップレベルで宣言されたボリュームを自動的に作成します。

バインドマウントは、ホストマシンのディレクトリをコンテナにマウントします。開発時にソースコードを即座に反映させたい場合などに使用します。

services:
  web:
    image: nginx:alpine
    volumes:
      - ./html:/usr/share/nginx/html

この設定は、以下のDockerコマンドと同等です。

Macの場合:

docker container run -v $(pwd)/html:/usr/share/nginx/html nginx:alpine

Windowsの場合(PowerShell):

docker container run -v ${PWD}/html:/usr/share/nginx/html nginx:alpine

ホストの./htmlディレクトリがコンテナの/usr/share/nginx/htmlにマウントされます。ホスト側でファイルを編集すると、コンテナ内にも即座に反映されます。

2.4 起動順序の制御

複数のコンテナが依存関係を持つ場合、起動順序を制御する必要があります。例えば、APIサーバはデータベースが起動してから起動する必要がある場合などです。

depends_on(基本的な起動順序)

depends_onは、サービス間の依存関係を定義し、起動順序を制御します。

services:
  api:
    build: ./api
    depends_on:
      - db

  db:
    image: mysql:8.0

この設定では、dbサービスが先に起動し、その後にapiサービスが起動します。docker compose upを実行すると、Docker Composeが自動的にこの順序を守ってコンテナを起動します。

Dockerコマンドで同じことを行う場合、手動で正しい順序でコンテナを起動し、さらに同じネットワークに接続する必要があります。

# ネットワークを作成
docker network create myapp

# 1. まずDBコンテナを起動(ネットワークに接続)
docker container run -d --name db --network myapp -e MYSQL_ROOT_PASSWORD=rootpassword mysql:8.0

# 2. 次にAPIコンテナを起動(同じネットワークに接続)
docker image build -t api ./api
docker container run -d --name api --network myapp api

Docker Composeのdepends_onを使えば、この順序とネットワーク接続を自動的に管理できます。

💡 ポイント
depends_onは「コンテナの起動順序」を制御するだけで、「アプリケーションの準備完了」を待つわけではありません。例えば、MySQLコンテナが起動しても、MySQL自体が接続を受け付けられる状態になるまでには時間がかかります。アプリケーションの準備完了を待つには、次に説明するヘルスチェックを使用します。

2.5 ヘルスチェック

healthcheckは、コンテナ内のアプリケーションが正常に動作しているかを定期的にチェックする機能です。ヘルスチェックとdepends_onを組み合わせることで、依存先のアプリケーションが本当に準備完了してから起動するように制御できます。Docker公式のCompose file reference - Servicesにも、depends_onの長い構文では「service_healthy: Specifies that a dependency is expected to be "healthy" (as indicated by healthcheck) before starting a dependent service.」と明記されており、condition: service_healthyを指定することで依存先のヘルスチェックがpassしてから起動できることが分かります。

services:
  api:
    build: ./api
    depends_on:
      db:
        condition: service_healthy

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: appdb
    healthcheck:
      test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$$MYSQL_ROOT_PASSWORD"]
      interval: 5s
      timeout: 3s
      retries: 10
      start_period: 30s

$$は、compose.yaml内で環境変数を参照するためのエスケープ記法です。シェルで実行される際に$MYSQL_ROOT_PASSWORDとして展開されます。

dbサービスのヘルスチェック設定は、以下のDockerコマンドと同等です。

docker container run \
  --health-cmd="mysqladmin ping -h localhost -u root -p\$MYSQL_ROOT_PASSWORD" \
  --health-interval=5s \
  --health-timeout=3s \
  --health-retries=10 \
  --health-start-period=30s \
  mysql:8.0

healthcheckの各オプションの意味は以下のとおりです。

オプション 説明
test ヘルスチェックで実行するコマンド。終了コード0で成功、1で失敗
interval チェックを実行する間隔(デフォルト: 30s)
timeout コマンドのタイムアウト時間(デフォルト: 30s)
retries 失敗と判定するまでの連続失敗回数(デフォルト: 3)
start_period コンテナ起動後、ヘルスチェックを開始するまでの待機時間(デフォルト: 0s)

depends_oncondition: service_healthyを指定すると、依存先サービスのヘルスチェックが成功するまで、自サービスの起動を待機します。これにより、データベースが本当に接続可能な状態になってからAPIサーバを起動できます。

2.6 ネットワークの設定

Docker Composeは、デフォルトで同じcompose.yaml内のサービスが通信できる専用ネットワークを自動作成します。そのため、通常はネットワークを明示的に定義する必要はありません。

ただし、より複雑な構成(フロントエンドとバックエンドを別のネットワークに分離するなど)が必要な場合は、networksを使用してカスタムネットワークを定義できます。

services:
  web:
    image: nginx:alpine
    networks:
      - frontend

  api:
    build: ./api
    networks:
      - frontend
      - backend

  db:
    image: mysql:8.0
    networks:
      - backend

networks:
  frontend:
  backend:

Dockerコマンドで同じ構成を実現するには、以下のように複数のコマンドを実行する必要があります。

# ネットワークを作成
docker network create frontend
docker network create backend

# コンテナを起動し、ネットワークに接続
docker container run -d --name web --network frontend nginx:alpine
docker container run -d --name db --network backend mysql:8.0

# apiコンテナは2つのネットワークに接続
docker container run -d --name api api-image
docker network connect frontend api
docker network connect backend api

Docker Composeを使えば、これらの複雑なネットワーク設定もcompose.yamlに記述するだけで自動的に構成されます。

この例では、2つのネットワーク(frontendbackend)を定義しています。

  • webサービスはfrontendネットワークにのみ接続
  • apiサービスは両方のネットワークに接続
  • dbサービスはbackendネットワークにのみ接続

この構成により、webサービスからdbサービスへの直接通信はできなくなり、必ずapiサービスを経由する必要があります。セキュリティやアーキテクチャ上の理由でネットワークを分離したい場合に有効です。

💡 ポイント
ネットワークを明示的に指定しない場合、すべてのサービスはデフォルトネットワーク(プロジェクト名_default)に接続され、サービス名でお互いに通信できます。多くの開発環境では、この自動設定で十分です。

2.7 設定の全体像

ここまで学んだ設定をすべて組み合わせると、以下のような構成になります。

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: appdb
    volumes:
      - db-data:/var/lib/mysql
    healthcheck:
      test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$$MYSQL_ROOT_PASSWORD"]
      interval: 5s
      timeout: 3s
      retries: 10
      start_period: 30s

  api:
    build: ./api
    ports:
      - "8000:8000"
    environment:
      DB_HOST: db
      DB_NAME: appdb
    depends_on:
      db:
        condition: service_healthy

  web:
    image: nginx:alpine
    ports:
      - "3000:80"
    volumes:
      - ./html:/usr/share/nginx/html

volumes:
  db-data:

このcompose.yamlは、3層アーキテクチャのアプリケーションを定義しています。docker compose up -dコマンド1つで、データベース・API・Webサーバの3つのコンテナが適切な順序で起動します。

3. Docker Composeコマンド

続いて、Docker Composeに関連するコマンドを見ていきたいと思います。

3.1 docker compose up

定義されたサービスを起動します。

docker composeの起動

docker compose up

このコマンドを実行すると、compose.yamlに定義されたすべてのサービスが起動します。必要なネットワークは自動的に作成され、depends_onで指定された順序でコンテナが起動します。フォアグラウンドで実行されるため、各コンテナのログがまとめて表示されます。

バックグラウンドで起動

バックグラウンドで起動する場合は-d(detached)オプションを付けます。

docker compose up -d

-dオプションを付けると、コンテナがバックグラウンドで起動し、ターミナルの制御がすぐに戻ります。ログを確認したい場合はdocker compose logsを使用します。

3.2 docker compose down

起動中のサービスを停止し、コンテナとネットワークを削除します。

docker compose down

このコマンドを実行すると、すべてのサービスのコンテナを停止し、停止したコンテナを削除します。さらに、Docker Composeが作成したネットワークも削除されます。

💡 ポイント
ボリュームも一緒に削除する場合はdocker compose down -vを使用します。-vオプションを付けると、名前付きボリュームも削除されます。データベースのデータなど、永続化されたデータが完全に削除されるため、注意が必要です。

3.3 docker compose ps

サービスの状態を確認します。

docker compose ps

このコマンドを実行すると、現在のプロジェクトに属するコンテナの一覧と状態が表示されます。

NAME                    IMAGE          COMMAND                  SERVICE   STATUS
project-api-1           project-api    "python app.py"          api       Up 2 minutes
project-db-1            mysql:8.0      "docker-entrypoint..."   db        Up 2 minutes (healthy)
project-web-1           nginx:alpine   "/docker-entrypoint..."  web       Up 2 minutes

表示される情報は以下のとおりです。

項目 説明
NAME コンテナ名(プロジェクト名-サービス名-番号)
IMAGE 使用しているイメージ
SERVICE compose.yamlで定義したサービス名
STATUS コンテナの状態(Up/Exited/healthyなど)

3.4 docker compose logs

サービスのログを表示します。

ログを全体出力

docker compose logs

このコマンドを実行すると、すべてのサービスのログがまとめて表示されます。各行の先頭にはサービス名が表示されるため、どのコンテナからのログかを識別できます。

db-1   | 2024-01-15 10:00:00 [Note] mysqld: ready for connections.
api-1  | INFO:     Application startup complete.
web-1  | 172.18.0.1 - - "GET / HTTP/1.1" 200 612

リアルタイムでログを監視する場合は-f(follow)オプションを付けます。

docker compose logs -f

-fオプションを付けると、新しいログが出力されるたびに画面に表示されます。Ctrl + Cで監視を終了します。

特定サービスのログだけ出力

特定のサービスのログだけを表示する場合は、サービス名を指定します。

docker compose logs api

この例では、apiサービスのログのみが表示されます。

3.5 docker compose exec

起動中のサービス内でコマンドを実行します。

基本的な使い方

docker compose exec db mysql -u root -p

この例では、dbサービス(MySQLコンテナ)内でMySQLクライアントを起動し、データベースに接続します。

コマンドの構成

  • db: compose.yamlで定義したサービス名
  • mysql -u root -p: コンテナ内で実行するコマンド(MySQLにrootユーザで接続)

実行すると、パスワードの入力を求められます(MYSQL_ROOT_PASSWORDで設定した値を入力)。

対応するDockerコマンド

docker container exec -it <コンテナ名> mysql -u root -p

3.6 docker compose build

Dockerfileを使用するサービスのイメージを(再)ビルドします。

docker compose build

このコマンドは、compose.yamlbuildオプションを指定しているサービスのイメージをビルドします。imageオプションのみを使用しているサービス(既存のイメージを使う場合)は対象外です。

services:
  api:
    build: ./api    # ← このサービスがビルド対象
  db:
    image: mysql:8.0  # ← このサービスはビルド対象外

特定のサービスだけをビルドする場合は、サービス名を指定します。

docker compose build api

ソースコードを変更した後、イメージを再ビルドして反映させたい場合に使用します。

4. 実践的な設定

ここでは、実際の開発・運用で役立つ追加の設定項目を紹介します。

4.1 env_file(環境変数ファイル)

これまでの例では、パスワードなどの環境変数をcompose.yamlに直接記述していました。しかし、この方法には問題があります。compose.yamlをチームで共有したり、バージョン管理システムに保存したりすると、パスワードも一緒に共有されてしまいます。

env_fileを使用すると、環境変数を別のファイルに分離できます。機密情報をcompose.yamlから切り離すことで、設定ファイルを安全に共有できるようになります。

環境変数ファイルの作成

一般的な方法として、compose.yamlと同じディレクトリに.envという名前のファイルを作成し、そこに環境変数を記述します。ファイル名の先頭にドット(.)が付いているのは、「隠しファイル」であることを示す慣習です。

.envファイルの例:

MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=appdb
MYSQL_USER=appuser
MYSQL_PASSWORD=apppassword

このように、変数名=値の形式で環境変数を1行ずつ記述します。

compose.yamlでの参照

compose.yamlでは、env_fileを使ってこのファイルを参照します。

services:
  db:
    image: mysql:8.0
    env_file:
      - .env

この設定により、.envファイルに記述されたすべての環境変数がdbサービスに渡されます。environmentで個別に指定する必要がなくなり、compose.yamlにパスワードを直接書かずに済みます。

💡 ポイント
.envファイルにはパスワードなどの機密情報が含まれます。このファイルは各自のマシンで作成し、チームメンバーには共有しないようにしましょう。代わりに.env.exampleのようなテンプレートファイル(値を空にしたもの)を用意し、「このような環境変数が必要です」という一覧を共有するのが一般的です。

4.2 restart(再起動ポリシー)

restartは、コンテナが停止した際の再起動ポリシーを設定します。本番環境やサーバ上でコンテナを常時稼働させたい場合に重要です。

services:
  api:
    build: ./api
    restart: unless-stopped

利用可能な再起動ポリシーは以下のとおりです。

ポリシー 説明
no 再起動しない(デフォルト)
always 常に再起動する(手動で停止しても再起動)
on-failure 異常終了時のみ再起動(終了コードが0以外)
unless-stopped 手動で停止するまで常に再起動する

開発環境ではno(デフォルト)、本番環境ではunless-stoppedまたはalwaysを使用することが多いです。

4.3 docker compose watch(ファイル監視)

docker compose watchは、ソースコードの変更を監視し、自動的にコンテナを更新する機能です。開発時のワークフローを効率化できます。

services:
  api:
    build: ./api
    develop:
      watch:
        - action: sync
          path: ./api/src
          target: /app/src
        - action: rebuild
          path: ./api/requirements.txt

この設定では以下の動作をします。

action 説明
sync 指定したパスのファイル変更を、コンテナ内のtargetにリアルタイムで同期
rebuild 指定したファイルが変更されたら、イメージを再ビルド

ファイル監視を開始するには、以下のコマンドを実行します。

docker compose watch

これにより、ソースコードを変更するたびに手動で再起動やリビルドをする必要がなくなります。

バインドマウントとの違い

syncの動作は、前述のバインドマウント(volumesでホストのディレクトリをマウントする方法)と似ていますが、いくつかの違いがあります。

項目 バインドマウント docker compose watch (sync)
仕組み ホストのディレクトリを直接マウント ファイル変更を検知してコピー
同期方向 双方向(ホスト↔コンテナ) 一方向(ホスト→コンテナ)
コンテナ内の変更 ホストにも反映される ホストには反映されない

バインドマウントは手軽ですが、コンテナ内で生成されるファイル(ビルド成果物やキャッシュなど)がホスト側に作成されることがあります。docker compose watchを使うと、ホストとコンテナのファイル構成を分離しつつ、開発時の変更を反映できます。

どちらを使うべきか?

多くの開発シーンでは、シンプルなバインドマウントで十分です。以下を参考に選択してください。

  • HTMLやCSS、設定ファイルなど、変更を即座に反映させたいだけの場合は、シンプルで設定も簡単なバインドマウントが向いています
  • Pythonの__pycache__(バイトコードキャッシュ)のように、コンテナ内で生成されるファイルをホストに反映させたくない場合や、ファイル変更時に自動でリビルドさせたい場合は、docker compose watchが便利です

一般的な用途であれば、まずバインドマウントで十分です。開発を進める中で不都合が出てきたら、docker compose watchを検討してみてください。

5. まとめ

この講座では、Docker Composeについて学びました。

  • Docker Composeは、複数コンテナを1つの設定ファイルで管理するツール
  • 設定はcompose.yamlファイルにYAML形式で記述する
  • servicesセクションで各コンテナの設定を定義する
  • portsenvironmentvolumesで詳細な設定を行う
  • depends_onでサービス間の起動順序を制御できる
  • healthcheckでアプリケーションの準備完了を待機できる
  • networksでサービス間の通信を分離できる
  • env_fileで機密情報を外部ファイルから読み込める
  • restartでコンテナの再起動ポリシーを設定できる
  • docker compose watchでファイル変更を監視し自動更新できる
  • docker compose up -dで一括起動、docker compose downで一括停止
  • 開発環境の構築や、ローカルでのテストに特に有用