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の基本の書き方

Last updated at Posted at 2026-01-21

はじめに

こんにちは。
プログラミング初心者wakinozaと申します。
勉強中に調べたことを記事にまとめています。

十分気をつけて執筆していますが、なにぶん初心者が書いた記事なので、理解が浅い点などあるかと思います。
間違い等あれば、指摘いただけると助かります。

記事を参考にされる方は、初心者の記事であることを念頭において、お読みいただけると幸いです。

記事のテーマ

この記事は、Docker Composeの書き方についてまとめています。

動作環境

  • Windows11
  • Visual Studio Code
  • Docker 29

目次

1. Docker Composeとは
2. 定義ファイルの書き方
3. 定義ファイルの記述ルール
4. シンプルなコード例の解説
5. .envの記述
6. healthCheckの記述

1. Docker Composeとは

Dockerは、互いに独立したコンテナ環境を作るためのシステムです。

管理や再利用のしやすさなどの理由から、1つのコンテナに1つのソフトウェアを格納するのが基本とされています。
しかし、実務の世界では、アプリケーション・DB・サーバーなど複数のソフトウェアを利用することが一般的です。
そのため、複数のソフトウェアをそれぞれ別のコンテナに格納して、それぞれをネットワークでつないで連携します。

ところが、コンテナを1つ1つ作成するとコードは冗長となり、管理漏れが起きるリスクがあります。依存関係を考慮した作成順を忘れて起動すると、ビルドに失敗する可能性もあります。

そこで、複数のDockerコンテナとそれらを利用するネットワークやボリュームをまとめて管理する「Docker Compose」が開発されました。

Docker Composeと似たものとして、Dockerfileがあります。
Dockerfileは、イメージを作るための設計図です。1つのイメージで1種類のコンテナしか作成できないうえ、ネットワークやボリュームの設定はできません。
一方のDocker Composeは、複数のコンテナを定義し、それらを紐づけるネットワークやボリュームまでまとめて管理できます。

つまり、Dockerfileがコンテナ(イメージ)を「作る」ものとすると、Docker Composeはコンテナ同士を「つなぐ」ものと言えます。
例えるなら、Dockerfileが「ゲームソフト」、Docker Composeが「マルチプレイ環境の設定」のようなものでしょうか。
ゲームソフト(Dockerfile)によって「ゲームがプレイできる状態」を準備し、プレイ環境同士をネットワークで繋ぎ、マルチプレイでのプレイ情報をセーブする仕組みを整えて、マルチプレイ環境を実現するのがDocker Composeです。

2. 定義ファイルの書き方

次にDocker Composeの記述方法について、説明します。

Docker Composeを利用する場合は、「compose.yml」というファイルを作成し、定義情報を記述します。
ファイル名は、「-f」オプションで別の名前を指定することもできますが、特別な理由がない場合は、「compose.yml」という名前の定義ファイルを作成します。(古い教材では docker-compose.yml と書かれていることもありますが、現在は compose.yml が標準です)

定義ファイルは、YAML形式で記述します。YAML形式については、次章で説明します。

定義ファイルでは、主に「services」「networks」「volumes」の3つの大項目の詳細を記述します。

「services」「networks」「volumes」の大項目の下に、要素名を指定します。コンテナなら「コンテナ名」、ネットワークなら「ネットワーク名」、ボリュームなら「ボリューム名」を記載します。

要素名の下に、定義項目を記述していきます。
よく使われる定義項目は以下の通りです。

項目 説明
image 利用するイメージを指定する
networks 接続するネットワークを指定する
volumes 記憶領域のマウントを設定する
ports 「ホストのポート : コンテナのポート」の順で、ポートのマッピングを指定する
environment 環境変数を設定する
depends_on 別のサービスに依存することを示す
restart コンテナが停止した時の再試行ポリシーを設定する

多くの項目は、コンテナを個別に起動する際に「docker run」コマンドで指定する項目と同様です。

Docker Compose固有の項目としては、「depends_on」や「restart」などがあります。

「depends_on」は、他のサービスに依存していることを示します。
例えば、サービスAがサービスBに依存しているとします。サービスBが起動していない状態で、サービスAを起動してもエラーがおこる可能性があります。そのため、必ずサービスBが起動した後にサービスAを起動する必要があります。また、サービス停止時も、依存先からサービスを停止すると、依存元にエラーが発生する可能性があります。このように、サービスに依存関係がある場合は、依存関係にそって処理を進める必要があります。
「depends_on」で依存関係を指定しておくことで、起動時は「依存先」→「依存元」の順番に、停止時は「依存元」→「依存先」の順に、処理を行うよう調整できます。

「restart」は、コンテナが停止した時の対応方法を指定します。
エラーや一時的なメモリ不足によってコンテナが停止したり、依存関係による起動の失敗など、コンテナが急に止まってしまう事態があり得ます。もし、24時間起動していてほしいコンテナであった場合、エラーによってコンテナが止まったまま時間が過ぎるとサービス提供に問題が出る可能性があります。そのため、コンテナが急に停止した場合に自動的に再起動するかどうかを設定しておくことができます。
主な「restart」の設定は以下の通りです。

設定値 説明
no デフォルト設定。コンテナが停止した場合も、再起動しない。
always どんな理由で止まっても、常に再起動する。データベースなど常時動いていないと困るサービスなどで用いられる
on-failure エラーの時だけ、再起動する
unless-stopped 基本的に常に再起動するが、手動で止めた場合は再起動しない。

3. 定義ファイルの記述ルール

YAML形式のファイルには、記述のルールがあります。
代表的なルールは以下の通りです。

  • 大項目「services」「networks」「volumes」に続いて設定内容を書く
  • 親子関係はスペースで字下げする。(タブで字下げすることは禁止されています)
  • 字下げのスペースは、「半角スペース2つ」や「半角スペース4つ」などのように2の倍数が望ましいです。また、ファイル内の字下げの数は一致している必要があるため、最初の字下げを「半角スペース2つ」としてた場合は、以後すべての字下げを「半角スペース2つ」に統一する必要があります。
  • 名前の後ろには「:」をつけて、「:」の後ろには半角スペース1つ入れる。
  • 「-」が入っていたら、複数行指定できる。
  • コメントを入れたい場合は、「#」を使う
  • 文字列を入れる場合は、「'(シングルクオート)」や「"(ダブルクオート)」で囲む

以下は、字下げを「半角スペース2つ」で統一した記述例です。

compose.yml
# 定義ファイルの記述例
services:
  コンテナ名1:
    image: イメージ名1
    networks:
      - ネットワーク名

  コンテナ名2:
    image: イメージ名2
    networks:
      - ネットワーク名

networks:
  ネットワーク名1:

volumes:
  ボリューム名1:
  ボリューム名2:

4. シンプルなコード例の解説

では、Docker Composeを利用して、Java実行コンテナとMySQLを紐づけた環境を作っていきます。

4-1. Javaプログラムを作成

まずは、実行するJavaプログラムを作成します。

ターミナルもしくはPowerShellで、PC内の適当な場所に作業用ディレクトリ(docker_study)を作成します。

mkdir docker_study

次に、docker_studyディレクトリの中に以下のJavaファイルを作成します。

Main.java
public class Main {
  public static void main(String[] args) {
    System.out.println("DockerCompose!!");
  }
}

4-2. Dockerfileを作成

次に、先ほどの作業ディレクトリ内に「Dockerfile」というファイルを作成して、以下の内容を記述します。

FROM eclipse-temurin:21-jdk-alpine AS builder

WORKDIR /app

COPY Main.java .

RUN javac Main.java


FROM eclipse-temurin:21-jre-alpine

WORKDIR /app

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

COPY --from=builder /app/Main.class .

CMD ["java", "Main"]

上のDockerfileの詳しい解説が読みたい場合は、前回の記事をご覧ください。

4-3. compose.ymlを作成

次に、作業用ディレクトリ内に「compose.yml」というファイルを作成して、以下の内容を記述します。

compose.yml
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - DB_HOST=db
      - DB_NAME=sample_db
      - DB_USER=app_user
      - DB_PASSWORD=app_password
    depends_on:
      - db

  db:
    image: mysql:8.4
    environment:
      MYSQL_DATABASE: sample_db
      MYSQL_USER: app_user
      MYSQL_PASSWORD: app_password
      MYSQL_ROOT_PASSWORD: root_password
    volumes:
      - db_data:/var/lib/mysql

volumes:
  db_data:

同一のcompose.yml内のサービスは、デフォルトで共通のネットワークに自動配置されます。そのため、networksの記述を省略しても、サービス名による名前解決が可能です。

4-4. 定義ファイルを実行する

では、ターミナルもしくはPowerShellで定義ファイルを実行してみましょう。

先ほどの作業用ディレクトリに移動し、以下のコマンドを実行します。

docker compose up

上のコマンドでは、作業用ディレクトリに移動してからコマンドを実行しましたが、「-f」オプションでcompose.ymlのパスを指定して実行することも可能です。

docker compose -f docker_study/compose.yml up

Main.javaに記載した「DockerCompose!!」が表示されていれば、実行成功です。
1つのコマンドで複数のコンテナが起動できました。

コンテナでの作業が終了したら、「down」コマンドでコンテナやネットワークを停止・削除します。複数コンテナの停止や削除も1つのコマンドで実行できる点も、Docker Composeのメリットです。

# コンテナの停止・削除
docker compose down

# コンテナの停止・削除できたかを確認
docker compose ps

「down」コマンドはあくまで「run(実行)」を終了させるコマンドであるため、コンテナとネットワークは停止・削除しますが、イメージとボリュームは削除しません。
そのため、イメージやボリュームが不要になった場合は、個別に削除する必要があります。

# 1. イメージの一覧を確認
docker image ls

# 2. イメージを削除する
docker image rm イメージ名
# 1. ボリュームの一覧を確認
docker volume ls

# 2. ボリュームを削除する
docker volume rm ボリューム名

5. .envの記述

次に、実務に使える応用的な記述方法を、2点説明していきます。
1つ目は、.envの記述です。

先ほどのcompose.ymlでは、環境変数をcompose.yml内に記載していました。
しかし、compose.ymlファイルは他のソースコードと同様に、Gitでコミットし、プロジェクトメンバー内で共有します。
そのため、compose.yml内にパスワードなどの機密情報を記載すると、情報が漏洩してしまいます。

そういった事態を防ぐため、Gitに含めない「.env」という別のファイルに、機密情報を記載します。

共有する情報は「compose.yml」に、共有しない情報は「.env」にと記述先を分けることで、情報漏洩を防ぎます。

では、実際に上のコードの環境変数を.envに分離してみましょう。

5-1. .gitignoreの設定

作業用ディレクトリをGitで管理している場合は、まず「.gitignore」ファイルに「.env」を追記します。

.gitignore
.env

「.gitignore」は、Gitで管理しないファイル名を明示するファイルです。
「.gitignore」に管理されたくないファイル名を記述することで、誤って機密情報をコミットし、情報を漏洩してしまうというミスを防ぐことができます。

5-2. .envの設定

次に、作業用ディレクトリに「.env」ファイルを作成し、以下の内容を記述します。

.env
# アプリケーション設定
APP_PORT=8080

# データベース設定
DB_NAME=sample_db
DB_HOST=db

# --- 一般ユーザー ---
DB_USER=app_user
DB_PASSWORD=app_password

# --- rootユーザー ---
MYSQL_ROOT_PASSWORD=root_password

5-3. compose.ymlの変更

次に、compose.ymlの環境変数の値を、 ${変数名} に置き換えます。
これにより、Dockerがcompose.ymlを読み込む際に、${変数名}の部分の.envの値に自動的に置き換えて、実行します。

compose.yml
services:
  app:
    # 省略
    ports:
      - "${APP_PORT}:8080"
    environment:
      - DB_HOST=${DB_HOST}
      - DB_NAME=${DB_NAME}
      - DB_USER=${DB_USER}
      - DB_PASSWORD=${DB_PASSWORD}
    # 省略

  db:
    # 省略
    environment:
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
    # 省略

以後の「up」コマンドは先ほどと同様です。

6. healthCheckの記述

サービスに依存関係がある場合は、「depends_on」を記述することで、適切な順に処理を行うよう調整できることを説明しました。

しかし、「depends_on」はサービスの処理順の調整を行うのみで、後の処理を実施する際に、その前の起動が完了しているかどうかまで確認しません。そのため、処理順は適切でも、依存先のサービスの準備が完了しない状態で依存元のサービスを起動し、エラーになってしまうケースがあります。
実際に、MySQLなどのデータベースは起動してから、実際に接続を受け付けられるようになるまで、少し時間がかかります。

依存先の準備が完了しないために依存元のサービス起動にエラーが出る場合は、依存先の準備が整っているかを確認する「ヘルスチェック」機能を追加します。
ヘルスチェックは、依存先のDockerfileもしくはcompose.ymlに記載することができます。
今回は、MySQLにDockerfileのイメージを利用していないので、compose.ymlに記載していきます。

まず、依存先のdbサービスに、「healthcheck」を追加します。
追加する内容は以下の通りです。

compose.yml
services:
  db:
    # 省略
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 1m30s
      timeout: 10s
      retries: 3
      start_period: 40s

「test:」項目では以下の事項を記述しています。
「CMD」で、コンテナ内のShellに対してコマンドを発行できます。
「mysqladmin」はMySQLの管理操作を実行するためのクライアントで、ステータスの確認に利用できます(認証設定によっては、ユーザー名やパスワードの指定が必要になる場合があります。)
「ping」はサーバーが使用可能かどうかをチェックするためのmysqladminのコマンドです。 サーバーが稼働中の場合は mysqladmin のリターンステータスは 0 になり、稼働していない場合は 1 になります。
「-h localhost」でコンテナ自身をターゲットとして指定しています。

項目 意味 内容
test: 確認方法  mysqladminを使い、自分自身(localhost)に対して「生きてる?(ping)」と送ります。成功すれば「正常」とみなします。
interval: 実行間隔 指定された時間ごとに上記のテストを繰り返します。
timeout: 待ち時間 テストの返事が指定された時間来なかったら、その回は「失敗」と判定します。
retries: 再試行回数 指定された回数連続で失敗したら、そのコンテナを「異常(unhealthy)」としてマークします。
start_period: 保留期間 起動後すぐは、失敗が続く可能性が高いので、失敗をカウントしない期間を設定します

intervalからstart_periodまでの項目は、公式リファレンスのコード例を参考にして記載しています。厳密な計測を基に設定した数値ではありませんので、コードを利用される方はご注意ください。

次に、依存元のappサービスの「depends_on」に「condition」の項目を追加します。

compose.yml
services:
  app:
    # 省略
    depends_on:
      db:
        condition: service_healthy

「condition: service_healthy」とすることで、依存先のサービスのヘルスチェックが「正常」であると確認できてから、依存元の起動を行うように設定できます。

以上の2つの応用的な記載方法を盛り込んだ、compose.ymlの最終版は以下の通りです。

compose.yml
services:
  app:
    build: .
    ports:
      - "${APP_PORT}:8080"
    environment:
      - DB_HOST=${DB_HOST}
      - DB_NAME=${DB_NAME}
      - DB_USER=${DB_USER}
      - DB_PASSWORD=${DB_PASSWORD}
    depends_on:
      db:
        condition: service_healthy

  db:
    image: mysql:8.4
    restart: always
    environment:
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 1m30s
      timeout: 10s
      retries: 3
      start_period: 40s

volumes:
  db_data:

まとめ

  • Docker Composeによる一元管理: 複数コンテナの依存関係やネットワーク、ボリュームの設定をcompose.ymlに集約することで、複数コンテナを利用した開発を容易にする。

  • 環境変数の分離: .envファイルと.gitignoreを活用し、機密情報をコードベースから分離することで、情報漏洩リスクを低減する。

  • Healthcheckによる起動制御: 起動までのタイムラグでエラーが発生する場合は、ヘルスチェックにより依存先サービスの「準備完了」を確認する。


記事は以上です。
最後までお読みいただき、ありがとうございました。

参考情報一覧

この記事は以下の情報を参考にして執筆しました。

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?