Se hai mai copiato un blocco YAML da una parte all'altra di un file, hai già incontrato il problema che anchors e aliases risolvono. I grandi config Kubernetes, i file Docker Compose multi-ambiente e le pipeline CI complesse tendono a ripetere gli stessi blocchi — limiti di risorse, variabili d'ambiente, mount dei volumi — più e più volte. YAML ha una risposta built-in per questo, e la maggior parte degli sviluppatori non sa che esiste.
Anchors (&) e aliases (*) sono il meccanismo di YAML per definire un valore una volta e
referenziarlo ovunque nello stesso file. Pensa a un anchor come alla dichiarazione di una variabile e a un alias come al suo utilizzo.
La merge key (<<) estende questo al merging di oggetti — simile all'operatore spread di JavaScript.
Entrambe le funzionalità sono definite nella sezione sugli ancore di nodo della specifica YAML 1.2.
Sintassi di base per anchor e alias
La sintassi è minimale. Anteponi &nome a un valore per creare un anchor,
poi usa *nome ovunque nel documento per referenziarlo:
# Define the anchor once
default_timeout: &timeout 30
# Reference it multiple times
services:
api:
timeout: *timeout # → 30
worker:
timeout: *timeout # → 30
scheduler:
timeout: *timeout # → 30Quando un parser YAML legge questo, ogni alias viene risolto con lo stesso valore dell'anchor. Cambia l'anchor e tutti gli alias si aggiornano. Niente ricerca-e-sostituzione.
Merging di oggetti con la chiave <<
Il riutilizzo scalare è utile, ma il vero potere viene dal merging di interi blocchi mapping.
La merge key << assorbe tutte le coppie chiave-valore dall'anchor referenziato nella mapping corrente:
# 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"]Le chiavi definite direttamente nella mapping sovrascrivono le chiavi dell'anchor mergiato.
Quindi se base imposta restart: unless-stopped e il servizio imposta anche
restart: no, vince il valore a livello di servizio.
Prima e dopo: Docker Compose nel mondo reale
Ecco come appare un tipico file Docker Compose multi-servizio senza anchors — pieno di ripetizioni:
# 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:
- postgresOra con anchors e merge key — ogni blocco ripetuto è definito esattamente una volta:
# 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- sui
campi di estensione
(come x-common) è una convenzione Docker Compose che impedisce a Compose di tentare di analizzare il blocco come definizione di servizio.
Viene ignorato dal motore Compose ma è pienamente utilizzabile come anchor YAML.GitHub Actions: matrix strategy con anchors
Anche la configurazione CI è un posto dove gli anchors rendono. Ecco un workflow GitHub Actions che riutilizza la configurazione comune degli step su più job:
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 buildNota: il parser YAML di GitHub Actions
supporta anchors e aliases. Tuttavia, alcuni sistemi CI
(in particolare il config.yml di CircleCI prima della versione 2.1) avevano una propria sintassi simile agli anchors.
Controlla sempre la documentazione dello strumento specifico che utilizzi.
Kubernetes: riutilizzo dei limiti di risorse
Nei manifest Kubernetes, i limiti di risorse e le variabili d'ambiente sono candidati ideali per gli anchors. Ecco un pattern che uso regolarmente:
# 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 profileMerge multipli e priorità di sovrascrittura
Puoi mergere da più anchors in una singola mapping. Forniscili come lista sotto <<.
Le voci precedenti nella lista hanno priorità su quelle successive per le chiavi duplicate:
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=widgetLimitazioni da conoscere
- Solo nello stesso file. Anchors e aliases funzionano solo all'interno di un singolo stream di documenti YAML. Non puoi referenziare un anchor da un file diverso. Per il riutilizzo tra file, hai bisogno del meccanismo del tuo strumento (es. template Helm, Kustomize, o processori YAML come
ytt). - Nessun valore calcolato. Gli aliases si risolvono in copie esatte del valore dell'anchor. Non c'è interpolazione o logica template — per quello servono strumenti come Helm o Jinja2.
- La merge key è un'estensione YAML. La merge key
<<è definita in una estensione YAML 1.1, non nella specifica core. La maggior parte dei parser la supporta, ma i parser YAML 1.2 strettamente conformi alle specifiche potrebbero non farlo. - I riferimenti circolari rompono i parser. Non tentare di ancorare un nodo e poi referenziarlo dall'interno di se stesso. È tecnicamente non valido.
- Gli aliases copiano, non referenziano. Modificare il valore di un alias risolto nel codice non cambia l'anchor o gli altri alias. Sono valori indipendenti dopo il parsing.
Conclusione
Gli anchors e gli aliases YAML sono una delle funzionalità più sottoutilizzate del formato. In qualsiasi file YAML più lungo di circa 50 righe in cui lo stesso blocco di configurazione appare più di due volte, gli anchors appartengono quasi certamente lì. La differenza nella manutenibilità prima/dopo è notevole — cambiare un profilo di limite di risorse o una variabile d'ambiente condivisa invece di cercare in tutto il file. Usa il YAML Formatter per mantenere ordinati i file con anchors, e il YAML Validator per confermare che i tuoi riferimenti anchor si risolvano correttamente.