Docker ComposeのYAMLアンカーとExtension fields(x-プレフィックス)でcompose.ymlの重複を排除する方法

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Docker > Docker ComposeのYAMLアンカーとExtension fields(x-プレフィックス)でcompose.ymlの重複を排除する方法
「コンテナが5個を超えたあたりから、compose.ymlが手がつけられないほど膨らんでしまった」

これはよく聞く現場の声です。Webサーバー・DBコンテナ・ジョブワーカー・メールモック・ログ収集エージェントと構成が増えるにつれ、全サービスに同じlogging設定やenvironment変数を何度もコピーペーストする作業が繰り返されます。設定を1か所変えると、10か所を修正しなければならない状態です。

この記事では、YAMLの標準機能であるアンカー(&・*・<<)と、Docker Compose固有のExtension fields(x-プレフィックス)を組み合わせてcompose.ymlの重複設定を削減する方法を解説します。最後に、docker compose configコマンドで設定が正しく展開されているかを確認する手順まで含めます。

動作確認環境: Docker Compose v2.24.6 / RHEL 9.4、Ubuntu 24.04 LTS

この記事のポイント

・YAMLアンカー(&)でブロックに名前を付け、エイリアス(*)で複数サービスから参照できる
・<<(マージキー)でアンカーを展開し、個別サービスの設定にマージできる
・Extension fields(x-プレフィックス)でアンカー定義をファイル先頭に集約して管理できる
・docker compose configで展開後の設定を確認してから本番へ適用するのが安全な手順


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜcompose.ymlは肥大化するのか(重複設定の正体)

Docker Composeで複数のサービスを運用していると、ほぼ確実に「同じ設定のコピー問題」にぶつかります。典型的なのがlogging設定です。json-fileドライバでログのローテーションを設定する場合、全サービスにこのブロックを記述する必要があります。

また、アプリのタイムゾーン(TZ)・ログレベル(LOG_LEVEL)・再起動ポリシー(restart)なども、複数サービスで共通して必要なケースが多いです。次のcompose.ymlを見てください。

# 問題のあるcompose.yml(重複設定が3か所に散在している) services: web: image: myapp:latest ports: - "8000:8000" restart: unless-stopped logging: driver: "json-file" options: max-size: "10m" max-file: "3" environment: - LOG_LEVEL=info - TZ=Asia/Tokyo worker: image: myapp:latest command: python worker.py restart: unless-stopped logging: driver: "json-file" options: max-size: "10m" max-file: "3" environment: - LOG_LEVEL=info - TZ=Asia/Tokyo scheduler: image: myapp:latest command: python scheduler.py restart: unless-stopped logging: driver: "json-file" options: max-size: "10m" max-file: "3" environment: - LOG_LEVEL=info - TZ=Asia/Tokyo

logging設定が3か所、環境変数が3か所、restartが3か所に分散しています。「json-fileのmax-sizeを10mから50mに変えたい」となった瞬間、3か所を同時に修正する必要があり、1か所でも直し漏れがあれば動作が不統一になります。

こういった問題を解決するのが、YAMLのアンカー機能とDocker ComposeのExtension fieldsです。

YAMLアンカーの基本(&・*・<<の3つの記号)

YAMLにはプログラミング言語の「変数」に相当する機能が組み込まれています。アンカー(Anchor)エイリアス(Alias)がそれです。Docker Compose固有の機能ではなく、YAML仕様(1.2)に含まれる標準機能なので、どのYAMLパーサーでも動作します。

1. &でアンカーを定義する

アンカーは &アンカー名 という書き方でYAMLノードに名前を付けます。名前はハイフンやアンダースコアを含む英数字で自由に命名できます。

# &logging-config でアンカーを定義する logging: &logging-config driver: "json-file" options: max-size: "10m" max-file: "3"

&logging-config を付けると、この logging ブロック全体に「logging-config」という名前が付きます。アンカー定義は参照が登場する前に書く必要があります。

2. *でエイリアスを参照する

*アンカー名 と書くことで、アンカー定義を参照(展開)できます。参照先のブロック全体がそのまま置き換えられます。

services: web: image: myapp:latest logging: &logging-config # アンカーをここで定義 driver: "json-file" options: max-size: "10m" max-file: "3" worker: image: myapp:latest command: python worker.py logging: *logging-config # アンカーを参照(展開) scheduler: image: myapp:latest command: python scheduler.py logging: *logging-config # 同様に参照

*logging-config と書いた部分は、アンカー定義のブロック全体に置き換えられます。logging設定の管理が1か所に集約されました。ただしこの書き方では、アンカー定義がサービスの途中に埋め込まれるため見つけにくい問題があります。この問題はExtension fieldsで解決します。

3. <<(マージキー)でブロックをマージする

*による参照はブロック全体を「そのまま置き換える」動作です。これに対して <<: *アンカー名 という書き方を使うと、参照先のマッピング(Key-Valueブロック)を現在のマッピングにマージできます。

マッピング(Mapping)とはKey: Valueのペアの集合です。YAMLでいうと字下げされたKey: Valueの塊のことです。マージキーが威力を発揮するのは「共通設定を持ちつつ、一部だけ個別サービスで上書きしたい」ケースです。

# 共通設定をアンカーで定義 x-common-settings: &common-settings restart: unless-stopped logging: driver: "json-file" options: max-size: "10m" max-file: "3" services: web: <<: *common-settings # 共通設定をマージ image: myapp:latest ports: - "8000:8000" worker: <<: *common-settings # 共通設定をマージ image: myapp:latest command: python worker.py restart: on-failure # restartだけ個別に上書き scheduler: <<: *common-settings # 共通設定をマージ image: myapp:latest command: python scheduler.py

workerサービスは <<: *common-settings で共通設定を取り込みつつ、restart: on-failure で個別の値を上書きしています。マージキーより後に書いたキーが優先されます。この挙動を利用して「デフォルト値を共通定義し、例外だけ個別指定」という設計が実現できます。

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、YAMLアンカーを含む実践的なDocker Compose設計手法をハンズオン形式でまとめた講座を用意しています。
Dockerマスター講座の詳細はこちら >>

Extension fields(x-プレフィックス)でアンカーをまとめる

前のセクションのコード例を見てください。x-common-settings: という書き方が出てきました。これがDocker Compose固有のExtension fields(拡張フィールド)です。

1. x-プレフィックスとは何か

Docker Compose Specでは、トップレベルのx-で始まるキーはDocker Composeが完全に無視する仕様になっています。実際には何も処理されませんが、YAMLとしては有効なので、アンカーの定義場所として利用できます。

「x-プレフィックスなしのアンカー」と「x-プレフィックスありのアンカー」の実質的な違いは、アンカー定義をどこに置くかです。

x-プレフィックスなし: アンカー定義を初めて使う場所(最初のサービス内など)に書く必要がある。ファイルの中盤に埋め込まれて見つけにくい。
x-プレフィックスあり: ファイルの先頭にまとめて書ける。設定の一覧性が高く、変更が容易になる。

x-プレフィックスはDocker Compose v2.x(Compose Spec準拠)で利用できます。古い version: "3" スキーマでも動作します。

2. x-プレフィックスを使った実装例

# compose.yml(x-プレフィックスでアンカーをファイル先頭に集約) # --- ここに共通設定をまとめる(x-プレフィックスはDockerが無視する) --- x-logging: &x-logging driver: "json-file" options: max-size: "10m" max-file: "3" x-common-env: &x-common-env LOG_LEVEL: info TZ: Asia/Tokyo APP_URL: http://api:8080 x-base-config: &x-base-config restart: unless-stopped logging: *x-logging # --- 各サービスの定義 --- services: web: <<: *x-base-config image: myapp:latest ports: - "8000:8000" environment: <<: *x-common-env APP_ROLE: web worker: <<: *x-base-config image: myapp:latest command: python worker.py environment: <<: *x-common-env APP_ROLE: worker CONCURRENCY: "4" scheduler: <<: *x-base-config image: myapp:latest command: python scheduler.py environment: <<: *x-common-env APP_ROLE: scheduler

x-logging・x-common-env・x-base-configの3つのアンカーをファイル先頭で定義しています。各サービスは <<: *x-base-config でまとめて取り込み、個別の値だけを追記します。新しいサービスを追加する際も、共通設定の存在をひと目で確認できます。

実践パターン:よく共通化する3つの設定

実際の現場で最もよく共通化される設定パターンを3つ紹介します。

1. ロギング設定のDRY化

ログの肥大化防止はコンテナ運用の基本です。json-fileドライバのmax-sizeとmax-fileを全サービスに統一するパターンは、最も一般的な活用例です。

x-logging: &x-logging driver: "json-file" options: max-size: "50m" # 1コンテナあたりの最大ログサイズ max-file: "5" # ローテーション世代数(古いものから削除) services: web: image: nginx:alpine logging: *x-logging api: image: myapi:latest logging: *x-logging worker: image: myapp:latest logging: *x-logging

max-sizeを変更する場合はx-loggingの定義1か所を修正するだけで、全サービスに即座に反映されます。ログの肥大化問題でサーバーのディスクが圧迫された経験のあるエンジニアなら、この一元管理の価値がすぐにわかるはずです。

2. 環境変数のDRY化

マッピング形式(KEY: VALUE形式)のenvironmentはマージキーと組み合わせられます。共通の設定を定義しておき、サービス固有の値だけを追記する設計です。

x-base-env: &x-base-env TZ: Asia/Tokyo LOG_LEVEL: info DB_HOST: db DB_PORT: "5432" REDIS_HOST: redis services: web: image: myapp:latest environment: <<: *x-base-env APP_ROLE: web PORT: "8000" SESSION_SECRET: "${SESSION_SECRET}" # .envから注入 worker: image: myapp:latest command: python worker.py environment: <<: *x-base-env APP_ROLE: worker CONCURRENCY: "4"

注意:environmentをリスト形式(- KEY=VALUE形式)で書いている場合はマージキーが使えません。マッピング形式への統一が必要です。なお、機密情報(パスワード・トークン等)はcompose.ymlに直接書かず、.envファイルと ${変数名} 参照でファイル外から注入することを強く推奨します。

3. デプロイ設定のDRY化(deploy.resources)

本番環境でコンテナにCPU・メモリ上限を設定する場合、deploy.resourcesのブロックも共通化できます。リソース枠の種類(小・大)を定義しておき、サービスの規模に応じて使い分けるパターンです。

# リソース枠の定義(小・大の2種類) x-resource-small: &x-resource-small deploy: resources: limits: cpus: "0.50" memory: 512M reservations: cpus: "0.25" memory: 256M x-resource-large: &x-resource-large deploy: resources: limits: cpus: "2.00" memory: 2048M reservations: cpus: "1.00" memory: 1024M services: web: <<: *x-resource-large # 大きめのリソース枠を適用 image: myapp:latest ports: - "8000:8000" worker: <<: *x-resource-small # 小さめのリソース枠を適用 image: myapp:latest command: python worker.py

リソース上限の値を変更する際も各アンカー定義1か所を修正するだけで済みます。CPU・メモリ上限の設定は、コンテナを本番運用する際にホスト側のリソースを守るための重要な設計です。

落とし穴とトラブルシュート

YAMLアンカーを使う際に必ず理解しておくべき制限と、設定確認の手順を解説します。

配列はマージされず上書きになる問題

マージキー(<<)が機能するのはYAMLのマッピング(Key-Valueペア)に限定されています。シーケンス(配列・リスト形式)には使えません。

environmentをリスト形式(- KEY=VALUE)で書いている場合、次のような書き方はできません。

# NG例: リスト形式のアンカーを << でマージしようとした場合 x-base-env: &x-base-env - TZ=Asia/Tokyo - LOG_LEVEL=info services: web: environment: <<: *x-base-env # エラーになる(シーケンスにマージキーは使えない) - PORT=8000 # docker compose up を実行するとエラーが発生する例: # Error response from daemon: Merge key and its value must be a mapping or # a list of mappings

リスト形式のアンカーを * で参照すると「リスト全体をそのまま置き換える」動作になるため、個別サービスの変数を追加できません。

解決策: environmentはマッピング形式に統一する

リスト形式(- KEY=VALUE)からマッピング形式(KEY: VALUE)に変換すれば << が使えるようになります。

# OK例: マッピング形式に変換してから << でマージ x-base-env: &x-base-env TZ: Asia/Tokyo LOG_LEVEL: info services: web: environment: <<: *x-base-env # 正常にマージされる PORT: "8000" # 個別の値を追加できる APP_ROLE: web # 個別の値を追加できる

docker compose configで合成後の設定を確認する

アンカーとExtension fieldsを組み合わせたcompose.ymlが意図した通りに展開されているかを確認するには、docker compose configコマンドを使います。このコマンドはアンカーやExtension fieldsを解決した後の最終的な設定をYAML形式で出力します。

# compose.ymlの展開結果を確認する $ docker compose config # 特定サービスだけ確認する $ docker compose config web # JSON形式で出力する(jqで絞り込みたい場合に便利) $ docker compose config --format json | jq '.services.web'

実際にサーバー(app-server01.example.internal)でdocker compose configを実行した出力の一部です。x-プレフィックスのフィールドが含まれず、アンカーが各サービスに展開された最終形が確認できます。

# 実行環境: app-server01.example.internal / Docker Compose v2.24.6 $ docker compose config name: myproject services: scheduler: command: python scheduler.py environment: APP_ROLE: scheduler LOG_LEVEL: info TZ: Asia/Tokyo image: myapp:latest logging: driver: json-file options: max-file: "3" max-size: 10m networks: default: null restart: unless-stopped web: environment: APP_ROLE: web LOG_LEVEL: info PORT: "8000" TZ: Asia/Tokyo image: myapp:latest logging: driver: json-file options: max-file: "3" max-size: 10m networks: default: null ports: - mode: ingress target: 8000 published: "8000" protocol: tcp restart: unless-stopped worker: command: python worker.py environment: APP_ROLE: worker CONCURRENCY: "4" LOG_LEVEL: info TZ: Asia/Tokyo image: myapp:latest logging: driver: json-file options: max-file: "3" max-size: 10m networks: default: null restart: unless-stopped networks: default: name: myproject_default

x-logging・x-common-env・x-base-configの定義が各サービスに正しく展開されていることが確認できます。logging.options.max-size: 10m が3サービス全てに共通で適用されています。コンテナを起動する前にこの出力で設定を確認する習慣をつけておくと、起動後のトラブルを大幅に減らせます。

また、コンテナ起動後にサービスが指定のポートでリッスンしているかは、ssコマンドやlsofを使ったポート確認で素早く検証できます。

本記事のまとめ

YAMLアンカーとExtension fieldsを使ってcompose.ymlの重複を排除する手法を解説しました。それぞれの機能と使い分けを表にまとめます。
機能 記号・書き方 用途・使いどころ
アンカー定義 &アンカー名 再利用したいブロックに名前を付ける
エイリアス参照 *アンカー名 アンカー定義のブロック全体をそのまま展開する
マージキー <<: *アンカー名 共通設定をマージしつつ個別値を追加・上書きする
Extension fields x-名前: &アンカー名(ファイル先頭) アンカー定義をファイル先頭に集約して可視性を高める
設定確認 docker compose config 展開後の最終設定を起動前に確認する
YAMLアンカーは強力ですが、使いすぎると「参照を追うのが大変で結果的に読みにくくなる」という逆効果も起こります。共通化する設定はlogging・基本環境変数・リソース枠など、全サービスで確実に統一したい設定に絞るのが運用上のコツです。

なお、環境(開発・本番)でサービスの起動有無を切り替えたい場合はDocker Composeのprofiles機能が適しています。アンカーとprofilesは相互排他ではなく、同一のcompose.yml内で組み合わせて使えます。

次に読む記事

DockerのENV・ARG・env_fileを正しく使う設計|ビルド時と実行時の値の渡し方とsecrets
Docker Composeのprofilesで開発・本番サービスを切り替える方法|環境別コンテナ起動の実践設計
Linuxのポート確認コマンド完全ガイド|ssとlsofの使い分け

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、YAMLアンカー・Extension fields・profilesを含む実践的なDocker Compose設計手法をハンズオン形式でまとめた講座を用意しています。
Dockerマスター講座の詳細はこちら >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。