Het eerste “productie” Kubernetes cluster dat ik draaide had één control plane node. Het tikte weken vrolijk door, totdat een disk kapotging en het hele ding meenam. Elke pod, elke service, weg. Die storing leerde me hoe “single point of failure” echt voelt, en duwde me naar een vraag die ik mensen steeds zie struikelen: als je een cluster bouwt dat node-verlies overleeft, draai je etcd dan op je control plane nodes, of op aparte nodes?

Die keuze heeft een naam. Stacked etcd versus external etcd. Beide zijn geldig, beide draaien in echte productieclusters, en het juiste antwoord hangt volledig van je context af. Ik wil ze doorlopen zoals ik had gewild dat iemand het mij had uitgelegd, met de trade-offs open en bloot in plaats van een vendor-diagram dat me vertelt wat “best practice” is.

Even een bias-check vooraf: ik host alles zelf, mijn clusters draaien op refurbished mini PCs in een homelab, en ik hecht meer aan begrijpen wat ik draai dan aan de meest schaalbare architectuur op papier. Houd dat in gedachten. Jouw randvoorwaarden kantelen de balans anders.

Wat etcd eigenlijk doet

etcd is het deel van Kubernetes dat dingen onthoudt. Het is een gedistribueerde key-value store met alle cluster state: pod definities, secrets, configmaps, service accounts, de hele mikmak. Elke keer dat je kubectl apply doet, landt de gewenste state in etcd. De control plane componenten lezen er vervolgens uit en werken eraan om de werkelijkheid te laten kloppen.

Als etcd dus down is, is je cluster effectief hersendood. De API server heeft niets om te serveren, controllers kunnen niets reconcilen, en je draaiende workloads driften zonder dat iemand stuurt. etcd beschikbaarheid en cluster beschikbaarheid zijn hetzelfde getal.

etcd blijft consistent met het Raft consensus algoritme, dat een quorum nodig heeft, een strikte meerderheid van members, om writes te accepteren. Drie members verdragen één failure. Vijf members verdragen er twee. Zak je onder quorum, dan wordt etcd read-only totdat er genoeg members terugkomen. Die quorum-rekensom is waar elke HA-keuze hieronder om draait, dus die is het waard om vast te houden.

De twee topologieën

De hele vergelijking komt neer op waar etcd zit ten opzichte van de rest van het control plane.

Stacked betekent dat etcd op elke control plane node draait, pal naast de API server, controller manager en scheduler.

flowchart TD
    subgraph stacked["Stacked etcd Topologie"]
        subgraph CP1["Control Plane Node 1"]
            API1["API Server"]
            Ctrl1["Controller"]
            Sched1["Scheduler"]
            etcd1["etcd"]
        end
        subgraph CP2["Control Plane Node 2"]
            API2["API Server"]
            Ctrl2["Controller"]
            Sched2["Scheduler"]
            etcd2["etcd"]
        end
        subgraph CP3["Control Plane Node 3"]
            API3["API Server"]
            Ctrl3["Controller"]
            Sched3["Scheduler"]
            etcd3["etcd"]
        end
    end

External betekent dat etcd op eigen aparte nodes draait, volledig gescheiden van het control plane.

flowchart TD
    subgraph external["External etcd Topologie"]
        subgraph etcd_cluster["etcd Cluster"]
            E1["etcd Node 1"]
            E2["etcd Node 2"]
            E3["etcd Node 3"]
        end
        etcd_cluster --> control_plane
        subgraph control_plane["Control Plane Nodes"]
            subgraph CP1["Control Plane 1"]
                API1["API Server<br/>Controller<br/>Scheduler"]
            end
            subgraph CP2["Control Plane 2"]
                API2["API Server<br/>Controller<br/>Scheduler"]
            end
            subgraph CP3["Control Plane 3"]
                API3["API Server<br/>Controller<br/>Scheduler"]
            end
        end
    end

Dezelfde componenten, een andere blast radius. Dat verschil is het hele verhaal. Laat me ze vergelijken op de criteria die de vraag echt beslissen: operationele complexiteit, failure-isolatie, schaalflexibiliteit en resource-gedrag.

Operationele complexiteit

Stacked wint hier zonder veel strijd. Drie nodes geven je een volledig HA control plane met etcd quorum ingebakken, en de lifecycle is op een handige manier gekoppeld: voeg een control plane node toe en je krijgt er automatisch een etcd member bij. kubeadm, K3s en de meeste installers defaulten naar stacked omdat dat het pad met de minste bewegende delen is. Je draait kubeadm init met --control-plane-endpoint, joint twee nodes erbij, en je bent klaar.

External etcd vraagt vooraf meer van je. Je bootstrapt en certificeert zelf een etcd cluster voordat kubeadm überhaupt het control plane wil initialiseren, en vanaf dat moment onderhoud je twee tiers met aparte upgrade- en backup-procedures. Meer nodes, meer netwerkpaden om te beveiligen, meer om over na te denken om 2 uur ’s nachts.

Als je team nog nooit etcd heeft gedraaid, is die complexiteit een echte kostenpost, geen abstracte. Ik snap liever drie nodes grondig dan zes half.

Failure-isolatie

Hier verdient external zijn plek. Met stacked etcd kost een dode node je twee dingen tegelijk: een control plane component en een etcd member. Verlies een node en je quorum-marge krimpt op hetzelfde moment dat je API-capaciteit dat doet. Met external etcd zijn die failures onafhankelijk. Een control plane node die sterft laat etcd quorum onaangeroerd, en een etcd node die sterft laat de API servers serveren.

Voor de meeste clusters is die koppeling prima, want drie nodes verdragen sowieso één failure. De isolatie telt wanneer je krappe SLAs draait en je je geen enkel node-verlies kunt veroorloven dat twee systemen tegelijk meeneemt.

Schaalflexibiliteit

Stacked dwingt je etcd member-aantal en je API server-aantal om samen te bewegen. Dat is meestal wat je wilt, maar niet altijd. etcd presteert juist slechter voorbij een bepaald member-aantal, omdat elke write een meerderheid moet bereiken, dus je wilt zelden meer dan vijf members. Je API server tier kan ondertussen echt baat hebben bij verder uitschalen onder zware read-load.

External ontkoppelt de twee. Draai drie etcd members voor stabiele consensus en vijf API servers voor throughput, los van elkaar gedimensioneerd. Op grote clusters, voorbij grofweg honderd nodes waar API- en etcd-load oploopt, houdt die onafhankelijkheid op theoretisch te zijn.

Resource-gedrag

etcd is ongewoon gevoelig voor disk latency. Het schrijft elke gecommitte entry naar disk en verwacht dat die write snel is, dus als iets anders op de machine de disk hamert, begint etcd heartbeats te missen en kan het een onnodige leader election triggeren. Op een stacked node is dat “iets anders” je eigen API server die een grote list operatie doet. De twee vechten om dezelfde disk.

External omzeilt dit door etcd eigen hardware te geven. Snelle NVMe voor de etcd tier, meer RAM voor de API tier, elk component getuned voor wat het werkelijk doet. Je kunt etcd ook backuppen, restoren en upgraden zonder het control plane aan te raken.

De trade-offs naast elkaar

CriteriumStacked etcdExternal etcd
Aantal nodes3 voor volledig HA6+ (3 etcd + 3 control plane)
Setup-moeiteLaag, installer defaultsHoog, handmatige etcd bootstrap
Failure-isolatieGekoppeld (node-verlies raakt beide)Onafhankelijk
Schalingetcd en API schalen samenSchaal elke tier apart
Disk contentionetcd deelt disk met API serveretcd krijgt eigen disk
Beste fitHomelab, kleine/medium clustersGrote clusters, strikte SLAs

De tabel is het snelle overzicht. Het eerlijke antwoord zit in de volgende sectie.

Dus welke moet je kiezen

Grijp naar stacked wanneer je een homelab of development cluster draait, wanneer je onder grofweg honderd nodes zit met gezonde workloads, wanneer je team nieuwer is in etcd operations, of wanneer je op managed Kubernetes zit (EKS, GKE, AKS regelen dit al en de keuze is niet aan jou). Het lagere node-aantal betekent minder stroomverbruik, minder kosten en minder om te begrijpen, wat voor een self-hoster precies het punt is.

Grijp naar external wanneer je voorbij honderd nodes zit en de API- en etcd-load eigen hardware rechtvaardigt, wanneer je SLAs echt geen node-verlies kunnen verdragen dat twee systemen raakt, wanneer je het control plane en etcd onafhankelijk moet schalen, of wanneer je team al comfortabel etcd clusters draait en de extra complexiteit goedkoop voor je is.

Er is ook een tussenpad dat het waard is om te kennen. K3s defaultt naar embedded etcd (stacked, met een veel lichtere setup) maar ondersteunt ook een externe SQL-database (MySQL, PostgreSQL) in plaats van etcd helemaal, wat echt handig is als je er al een draait en backupt.

Stacked HA opzetten met kubeadm

# Op eerste control plane node
kubeadm init \
  --control-plane-endpoint "loadbalancer.example.com:6443" \
  --upload-certs

# Bewaar de join commands voor andere control planes
# Join extra control plane nodes
kubeadm join loadbalancer.example.com:6443 \
  --token <token> \
  --discovery-token-ca-cert-hash sha256:<hash> \
  --control-plane \
  --certificate-key <cert-key>

De dragende flag is --control-plane-endpoint. Die moet naar een load balancer voor je API servers wijzen, nooit naar een enkel node IP, anders heb je stilletjes de single point of failure terug die je probeerde weg te halen.

External etcd opzetten

Voor external etcd breng je eerst het etcd cluster omhoog:

# Op elke etcd node, configureer etcd
cat > /etc/etcd/etcd.conf.yaml << EOF
name: etcd-1
data-dir: /var/lib/etcd
initial-cluster-state: new
initial-cluster: etcd-1=https://10.0.0.1:2380,etcd-2=https://10.0.0.2:2380,etcd-3=https://10.0.0.3:2380
listen-peer-urls: https://10.0.0.1:2380
listen-client-urls: https://10.0.0.1:2379,https://127.0.0.1:2379
advertise-client-urls: https://10.0.0.1:2379
initial-advertise-peer-urls: https://10.0.0.1:2380
client-transport-security:
  cert-file: /etc/etcd/pki/server.crt
  key-file: /etc/etcd/pki/server.key
  trusted-ca-file: /etc/etcd/pki/ca.crt
peer-transport-security:
  cert-file: /etc/etcd/pki/peer.crt
  key-file: /etc/etcd/pki/peer.key
  trusted-ca-file: /etc/etcd/pki/ca.crt
EOF

Wijs kubeadm dan naar dat externe cluster:

# kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
etcd:
  external:
    endpoints:
      - https://10.0.0.1:2379
      - https://10.0.0.2:2379
      - https://10.0.0.3:2379
    caFile: /etc/kubernetes/pki/etcd/ca.crt
    certFile: /etc/kubernetes/pki/apiserver-etcd-client.crt
    keyFile: /etc/kubernetes/pki/apiserver-etcd-client.key

Wat ik zelf draai

Voor mij wint stacked, en het is niet eens close. Ik draai stacked etcd in mijn homelab op drie control plane nodes. Het heeft disk failures, netwerk-hikjes en de occasionele “oeps, ik reboootte de verkeerde server” overleefd zonder het cluster te verliezen. Stacked betekent niet fragiel. Het betekent gekoppeld, en drie nodes koppelen die ik volledig begrijp wint van state verspreiden over zes die ik half begrijp.

Maar koppel dat terug aan mijn bias: ik ben een self-hoster die optimaliseert voor begrip en een laag node-aantal, geen platform-team dat een fleet van duizend nodes draait onder een five-nines SLA. Is dat jouw wereld, dan is de isolatie van external etcd elke extra node waard. Jouw context beslist, niet de mijne.

Wat meer telt dan de topologie is het node-aantal eronder. Eén node is geen HA, het is een single point of failure met extra stappen. Twee nodes is erger dan één, want quorum heeft een meerderheid nodig en 2/2 - 1 = 1, dus je kunt eigenlijk geen enkele failure verdragen. Drie is de bodem voor echte HA, met beide topologieën.

Kubernetes HA gaat over failures overleven, niet voorkomen. Disks gaan dood, netwerken flapperen, en uiteindelijk reboot iemand de verkeerde machine. De enige vraag die ertoe doet is of het cluster zijn schouders ophaalt en doorgaat, en drie control plane nodes met gezond etcd quorum is wat dat mogelijk maakt.