設定ファイルの3つの異なる場所に同じ10行のYAMLをコピー&ペーストしたことがあるなら、YAMLアンカーが解決しようとしている痛みを感じたことがあるだろう。アンカーは値を1回だけ定義し、同じドキュメント内のどこからでも参照できるようにする。値を1箇所で変更すれば、すべての参照が更新される。
この投稿は、アンカーとエイリアスがどのように機能し、いつ使い、人々をひっかけるエッジケースについて説明する。
基本構文
YAMLアンカーはノードに与える名前だ:
defaults: &defaults
retries: 3
timeout: 30
log_level: info
&defaultsがアンカーだ。これはdefaultsの値(マップ)に付与される。アンカーは任意のノードに置ける — 文字列、数値、リスト、マップ、シーケンス。
エイリアスはアンカーへの参照だ:
production:
<<: *defaults
region: us-east-1
replicas: 5
staging:
<<: *defaults
region: us-west-2
replicas: 2
<<: *defaultsは「マージキー」だ。「defaultsというアンカーからすべてを取り込み、このマップにマージする」という意味だ。結果はdefaultsの内容をインラインで書いたのと同じだ:
production:
retries: 3
timeout: 30
log_level: info
region: us-east-1
replicas: 5
しかしdefaultsのretriesを変更すると、アンカーを使うすべての場所が更新される。それがポイントだ。
単一値へのアンカー
アンカーはマップにマージする必要はない。単一値にアンカーを付けて参照できる:
api_version: &api "v2.1.0"
services:
auth:
version: *api
billing:
version: *api
reports:
version: *api
これでAPIのバージョンを変更するのは1行の変更だ。参照はすべて追従する。
リストにアンカーを付けることもできる:
allowed_origins: &origins
- https://app.example.com
- https://admin.example.com
- https://staging.example.com
cors:
web: *origins
api: *origins
リストはそのまま再利用される。
アンカーが役立つ場合
アンカーは複数のインスタンスに共有デフォルトがある場合に輝く。Kubernetesマニフェスト、docker-composeファイル、CIマトリクスはこのパターンにあふれている。
2026年の注記:HelmとKustomizeがKubernetesテンプレートの主流であり、YAMLのアンカーシステム外で動作する — YAMLを生成し、パースしない。HelmまたはKustomizeを使っているなら、アンカーは不要だ(values.yamlとパッチオーバーレイがある)。アンカーはまだkubectl apply -fワークフロー、GitHub Actionsマトリクス、docker-composeファイル、テンプレートエンジンを通らない手動設定に適している。
Kubernetesの例 — 同じリソース制限を持つ3つのサービス:
base_resources: &base_resources
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
apiDeployment:
spec:
template:
spec:
containers:
- name: api
image: myapp/api:1.2.3
resources: *base_resources
workerDeployment:
spec:
template:
spec:
containers:
- name: worker
image: myapp/worker:1.2.3
resources: *base_resources
docker-composeの例 — 開発と本番で同じ環境:
common_env: &common_env
DATABASE_URL: postgres://db.internal/app
REDIS_URL: redis://cache.internal:6379
LOG_LEVEL: info
services:
api:
environment:
<<: *common_env
ENVIRONMENT: dev
worker:
environment:
<<: *common_env
ENVIRONMENT: dev
どちらのケースでも、共通環境を変更するのは1行の編集だ。
Gotcha
アンカーは強力だが、いくつかの鋭い刃がある。
アンカーはドキュメント内にローカルだ。 ドキュメント区切り(---)を持つ複数ドキュメントYAMLファイルは、アンカーに独自のスコープを持つ。ドキュメント1で定義されたアンカーはドキュメント2から参照できない。複数リソースのkubectl get -o yamlなど、複数ドキュメントYAMLを生成するツールを使っている場合、アンカーで値を共有できない。
アンカーとマージはオーバーライドと驚くべき方法で相互作用する。 アンカーと参照マップの両方が同じキーを定義している場合、参照マップの値が勝つ:
defaults: &defaults
retries: 3
timeout: 30
production:
<<: *defaults
retries: 10 # これが勝つ
つまりproduction.retriesは10であり、3ではない。これは期待される動作だが、「最後のものが勝つ」または「最初のものが勝つ」を順序に基づいて期待する人々をひっかける。
マージされたマップのキーは削除できない。 defaultsにlegacy_setting: trueキーがあり、productionで「解除」したい場合、それはできない。マージはキーを追加するだけで、決して削除しない。回避策:アンカーにそのキーを含めない。
アンカーは自身を参照できない。 自己参照アンカーはサイクルを生む。ほとんどのパーサーはこれを検出しエラーをスローする。「再帰的アンカー」エラーが出たら、それを含むアンカーを指し戻すエイリアスを探す。
アンカーとJSONは相容れない。 JSONにはアンカーの概念がない。アンカーを持つYAMLをJSONに変換すると、アンカーは展開される(JSONが大きくなる)か削除される(JSONが小さくなる)。どちらにせよ、結果はYAMLより大きい。JSONバージョンが必要な場合、通常は異なる設定ソースが必要だ。
一部のリンターはアンカーに文句を言う。 yamllintはデフォルトでスタイル上の理由からアンカーを無効にしている(anchors: disable)。アンカーを使っている場合、リンターを設定するか警告を受け入れる必要がある。アンカーを使うチームのほとんどは、リンとの摩擦に値すると考えている。
アンカーはパースされたオブジェクトグラフで機能し、テキストではない。 yqなどのツールでキーをアルファベット順にソートしてから再シリアライズすると、アンカーは可視キー順ではなく参照によって保持される。出力は正しいが、見た目は驚くことがある — アンカーが1箇所に表示され、エイリアスが別の場所にあり、同じオブジェクトを参照している。
設定でアンカーを検証する
アンカーが機能していることを確認する最速の方法は、YAMLを它们を解決するパーサーで読み込み、結果を表示することだ。DevSpeedToolsのYAMLバリデーターは解決されたオブジェクトグラフを表示する — アンカーが正しく配線されていれば、すべての場所で同じ値が見える。
より詳細な検査には、YAML統計ツールが一意キーと重複キーの合計数を示し、「アンカーが実際に重複を解消しているか」の良い指標になる。
インタラクティブなエディター体験には、Red Hat YAML拡張機能付きVS Codeがアンカーをホバーで解決し、各エイリアスがどこを指しているかを表示する。
まとめ
アンカーはYAML版の変数だ。パース時に機能し、シリアライズ時ではない。因此、書いたファイルにはアンカーが1回、参照が多くの場所に含まれる。パーサーは它们を同じ値に展開する。
共有デフォルトに使おう。ドキュメント間で状態を共有しようとするな。JSON変換が它们を保持すると期待するな。そして迷ったら、ファイルをバリデーターで実行して、展開が意図と一致することを確認しよう — 黙って間違った値を生成するアンカーは、本番にリリースされ、1年間そこに住む種類のバグだ。