ファイルのある場所から別の場所にYAMLブロックをコピーしたことがあれば、 アンカーとエイリアスが解決する問題にすでに直面したことがあります。大規模なKubernetes 設定、マルチ環境のDocker Composeファイル、複雑なCIパイプラインは、 同じブロック—リソース制限、環境変数、ボリュームマウント—を繰り返す傾向があります。 YAMLにはこれに対する組み込みの答えがあり、ほとんどの開発者はそれが存在することを知りません。

アンカー(&)とエイリアス(*)は、値を一度定義して同じファイルの他の場所で参照するYAMLのメカニズムです。 アンカーは変数の宣言、エイリアスはその使用と考えてください。 マージキー(<<)はこれをオブジェクトのマージに拡張します—JavaScriptのスプレッド演算子に似ています。 両方の機能はYAML 1.2仕様のノードアンカーセクションで定義されています。

アンカーとエイリアスの基本構文

構文は最小限です。アンカーを作成するには値に&名前をプレフィックスとして付け、 ドキュメントの任意の場所で*名前を使って参照します:

yaml
# Define the anchor once
default_timeout: &timeout 30

# Reference it multiple times
services:
  api:
    timeout: *timeout      # → 30
  worker:
    timeout: *timeout      # → 30
  scheduler:
    timeout: *timeout      # → 30

YAMLパーサーがこれを読むと、各エイリアスはアンカーとまったく同じ値に解決されます。 アンカーを変更するとすべてのエイリアスが更新されます。検索・置換は不要です。

<<キーによるオブジェクトのマージ

スカラーの再利用は便利ですが、本当の力はマッピングブロック全体のマージから来ます。 <<マージキーは参照されたアンカーのすべてのキーと値のペアを現在のマッピングに取り込みます:

yaml
# Define a base configuration block
base_service: &base
  restart: unless-stopped
  logging:
    driver: json-file
    options:
      max-size: "10m"
      max-file: "3"
  networks:
    - app-network

# Merge it into specific services
services:
  api:
    <<: *base              # merge all keys from base_service
    image: my-api:latest
    ports:
      - "8080:8080"

  worker:
    <<: *base              # same base config
    image: my-worker:latest
    command: ["node", "worker.js"]

  scheduler:
    <<: *base              # and again
    image: my-scheduler:latest
    command: ["node", "cron.js"]

マッピングに直接定義されたキーはマージされたアンカーのキーを上書きします。 baserestart: unless-stoppedを設定し、サービスも restart: noを設定している場合、サービスレベルの値が優先されます。

変更前と変更後:実際のDocker Compose

アンカーなしの典型的なマルチサービスDocker Composeファイルはどのように見えるか—繰り返しだらけです:

yaml
# BEFORE — repetitive and hard to maintain
version: "3.9"
services:
  api:
    restart: unless-stopped
    env_file: .env
    networks: [app-network]
    logging:
      driver: json-file
      options:
        max-size: "10m"
    depends_on:
      - postgres

  worker:
    restart: unless-stopped
    env_file: .env
    networks: [app-network]
    logging:
      driver: json-file
      options:
        max-size: "10m"
    depends_on:
      - postgres

  mailer:
    restart: unless-stopped
    env_file: .env
    networks: [app-network]
    logging:
      driver: json-file
      options:
        max-size: "10m"
    depends_on:
      - postgres

アンカーとマージキーを使うと—各繰り返しブロックが正確に一度だけ定義されます:

yaml
# AFTER — DRY and easy to maintain
version: "3.9"

x-common: &common
  restart: unless-stopped
  env_file: .env
  networks: [app-network]
  logging:
    driver: json-file
    options:
      max-size: "10m"
  depends_on:
    - postgres

services:
  api:
    <<: *common
    image: my-api:2.4.1
    ports:
      - "8080:8080"

  worker:
    <<: *common
    image: my-worker:2.4.1
    command: ["node", "worker.js"]

  mailer:
    <<: *common
    image: my-mailer:2.4.1
    command: ["node", "mailer.js"]
プロのヒント: x-プレフィックスを 拡張フィールドx-commonなど)に付けるのはDocker Composeの慣習で、ComposeがそのブロックをService定義として解析しようとするのを防ぎます。 Composeエンジンには無視されますが、YAMLアンカーとして完全に使用できます。

GitHub Actions:アンカーを使ったMatrix戦略

CI設定はアンカーが効果を発揮する別の場所です。複数のJobにわたって共通のステップ設定を 再利用するGitHub Actionsワークフローの例です:

yaml
name: Build and Test

on: [push, pull_request]

# Shared setup steps
x-setup-steps: &setup-steps
  - uses: actions/checkout@v4
  - uses: actions/setup-node@v4
    with:
      node-version: "20"
      cache: npm
  - run: npm ci

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - *setup-steps
      - run: npm run lint

  test:
    runs-on: ubuntu-latest
    steps:
      - *setup-steps
      - run: npm test -- --coverage

  build:
    runs-on: ubuntu-latest
    steps:
      - *setup-steps
      - run: npm run build

注意:GitHub ActionsのYAMLパーサー はアンカーとエイリアスをサポートしています。ただし一部のCIシステム (特にバージョン2.1以前のCircleCI)はアンカーに似た独自の拡張構文を持っていました。 必ず特定のツールのドキュメントを確認してください。

Kubernetes:リソース制限の再利用

Kubernetesマニフェストでは、リソース制限と環境変数がアンカーの主要な候補です。 私が定期的に使うパターンを紹介します:

yaml
# Anchors at the top of the file (or in a shared ConfigMap generator)
x-resources:
  small: &resources-small
    requests:
      memory: "64Mi"
      cpu: "50m"
    limits:
      memory: "256Mi"
      cpu: "200m"
  large: &resources-large
    requests:
      memory: "256Mi"
      cpu: "200m"
    limits:
      memory: "1Gi"
      cpu: "1000m"

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  template:
    spec:
      containers:
        - name: frontend
          image: frontend:latest
          resources: *resources-small    # reuse small profile

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  template:
    spec:
      containers:
        - name: api
          image: api:latest
          resources: *resources-large    # reuse large profile

複数マージと上書き優先度

1つのマッピングで複数のアンカーからマージできます。<<の下にリストとして提供します。 重複キーについては、リストの前のエントリが後のエントリより優先されます:

yaml
defaults: &defaults
  color: blue
  size: medium
  weight: light

overrides: &overrides
  color: red          # will override defaults.color
  weight: heavy       # will override defaults.weight

result:
  <<: [*overrides, *defaults]    # overrides takes priority
  name: widget
# Result: color=red, size=medium, weight=heavy, name=widget

知っておくべき制限事項

  • 同一ファイル内のみ。 アンカーとエイリアスは単一のYAMLドキュメントストリーム内でのみ機能します。別のファイルのアンカーを参照することはできません。ファイルをまたいだ再利用には、ツール独自のメカニズムが必要です(例:HelmテンプレートKustomize、またはyttのようなYAMLプロセッサ)。
  • 計算値なし。 エイリアスはアンカーの値の正確なコピーに解決されます。補間やテンプレートロジックはありません—それはHelmやJinja2のようなツールのためのものです。
  • マージキーはYAML拡張です。 <<マージキーはYAML 1.1の拡張で定義されており、コア仕様ではありません。ほとんどのパーサーはサポートしていますが、仕様に厳密に準拠したYAML 1.2パーサーはサポートしない場合があります。
  • 循環参照はパーサーを壊します。 ノードをアンカーして、そのノード自身の内部から参照しようとしないでください。技術的に無効です。
  • エイリアスはコピーであり参照ではありません。 コード内で解決されたエイリアスの値を変更しても、アンカーや他のエイリアスは変わりません。解析後は独立した値です。

まとめ

YAMLのアンカーとエイリアスは、フォーマットの最も活用されていない機能の1つです。 同じ設定ブロックが2回以上現れる50行以上のYAMLファイルでは、アンカーがほぼ確実に使用されるべきです。 メンテナビリティの改善—ファイル全体を検索するのではなく1つのリソース制限プロファイルや 共有環境変数を変更するだけ—は劇的です。 YAML フォーマッターでアンカーを含むファイルを整理し、 YAML バリデータでアンカー参照が正しく解決されることを確認しましょう。