Docker

Docker ComposeでCI対応のマルチコンテナ統合テスト環境

ⓘ本ページはプロモーションが含まれています

もっとスキルを活かしたいエンジニアへ

スポンサードリンク
働き方から選べる

無料で使えて良質な案件の情報収集ができるサービス

エンジニアの世界では、「いつでも動ける状態を作っておけ」とよく言われます。
技術やポートフォリオがあっても、自分に合う案件情報を日常的に見れていないと、いざ動こうと思った時に比較や判断が難しくなってしまいます。
普段から案件情報が集まる環境を作っておくと、良い案件が出た時にすぐ動きやすくなりますよ。
筆者自身も、メガベンチャー勤務時代に年収1,500万円を超えた経験があります。振り返ると、技術だけでなく「どんな案件や働き方があるか」を日頃から見ていたことが、キャリアの選択肢を広げるきっかけになりました。
このブログを読んでくれた方に感謝を込めて、実際に使っている情報収集サービスを紹介します。

フルリモート・週3日・高単価、どんな条件も妥協したくないなら

フリーランスボードに無料会員登録する

利用者10万人以上。業界最大規模45万件の案件。AIマッチ機能や無料の相場情報が人気。

年収800万円以上のキャリアアップ・ハイクラス正社員を視野に入れているなら

Beyond Careerに無料相談する

内定獲得率90%以上。紹介先企業とは役員クラスのコネクションがある安心と信頼できるエージェント。


スポンサードリンク

導入と目的:なぜComposeでテスト環境を作るのか

Docker Composeでテスト環境を定義すると、ローカルとCIで同一の構成を使えます。
これにより、環境差分によるテスト誤差を減らし、トラブルシュートが容易になります。

この記事のゴールと対象読者

この記事は中級〜上級の開発者、QAエンジニア、CI担当者向けに、即コピー&実行できるテンプレートと運用上の注意点を提供します。

基本テンプレート(実務向け docker-compose.yml の実例)

ここでは即コピーして試せるテンプレートを示します。コードの直前に目的と想定シナリオを記載しますので、そのままCIに組み込める構成です。

.env.example と最小 .env サンプル

このブロックの目的: リポジトリに置く .env.example の具体例を示します。想定される利用シナリオ(ローカル/CI): リポジトリをクローンしたら .env を作成してすぐ動かせるようにするため。

このブロックの目的: 最小限の .env サンプル(試用向け)を示します。想定される利用シナリオ(ローカル): クローン後すぐローカルで動かすための簡易値。

docker-compose.yml(ベース)

このブロックの目的: ベースの docker-compose.yml(サービス定義)を示します。想定される利用シナリオ(ローカル/CI): ローカルは override を使いソースマウントやポート公開、CIは --profile ci でテスト専用サービスを起動します。

補足(docker-compose.yml に関する重要ポイント):

  • healthcheck のコマンドで環境変数展開を使う場合は CMD-SHELL を使ってください。JSON 形式(["CMD", ...])だとシェル拡張が行われません。
  • depends_on は単に起動順のヒントであり、readiness は保証しません。readiness は healthcheck と下記の待ち合わせスクリプトで扱います。
  • test サービスのソースを read-only マウントする場合、テストやマイグレーションで書き込みが必要だと失敗する可能性があります。CIでは可能ならソースをイメージに含める(マウントしない)か、rw マウントを使ってください。

Dockerfile(multi-stage、ビルド依存分離)

このブロックの目的: ビルド依存を分離して最終イメージを小さく保つ Dockerfile の例を示します。想定される利用シナリオ(ローカル/CI): ローカルは app ターゲットを使い起動、CI は test ターゲットでテスト実行。

補足(Dockerfile 最適化):

  • build-essential などのビルド依存は builder ステージに限定して最終イメージには含めません。
  • pip の wheel キャッシュや --no-index を使うと CI の再現性とビルド時間が改善します。
  • サイズが問題になる場合は alpine ベースや distroless を検討してください。ただしビルドやライブラリの互換性に注意が必要です。

待ち合わせスクリプト(ci/wait-for-services.sh)

このブロックの目的: DB/Redis 等の到達性を確認し、認証や DATABASE_URL の形式に対応する待ち合わせスクリプトの例です。想定される利用シナリオ(CI): test コンテナのエントリポイントで実行してマイグレーション→テストを行います。

補足(healthcheck と wait スクリプトの役割分担):

  • healthcheck はコンテナ内部プロセスやポートが「生きている」かの簡易判定に使います(TCP/HTTP レベル)。
  • 待ち合わせスクリプトは「マイグレーション実行可否」「認証を伴う接続チェック」「アプリケーションレベルの準備完了」などより厳密な readiness を担います。
  • pg_isready はサーバが接続を受け付けているかを確認しますが、必ずしも「指定ユーザーで認証できるか」を保証するものではありません。認証を検証したい場合は psql での接続試行を行ってください。

開発・テスト・CIでの設定切り替えと待ち合わせ戦略

ここではローカル/CIでの設定切り替えパターンと安全な待ち合わせ方法を示します。各環境で同一ファイルを使いながら挙動を分ける手法を紹介します。

override/profiles/環境変数での切り替え

ローカルは override、CI は profiles を使って振る舞いを分けるのが実務的です。環境変数は .env / CI シークレットで注入します。

  • ローカル: docker-compose.override.yml でソースマウントとポート公開を定義します(ホスト競合回避が必須)。
  • CI: docker-compose.ci.yml を用意し --profile ci で test サービスを起動します。
  • 実行例(ローカル):
  • docker compose -f docker-compose.yml -f docker-compose.override.yml up --build
  • 実行例(CI):
  • docker compose -f docker-compose.yml -f docker-compose.ci.yml --profile ci up --build --abort-on-container-exit --exit-code-from test

ローカルではホストポートの公開、CIでは非公開を徹底するとポート競合を減らせます。

ヘルスチェックと待機(マイグレーション実行)のパターン

ヘルスチェックは簡易判定に留め、マイグレーションとテスト開始は待ち合わせスクリプトに任せるのが堅牢です。

  • 推奨パターン:
  • 各サービスに簡易 healthcheck(ポート/HTTP)を設定。
  • test コンテナのエントリで wait→migrate→test の順で実行。
  • あるいは CI の最初のステップで migrate サービスを単体実行して終了コードで判定する方法も有効です。
  • 注意点:
  • healthcheck のコマンドに環境変数を使う場合は CMD-SHELL を使う必要があります。
  • Redis が Unix ソケットで動作している場合は redis-cli の接続先を調整してください。

test サービスの read-only マウントに関するリスクと代替案

read-only マウントはイミュータブル性を保てますが、テストやマイグレーションで書き込みが必要だと失敗します。

  • 代替案:
  • CI ではソースをイメージに組み込んでマウントしない(推奨)。これによりファイルシステムの互換性問題を回避できます。
  • または rw マウント(./:/app:rw)にするか、一時ディレクトリだけを名付けボリュームで書き込み可能にする(例: - /tmp/testdata)。
  • 目的に応じて readonly を採用するか決めてください(セキュリティと互換性のトレードオフがあります)。

データ永続化・クリーンアップ、ネットワーク設計と並列テスト、ロギング/シークレット

ここではボリューム設計、並列実行時の名前衝突回避、ログ管理とシークレット運用の実務的な注意点を説明します。

ボリューム設計と初期化の注意点

docker-entrypoint-initdb.d に置いた初期化スクリプトは、Postgres コンテナのデータディレクトリが空のときのみ実行されます。既存ボリュームがあると初期データは読み込まれません。

  • 再初期化が必要な場合:
  • テスト用なら docker compose down -v でボリュームを削除してから再作成します。
  • データを保持する必要がある場合は pg_dump/restore でスナップショット運用を行うか、初期データをマイグレーションで整備してください。
  • CI 高速化の工夫:
  • ベースイメージに初期 DB を bake しておき起動を早める(pg_dump → イメージに組み込む)。
  • 最小限のマイグレーション+シードのみ実行する。

COMPOSE_PROJECT_NAME とクリーンアップ運用(集約)

CI でのリソース衝突を避けるためにプロジェクト名/ボリューム名や DB 名を一意化する運用を標準化します。またクリーンアップ手順は1箇所にまとめて確実に実行してください。

  • 一意化の方法例:
  • export COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_PREFIX}-${CI_JOB_ID:-local}-${GITHUB_RUN_ID:-}
  • あるいは環境ごとに POSTGRES_DB=test_${CI_JOB_ID}
  • クリーンアップ(CI の最後に必ず実行)
  • docker compose -f docker-compose.yml -f docker-compose.ci.yml down -v --remove-orphans
  • 名前付きボリュームを手動で削除する場合: docker volume rm
  • 注意:
  • down -v は重要ですが多数の箇所で繰り返すと冗長になります。CI ワークフローの一箇所(after_script / teardown)に集約してください。

ロギングとアーティファクトサイズ管理

CIでコンテナログをそのままアーティファクトにする場合、巨大化に注意してください。対策を必ず入れます。

  • ログの切り出し・圧縮例:
  • docker compose logs --no-color | tail -n 2000 | gzip -9 > compose.log.gz
  • docker compose logs --no-color --since 10m > compose-recent.log
  • ポイント:
  • 長時間のジョブや大量ログ出力のテストではログをtailしてアップロードするか、圧縮してアップロードしてください。
  • Artifact ストレージ上限を超えないように事前に制限を設けます。

シークレットの扱いと docker stack deploy / docker compose の違い

Compose の secrets 機能は環境によって実装差があります。Swarm の secret と docker compose plugin の扱いは異なるため注意してください。

  • 違いの概略:
  • docker stack deploy(Swarm): Swarm のシークレットストアに登録し、サービスに安全に注入する。Swarm の機能に依存する。
  • docker compose(プラグイン): Compose Spec による secrets サポートがあるが、実装や扱いが Swarm と完全一致しない場合がある。古い docker-compose ではサポートが弱い。
  • 実務的な推奨:
  • ポータビリティの高い運用としては CI のシークレット機能で環境変数注入か、ジョブ内で一時ファイルを作成してマウントする運用が無難です(ジョブ終了時に削除)。
  • 機密性が高い場合は自己管理 runner + Swarm/KMS などの仕組みを検討してください。

CI統合、代替技術との使い分け、トラブルシューティングと実務テンプレートの実行手順

この記事のテンプレートを CI に組み込む際の具体例と、Testcontainers や Kubernetes といった代替技術の使い分けを示します。CI ランナー固有の制約に注意が必要です。

GitHub Actions/GitLab CI の実例とランナー設定注意

このブロックの目的: GitHub Actions と GitLab CI での代表的な実装例と、DinD(Docker-in-Docker)やソケット共有の注意点を示します。想定される利用シナリオ(CI): hosted runner と self-hosted runner の違いに応じた設定。

  • GitHub Actions(ホストされている ubuntu-latest 上で docker compose を直接使う例)
  • hosted runner は既に Docker が入っているため、多くの場合 docker compose がそのまま動きます。
  • buildx を使う場合は docker/setup-buildx-action を導入すると安定します。

このブロックの目的: GitHub Actions の例ワークフロー。想定される利用シナリオ(CI): 普通の統合テスト(self-hosted を必要としないケース)。

  • GitLab CI(docker:24 + docker:24-dind を使う例)
  • dind をサービスとして使う場合は runner 側で privileged: true を許可する必要があることが多いです。共有ランナーでは制限されている場合があるため事前に runner 設定を確認してください。
  • 代替として self-hosted runner やソケット共有(/var/run/docker.sock)を検討します。ソケット共有はセキュリティリスクが大きい点に注意。

このブロックの目的: GitLab CI の例(DinD 使用)。想定される利用シナリオ(CI): 自己管理 runner または privileged が許可された環境。

代替技術の比較と使い分け(簡易表)

このブロックの目的: Compose / Testcontainers / Kubernetes の実務的な選択基準を示します。想定される利用シナリオ(選定時の意思決定): プロジェクト要件に合わせて技術を選ぶ参考にしてください。

技術 長所 短所 選ぶべきケース
Docker Compose 導入が簡単でローカル開発と親和性が高い 大規模なクラスター本番挙動と差異が出る 軽量な統合テスト、ローカルとCIの共通構成
Testcontainers テストコード内でライフサイクル管理可能、分離が容易 テスト毎にコンテナ起動で遅くなることがある 単体テストで外部依存を隔離したい場合
Kubernetes (Kind/k3s) 本番クラスターに近い検証が可能 セットアップとリソースコストが高い 本番に近いE2Eや複雑なオーケストレーション検証

よくある失敗と即効対処(Q&A)

  • 問題: サービスが接続できない(Connection refused)。
  • 対処: docker compose ps / docker compose logs を確認し、healthcheck と待ち合わせスクリプトのログを見直す。
  • 問題: depends_on で readiness が保証されていると思っていた。
  • 対処: depends_on は起動順のみ。readiness は healthcheck と待機ロジックで保証してください。
  • 問題: CI 上で DinD が使えない / 権限エラー。
  • 対処: runner 設定を確認し、privileged が許可されないなら self-hosted runner やソケット共有、あるいは buildx を使ったリモートビルドを検討してください。
  • 問題: 古いボリュームが残ってテストに影響する。
  • 対処: CI の teardown で docker compose down -v を実行するか、手動でボリュームを削除します。永続化が必要ならスナップショット戦略に切り替えます。

実務向けテンプレートのローカル実行とCI組み込み(実行手順)

ここではテンプレートを手元で動かし、問題を切り分けるための段階的手順を示します。手順通りに実行してログを確認してください。

  • ローカルでの素早い検証手順(推奨)
  • リポジトリを clone して .env.example をコピーし .env を作成します。
    • cp .env.example .env
  • ローカル専用の override を確認(docker-compose.override.yml)。
  • ローカル検証実行:
    • docker compose -f docker-compose.yml -f docker-compose.override.yml up --build --abort-on-container-exit --exit-code-from test
  • 結果のログを確認・切り出し:

    • docker compose logs --no-color | tail -n 2000 > local-compose.log
  • CI への組み込み(概略)

  • CI 環境に必要なシークレットを登録します(POSTGRES_PASSWORD 等)。
  • CI のワークフローに docker compose 起動ステップを追加します(上記 GitHub Actions 例を参照)。
  • CI では必ずユニークな COMPOSE_PROJECT_NAME を使う、または DB 名を差し替えて衝突を防ぎます。
  • テスト後は CI の teardown で docker compose down -v --remove-orphans を実行します。
  • 失敗時はログを tail して圧縮したアーティファクトを保存してください。

まとめ

Docker Compose テスト環境をローカルとCIで共通化すると、再現性とデバッグ効率が向上します。ここでは即利用できる .env.example、待ち合わせスクリプト、ビルド最適化、CI 統合の注意点を実務的に示しました。

  • .env.example を用意してすぐ試せる状態にすること
  • healthcheck は簡易判定、待ち合わせスクリプトでマイグレーション等を確実に行うこと
  • test コンテナの read-only マウントはリスクがあるためCIではイメージ内実行や rw を検討すること
  • COMPOSE_PROJECT_NAME / DB 名は CI で一意化し、クリーンアップを teardown に集約すること
  • Dockerfile はマルチステージでビルド依存を分離しイメージサイズを抑えること

これらを踏まえてテンプレートを手元で検証し、CI 環境に順次組み込んでください。

スポンサードリンク

もっとスキルを活かしたいエンジニアへ

スポンサードリンク
働き方から選べる

無料で使えて良質な案件の情報収集ができるサービス

エンジニアの世界では、「いつでも動ける状態を作っておけ」とよく言われます。
技術やポートフォリオがあっても、自分に合う案件情報を日常的に見れていないと、いざ動こうと思った時に比較や判断が難しくなってしまいます。
普段から案件情報が集まる環境を作っておくと、良い案件が出た時にすぐ動きやすくなりますよ。
筆者自身も、メガベンチャー勤務時代に年収1,500万円を超えた経験があります。振り返ると、技術だけでなく「どんな案件や働き方があるか」を日頃から見ていたことが、キャリアの選択肢を広げるきっかけになりました。
このブログを読んでくれた方に感謝を込めて、実際に使っている情報収集サービスを紹介します。

フルリモート・週3日・高単価、どんな条件も妥協したくないなら

フリーランスボードに無料会員登録する

利用者10万人以上。業界最大規模45万件の案件。AIマッチ機能や無料の相場情報が人気。

年収800万円以上のキャリアアップ・ハイクラス正社員を視野に入れているなら

Beyond Careerに無料相談する

内定獲得率90%以上。紹介先企業とは役員クラスのコネクションがある安心と信頼できるエージェント。


-Docker