Alle inzichten
DevOps & platform engineering Artikel

Kubernetes high availability: acht punten die samenwerken

Hoge beschikbaarheid voor applicaties op Kubernetes.

Een node valt uit. De applicatie moet blijven draaien. Daarvoor zijn voldoende replicas nodig, verspreid over nodes en zones, met ruimte voor herstel en updates die verkeer blijven afhandelen.

De acht instellingen in dit artikel zijn uitgewerkt voor één stateless API. Het voorbeeld gebruikt drie replicas en drie zones. De basis van het cluster komt eerst aan bod; daarna volgen de instellingen voor de applicatie. Databases, singletons en batchtaken vragen om eigen keuzes.

Test wat er bij uitval blijft werken. Meet tijdens de verstoring of de applicatie nog voldoende verkeer afhandelt en hoe lang herstel duurt.

Download het artikel als Markdown
In dit artikel

Begin bij de basis van het cluster.

De control plane stuurt het cluster aan; etcd bewaart de clusterstatus. Bij stacked etcd draaien beide op dezelfde nodes. Een gebruikelijke HA-opstelling voor een zelfbeheerd cluster bestaat uit minimaal drie control-plane-nodes achter een redundant API-endpoint. Drie etcd-leden hebben een meerderheid van twee nodig en kunnen daardoor één lid verliezen. Het consensusprotocol zorgt voor die meerderheid.

Bij managed Kubernetes beheert de leverancier een deel van deze basis. Controleer welke onderdelen over zones zijn verdeeld, welke beschikbaarheid is afgesproken en wie verantwoordelijk is voor herstel. Als de control plane uitvalt, kunnen bestaande pods doorwerken. Nieuwe pods plaatsen, configuratie wijzigen en herstel aansturen kan dan worden geblokkeerd.

Controleer ook ingress of Gateway, DNS, het containernetwerk, opslag en externe afhankelijkheden. Een database heeft een eigen beschikbaarheidsontwerp nodig, ook als de operator ervan redundant draait. Zonegebonden volumes kunnen herplaatsing beperken. Zorg daarnaast voor back-ups en een geteste restore.

Spreek af welke uitval het ontwerp moet opvangen. Leg het doel vast in een Service Level Objective (SLO). Benoem ook de gewenste hersteltijd, de Recovery Time Objective (RTO), en het aanvaardbare dataverlies in tijd, de Recovery Point Objective (RPO). Uitval van één zone en uitval van een hele regio zijn verschillende scenario’s.

1. Replicas: voldoende kopieën én resterende capaciteit.

Kies genoeg replicas om de afgesproken belasting ook na uitval af te handelen. In dit voorbeeld draaien drie replicas. Als er één uitvalt, blijven er twee over. Meet of die twee de piekbelasting aankunnen.

Een Deployment legt met spec.replicas het gewenste aantal pods vast. De ReplicaSet probeert dat aantal in stand te houden. De applicatie moet meerdere instanties tegelijk ondersteunen: let op lokale sessies, bestanden, locks en achtergrondtaken. Gedeelde state kan buiten de pods worden opgeslagen; die datastore heeft een eigen beschikbaarheidsontwerp nodig.

Eén replica kan met extra surge-capaciteit soms zonder geplande onderbreking worden bijgewerkt, maar kan de uitval van die ene pod niet opvangen. Kies het aantal op basis van de belasting, het belang van de applicatie en de uitval die moet worden opgevangen. Een oneven aantal replicas geeft een gewone API geen quorum of bescherming tegen split brain.

spec:
  replicas: 3

Controle in de praktijkMeet of twee overblijvende replicas de afgesproken piekbelasting verwerken.

2. PodDisruptionBudget: onderhoud begrenzen.

Beperk hoeveel gezonde pods tijdens onderhoud mogen verdwijnen. Een PodDisruptionBudget (PDB) doet dat voor vrijwillige verstoringen via de Eviction API, bijvoorbeeld bij een node-drain. Met drie gezonde replicas en minAvailable: 2 is normaal één eviction toegestaan. Gebruik minAvailable óf maxUnavailable en selecteer de bedoelde pods met de labelselector.

Het PDB begrenst geen nodecrash, zone-uitval, Deployment-rollout, directe podverwijdering of afschaling via het scale-subresource. Een HorizontalPodAutoscaler (HPA) kan dus terugschalen ondanks het PDB. Stel het minimale aantal replicas apart in.

Bepaal ook wat er met ongezonde pods moet gebeuren. Zonder unhealthyPodEvictionPolicy geldt IfHealthyBudget: een draaiende pod die niet Ready is, mag alleen worden geëvict als currentHealthy minimaal desiredHealthy is. Als dit budget niet wordt gehaald, kan zo’n pod een drain blokkeren.

Het voorbeeld kiest unhealthyPodEvictionPolicy: AlwaysAllow voor deze stateless API. Draaiende, ongezonde pods mogen dan ook bij een tekort aan gezonde pods worden geëvict. Gezonde pods blijven onder het PDB vallen. De afweging: onderhoud hoeft niet op de ongezonde pod te wachten, maar die krijgt ook geen extra kans om op dezelfde node te herstellen. Kies de policy op basis van het herstelgedrag van de applicatie.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: example-api
  namespace: ha-example
spec:
  minAvailable: 2
  unhealthyPodEvictionPolicy: AlwaysAllow
  selector:
    matchLabels:
      app.kubernetes.io/name: example-api
Drie gezonde replicas en minAvailable: 2 laten normaal één vrijwillige eviction toe. Een nodefout, directe scale-wijziging en Deployment-rollout gebruiken niet dit beschermingsmechanisme. Drie gezonde replicas en minAvailable: 2 laten normaal één vrijwillige eviction toe. Een nodefout, directe scale-wijziging en Deployment-rollout gebruiken niet dit beschermingsmechanisme.
Drie gezonde replicas en minAvailable: 2 laten normaal één vrijwillige eviction toe. Een nodefout, directe scale-wijziging en Deployment-rollout gebruiken niet dit beschermingsmechanisme.

Controle in de praktijkControleer status.disruptionsAllowed. Test een drain met drie gezonde pods, met een ontbrekende pod en met een draaiende pod die niet Ready wordt.

3. Node anti-affinity: kopieën op verschillende hosts.

Zet replicas op verschillende nodes, zodat één defecte node niet alle kopieën raakt. Pod anti-affinity regelt dit voor nieuwe pods met hetzelfde applicatielabel. Het voorbeeld gebruikt topologyKey: kubernetes.io/hostname om per host te spreiden.

requiredDuringSchedulingIgnoredDuringExecution is een harde plaatsingsregel; preferredDuringSchedulingIgnoredDuringExecution is een voorkeur. Bij een harde regel blijft een pod Pending als geen node aan de voorwaarden voldoet. Vrije resources, taints, tolerations en node selectors tellen ook mee. Drie replicas vragen hier drie geschikte hosts; een extra surge-pod heeft tijdelijk een vierde nodig.

IgnoredDuringExecution betekent dat de regel bestaande pods niet automatisch verplaatst wanneer labels wijzigen. Controleer daarom de werkelijke plaatsing na een rollout en na onderhoud.

affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchLabels:
            app.kubernetes.io/name: example-api
        topologyKey: kubernetes.io/hostname

Controle in de praktijkControleer de nodekolom: iedere replica staat op een andere geschikte host.

4. Zonespreiding: ook gedeelde infrastructuur kan uitvallen.

Verdeel replicas over zones om gedeelde uitval te beperken. Verschillende hosts kunnen nog steeds dezelfde stroomvoorziening of netwerkverbinding delen. topologySpreadConstraints gebruikt het nodelabel topology.kubernetes.io/zone voor die verdeling. De labels moeten echte, onafhankelijke infrastructuur beschrijven.

Met maxSkew: 1 en DoNotSchedule begrenst de scheduler het verschil ten opzichte van de zones die meetellen en de minste pods bevatten. Hij telt pods die bij de labelselector passen, ongeacht hun verwerkingscapaciteit. ScheduleAnyway maakt van de spreiding een voorkeur. Bestaande pods worden niet automatisch verplaatst.

nodeAffinityPolicy en nodeTaintsPolicy bepalen mede welke zones meetellen. Het voorbeeld gebruikt Honor voor beide. minDomains staat bewust niet op 3: die ondergrens kan het plaatsen van vervangers op twee overgebleven zones blokkeren. Zonder dit veld gebruikt de berekening 1. Controleer bij zone-uitval ook taints, tolerations en vrije capaciteit op de resterende nodes.

topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    nodeAffinityPolicy: Honor
    nodeTaintsPolicy: Honor
    labelSelector:
      matchLabels:
        app.kubernetes.io/name: example-api
Schematisch: drie Ready-replicas op verschillende nodes en zones. Een vierde geschikte node biedt plaats voor surge; resources en overige plaatsingsvoorwaarden moeten ook passen. Schematisch: drie Ready-replicas op verschillende nodes en zones. Een vierde geschikte node biedt plaats voor surge; resources en overige plaatsingsvoorwaarden moeten ook passen.
Schematisch: drie Ready-replicas op verschillende nodes en zones. Een vierde geschikte node biedt plaats voor surge; resources en overige plaatsingsvoorwaarden moeten ook passen.

Controle in de praktijkToets zowel resterende capaciteit als Pending-pods bij het wegvallen van één zone.

5. Rolling updates: eerst een bruikbare vervanger.

Start een bruikbare vervanger voordat oude capaciteit verdwijnt. Bij een gezonde start houdt RollingUpdate met maxUnavailable: 0 de gewenste beschikbare capaciteit aan. maxSurge: 1 laat één extra pod toe. Met minReadySeconds: 10 moet een nieuwe pod tien seconden aaneengesloten Ready blijven voordat die als Available telt.

De Deployment regelt dit los van het PDB. Zonder plek voor de extra pod kan de rollout vastlopen, terwijl oude pods blijven draaien. Pods die nog worden beëindigd, kunnen tijdelijk ook resources gebruiken. Houd daar rekening mee boven op maxSurge. progressDeadlineSeconds markeert een vastgelopen rollout; een rollback moet apart worden geregeld.

Laat oude en nieuwe versies veilig naast elkaar werken, ook met gedeelde data en databaseschema’s. Laat de applicatie bij SIGTERM stoppen met nieuw werk en lopende requests afronden. terminationGracePeriodSeconds omvat de hele beëindiging, inclusief eventuele preStop-hooks. Geef route- en endpointwijzigingen tijd om door te werken en test het resultaat onder belasting.

spec:
  minReadySeconds: 10
  progressDeadlineSeconds: 600
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    spec:
      terminationGracePeriodSeconds: 60
Eén stap van een gezonde rollout: eerst een extra pod, daarna een oude pod afbouwen wanneer de vervanger Available is. Terminating pods kunnen tijdelijk nog resources gebruiken. Eén stap van een gezonde rollout: eerst een extra pod, daarna een oude pod afbouwen wanneer de vervanger Available is. Terminating pods kunnen tijdelijk nog resources gebruiken.
Eén stap van een gezonde rollout: eerst een extra pod, daarna een oude pod afbouwen wanneer de vervanger Available is. Terminating pods kunnen tijdelijk nog resources gebruiken.

Controle in de praktijkVoer een rollout onder belasting uit en meet foutpercentage, latency en beschikbare replicas.

6. Resources: ruimte voor normaal bedrijf én verstoring.

Reserveer ook capaciteit voor uitval en updates. De scheduler gebruikt resource requests om pods te plaatsen. Te lage requests kunnen een node te vol laten lopen bij piekbelasting; te hoge requests houden ruimte bezet die andere pods zouden kunnen gebruiken. Meet normaal verbruik, startpieken en gedrag bij uitval.

CPU-limits kunnen leiden tot throttling: de applicatie krijgt dan minder CPU-tijd. Overschrijding van een geheugenlimiet kan tot een OOM-kill leiden, een beëindiging wegens geheugentekort. Dit voorbeeld laat de CPU-limit weg. Bij gedeeld gebruik, isolatie-eisen of clusterbeleid kan een expliciete CPU-limit wel nodig zijn.

Houd ruimte vrij voor systeemtaken, een extra pod tijdens de rollout en herplaatsing na uitval. Een CPU-gebaseerde HPA vergelijkt het gebruik met de CPU-request. Kies die request daarom op basis van metingen. Met deze requests en alleen een geheugenlimiet krijgt de pod niet de Guaranteed QoS-klasse.

resources:
  requests:
    cpu: 250m
    memory: 256Mi
  limits:
    memory: 512Mi

Controle in de praktijkMeet startpieken, OOM-kills, throttling en plaatsbaarheid tijdens een verstoring.

7. Probes: opstarten, verkeer ontvangen en herstellen scheiden.

Geef opstarten, verkeer ontvangen en herstarten elk een eigen controle. Een startupProbe geeft tijd voor initialisatie. Tot die slaagt, starten de liveness- en readiness-controles niet. periodSeconds: 5 en failureThreshold: 24 geven hier grofweg twee minuten opstarttijd; de probeplanning en uitvoering bepalen de werkelijke tijd.

Readiness bepaalt of een pod via een gewone Service verkeer kan ontvangen. Een mislukte readiness-probe herstart de container niet. Liveness is bedoeld voor een proces dat niet zelfstandig herstelt, zoals bij een deadlock. Na voldoende mislukte controles wordt de container herstart. Dat kan ook gebeuren als de startup-probe blijft mislukken.

Houd een externe database of API buiten liveness, zodat een gedeelde storing geen herstartgolf veroorzaakt. Readiness kan essentiële afhankelijkheden controleren. Houd er dan rekening mee dat database-uitval alle replicas NotReady kan maken. Houd de probes licht en kies timeouts en thresholds op basis van gemeten gedrag.

startupProbe:
  httpGet: { path: /health/startup, port: http }
  periodSeconds: 5
  timeoutSeconds: 2
  failureThreshold: 24
readinessProbe:
  httpGet: { path: /health/ready, port: http }
  periodSeconds: 5
  timeoutSeconds: 2
  failureThreshold: 2
livenessProbe:
  httpGet: { path: /health/live, port: http }
  periodSeconds: 10
  timeoutSeconds: 3
  failureThreshold: 3
Startup is de toegangspoort. Daarna beantwoorden readiness en liveness onafhankelijk verschillende vragen. Readiness zet geen containerherstart in gang. Startup is de toegangspoort. Daarna beantwoorden readiness en liveness onafhankelijk verschillende vragen. Readiness zet geen containerherstart in gang.
Startup is de toegangspoort. Daarna beantwoorden readiness en liveness onafhankelijk verschillende vragen. Readiness zet geen containerherstart in gang.

Controle in de praktijkTest trage startup, een NotReady-pod en een externe dependency-storing afzonderlijk.

8. PriorityClass: bewust kiezen bij schaarste.

Spreek af welke workloads voorrang krijgen als capaciteit schaars is. Een PriorityClass bepaalt de relatieve prioriteit voor scheduling. Het is een clusterbreed object dat vooraf moet bestaan. De pod verwijst ernaar met priorityClassName. Gebruik een afgesproken indeling, zodat de waarde een duidelijke betekenis heeft.

Met preemptionPolicy: Never krijgt de pod voorrang in de schedulingwachtrij, maar verdringt die geen draaiende pods met een lagere prioriteit. De standaardinstelling PreemptLowerPriority kan dat onder voorwaarden wel doen. Daarbij probeert Kubernetes een PDB te respecteren, maar kan het budget toch worden overschreden.

Een PriorityClass reserveert geen CPU, geheugen of nodes. Een pod met hoge prioriteit kan dus Pending blijven door capaciteitstekort of harde plaatsingsregels. Combineer prioriteit met voldoende capaciteit en beperk wie hoge prioriteiten mag gebruiken.

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: example-critical
value: 1000000
globalDefault: false
preemptionPolicy: Never
description: "Explicit priority without preemption"
# Pod spec: priorityClassName: example-critical

Controle in de praktijkToets wachtrijgedrag en Pending-redenen bij schaarste zonder onbedoelde verdringing.

Alle acht punten in één manifest.

Het referentiemanifest combineert drie replicas, drie zones, een PDB met minAvailable: 2 en ruimte voor één extra pod tijdens een update. De twee overblijvende replicas moeten de afgesproken belasting aankunnen. De applicatie moet meerdere instanties, de drie probe-endpoints en graceful shutdown ondersteunen.

Door de harde hostname anti-affinity heeft iedere replica een eigen node nodig. Met drie replicas en maxSurge: 1 zijn tijdens een update minimaal vier geschikte worker-nodes nodig. Controleer of zoneplaatsing, taints, requests en quota de extra pod toelaten, ook bij uitval. Maak de namespace en PriorityClass aan voordat de Deployment start.

De image onder registry.example.com is een placeholder. Vervang die door een gecontroleerde image, bij voorkeur gepind op digest. Pas resources, labels, probes en rechten aan op de applicatie. De gebruikte node inclusion policies voor topology spread zijn stabiel vanaf Kubernetes 1.33; controleer of het doelcluster deze ondersteunt.

Download de YAML-referentie
Bekijk het volledige referentiemanifest
# Illustrative stateless API reference, not a ready-to-apply application.
# Replace the placeholder image, health endpoints and resource values.
# Assumptions: three real zone domains and >=4 eligible worker nodes for surge.
# Two surviving replicas must handle the agreed load (verify by load testing).
# Topology-spread node inclusion policies are stable since Kubernetes 1.33.
apiVersion: v1
kind: Namespace
metadata:
  name: ha-example
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: example-critical
value: 1000000
globalDefault: false
preemptionPolicy: Never
description: "Explicit application priority without preemption"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-api
  namespace: ha-example
spec:
  replicas: 3
  minReadySeconds: 10
  progressDeadlineSeconds: 600
  revisionHistoryLimit: 5
  selector:
    matchLabels:
      app.kubernetes.io/name: example-api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    metadata:
      labels:
        app.kubernetes.io/name: example-api
    spec:
      priorityClassName: example-critical
      terminationGracePeriodSeconds: 60
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        fsGroup: 10001
        seccompProfile:
          type: RuntimeDefault
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchLabels:
                  app.kubernetes.io/name: example-api
              topologyKey: kubernetes.io/hostname
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          nodeAffinityPolicy: Honor
          nodeTaintsPolicy: Honor
          labelSelector:
            matchLabels:
              app.kubernetes.io/name: example-api
      containers:
        - name: api
          # Placeholder: replace with a reviewed, digest-pinned application image.
          image: registry.example.com/team/example-api:1.0.0
          ports:
            - name: http
              containerPort: 8080
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              memory: 512Mi
          startupProbe:
            httpGet:
              path: /health/startup
              port: http
            periodSeconds: 5
            timeoutSeconds: 2
            failureThreshold: 24
          readinessProbe:
            httpGet:
              path: /health/ready
              port: http
            periodSeconds: 5
            timeoutSeconds: 2
            failureThreshold: 2
          livenessProbe:
            httpGet:
              path: /health/live
              port: http
            periodSeconds: 10
            timeoutSeconds: 3
            failureThreshold: 3
          volumeMounts:
            - name: temporary-files
              mountPath: /tmp
      volumes:
        - name: temporary-files
          emptyDir:
            sizeLimit: 128Mi
---
apiVersion: v1
kind: Service
metadata:
  name: example-api
  namespace: ha-example
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: example-api
  ports:
    - name: http
      port: 80
      targetPort: http
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: example-api
  namespace: ha-example
spec:
  minAvailable: 2
  # Healthy pods retain the budget; unhealthy Running pods need not block drain.
  unhealthyPodEvictionPolicy: AlwaysAllow
  selector:
    matchLabels:
      app.kubernetes.io/name: example-api

Stem autoscaling af op herstel en capaciteit.

Een HorizontalPodAutoscaler (HPA) past het aantal replicas aan op gemeten belasting. Bij een CPU-utilization-target vergelijkt de HPA het CPU-gebruik met de CPU-requests van de pods. Daarvoor zijn werkende metrics en passende requests nodig.

De HPA maakt geen nodes aan. Regel voldoende ruimte via vrije capaciteit en passende node-autoscaling en quota. Opschalen kost tijd. Voor het voorbeeld blijft het minimum op drie replicas staan, bijvoorbeeld met minReplicas: 3 in een HPA. Het PDB bepaalt dit minimum niet. Stem bij eventgedreven autoscaling met KEDA dezelfde ondergrens af; terugschalen naar nul past niet bij deze API.

Als de HPA het aantal replicas beheert, moet een GitOps-controller dat veld met rust laten. Stel de diff- en syncconfiguratie daarop in en leg vast welke controller welk veld beheert. Laat geen twee autoscalers onafhankelijk dezelfde workload besturen.

Meet foutpercentage, responstijd en succesvolle requests aan de ingang van de dienst. Gebruik Pending-pods, Ready/Available-replicas, PDB-status, OOM-kills en throttling om de oorzaak van verstoringen te onderzoeken.

Test wat er bij uitval blijft werken.

Controleer de manifests die daadwerkelijk worden uitgerold en vergelijk die met de live plaatsing. Pods nemen de zonelabels van hun node niet automatisch over. Koppel daarom de nodekolom van de pods aan de zonelabels van de nodes. Deze opdrachten lezen alleen status uit.

kubectl get nodes -L topology.kubernetes.io/zone
kubectl get pods -n ha-example \
  -l app.kubernetes.io/name=example-api -o wide
kubectl get pdb example-api -n ha-example
kubectl describe pdb example-api -n ha-example
kubectl rollout status deployment/example-api -n ha-example
kubectl top pods -n ha-example \
  -l app.kubernetes.io/name=example-api

kubectl top geeft huidig verbruik; voor sizing zijn historie en piekmetingen nodig. Plan verstoringstests vooraf in een afgesproken testomgeving, met monitoring en een herstelpad. Leg bij iedere proef de omstandigheden en uitkomst vast.

Node-uitval
Resterende replicas dragen het afgesproken verkeer; fouten en herstelduur blijven binnen doel.
Gecontroleerde drain
Evictions volgen het PDB; herstel maakt verdere onderhoudsstappen mogelijk.
Zone-uitval
Verkeer, compute, opslag en afhankelijkheden worden samen getest; Pending-redenen zijn verklaard.
Rollout onder belasting
Nieuwe pods worden Available, oude requests eindigen netjes en versies kunnen naast elkaar werken.
Dependency-storing
Geen liveness-herstartgolf; gedegradeerd gedrag en herstel zijn voorspelbaar.
Herstel van data
Een uitgevoerde restore toont haalbare RTO/RPO voor de betreffende datastore.

Leg per test vast hoeveel verkeer nog wordt afgehandeld, welke fouten optreden en hoe lang herstel duurt. Vergelijk de uitkomst met het afgesproken doel. Pas daarna replicas, capaciteit of plaatsing aan en herhaal de test.

Bespreek een beschikbaarheidsvraagstuk

Bronnen en technische controle

Gecontroleerd op 30 oktober 2026. Raadpleeg de officiële documentatie voor de gebruikte Kubernetes-versie.