Hvis du noen gang har kopiert en YAML-blokk fra ett sted i en fil til et annet, har du allerede støtt på problemet som ankre og aliaser løser. Store Kubernetes-konfigurasjoner, multi-miljø Docker Compose-filer og komplekse CI-pipelines har en tendens til å gjenta de samme blokkene — ressursgrenser, miljøvariabler, volummonteringer — om og om igjen. YAML har et innebygd svar på dette, som de fleste utviklere ikke vet eksisterer.

Ankre (&) og aliaser (*) er YAMLs mekanisme for å definere en verdi én gang og referere til den hvor som helst i den samme filen. Tenk på et anker som å deklarere en variabel og et alias som å bruke den. Sammenslåingsnøkkelen (<<) utvider dette til objektsammenslåing — likt JavaScripts spread-operator. Begge funksjonene er definert i YAML 1.2-spesifikasjonens seksjon om nodankre.

Grunnleggende anker- og alias-syntaks

Syntaksen er minimal. Prefiks en verdi med &name for å opprette et anker, bruk deretter *name hvor som helst i dokumentet for å 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 leser dette, løses hvert alias opp til nøyaktig samme verdi som ankeret. Endre ankeret og alle aliaser oppdateres. Ingen søk-og-erstatt nødvendig.

Sammenslåing av objekter med nøkkelen <<

Gjenbruk av skalarer er nyttig, men den virkelige kraften kommer fra å slå sammen hele mapping-blokker. Sammenslåingsnøkkelen << absorberer alle nøkkel-verdi-par fra det refererte ankeret inn i den gjeldende mappingen:

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økler definert direkte i mappingen overstyrer nøkler fra det sammenslåtte ankeret. Så hvis base setter restart: unless-stopped og tjenesten også setter restart: no, vinner verdien på tjenestenivå.

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

Slik ser en typisk multi-tjeneste Docker Compose-fil ut uten ankre — full av gjentakelser:

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

Nå med ankre og sammenslåingsnøkkelen — hver gjentatt blokk er definert nøyaktig é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-tips: Prefikset x-utvidelsesfelter (som x-common) er en Docker Compose-konvensjon som forhindrer Compose fra å forsøke å tolke blokken som en tjenestedefinisjon. Det ignoreres av Compose-motoren, men er fullt brukbart som et YAML-anker.

GitHub Actions: Matrisestrategi med ankre

CI-konfigurasjon er et annet sted der ankre lønner seg. Her er en GitHub Actions-arbeidsflyt som gjenbruker felles trinnkonfigurasjon på tvers av flere jobber:

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

Merk: GitHub Actions' YAML-parser støtter ankre og aliaser. Imidlertid har noen CI-systemer (spesielt CircleCIs config.yml før versjon 2.1) hatt sin egen anker-lignende utvidelsessyntaks. Sjekk alltid dokumentasjonen for ditt spesifikke verktøy.

Kubernetes: Gjenbruk av ressursgrenser

I Kubernetes-manifester er ressursgrenser og miljøvariabler fremragende kandidater for ankre. Her er et mønster jeg bruker jevnlig:

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 sammenslåinger og prioritet for overstyring

Du kan slå sammen fra flere ankre i en enkelt mapping. Oppgi dem som en liste under <<. Tidligere oppføringer i listen har prioritet over senere oppføringer for dupliserte nøkler:

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

Begrensninger å kjenne til

  • Kun samme fil. Ankre og aliaser fungerer bare innenfor en enkelt YAML-dokumentstrøm. Du kan ikke referere til et anker fra en annen fil. For gjenbruk på tvers av filer trenger du verktøyets egen mekanisme (f.eks. Helm-maler, Kustomize, eller YAML-prosessorer som ytt).
  • Ingen beregnede verdier. Aliaser løser seg opp til nøyaktige kopier av ankerverdien. Det finnes ingen interpolasjon eller mallogikk — det er for verktøy som Helm eller Jinja2.
  • Sammenslåingsnøkkelen er en YAML-utvidelse. Sammenslåingsnøkkelen << er definert i en YAML 1.1-utvidelse, ikke i kjernespesifikasjonen. De fleste parsere støtter den, men strengt spesifikasjonskompatible YAML 1.2-parsere gjør det kanskje ikke.
  • Sirkulære referanser ødelegger parsere. Ikke prøv å ankre en node og deretter referere til den fra innsiden av seg selv. Det er teknisk ugyldig.
  • Aliaser kopierer, refererer ikke. Å endre en løst aliasverdi i koden din endrer ikke ankeret eller andre aliaser. De er uavhengige verdier etter parsing.

Oppsummering

YAML-ankre og aliaser er en av formatets mest underutnyttede funksjoner. I hvilken som helst YAML-fil lengre enn ca. 50 linjer der den samme konfigurasjonsblokken vises mer enn to ganger, hører ankre nesten helt sikkert hjemme der. Forskjellen i vedlikeholdbarhet før/etter er dramatisk — å endre én ressursgrense-profil eller én delt miljøvariabel i stedet for å søke gjennom hele filen. Bruk YAML-formateringen for å holde ankrene dine ryddige, og YAML-validatoren for å bekrefte at ankerreferansene dine løser seg opp korrekt.