肥大化したdocker-compose.ymlという「悪夢」
実際のマイクロサービスプロジェクトに携わったことがある方なら、docker-compose.ymlファイルが際限なく長くなっていく光景に見覚えがあるでしょう。私はかつて、15個のマイクロサービスと5つのデータベースを持つEコマースシステムを管理していました。その結果、設定ファイルは800行を超えるほどに膨れ上がりました。
その弊害は明らかです。情報の検索が極めて困難になります。わずかなインデント(indentation)のミスだけでシステム全体がダウンすることもあります。チーム内で5〜7人が同時に1000行のYAMLファイルを編集すると、Gitのマージ時にコンフリクト(競合)という「惨事」が頻発します。複雑に絡み合ったサービスの構成の競合を解決するためだけに、丸一日潰してしまったこともありました。
なぜComposeファイルは肥大化し続けるのか?
実のところDockerが悪いわけではなく、私たちの「詰め込みすぎ」が原因です。最初はWebとDBだけで十分かもしれませんが、規模が拡大するにつれて、以下のような要素が必要になります。
- キューイングシステム:RabbitMQ、Redis、Kafka
- 個別のビジネスサービス:Auth、Order、Payment、Shipping
- 監視ツール:Prometheus、Grafana
- ログ管理:ELKスタックやLoki
「動かしやすいようにすべて一箇所にまとめる」という考え方が、設定ファイルをメンテナンス不可能なスパゲッティ状態に変えてしまうのです。
include機能が登場する前の分割への試み
Docker Compose V2(バージョン2.20.0以降)が登場する前は、主に以下の2つの方法が使われていましたが、どちらも制限がありました。
方法1:-fフラグで複数のファイルを使用する
docker-compose.db.ymlやdocker-compose.app.ymlのように分割します。起動時には、次のような非常に長いコマンドを入力する必要があります。
docker compose -f docker-compose.yml -f docker-compose.db.yml -f docker-compose.app.yml up -d
-fフラグを一つ忘れるだけでトラブルの元になります。コンテナ間でお互いが見えなくなったり、ボリュームのマウント先が間違っていてデータが消えたりすることもあります。
方法2:extendsを使用する
extendsキーワードは、サービス間での設定の再利用に役立ちます。しかし、これは表面的な解決に過ぎません。ネットワークやボリュームを含むリソースグループ全体をすっきりとモジュール化することはできません。
最適なソリューション:include機能によるモジュール化
私はプロジェクトのスタック全体をDocker Compose V2に移行し、その快適さに驚きました。include機能を使えば、関連するサービスグループを個別のファイルに切り出し、メインファイルにプロフェッショナルな形で「組み込む」ことができます。
includeの最大のメリットとは?
最大の違いは、相対パス(relative path)を自動的に処理できる点です。別のディレクトリにあるファイルをincludeする場合、Docker Composeはbuild contextやenv_fileなどのパスを、includeされたファイルの場所を基準に自動的に解釈してくれます。ファイルを移動するたびに手動でパスを修正する必要はもうありません。
実践例:マイクロサービスプロジェクトの構造
インフラ(DB、Redis)とアプリケーション(Web、Worker)を以下のように構成したと仮定します。
.
├── docker-compose.yml
├── infrastructure/
│ └── db-stack.yml
└── services/
└── app-stack.yml
infrastructure/db-stack.ymlの内容は非常にシンプルです:
services:
postgres:
image: postgres:15-alpine
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
そして、プロジェクトルートのdocker-compose.ymlからすべてを呼び出す方法は以下の通りです:
name: my-ecommerce-platform
include:
- infrastructure/db-stack.yml
- services/app-stack.yml
services:
gateway:
image: nginx:alpine
ports:
- "80:80"
depends_on:
- web
これで、たった一つのdocker compose up -dコマンドだけで、Dockerがすべてのモジュールを自動的に統合し、完全なスタックとして起動します。
今すぐincludeを使うべき4つの理由
- 明確な役割分担:DevOpsチームが
infrastructureフォルダを管理し、開発チームはservicesに集中できます。コード編集時の競合を防げます。 - 高速な再利用:標準的なDB構成(ゴールドスタンダード)を作成しておけば、30秒で別の10個のプロジェクトにincludeできます。
- 超速デバッグ:エラーが発生した場所のモジュールファイルを開くだけで済みます。メインファイルは全体像を示す「地図」のような役割になります。
- スマートなネットワーク管理:メインファイルの
name属性により、異なるモジュールのすべてのコンテナが自動的に同じデフォルトネットワークに属します。
実践から得た教訓
導入を進める中で、いくつか注意すべき点が見つかりました:
docker psコマンドでコンテナを管理しやすくするため、常にメインファイルでname:を使用してプロジェクト名を指定しましょう。- 環境変数の競合を避けるため、モジュールごとに個別の
.envファイルを活用してください。 - 細かく分けすぎないようにしましょう。サービスごとにファイルを分けるのではなく、ビジネスロジック(例:Paymentグループ、Shipグループ)に基づいてグループ化してください。
includeに移行したことで、プロジェクトをスケールさせる際の手間が少なくとも30%削減されました。煩雑なYAMLファイルに悩まされているなら、今すぐ導入してみてください。これは単なる技術的な改善ではなく、システムをよりスマートに管理するための方法です。

