Hvis du nogensinde har kopieret et YAML-blok fra ét sted i en fil til et andet, har du allerede stødt på det problem, som ankre og aliaser løser. Store Kubernetes-konfigurationer, multi-miljø Docker Compose-filer og komplekse CI-pipelines har tendens til at gentage de samme blokke — ressourcegrænser, miljøvariabler, volumenmonteringer — igen og igen. YAML har et indbygget svar på dette, som de fleste udviklere ikke ved eksisterer.

Ankre (&) og aliaser (*) er YAMLs mekanisme til at definere en værdi én gang og referere til den et hvilket som helst sted i den samme fil. Tænk på et anker som at erklære en variabel og et alias som at bruge den. Sammenslagningsnøglen (<<) udvider dette til objektsammenslagning — svarende til JavaScripts spread-operator. Begge funktioner er defineret i YAML 1.2-specifikationens afsnit om nodankre.

Grundlæggende anker- og alias-syntaks

Syntaksen er minimal. Sæt &name foran en værdi for at oprette et anker, og brug derefter *name et vilkårligt sted i dokumentet for at referere til det:

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

Når en YAML-parser læser dette, opløses hvert alias til nøjagtig samme værdi som ankret. Ændr ankret, og alle aliaser opdateres. Ingen søg-og-erstat nødvendig.

Sammenslagning af objekter med nøglen <<

Genbrug af skalarer er nyttigt, men den virkelige styrke kommer fra at sammenlægge hele mappingblokke. Sammenslagningsnøglen << absorberer alle nøgle-værdi-par fra det refererede anker ind i den 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"]

Nøgler defineret direkte i mappingen tilsidesætter nøgler fra det sammenlagte anker. Så hvis base sætter restart: unless-stopped og tjenesten også sætter restart: no, vinder værdien på tjenesteniveau.

Før og efter: Docker Compose i den virkelige verden

Sådan ser en typisk multi-service Docker Compose-fil ud uden ankre — fuld af gentagelser:

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

Nu med ankre og sammenslagningsnøglen — hvert gentaget blok er defineret præcis én gang:

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"]
Pro-tip: Præfikset x-udvidelsesfelter (som x-common) er en Docker Compose-konvention, der forhindrer Compose i at forsøge at tolke blokken som en tjenestedefinition. Det ignoreres af Compose-motoren, men er fuldt brugbart som et YAML-anker.

GitHub Actions: Matrixstrategi med ankre

CI-konfiguration er et andet sted, hvor ankre betaler sig. Her er et GitHub Actions-workflow, der genbruger fælles trinkonfiguration på tværs af flere job:

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

Bemærk: GitHub Actions' YAML-parser understøtter ankre og aliaser. Dog har visse CI-systemer (navnlig CircleCIs config.yml før version 2.1) haft deres egen anker-lignende udvidelsessyntaks. Tjek altid din specifikke værktøjsdokumentation.

Kubernetes: Genbrug af ressourcegrænser

I Kubernetes-manifester er ressourcegrænser og miljøvariabler fremragende kandidater til ankre. Her er et mønster, jeg bruger regelmæssigt:

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

Flere sammenslagninger og tilsidesættelsesprioriteter

Du kan sammenlægge fra flere ankre i en enkelt mapping. Angiv dem som en liste under <<. Tidligere poster på listen har prioritet over senere poster for duplikerede nøgler:

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

Begrænsninger at kende til

  • Kun samme fil. Ankre og aliaser fungerer kun inden for en enkelt YAML-dokumentstrøm. Du kan ikke referere til et anker fra en anden fil. Til genbrug på tværs af filer har du brug for dit værktøjs egen mekanisme (f.eks. Helm-skabeloner, Kustomize eller YAML-processorer som ytt).
  • Ingen beregnede værdier. Aliaser opløses til nøjagtige kopier af ankrets værdi. Der er ingen interpolation eller skabelonlogik — det er for værktøjer som Helm eller Jinja2.
  • Sammenslagningsnøglen er en YAML-udvidelse. Sammenslagningsnøglen << er defineret i en YAML 1.1-udvidelse, ikke i kernespecifikationen. De fleste parsere understøtter den, men strengt spec-kompatible YAML 1.2-parsere gør måske ikke.
  • Cirkulære referencer ødelægger parsere. Forsøg ikke at ankre en node og derefter referere til den inde fra sig selv. Det er teknisk ugyldigt.
  • Aliaser kopierer, refererer ikke. Ændring af en opløst aliasværdi i din kode ændrer ikke ankret eller andre aliaser. De er uafhængige værdier efter parsing.

Afslutning

YAML-ankre og aliaser er en af formatets mest underudnyttede funktioner. I enhver YAML-fil længere end ca. 50 linjer, hvor det samme konfigurationsblok optræder mere end to gange, hører ankre næsten bestemt hjemme der. Forskellen i vedligeholdelighed før/efter er dramatisk — at ændre én ressourcegrænse-profil eller én delt miljøvariabel i stedet for at lede gennem hele filen. Brug YAML-formateringen til at holde dine ankrede filer pæne, og YAML-validatoren til at bekræfte, at dine ankerreferencer opløses korrekt.