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:

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

Wenn 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:

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"]

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:

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

Jetzt mit Ankern und Merge-Key — jeder wiederholte Block wird genau einmal definiert:

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"]
Profi-Tipp: Das 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:

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

Hinweis: 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:

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

Mehrfache 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:

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

Bekannte 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.