
Docker Composeのprofiles機能は、この起動構成をサービス単位で切り替えるための仕組みである。Composeファイルを開発用、テスト用、本番用に分割せず、同じ定義の中で起動対象を制御できる。
ただし、profilesは環境変数やシークレットを分離する機能ではない。ネットワークやボリュームの設計を自動で安全にする機能でもない。担当するのは、あくまでサービスの有効化である。この境界を理解せずに導入すると、起動コマンドだけが複雑になる。
この記事では、Docker Composeのprofilesを開発環境とテスト環境で使い分ける方法を、起動ルール、依存関係、複数プロファイル、運用上の制約に分けて整理する。
profilesの基本動作
Composeのサービスは、profiles属性を持つかどうかで扱いが変わる。
profilesを指定していないサービスは、プロファイルを指定せずにdocker compose upを実行しても起動対象になる。これが常時有効なサービスである。Laravelであれば、アプリケーション本体やデータベースなど、通常の開発で必ず必要になるサービスが該当する。
一方、profilesを指定したサービスは、そのプロファイルが有効な場合だけ起動する。例えば、デバッグ用サービスにdebug、テスト用サービスにtestを割り当てる設計である。
概念的には、次のように分類する。
| サービスの定義 | docker compose up | プロファイル指定時 |
|---|---|---|
profilesなし | 起動する | 起動する |
profiles: ["debug"] | 起動しない | debug有効時に起動 |
profiles: ["test"] | 起動しない | test有効時に起動 |
profiles: ["debug", "test"] | 起動しない | どちらか有効時に起動 |
ここで注意すべきなのは、プロファイルを付けたサービスが「通常起動するが、必要に応じて追加されるサービス」ではない点である。明示的にプロファイルを有効化しない限り、対象外になる。
したがって、主要サービスに安易にprofilesを付けてはいけない。例えばapp、db、webにプロファイルを付けると、通常のdocker compose upではこれらも起動しなくなる。コアサービスはprofilesなしで定義し、追加機能だけにプロファイルを付ける構成が基本である。
profilesはサービスを分類する機能ではない。起動対象を明示的に絞り込む機能である。開発環境での使い方
開発環境では、常時必要なサービスと、特定の作業だけで必要なサービスを分けると効果が出る。
例えば、Laravelの開発環境で常時起動するサービスがapp、db、webであるとする。これに加えて、次のようなサービスを使うケースがある。
- メール送信を確認するためのメールモック
- Xdebugなどを使ったデバッグ用サービス
- ブラウザテスト用のブラウザコンテナ
- ログ収集やメトリクス確認用の監視コンテナ
- 非同期ジョブの動作確認に使う専用ワーカー
- 開発時だけ必要な管理ツール
これらをすべて通常起動に含めると、開発者が単純に画面を確認したいだけでも、不要なコンテナが起動する。メモリ使用量が増える。ポートの競合も起きる。問題の切り分け対象も増える。
この場合、app、db、webはプロファイルなしにする。メールモックにはmail、ブラウザテストにはbrowser、デバッグ用にはdebugを設定する。通常の起動では基幹サービスだけを起動し、必要な作業に応じて追加プロファイルを有効にする。
例えば、通常の開発環境を起動する場合はdocker compose upを使う。デバッグ用サービスを追加する場合はdocker compose --profile debug upを使う。メールモックとデバッグ環境を同時に使う場合は、docker compose --profile debug --profile mail upのように複数の--profileを指定できる。
この設計では、起動コマンドが作業内容を表す。何を起動したかがコマンドから読み取れる。サービスの一覧を毎回手作業で指定する必要もない。
開発用プロファイルの分け方
プロファイル名は任意に設定できる。dev、test、debugなどの名前が使われることは多いが、プロジェクト全体で統一された標準名があるわけではない。
実際には、環境名ではなく機能単位で切るほうが運用しやすい場合がある。
| 分け方 | 例 | 向いているケース |
|---|---|---|
| 環境単位 | dev、test | 環境ごとに起動サービスが明確に異なる |
| 機能単位 | debug、mail、browser | 必要な機能を個別に追加したい |
| 作業単位 | e2e、queue、benchmark | 特定の検証作業でだけ使う |
| 運用単位 | monitoring、admin | 監視や管理系サービスを分離したい |
devというプロファイルにすべての開発用サービスを集約すると、開発環境の一括起動は簡単になる。一方で、デバッグだけを有効化したい場合にもメールモックやブラウザまで起動する可能性がある。
機能単位で分けると組み合わせは増えるが、起動対象を細かく制御できる。サービス数が増えるプロジェクトでは、プロファイルの粒度を機能に寄せたほうが依存関係を把握しやすい。
プロファイルを一つのサービスに複数割り当てる
一つのサービスに複数のプロファイルを割り当てることもできる。例えば、ブラウザテスト用のサービスをtestとe2eの両方に属させる設計である。
この場合、対象サービスはtestまたはe2eのどちらかが有効になれば起動対象になる。プロファイルは排他的な所属ではない。
ただし、複数割り当てを増やしすぎると、どのコマンドでサービスが起動するのか分かりにくくなる。プロファイルの組み合わせが増えるほど、起動条件の計算量ではなく、運用上の認知負荷がボトルネックになる。
一つのサービスに複数プロファイルを付けるのは、意味が近い場合に限定するのが妥当である。testとe2eのように包含関係がある組み合わせは管理しやすい。一方、debug、production、temporaryのように意味の異なる軸を一つのサービスに混在させると、定義の意図が曖昧になる。
起動方法と環境変数
プロファイルを有効にする方法は主に二つある。コマンドラインオプションを使う方法と、環境変数を使う方法である。
--profileで一時的に有効化する
一時的な検証や、個人の作業で使う場合は--profileが適している。
docker compose --profile debug upでは、debugプロファイルを有効にしてサービスを起動する。複数の場合はdocker compose --profile debug --profile test upのように指定する。
この方法の利点は、実行したコマンドに起動条件が残ることだ。シェル履歴を見れば、どのプロファイルを使ったか分かる。環境変数の状態に依存しないため、別の端末でも再現しやすい。
欠点は、コマンドが長くなりやすいことである。プロファイルの組み合わせが毎回同じなら、後述する環境変数やタスクランナーで短縮したほうがよい。
COMPOSE_PROFILESで固定する
環境変数COMPOSE_PROFILESにプロファイル名を設定すると、そのシェル環境で有効なプロファイルを固定できる。
一つの場合はCOMPOSE_PROFILES=debug、複数の場合はCOMPOSE_PROFILES=frontend,debugのようにカンマ区切りで指定する。複数プロファイルをまとめて扱える点が特徴である。
開発チームでよく使う組み合わせを.envやシェルスクリプトにまとめる場合、この方法が便利になる。ただし、.envに設定したまま忘れると、意図しないサービスが常時起動する。特に、シェルの環境変数とComposeの変数展開を混同すると、調査に時間がかかる。
COMPOSE_PROFILESは、サービスの環境変数として渡される設定ではない。Compose CLIが起動時に参照する制御用の環境変数である。アプリケーションコンテナ内でCOMPOSE_PROFILESを読み取れることを前提にしてはいけない。
コマンドと環境変数の使い分け
使い分けは単純である。
- 一度だけ追加サービスを起動するなら
--profile - チームや作業単位で定型化するなら
COMPOSE_PROFILES - CIで明示性を重視するなら
--profile - ローカルのショートカットを作るなら環境変数やスクリプト
プロファイルの有効状態が見えにくくなる構成は避けるべきである。例えば、複数の.envファイルが読み込まれ、シェル側にもCOMPOSE_PROFILESが設定されていると、最終的な起動対象をコマンドだけで判断できない。
環境変数を利用する場合は、プロジェクトのREADMEに設定例を残す。実行前にCOMPOSE_PROFILESの値を確認できる運用にする。これは機能の問題ではなく、再現性の問題である。
テスト環境での設計
テスト用途では、アプリケーション本体とテスト専用サービスを分ける。
例えば、通常の開発では不要だが、テスト時だけ必要なサービスがある。
- テスト専用のデータベース
- ブラウザ操作を実行するコンテナ
- テスト用のメール受信サーバー
- 外部APIのモック
- キュー処理を検証するワーカー
- カバレッジ取得や静的解析の実行環境
これらをtestやe2eプロファイルに割り当てると、通常の開発起動から除外できる。ここでいう除外は、Composeファイルから消すことではない。定義は残したまま、通常の起動対象から外すという意味である。
docker compose --profile test upを実行すると、プロファイルなしの基幹サービスに加え、testを持つサービスが起動する。通常サービスとテスト用サービスが同じネットワークに参加する構成であれば、サービス名による接続も維持できる。
ただし、テスト用データベースをプロファイル付きにする場合は、アプリケーション側の接続先と起動順序を明確にする必要がある。プロファイルを有効にしていない状態では、そのデータベースサービス自体が存在しない。アプリケーションの環境変数だけを切り替えても、接続先コンテナは自動で用意されない。
docker compose runの注意点
テストコマンドを実行するためにdocker compose runを使う場合、プロファイルの挙動はupと同一ではない。
docker compose run <サービス名>で指定した対象サービスにプロファイルが割り当てられている場合、そのサービスのプロファイルは自動的に有効化される。しかし、depends_onで指定された依存サービスのプロファイルまで自動的に有効になるわけではない。
ここは誤解が多い。例えば、test-runnerがtestプロファイルに属し、test-dbもtestプロファイルに属しているとする。test-runnerからtest-dbに依存していても、docker compose run test-runnerだけでtest-dbが同じように有効化されるとは限らない。
依存関係があるから起動条件も追従する、という推測は成立しない。プロファイルの有効化とdepends_onの解決は別の処理である。
この構成を使う場合の対策は三つある。
1. docker compose --profile test run test-runnerのように、必要なプロファイルを明示する。
2. test-runnerと依存サービスに同じプロファイルを割り当てる。
3. テスト実行前にdocker compose --profile test up -dで依存サービスを起動しておく。
どれを採用するかは、CIの実行単位とローカル開発の使い方で決める。重要なのは、runが依存サービスのプロファイルまで自動で解決する設計ではないことだ。
docker compose runで対象サービスが選ばれても、依存サービスのプロファイルは別途設計する必要がある。サービスを除外する設計と依存関係
docker compose サービス 除外という目的でprofilesを使う場合、まず「除外」の意味を分ける必要がある。
一つは、通常の起動から外すこと。もう一つは、特定の環境では絶対に起動させないこと。この二つは異なる。
profilesが解決するのは前者である。debugを有効化しなければデバッグ用サービスは起動しない。しかし、誰かが--profile debugを付ければ起動できる。実行可能性を禁止する機能ではない。
本番環境でデバッグ用サービスを起動できないようにしたい場合、profilesだけでは不十分である。Composeファイルを環境別に分ける、デプロイパイプラインで使用ファイルを固定する、権限やネットワークを分離する、といった別の制御が必要になる。
depends_onとの組み合わせ
profilesを付けるサービス間でdepends_onを使う場合、起動条件の組み合わせを確認する。
例えば、adminerにtoolsプロファイルを付け、依存先のdbにはプロファイルを付けない。この場合、toolsを有効化すればadminerとdbの組み合わせは成立する。dbは常時有効だからである。
逆に、adminerとdbの両方にtoolsを付けると、通常起動では両方が対象外になる。toolsを有効にした場合だけ、両方が起動対象になる。
さらに、依存元と依存先でプロファイルが異なる場合は、組み合わせが壊れる可能性がある。
| 依存元 | 依存先 | 起動条件の問題 |
|---|---|---|
tools | プロファイルなし | tools有効時に成立しやすい |
tools | test | toolsだけでは依存先が対象外になる |
tools | tools | 同時有効化で成立する |
| プロファイルなし | test | 通常起動時に依存先が起動しない |
依存元が常時有効で、依存先だけがプロファイル付きという構成は特に危険である。通常のdocker compose upでは、依存先が存在しない状態になるからだ。
サービスの役割とプロファイルの役割を混ぜると、Composeの起動グラフが不安定になる。基幹サービスを常時有効にする理由は、依存関係の根を安定させるためでもある。
開発・テスト・本番を一つにまとめる場合
profilesを使うと、開発、テスト、本番のサービス定義を一つのcompose.ymlにまとめられる。これは管理対象を減らす効果がある。
一方で、一つにまとめることが常に正解ではない。環境差分が大きい場合、profilesの数が増え、条件の組み合わせが複雑になる。
一つのComposeファイルにまとめやすいケース
次のような差分であれば、profilesと環境変数の組み合わせで管理しやすい。
- 開発時だけデバッグ用サービスを追加する
- テスト時だけモックやブラウザを追加する
- 監視用サービスを必要なときだけ起動する
- 同じイメージを使い、起動する補助サービスだけが異なる
- ネットワークやボリュームの基本構成が共通している
この場合、サービス定義の重複を減らせる。変更漏れも抑えられる。
分割したほうがよいケース
次のような差分までprofilesで表現すると、設計が崩れやすい。
- 本番と開発でイメージの作り方が異なる
- ボリューム構成が完全に異なる
- ネットワーク境界が異なる
- シークレット管理の方法が異なる
- 公開ポートや権限が大きく異なる
- 本番では起動させてはいけない管理ツールが含まれる
- オーケストレーション方式自体が異なる
profilesは起動対象を制御するが、セキュリティ境界やデプロイ方式を代替しない。開発と本番を同じComposeファイルに置くこと自体が問題なのではない。差分の責務をprofilesだけに集約することが問題である。
開発・テスト用のComposeファイルと、本番デプロイ用の定義を分ける構成も合理的である。特に、本番環境でDocker Composeを直接使わず、クラウドサービスやコンテナオーケストレーションを使う場合は、無理に一つへ統合する必要はない。
プロファイル名と命名規則
プロファイル名には利用できる文字列の規則がある。正規表現では[a-zA-Z0-9][a-zA-Z0-9_.-]+に準拠する必要がある。
先頭は英字または数字で始める。続く文字には英字、数字、アンダースコア、ドット、ハイフンを使える。つまり、最低でも2文字以上、英数字と記号で構成される名前だけが受け入れられる。プロジェクト内でプロファイル名を増やす場合は、Composeが受け付ける形式だけでなく、意味の一貫性も決めておく必要がある。
例えば、次のような命名は読み取りやすい。
debugteste2emailbrowsermonitoringlocal-tools
反対に、ab、tmp1、new-debug2のような名前は、時間が経つと用途が分からなくなる。短さよりも、起動対象を推測できることを優先する。
環境名と機能名を混在させる場合は、命名規則を文書化する。devが環境全体を示すのか、開発者向け補助サービスだけを示すのかで意味が変わる。プロファイル名に曖昧さがあると、Composeファイルを読むコストが上がる。
プロファイルの設計表を先に作る
サービス定義を書く前に、起動構成を表にすると設計ミスが減る。
| サービス | 通常の開発 | デバッグ | テスト | ブラウザテスト |
|---|---|---|---|---|
| Laravelアプリケーション | 起動 | 起動 | 起動 | 起動 |
| データベース | 起動 | 起動 | 起動または専用DB | 起動 |
| リバースプロキシ | 起動 | 起動 | 必要時 | 必要時 |
| メールモック | 起動しない | 必要時 | 必要時 | 起動しない |
| デバッグ用サービス | 起動しない | 起動 | 起動しない | 起動しない |
| ブラウザ実行環境 | 起動しない | 起動しない | 起動しない | 起動 |
| テスト用モックAPI | 起動しない | 起動しない | 起動 | 起動 |
この表で「どの場面でも起動する」サービスは、profilesなしにできる可能性が高い。特定用途だけで必要なものは、用途に対応するプロファイルへ移す。
逆に、複数の環境で必要なサービスを無理に複数プロファイルへ登録すると、定義の意味がぼやける。常時必要なら、プロファイルなしのほうが単純である。
実行確認で見るべきポイント
profilesを追加したあと、確認すべきなのはコンテナが起動したかどうかだけではない。起動対象の集合が意図どおりかを確認する必要がある。
通常のdocker compose upでは、プロファイルなしのサービスだけが起動することを確認する。debugを指定した場合は、基幹サービスに加えてデバッグ用サービスが追加されることを確認する。testを指定した場合は、テスト用モックやテスト用データベースの接続先が正しいことを確認する。
確認項目は次のように整理できる。
- プロファイルなしの基幹サービスが、指定なしで起動する
- プロファイル付きサービスが、指定なしでは起動しない
--profileで指定したサービスだけが追加される- 複数プロファイルを指定した場合、重複起動や不要なサービスが発生しない
COMPOSE_PROFILESのカンマ区切り指定が意図どおり反映されるdepends_onの依存先が同じ起動条件を満たしているdocker compose runで依存サービスが不足しない- ポート番号がプロファイル間で競合しない
- ボリュームを共有するサービスの初期化順序が壊れていない
- テスト用サービスが開発用データを参照しない
ポート競合は、profiles導入後に発生しやすい問題である。通常の開発では起動しないサービスが、debugとtestを同時に有効化したときだけ起動し、同じホストポートを要求することがある。
サービスの起動条件を分けても、ホスト側のリソースは分離されない。ポート、メモリ、CPU、ボリューム名、ネットワーク名は、プロファイルの組み合わせを前提に設計する必要がある。
CIでの使い方
CIでは、起動条件を暗黙にしないほうがよい。COMPOSE_PROFILESをジョブ環境に常時設定するより、テストジョブのコマンドで--profile testを明示するほうが、ログから実行内容を追いやすい。
テストサービスを起動してからテストコマンドを実行する場合は、プロファイルの有効化とサービスの準備完了を分けて考える必要がある。profilesはサービスを起動対象に含めるだけで、アプリケーションが接続可能になるまで待機する仕組みではない。
例えば、テスト用データベースのコンテナが起動しても、データベースプロセスが接続受付を開始したとは限らない。Composeの起動完了と、依存サービスの可用性は別の状態である。
そのため、CIでは次の順序を明確にする。
1. testプロファイルを有効にして必要なサービスを起動する。
2. データベース、モックAPI、ブラウザ環境などの準備状態を確認する。
3. マイグレーションやテスト用初期化処理を実行する。
4. アプリケーションテストを実行する。
5. ログと終了コードを回収する。
6. 不要なコンテナとボリュームを後処理する。
この手順はprofiles固有の仕様ではない。しかし、起動対象を条件分岐できるようになると、起動完了だけを見てテストを始める実装が目立つようになる。そこで別のボトルネックが発生する。
CIでは、どのプロファイルを使ったかをログへ残す。テスト失敗時に、アプリケーションの不具合なのか、必要な補助サービスが起動していなかったのかを判定しやすくなる。
profilesで解決しない問題
profilesは便利だが、適用範囲は狭い。これを環境分離の全機能として扱うと、設計上の責務が混ざる。
環境変数の値は切り替わらない
debugプロファイルを有効にしたからといって、アプリケーションのAPP_DEBUGやデータベース接続先が自動で変わるわけではない。profilesが制御するのはサービスの起動対象である。
環境変数の値を切り替える場合は、Composeの変数展開、.envファイル、オーバーライド用Composeファイル、実行スクリプトなどを別途設計する必要がある。
ボリュームのデータは分離されない
開発用データベースとテスト用データベースを同じボリューム名で動かすと、プロファイルを分けてもデータは分離されない。サービスが別プロファイルに属していることと、永続データの保存先が別であることは無関係である。
テストで破棄可能なデータを使うなら、ボリューム名や起動プロジェクト名を分ける。既存データを誤って参照しないように、接続先と永続化方針を明示する。
ネットワーク境界は変わらない
同じComposeプロジェクト内のサービスをprofilesで切り替えても、ネットワーク設計が自動で変わるわけではない。デバッグ用サービスを起動したとき、そのサービスがどのネットワークへ接続できるかはComposeのネットワーク定義で決まる。
本番環境でアクセス範囲を制限したい場合は、ネットワークやアクセス制御を設計する。profilesにセキュリティ上の役割を持たせてはいけない。
イメージの安全性は変わらない
プロファイル付きサービスは通常起動されないだけで、定義から消えるわけではない。イメージの脆弱性、不要な権限、公開ポートの設定は、起動される可能性があるサービスとして管理する必要がある。
開発用の管理ツールやデバッグサービスを本番向けComposeファイルに残す場合は、誤起動時の影響を評価する。起動しないことを安全性の根拠にしてはいけない。
よくある設計ミス
profilesの導入で発生する問題は、構文エラーよりも起動モデルの誤解に起因することが多い。
主要サービスにプロファイルを付ける
app、db、webをdevプロファイルにまとめると、開発環境を明示できるように見える。しかし、プロファイル指定なしのdocker compose upでは、これらが起動しなくなる。
これは「開発環境だから常に起動する」という期待と、Composeの仕様が一致していない状態である。主要サービスを常時起動したいなら、profilesを付けない。環境全体を明示的に選択したいなら、別のComposeファイルや実行スクリプトを検討する。
プロファイル名を環境変数の代わりに使う
testプロファイルを有効化しただけで、Laravelがテスト用設定を使うと考えるのは誤りである。サービスの起動条件と、アプリケーション設定の注入は別レイヤーにある。
testの有効化と同時にテスト用環境変数を設定するなら、CIのジョブ定義やスクリプトで両方を明示する。片方だけを切り替える設計は再現性が低い。
すべてをプロファイル付きにする
プロファイルなしのサービスをゼロにする設計も可能ではある。しかし、毎回どのプロファイルを指定すべきか分からなくなる。複数サービスの依存関係も追いにくい。
常時必要なサービスは常時有効にする。追加機能だけをprofilesへ移す。この原則を崩す理由は少ない。
プロファイルを増やしすぎる
サービスごとに異なるプロファイルを設定すると、起動パターンが増える。debug、debug-local、debug-test、debug-fullのような名前が増え始めたら、プロファイルの設計を見直すべきである。
プロファイルは条件分岐の道具である。条件分岐が増えるほど、組み合わせの検証範囲も増える。サービス数とプロファイル数の関係を定期的に確認し、不要になった用途は削除する。
runの依存サービスを自動追従と考える
前述のとおり、docker compose runで対象サービスのプロファイルが有効化されても、depends_onにある他サービスのプロファイルが自動で有効化されるわけではない。
この仕様を知らずにテストコマンドを短縮すると、ローカルでは動くがCIでは失敗する、という差が生まれる。runを使う場合は、依存サービスのプロファイルを明示的に検証する必要がある。
実装前に決める設計方針
profilesはComposeファイルへ属性を追加するだけで使える。しかし、運用ルールを決めずに導入すると、コマンドの種類だけが増える。
最低限、次の方針を決めるとよい。
- プロファイルなしで起動するコアサービスを定義する
- 開発、テスト、デバッグの責務を分ける
- プロファイル名の命名規則を決める
- 一時利用は
--profile、定型利用はCOMPOSE_PROFILESと使い分ける - 複数プロファイルの標準的な組み合わせを文書化する
docker compose runで依存サービスがどう起動するかを確認する- テスト用ボリュームと開発用ボリュームを分離する
- CIでは有効化したプロファイルをログへ残す
- 本番のセキュリティ境界をprofilesに依存させない
- 不要になったプロファイルを定期的に削除する
特に重要なのは、標準コマンドを決めることである。例えば、通常開発はdocker compose up、デバッグはdocker compose --profile debug up、テストはdocker compose --profile test upという具合に、プロジェクト内で基本形を固定する。
複数プロファイルの組み合わせが必要なら、シェルスクリプトやタスク定義で名前を付ける。長いコマンドを各開発者が個別に覚える構成は、操作ミスを増やす。
まとめ
Docker Composeのprofilesは、一つのComposeファイルに複数の起動構成を持たせるための機能である。サービスを常時起動するものと、用途に応じて追加するものへ分けられる。
設計上の要点は次のとおりである。
profilesなしのサービスは、通常のdocker compose upで常時起動する- プロファイル付きサービスは、
--profileまたはCOMPOSE_PROFILESで有効化する - 複数プロファイルはカンマ区切り、または複数の
--profileで指定できる - コアサービスへ安易にプロファイルを付けない
docker compose runでは依存サービスのプロファイルが自動追従しない- profilesはサービスの起動制御であり、設定値やセキュリティ境界の分離機能ではない
- 開発とテストの差分が大きい場合は、Composeファイルの分割も選択肢になる
profilesの導入効果は、起動時間の短縮だけではない。不要なサービスを起動対象から外すことで、ポート競合、リソース消費、ログの混雑、依存関係の調査範囲を減らせる。
一方で、プロファイルの数が増えるほど起動条件の組み合わせは複雑になる。改善率だけを見て多用する機能ではない。常時必要なサービスを安定した基盤として残し、用途が限定されるサービスだけをprofilesへ移す。この線引きが、Docker Composeの構成を長期的に保つための実質的なトレードオフである。