Om du någonsin har kopierat ett YAML-block från ett ställe i en fil till ett annat, har du redan stött på problemet som ankare och alias löser. Stora Kubernetes-konfigurationer, multi-miljö Docker Compose-filer och komplexa CI-pipelines tenderar att upprepa samma block — resursbegränsningar, miljövariabler, volymmonteringar — om och om igen. YAML har ett inbyggt svar på detta, och de flesta utvecklare vet inte att det finns.

Ankare (&) och alias (*) är YAMLs mekanism för att definiera ett värde en gång och referera till det var som helst i samma fil. Tänk på ett ankare som att deklarera en variabel och ett alias som att använda den. Sammanslagningsnyckeln (<<) utökar detta till objektsammanslagning — liknande JavaScripts spread-operator. Båda funktionerna är definierade i YAML 1.2-specifikationens avsnitt om nodankare.

Grundläggande ankare- och alias-syntax

Syntaxen är minimal. Prefixera ett värde med &name för att skapa ett ankare, använd sedan *name var som helst i dokumentet för att referera till 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 detta löser varje alias upp till exakt samma värde som ankaret. Ändra ankaret och alla alias uppdateras. Ingen söka-och-ersätta behövs.

Sammanslagning av objekt med nyckeln <<

Skalär-återanvändning är användbart, men den verkliga kraften kommer från att slå samman hela mappningsblock. Sammanslagningsnyckeln << absorberar alla nyckel-värde-par från det refererade ankaret in i den aktuella mappningen:

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

Nycklar definierade direkt i mappningen åsidosätter nycklar från det sammanslagna ankaret. Så om base sätter restart: unless-stopped och tjänsten också sätter restart: no, vinner värdet på tjänstenivå.

Före och efter: Docker Compose i verkligheten

Så här ser en typisk multi-tjänst Docker Compose-fil ut utan ankare — full av upprepningar:

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 ankare och sammanslagningsnyckeln — varje upprepat block är definierat exakt en gång:

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: Prefixet x-tilläggsfält (som x-common) är en Docker Compose-konvention som förhindrar att Compose försöker tolka blocket som en tjänstedefinition. Det ignoreras av Compose-motorn men är fullt användbart som ett YAML-ankare.

GitHub Actions: Matrisstrategi med ankare

CI-konfiguration är ett annat ställe där ankare lönar sig. Här är ett GitHub Actions-arbetsflöde som återanvänder gemensam stegkonfiguration över flera jobb:

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

Observera: GitHub Actions' YAML-parser stöder ankare och alias. Vissa CI-system (framförallt CircleCIs config.yml före version 2.1) hade sin egen ankare-liknande tilläggssyntax. Kontrollera alltid din specifika verktygets dokumentation.

Kubernetes: Återanvändning av resursbegränsningar

I Kubernetes-manifest är resursbegränsningar och miljövariabler utmärkta kandidater för ankare. Här är ett mönster jag använder regelbundet:

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

Flera sammanslagningar och åsidosättningsprioritet

Du kan slå samman från flera ankare i en enda mappning. Ange dem som en lista under <<. Tidigare poster i listan har prioritet över senare poster för duplicerade nycklar:

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änsningar att känna till

  • Endast samma fil. Ankare och alias fungerar bara inom ett enda YAML-dokumentflöde. Du kan inte referera till ett ankare från en annan fil. För återanvändning mellan filer behöver du ditt verktygs egna mekanism (t.ex. Helm-mallar, Kustomize, eller YAML-processorer som ytt).
  • Inga beräknade värden. Alias löser upp till exakta kopior av ankarvärdet. Det finns ingen interpolation eller malllogik — det är för verktyg som Helm eller Jinja2.
  • Sammanslagningsnyckeln är ett YAML-tillägg. Sammanslagningsnyckeln << är definierad i ett YAML 1.1-tillägg, inte i kärnspecifikationen. De flesta parsers stöder det, men strikt spec-kompatibla YAML 1.2-parsers kanske inte.
  • Cirkulära referenser bryter parsers. Försök inte ankra en nod och referera till den inifrån sig själv. Det är tekniskt ogiltigt.
  • Alias kopierar, refererar inte. Att modifiera ett löst alias-värde i din kod ändrar inte ankaret eller andra alias. De är oberoende värden efter parsning.

Sammanfattning

YAML-ankare och alias är en av formatets mest underutnyttjade funktioner. I vilken YAML-fil som helst längre än ungefär 50 rader där samma konfigurationsblock förekommer mer än två gånger hör ankare nästan säkert hemma där. Skillnaden i underhållbarhet före/efter är dramatisk — att ändra en resursbegränsningsprofil eller en delad miljövariabel istället för att söka igenom hela filen. Använd YAML-formateraren för att hålla dina ankrade filer prydliga, och YAML-valideraren för att bekräfta att dina ankarreferenser löser upp korrekt.