Als er iets misgaat in Kubernetes, leidt het spoor meestal terug naar etcd. API server timeout? Check etcd. Pods stuck in pending? Misschien etcd. Cluster voelt traag? Waarschijnlijk etcd.

Ik behandelde etcd lang zoals de meeste operators dat doen: als een black box die naast het control plane meedraait. “De database.” Je maakt er een backup van en laat het verder met rust. Maar black boxes voelen voor mij als splinters, en de eerste keer dat een etcd cluster om 2 uur ’s nachts omviel besefte ik dat ik geen idee had waar ik naar keek. Dus heb ik het uitgezocht. En het blijkt te draaien op een handjevol ideeën die, zodra ze landen, de meeste etcd problemen diagnoseerbaar maken in plaats van eng.

Laat me het van onderaf opbouwen.

Wat is etcd?

etcd is een gedistribueerde key-value store. Een zeer betrouwbaar woordenboek waar meerdere servers het over eens zijn. Kubernetes gebruikt het om alle cluster state in te bewaren: elke pod, deployment, secret, configmap en custom resource leeft daarin.

De naam komt van “/etc distributed”. De Unix /etc directory bevat systeemconfiguratie, en etcd is dat idee verspreid over nodes.

Vier eigenschappen definiëren het:

  • Strongly consistent: elke node ziet dezelfde data op hetzelfde moment
  • Highly available: het overleeft node failures, zolang je quorum houdt
  • Watch-capable: clients kunnen subscriben op wijzigingen
  • Ordered: operaties gebeuren in een strikte volgorde

Die vier woorden verklaren bijna alles wat etcd doet. De rest van deze post pakt eigenlijk alleen maar uit hoe het ze waarmaakt.

Het Raft consensus algoritme

etcd gebruikt Raft om consistent te blijven tussen nodes. Als je Raft snapt, snap je het meeste etcd gedrag, inclusief de stukken die op bugs lijken maar het niet zijn.

Leader election

In elk etcd cluster is één node de leader. De leader handelt alle writes af. Followers repliceren de log van de leader.

flowchart LR
    L["Leader<br/>(etcd1)"] -->|replicates| F1["Follower<br/>(etcd2)"]
    L -->|replicates| F2["Follower<br/>(etcd3)"]
    L -.->|heartbeats| F1
    L -.->|heartbeats| F2

De leader stuurt heartbeats naar followers. Als een follower de leader een election timeout lang niet hoort (typisch 1-2 seconden), wordt het een candidate en start een verkiezing. Daarom laat een wankel netwerk zich zien als constante leader churn: de followers blijven denken dat de leader dood is.

# Zie wie de leader is
etcdctl endpoint status --cluster -w table

Log replication

Wanneer een write binnenkomt, is dit het pad dat hij aflegt:

  1. Client stuurt de write naar de leader
  2. Leader voegt hem toe aan zijn log
  3. Leader repliceert naar followers
  4. Zodra een meerderheid bevestigt, commit de leader
  5. Leader antwoordt aan de client

Stap 4 is de hele reden dat etcd een meerderheid (quorum) nodig heeft om te schrijven. Met 3 nodes heb je er 2 nodig. Met 5 heb je er 3 nodig. Verlies je quorum, dan gaat etcd read-only in plaats van het risico te lopen uiteen te lopen. Die weigering om te gokken is precies wat je wilt van je source of truth.

Term numbers

Elke verkiezing verhoogt het term number. Zie het als een logische klok. Als een node een hogere term ziet, weet het dat er een nieuwere leader gekozen is en voegt het zich. Een term number dat snel blijft oplopen is een teken van een instabiel cluster.

# Check cluster health en terms
etcdctl endpoint health --cluster -w table

Data model

Tot nu toe hebben we een consistente, gerepliceerde log. Kijk nu naar wat er daadwerkelijk in opgeslagen wordt.

etcd bewaart data als key-value paren in een platte namespace. Kubernetes legt daar een hiërarchische conventie overheen:

/registry/pods/default/nginx-abc123
/registry/pods/kube-system/coredns-xyz789
/registry/deployments/default/my-app
/registry/secrets/default/my-secret

Alles zit onder /registry/. De Kubernetes API server is grotendeels een vertaallaag tussen de Kubernetes API en etcd’s key-value model. Als je kubectl get pods doet, lees je een stukje van die boom.

Revisions en MVCC

etcd gebruikt Multi-Version Concurrency Control (MVCC). Elke modificatie creëert een nieuwe revision, en oude revisions blijven een tijdje hangen. Die ene ontwerpkeuze levert je drie dingen op:

  • Watch from revision: “vertel me alles dat veranderd is sinds revision 12345”
  • Historical reads: “wat was de waarde bij revision 12345?”
  • Optimistic locking: “update alleen als nog steeds op revision 12345”
# Huidige revision ophalen
etcdctl get / --prefix --limit=1 -w json | jq '.header.revision'

# Key lezen met zijn revision
etcdctl get /registry/pods/default/nginx -w json | jq '.kvs[0].mod_revision'

Die eerste, watch-from-revision, is degene waar Kubernetes het hardst op leunt. Onthoud die even.

Waarom etcd performance ertoe doet

Kubernetes is spraakzaam met etcd. Elke API call, elke controller reconciliation, elke watch notification raakt het aan. Een trage etcd is een traag cluster, punt. Dit is waar de abstracte Raft theorie omslaat in heel concrete operationele pijn.

Kritieke metrics

fsync latency: tijd om naar disk te persisteren. Mik op onder 10ms. Voorbij 20ms ontstaan problemen, want denk aan stap 4 van log replication: die wacht op de disk.

# Check disk latency
etcdctl check perf --load="s"

Raft proposal latency: tijd om een write te committen via Raft. Moet onder 50ms.

Watch count: aantal actieve watches. Kubernetes draait er duizenden.

Veelvoorkomende performance problemen

Trage disks: etcd schrijft synchroon naar disk. Gebruik SSDs, geen roestende schijven. NVMe als je het kunt krijgen. Dit is veruit de meest voorkomende oorzaak van een verdrietig cluster.

Netwerk latency: Raft heartbeats en log replication willen lage latency tussen nodes. Houd etcd nodes op hetzelfde netwerksegment.

Te veel objecten: een cluster met 100k pods leunt veel harder op etcd dan een met 1k. Watch kardinaliteit explodeert.

Grote objecten: secrets volgepropt met enorme certificaten, ConfigMaps met megabytes data, het gaat allemaal door etcd.

Backup van etcd

Hier de botte versie: als etcd data weg is, is je cluster weg. Al het andere is afgeleide state die je opnieuw kunt opbouwen. Dit is het enige dat je je niet kunt veroorloven te verliezen, dus maak er op schema een backup van.

# Snapshot backup
etcdctl snapshot save /backup/etcd-$(date +%Y%m%d-%H%M%S).db

# Verifieer de backup
etcdctl snapshot status /backup/etcd-*.db -w table

Voor geautomatiseerde backups:

#!/bin/bash
# /etc/cron.daily/etcd-backup

BACKUP_DIR=/var/backups/etcd
RETENTION_DAYS=7

etcdctl snapshot save $BACKUP_DIR/etcd-$(date +%Y%m%d-%H%M%S).db

# Oude backups opruimen
find $BACKUP_DIR -name "etcd-*.db" -mtime +$RETENTION_DAYS -delete

Restore van backup

etcd restoren is serieuze business. Je reset cluster state naar een punt in tijd, met alle gevolgen van dien.

# Stop etcd op alle nodes eerst

# Restore op elke node (andere data-dir)
etcdctl snapshot restore /backup/etcd-snapshot.db \
  --name etcd1 \
  --initial-cluster etcd1=https://10.0.0.1:2380,etcd2=https://10.0.0.2:2380,etcd3=https://10.0.0.3:2380 \
  --initial-cluster-token etcd-cluster-restored \
  --initial-advertise-peer-urls https://10.0.0.1:2380 \
  --data-dir /var/lib/etcd-restored

# Start etcd met nieuwe data-dir

Na restore is alles dat gebeurde na de snapshot weg. Pods refereren misschien verwijderde deployments. Services wijzen misschien naar endpoints die niet meer bestaan. Het cluster reconcileert zich er weer uit, maar verwacht onderweg wat chaos. Test dit op een wegwerp cluster voordat je het ooit echt nodig hebt.

Compaction en defragmentation

Weet je nog die oude revisions die MVCC bewaart? Die verdwijnen niet uit zichzelf. etcd houdt ze vast tot compaction draait, en zonder dat groeit de database tot hij omvalt.

# Check huidige database grootte
etcdctl endpoint status --cluster -w table

# Compact naar revision
etcdctl compact 123456

# Defragmenteer om ruimte terug te winnen
etcdctl defrag --cluster

Kubernetes auto-compacteert etcd via --etcd-compaction-interval, maar defragmentation blijft handmatig. Compaction gooit de oude revisions weg; defrag geeft de vrijgekomen diskruimte daadwerkelijk terug aan het filesystem.

etcd watchen

Dit is waar die watch-from-revision feature zich terugbetaalt. Kubernetes controllers doen hun werk door etcd te watchen. Er verandert iets, etcd notificeert de watchers direct, geen polling nodig.

# Watch alle pod wijzigingen
etcdctl watch /registry/pods --prefix

# Watch specifieke namespace
etcdctl watch /registry/pods/default --prefix

Dat mechanisme is hoe de scheduler direct weet van nieuwe pods zodra ze verschijnen, hoe de kubelet van pod assignments weet, hoe het hele control plane in sync blijft zonder centrale klok. Het volledige reconciliation model van Kubernetes rijdt op dit ene etcd primitief.

etcd issues debuggen

Nu je weet hoe de stukken in elkaar passen, stopt debuggen met gokwerk te zijn. Elk symptoom wijst naar een laag die je al begrijpt.

Hoge latency

# Check of disk het probleem is
etcdctl check perf --load="s"

# Check cluster health
etcdctl endpoint health --cluster -w table

# Check leader changes (te veel = instabiliteit)
etcdctl endpoint status --cluster -w table

Disk space issues

# Check database grootte
etcdctl endpoint status --cluster -w table

# Forceer compaction en defrag
REV=$(etcdctl endpoint status -w json | jq '.[0].Status.header.revision')
etcdctl compact $REV
etcdctl defrag --cluster

Netwerk issues

# Check of nodes elkaar kunnen bereiken
for ep in https://10.0.0.1:2379 https://10.0.0.2:2379 https://10.0.0.3:2379; do
  etcdctl --endpoints=$ep endpoint health
done

etcd in productie

Hoe dit eruitziet op mijn eigen clusters:

  1. Dedicated SSDs: etcd wil snelle, consistente disk I/O. Deel geen schijven met andere workloads.

  2. Monitor meedogenloos: etcd_disk_backend_commit_duration_seconds, etcd_network_peer_round_trip_time_seconds, etcd_server_leader_changes_seen_total. Die drie vertellen je over respectievelijk disk, netwerk en stabiliteit.

  3. Regelmatige backups: minstens dagelijks, en test de restore af en toe zodat je hem vertrouwt.

  4. Apart netwerk: waar mogelijk, zet etcd peer traffic op een eigen netwerk om contention te ontwijken.

  5. Right-size het cluster: 3 nodes voor de meeste workloads, 5 voor de kritieke. Boven de 5 betaal je vooral replicatiekosten voor heel weinig extra resilience.

Afronding

etcd is simpel in concept en onverbiddelijk in uitvoering. Het is de basis waar al het andere op bouwt, dus als etcd gezond is, is Kubernetes gezond, en als etcd worstelt, worstelt het hele cluster mee.

De beloning voor het leren van de internals is dat de splinter geen splinter meer is. Zodra je Raft consensus, quorum en disk latency ziet voor wat ze zijn, is etcd een goed ontworpen gedistribueerd systeem dat precies doet wat gedistribueerde systemen doen. Geen magie, alleen mechaniek, en mechaniek waarover je om 2 uur ’s nachts kunt redeneren.

Geef het snelle disks, maak er een backup van, kijk naar de metrics, en het houdt je cluster stil draaiende.


etcd is de source of truth. Al het andere in Kubernetes is afgeleide state. Bescherm je source of truth.