Jeśli kiedykolwiek zarządzałeś klastrem Kubernetes, konfigurowałeś przepływ pracy GitHub Actions lub pisałeś plik Docker Compose, to już pisałeś YAML — zapewne całkiem sporo. YAML to format konfiguracyjny napędzający większość współczesnych narzędzi DevOps. Ale mimo że jest wszechobecny, wciąż potrafi zaskoczyć. Naprawiałem więcej niż swój udział w błędach wcięć YAML o 23:00 z zablokowanym deployem. Zadbajmy o to, żebyś nie musiał.

YAML to skrót od YAML Ain't Markup Language — tak, to rekurencyjny akronim. Pierwotnie brzmiał „Yet Another Markup Language", ale zmieniono go, aby podkreślić, że jest to format serializacji danych, a nie język znaczników dokumentów. Specyfikacja YAML 1.2 to aktualny standard, choć wiele narzędzi nadal implementuje YAML 1.1, który ma kilka naprawdę groźnych różnic, które omówimy poniżej.

Podstawy: Składnia YAML w pigułce

YAML używa wcięć (tylko spacje — nigdy tabulatory) do wyrażania struktury. Dokument YAML to odwzorowanie (pary klucz-wartość), sekwencja (lista) lub skalar (pojedyncza wartość). Oto prawdziwa konfiguracja w stylu Kubernetes, która obejmuje wszystkie podstawy:

yaml
# This is a comment — YAML supports them, JSON does not
app:
  name: payment-service
  version: "2.1.0"         # quoted to keep it a string
  port: 8080
  debug: false
  timeout: 30.5
  tags:
    - payments
    - backend
    - critical
  database:
    host: postgres.internal
    port: 5432
    ssl: true
    password: null          # explicitly no value

Żadnych nawiasów klamrowych, żadnych cudzysłowów przy większości łańcuchów, żadnych przecinków. W pliku, który ludzie edytują codziennie, ta czytelność naprawdę robi różnicę. Ale YAML płaci za tę czytelność złożonością — pod maską dzieje się całkiem sporo.

Typy skalarne: Łańcuchy, liczby, wartości logiczne, null

YAML wywnioskuje typy na podstawie wyglądu wartości. Jest to wygodne przez większość czasu i po cichu katastrofalne w kilku szczególnych przypadkach.

yaml
# Strings — quotes are optional unless value is ambiguous
name: Alice
greeting: "Hello, world"
message: 'single quotes work too'
path: /usr/local/bin           # unquoted — still a string

# Numbers
integer: 42
negative: -7
float: 3.14159
scientific: 6.022e23

# Booleans
ssl_enabled: true
verbose: false

# Null
api_key: null
legacy_field: ~               # tilde is also null in YAML
Problem Norwegii: W YAML 1.1 (używanym przez PyYAML i wiele starszych narzędzi) niecytowane wartości yes, no, on, off, true, false, y, n, Y, N — i ich wielkie warianty — są wszystkie parsowane jako wartości logiczne. Oznacza to, że kod kraju ISO NO (Norwegia) staje się wartością logiczną false. YAML 1.2 to naprawił. Zawsze cytuj łańcuchy wyglądające jak wartości logiczne. Zobacz artykuł Wikipedii o Problemie Norwegii po pełną historię.
yaml
# YAML 1.1 implicit type coercion — these silently become booleans:
country: NO       # → false  (Norway Problem!)
enabled: yes      # → true
toggle: on        # → true

# The fix: always quote when there's any ambiguity
country: "NO"
enabled: "yes"
version: "1.0"    # also quote version numbers — 1.0 would be a float

Sekwencje: Listy w YAML

Listy używają prefiksu myślnik-spacja. Każdy element może być skalarem, odwzorowaniem lub inną sekwencją:

yaml
# Simple list
languages:
  - Python
  - Go
  - TypeScript

# List of objects (common in Kubernetes)
containers:
  - name: api
    image: my-api:latest
    port: 8080
  - name: sidecar
    image: envoy:v1.28
    port: 9901

# Inline flow style (valid YAML, less readable)
tags: [payments, backend, v2]

Łańcuchy wieloliniowe: Dosłowny blok vs. złożony

To właśnie tutaj YAML przewyższa JSON dla plików konfiguracyjnych. Dwa specjalne operatory elegancko obsługują łańcuchy wieloliniowe:

yaml
# Literal block scalar (|) — preserves newlines exactly
startup_script: |
  #!/bin/bash
  set -e
  echo "Starting service..."
  npm start

# Folded scalar (>) — folds newlines into spaces (good for long descriptions)
description: >
  This service handles payment processing
  for all regions. It connects to Stripe
  and falls back to PayPal.
# Result: "This service handles payment processing for all regions. It connects to Stripe and falls back to PayPal."

# Chomp modifiers
keep_trailing: |+
  line one
  line two

strip_trailing: |-
  line one
  line two

Operator | sprawia, że wieloliniowe skrypty run: w GitHub Actions są czytelne. Ściągawka yaml-multiline.info to najszybszy sposób, by zapamiętać, który modyfikator chomp robi co. Bez tych bloków skalarnych musiałbyś pisać escapeowane znaki nowej linii w jednym łańcuchu — do czego zmusza cię JSON.

Tabulatory vs. spacje — Kardynalna zasada

YAML zabrania tabulatorów do wcięć. Całkowicie. Jeśli Twój edytor wstawi znak tabulatora w wcięciu pliku YAML, plik albo nie sparsuje się, albo — co gorsza — sparsuje się niepoprawnie. Skonfiguruj swój edytor, aby używał spacji dla plików .yml i .yaml. Dwie spacje to niemal powszechna konwencja.

To jest przyczyna numer jeden tajemniczych błędów YAML. Tabulator wygląda identycznie jak spacje w większości edytorów. Włącz "pokaż znaki białe" w swoim edytorze podczas debugowania YAML, lub wklej plik do Walidatora YAML, aby uzyskać jasny komunikat błędu wskazujący dokładną linię.

Prawdziwy przykład: Przepływ pracy GitHub Actions

Oto prawdziwy fragment przepływu pracy GitHub Actions. Zwróć uwagę, jak wieloliniowy blok run: używa dosłownego skalara blokowego i jak wcięcia determinują strukturę:

yaml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: |
          npm run lint
          npm run test -- --coverage
          npm run build

Prawdziwy przykład: Manifest wdrożenia Kubernetes

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-api
  namespace: production
  labels:
    app: payment-api
    version: "2.1.0"
spec:
  replicas: 3
  selector:
    matchLabels:
      app: payment-api
  template:
    metadata:
      labels:
        app: payment-api
    spec:
      containers:
        - name: api
          image: payment-api:2.1.0
          ports:
            - containerPort: 8080
          env:
            - name: NODE_ENV
              value: production
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: password
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "512Mi"
              cpu: "500m"

Parsowanie YAML w Pythonie i JavaScript

W Pythonie biblioteka PyYAML to standardowy wybór. Zawsze używaj safe_load(), nie load() — niebezpieczna wersja może wykonywać dowolny kod Pythona z pliku YAML:

python
import yaml

# Reading a YAML config file
with open('config.yaml', 'r') as f:
    config = yaml.safe_load(f)

print(config['app']['name'])        # payment-service
print(config['app']['port'])        # 8080 (integer, not string)
print(config['app']['tags'])        # ['payments', 'backend', 'critical']

# Writing Python data to YAML
data = {
    'service': 'auth-api',
    'replicas': 2,
    'endpoints': ['/login', '/logout', '/refresh']
}
with open('output.yaml', 'w') as f:
    yaml.dump(data, f, default_flow_style=False)

W JavaScript/Node.js, js-yaml to najszerzej używana biblioteka:

js
import yaml from 'js-yaml';
import fs from 'fs';

// Parse YAML string
const raw = fs.readFileSync('config.yaml', 'utf8');
const config = yaml.load(raw);

console.log(config.app.name);     // payment-service
console.log(config.app.tags);     // ['payments', 'backend', 'critical']

// Dump an object to YAML string
const output = yaml.dump({
  name: 'my-service',
  port: 3000,
  tags: ['api', 'public']
});
console.log(output);

Podsumowanie

Składnia oparta na wcięciach w YAML, obsługa komentarzy i czytelne łańcuchy wieloliniowe sprawiają, że jest właściwym wyborem dla plików konfiguracyjnych, które ludzie piszą i utrzymują. Ale wiąże się z nim prawdziwe ryzyko: Problem Norwegii, mylenie tabulatorów ze spacjami i niejawna konwersja typów z parserów YAML 1.1. Znaj zasady, cytuj wszystko niejednoznaczne, skonfiguruj edytor tak, aby pokazywał znaki białe, i używaj Formatera YAML, aby pliki były spójne. YAML to potężne narzędzie, gdy rozumiesz, gdzie może ugryźć.