Wenn Sie schon einmal einen YAML-Block von einer Stelle in einer Datei an eine andere kopiert haben, haben Sie bereits das Problem erlebt, das Anker und Aliasse lösen. Große Kubernetes-Konfigurationen, Multi-Environment-Docker Compose-Dateien und komplexe CI-Pipelines tendieren dazu, dieselben Blöcke — Ressourcenlimits, Umgebungsvariablen, Volume-Mounts — immer wieder zu wiederholen. YAML hat eine eingebaute Antwort darauf, und die meisten Entwickler wissen nicht, dass sie existiert.
Anker (&) und Aliasse (*) sind YAMLs Mechanismus, einen Wert einmal zu definieren und
ihn überall sonst in derselben Datei zu referenzieren. Stellen Sie sich einen Anker wie die Deklaration einer Variable und einen Alias wie ihre Verwendung vor.
Der Merge-Key (<<) erweitert dies auf das Zusammenführen von Objekten — ähnlich dem Spread-Operator in JavaScript.
Beide Funktionen sind im Abschnitt über Knoten-Anker der YAML 1.2-Spezifikation definiert.
Grundlegende Anker- und Alias-Syntax
Die Syntax ist minimal. Fügen Sie einem Wert &Name voran, um einen Anker zu erstellen,
und verwenden Sie dann *Name an beliebiger Stelle im Dokument, um ihn zu referenzieren:
# Define the anchor once
default_timeout: &timeout 30
# Reference it multiple times
services:
api:
timeout: *timeout # → 30
worker:
timeout: *timeout # → 30
scheduler:
timeout: *timeout # → 30Wenn ein YAML-Parser dies liest, löst jeder Alias auf genau denselben Wert wie der Anker auf. Ändern Sie den Anker und alle Aliasse werden aktualisiert. Kein Suchen-und-Ersetzen erforderlich.
Objekte mit dem <<-Key zusammenführen
Die Wiederverwendung von Skalaren ist nützlich, aber die eigentliche Stärke liegt im Zusammenführen ganzer Mapping-Blöcke.
Der Merge-Key << übernimmt alle Schlüssel-Wert-Paare des referenzierten Ankers in das aktuelle Mapping:
# 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"]Direkt im Mapping definierte Schlüssel überschreiben Schlüssel aus dem zusammengeführten Anker.
Wenn also base restart: unless-stopped setzt und der Service auch
restart: no setzt, gewinnt der Wert auf Service-Ebene.
Vorher und Nachher: Docker Compose in der Praxis
So sieht eine typische Multi-Service-Docker-Compose-Datei ohne Anker aus — voller Wiederholungen:
# 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:
- postgresJetzt mit Ankern und Merge-Key — jeder wiederholte Block wird genau einmal definiert:
# 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--Präfix bei
Erweiterungsfeldern
(wie x-common) ist eine Docker Compose-Konvention, die verhindert, dass Compose versucht, den Block als Service-Definition zu parsen.
Es wird von der Compose-Engine ignoriert, ist aber vollständig als YAML-Anker nutzbar.GitHub Actions: Matrix-Strategie mit Ankern
CI-Konfiguration ist ein weiterer Bereich, in dem sich Anker auszahlen. Hier ist ein GitHub Actions-Workflow, der gemeinsame Schritt-Konfigurationen in mehreren Jobs wiederverwendet:
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 buildHinweis: Der YAML-Parser von GitHub Actions
unterstützt Anker und Aliasse. Einige CI-Systeme
(insbesondere CircleCIs config.yml vor Version 2.1) hatten jedoch ihre eigene ankerähnliche Erweiterungssyntax.
Prüfen Sie immer die Dokumentation Ihres spezifischen Tools.
Kubernetes: Ressourcenlimits wiederverwenden
In Kubernetes-Manifesten sind Ressourcenlimits und Umgebungsvariablen ideale Kandidaten für Anker. Hier ist ein Muster, das ich regelmäßig verwende:
# 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 profileMehrfache Zusammenführungen und Überschreibungspriorität
Sie können in einem einzelnen Mapping aus mehreren Ankern zusammenführen. Geben Sie sie als Liste unter << an.
Frühere Einträge in der Liste haben Vorrang vor späteren für doppelte Schlüssel:
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=widgetBekannte Einschränkungen
- Nur innerhalb derselben Datei. Anker und Aliasse funktionieren nur innerhalb eines einzelnen YAML-Dokumentstroms. Sie können keinen Anker aus einer anderen Datei referenzieren. Für dateiübergreifende Wiederverwendung benötigen Sie den eigenen Mechanismus Ihres Tools (z.B. Helm-Templates, Kustomize oder YAML-Prozessoren wie
ytt). - Keine berechneten Werte. Aliasse lösen auf exakte Kopien des Ankerwerts auf. Es gibt keine Interpolation oder Template-Logik — das ist für Tools wie Helm oder Jinja2.
- Merge-Key ist eine YAML-Erweiterung. Der
<<-Merge-Key ist in einer YAML 1.1-Erweiterung definiert, nicht in der Kernspezifikation. Die meisten Parser unterstützen ihn, aber strikt spezifikationskonforme YAML 1.2-Parser möglicherweise nicht. - Zirkuläre Referenzen brechen Parser. Versuchen Sie nicht, einen Knoten zu verankern und ihn dann von innen heraus zu referenzieren. Es ist technisch ungültig.
- Aliasse kopieren, referenzieren nicht. Das Modifizieren eines aufgelösten Alias-Werts in Ihrem Code ändert weder den Anker noch andere Aliasse. Nach dem Parsen sind es unabhängige Werte.
Fazit
YAML-Anker und -Aliasse sind eines der am wenigsten genutzten Features des Formats. In jeder YAML-Datei mit mehr als etwa 50 Zeilen, in der derselbe Konfigurations-Block mehr als zweimal vorkommt, gehören Anker fast sicher hinein. Der Vorher-Nachher-Unterschied in der Wartbarkeit ist dramatisch — ein Ressourcenlimit-Profil oder eine gemeinsame Umgebungsvariable ändern, anstatt die gesamte Datei zu durchsuchen. Verwenden Sie den YAML-Formatierer, um Ihre Dateien mit Ankern ordentlich zu halten, und den YAML-Validator, um zu bestätigen, dass Ihre Anker-Referenzen korrekt aufgelöst werden.