【第5回】Docker Composeの使い方|compose.yaml構文から起動順序制御まで【Rocky Linux 9】

この記事はDocker連載の第5回です。docker run でコンテナを起動できる方を対象に、Docker Composeによる複数コンテナの一括管理方法を解説します。

目次

この記事で分かること

  • compose.yaml の基本構文と services / volumes / networks の書き方
  • Rocky Linux 9 + SELinux環境でPermission denied を回避するボリュームマウント設定
  • depends_on + healthcheck でデータベースの起動完了を待ってからアプリを起動する方法

docker run コマンドでコンテナを1つずつ起動している方は多いと思います。しかし、Webアプリ + データベース + リバースプロキシのように複数コンテナを組み合わせる構成では、毎回長いコマンドを打つのは大変です。ネットワークやボリュームの作成も手動になり、ミスが起きやすくなります。

Docker Compose を使えば、1つの設定ファイル(compose.yaml)に全コンテナの構成を記述し、docker compose up -d の一発で起動できます。この記事では、Rocky Linux 9 環境を前提に、基本構文から実践的なテクニックまでを順番に解説していきます。

Docker Composeとは?docker runとの違いと導入メリット

docker run の繰り返し問題

たとえば WordPress + MySQL の環境を docker run で構築する場合、以下のようなコマンドが必要になります。

# ネットワーク作成
docker network create wp-net

# MySQLコンテナ起動
docker run -d \
  --name mysql \
  --network wp-net \
  -e MYSQL_ROOT_PASSWORD=rootpass \
  -e MYSQL_DATABASE=wordpress \
  -e MYSQL_USER=wpuser \
  -e MYSQL_PASSWORD=wppass \
  -v mysql-data:/var/lib/mysql \
  mysql:8.0

# WordPressコンテナ起動
docker run -d \
  --name wordpress \
  --network wp-net \
  -p 8080:80 \
  -e WORDPRESS_DB_HOST=mysql \
  -e WORDPRESS_DB_USER=wpuser \
  -e WORDPRESS_DB_PASSWORD=wppass \
  -e WORDPRESS_DB_NAME=wordpress \
  wordpress:latest

これを毎回入力するのは手間ですし、コマンドの順序を間違えると起動に失敗します。停止・削除するときも個別にコマンドを実行する必要があります。

Docker Compose なら compose.yaml 1ファイルで完結

Docker Compose は、複数のコンテナ・ネットワーク・ボリュームの定義を1つの YAML ファイルにまとめて管理するツールです。上記の構成は compose.yaml に記述しておけば、docker compose up -d 一発で起動、docker compose down 一発で停止・削除できます。

チーム開発でも compose.yaml をリポジトリに含めておけば、全員が同じ環境を再現できます。「自分のPCでは動くのに……」という問題を防げるのも大きなメリットです。

docker compose v2 と旧版 docker-compose v1 の違い

現在は2種類の Docker Compose が存在しますが、v2(docker compose)を使うのが正解です。

  • v2(推奨):Docker CLI のプラグインとして動作。コマンドは docker compose(スペース区切り)。Go言語で書かれており高速
  • v1(非推奨):スタンドアロンの docker-compose(ハイフン区切り)コマンド。Python製。2023年6月にEOL(サポート終了)済み

古い記事やチュートリアルでは docker-compose(ハイフン)で書かれていることがありますが、v1はすでにサポート終了しています。必ず docker compose(スペース)を使ってください。

Rocky Linux 9 で Docker をインストール済みであれば、v2 プラグインは通常すでに含まれています。以下のコマンドでバージョンを確認しましょう。

docker compose version
# Docker Compose version v2.x.x と表示されればOK

compose.yaml の基本構文を理解する

version: フィールドは不要(廃止済み)

古い記事では version: "3.8" のような記述を見かけますが、現在の Compose Specification では version: フィールドは廃止されています。Docker Compose v2.x 以降で書いても無視されるだけで、以下のような警告が表示されます。削除することを推奨します。

WARN[0000] /path/to/compose.yaml: the attribute `version` is obsolete, it will be ignored, please remove it

以前はバージョンによって使える機能が異なりましたが、現在は Compose Specification に統一されました。新規で compose.yaml を書く場合は version: を書かないのがベストプラクティスです。

compose.yaml の3大要素:services / volumes / networks

compose.yaml はトップレベルに以下の3つの要素を記述します。

  • services:起動するコンテナの定義(必須)
  • volumes:名前付きボリュームの定義(データの永続化に使用)
  • networks:カスタムネットワークの定義(省略するとデフォルトネットワークが自動作成される)

services で使う主要キー

  • image:使用するDockerイメージを指定(例:nginx:latest
  • build:Dockerfileからイメージをビルドする場合に指定(例:build: ./app
  • ports:ホスト側ポート:コンテナ側ポートのマッピング(例:"8080:80"
  • volumes:ボリュームマウントの定義(例:./html:/usr/share/nginx/html
  • environment:環境変数の設定
  • restart:再起動ポリシー(no / always / unless-stopped / on-failure

シンプルなNginx起動例

まずは最もシンプルな例として、Nginx を1コンテナだけ起動する compose.yaml を見てみましょう。

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    restart: unless-stopped

このファイルを compose.yaml として保存し、同じディレクトリで以下を実行します。

# htmlディレクトリとテスト用ファイルを作成
mkdir -p html
echo "<h1>Hello from Docker Compose!</h1>" > html/index.html

# コンテナをバックグラウンドで起動
docker compose up -d

# 動作確認
curl http://localhost:8080

# 停止・削除
docker compose down

docker compose up -d-d は detach(バックグラウンド実行)の意味です。つけない場合はフォアグラウンドで実行され、ログがターミナルに表示されます。

Rocky Linux 9 + SELinux環境でのボリュームマウント注意点

Rocky Linux 9 では SELinux(Security-Enhanced Linux)がデフォルトでenforcing(強制)モードで動作しています。この環境でホストディレクトリをコンテナにバインドマウントすると、Permission denied エラーが発生することがあります。

エラーの原因

SELinux はファイルやディレクトリに「セキュリティコンテキスト」というラベルを付けてアクセス制御を行います。ホスト上のディレクトリはデフォルトで通常ユーザー向けのラベルが付いており、コンテナプロセスからはアクセスが拒否されます。

# SELinuxが原因のエラー例
nginx: [emerg] open() "/usr/share/nginx/html/index.html" failed (13: Permission denied)

「SELinux を disabled にすれば解決する」という情報を見かけますが、セキュリティリスクが高まるため非推奨です。正しい対処法は :z または :Z フラグを使うことです。

:z(共有ラベル)と :Z(プライベートラベル)の使い分け

  • :z(小文字):共有ラベル。container_file_t というラベルを付与し、複数のコンテナから同じディレクトリにアクセスできるようにする
  • :Z(大文字):プライベートラベル。コンテナ固有のラベルを付与し、そのコンテナだけがアクセスできるようにする

compose.yaml での書き方は以下の通りです。

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
    volumes:
      # 複数コンテナで共有する場合は :z(小文字)
      - ./html:/usr/share/nginx/html:z
      # このコンテナ専用の場合は :Z(大文字)・読み取り専用の場合は ,ro も追加
      - ./conf/nginx.conf:/etc/nginx/nginx.conf:Z,ro

:Z を使うと、そのディレクトリには他のコンテナからアクセスできなくなります。複数コンテナで共有するボリューム(例:静的ファイルをNginxとアプリで共有)には必ず :z(小文字)を使いましょう。

SELinuxの状態確認コマンド

# SELinuxの動作モードを確認
getenforce
# Enforcing と表示されれば有効

# :z/:Z 設定後のファイルラベルを確認
ls -Z ./html/
# system_u:object_r:container_file_t:s0 と表示されれば正しく設定済み

コンテナの起動順序を正しく制御する(depends_on + healthcheck)

depends_on だけでは不十分な理由

depends_on を使えばコンテナの起動順序を指定できますが、これだけでは「コンテナが起動した」ことしか保証しません。データベースの場合、コンテナが起動してからMySQL/PostgreSQLのプロセスが接続を受け付けるまでに数秒〜数十秒かかることがあります。

その結果、「MySQLコンテナは起動しているのに、WordPressが接続エラーで落ちる」という問題が発生します。

# これだけでは不十分
services:
  app:
    depends_on:
      - db    # dbコンテナの「起動」は待つが「準備完了」は待たない

healthcheck + condition: service_healthy で解決する

depends_oncondition: service_healthy を指定すると、依存先のヘルスチェックが通るまで起動を待機します。これにより「データベースが実際に接続可能になってからアプリを起動する」という制御が可能になります。service_started(デフォルト)との違いは、コンテナ起動の確認だけでなく、サービスの「準備完了」まで待つ点です。

WordPress + MySQL の完全な実装例

services:
  db:
    image: mysql:8.0
    container_name: wp-mysql
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - db-data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost",
             "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
      interval: 5s
      timeout: 5s
      retries: 20
      start_period: 10s

  wordpress:
    image: wordpress:latest
    container_name: wp-app
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: ${MYSQL_USER}
      WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
      WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
    volumes:
      - wp-data:/var/www/html

volumes:
  db-data:
  wp-data:

healthcheck ブロックの各設定値の意味は以下の通りです。

  • test:ヘルスチェックで実行するコマンド。MySQLの場合は mysqladmin ping(認証情報付き)が確実
  • interval:チェックの実行間隔(この例では5秒ごと)
  • timeout:チェックのタイムアウト時間
  • retries:連続失敗の許容回数。この回数を超えると unhealthy(異常)と判定
  • start_period:コンテナ起動後、ヘルスチェック開始までの猶予時間

名前付きボリューム(db-data:wp-data:)はDockerが管理するため、SELinuxのラベル問題が発生しません。データベースやアプリケーションデータの永続化には名前付きボリュームを使うのがベストプラクティスです。

環境変数の外部化と .env ファイルの使い方

前のセクションの compose.yaml では ${MYSQL_ROOT_PASSWORD} のように変数を使っていました。パスワードなどの機密情報を compose.yaml に直接書くのはセキュリティ上好ましくありません。.env ファイルを使って環境変数を外部化しましょう。

.env ファイルの書き方と自動読み込み

compose.yaml と同じディレクトリに .env ファイルを作成します。Docker Compose は自動的にこのファイルを読み込み、compose.yaml 内の ${変数名} を置き換えます。

# .env ファイル(compose.yaml と同じディレクトリに配置)
MYSQL_ROOT_PASSWORD=my-secret-rootpass
MYSQL_DATABASE=wordpress
MYSQL_USER=wpuser
MYSQL_PASSWORD=my-secret-wppass

.env ファイルにはクォーテーション(引用符)を付けないのが基本です。MYSQL_PASSWORD="pass" と書くと、ダブルクォーテーションも値の一部として解釈される場合があります。

.gitignore でセキュリティを確保する

.env ファイルには機密情報が含まれるため、Gitリポジトリにコミットしてはいけません.gitignore に必ず追加しましょう。

# .gitignore に追加
.env

代わりに、変数名だけを記載した .env.example をリポジトリに含めておくと、他のメンバーが必要な変数を把握できます。

# .env.example(リポジトリに含める・値は空にしておく)
MYSQL_ROOT_PASSWORD=
MYSQL_DATABASE=
MYSQL_USER=
MYSQL_PASSWORD=

docker compose config でデバッグする

環境変数が正しく展開されているか確認するには、docker compose config コマンドが便利です。

# 変数展開後の compose.yaml を表示(構文エラーのチェックにも使える)
docker compose config

docker compose config は構文エラーのチェックにも使えます。compose.yaml を書き換えた後、up する前にこのコマンドで検証する習慣をつけると、トラブルを未然に防げます。

よく使う docker compose コマンド一覧

# コンテナをバックグラウンドで起動
docker compose up -d

# コンテナの状態を確認
docker compose ps

# ログを表示(リアルタイム追跡)
docker compose logs -f

# 特定サービスのログだけ表示
docker compose logs -f wordpress

# コンテナを停止(コンテナとネットワークを削除)
docker compose down

# コンテナ + ボリュームも削除(データも消える)
docker compose down -v

# イメージを再ビルドして起動
docker compose up -d --build

# 変数展開後の設定を確認
docker compose config

よくある質問(FAQ)

docker-compose と docker compose、どちらを使えばいい?

docker compose(スペース区切り・v2)を使ってください。ハイフン区切りの docker-compose はv1であり、2023年6月にEOL(サポート終了)しています。Docker Engine をインストールすれば、v2 プラグインが自動的に利用可能です。

version: はいつまで書けばいい?

現在は不要です。削除を推奨します。Compose Specification に統一されたため、version: を書いても無視されます。Docker Compose v2.x 以降では「obsolete(廃止)」という警告が表示されます。混乱を避けるため削除しましょう。

SELinuxを無効にしてはダメ?

セキュリティリスクが高まるため非推奨です。SELinux はコンテナからのホスト侵害を防ぐ重要なセキュリティ機構です。ボリュームマウントの Permission denied は :z または :Z フラグで正しく対処しましょう。名前付きボリュームを使えば、SELinuxの問題自体が発生しません。

depends_on だけでDBが起動前にアプリが動いてしまう

healthcheckcondition: service_healthy を組み合わせてください。depends_on のデフォルト(condition: service_started)はコンテナの起動のみを待つため、データベースプロセスの準備完了は保証しません。この記事のWordPress + MySQLの例を参考に、mysqladmin ping(認証情報付き)のヘルスチェックを設定しましょう。

まとめ

この記事では、Docker Compose の基本から実践的なテクニックまでを解説しました。重要なポイントを振り返ります。

  • Docker Compose v2docker compose)を使う。v1(docker-compose)は2023年EOL済み
  • compose.yamlversion: は廃止済み。services / volumes / networks の3要素で構成する
  • SELinux環境ではバインドマウントに :z(共有)/ :Z(プライベート)を付ける。名前付きボリュームなら不要
  • 起動順序制御depends_on + healthcheck + condition: service_healthy で確実に行う
  • 環境変数.env ファイルに外部化し、.gitignore で管理する

Docker Compose を使いこなせるようになると、開発環境の構築が格段に効率化します。次のステップとして、Docker ネットワークの詳細設計マルチステージビルドを学ぶと、より本格的なコンテナ運用に対応できるようになります。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次