Kubernetes spot en preemptible instances: kostenbesparing met interruption safety

Spot instances op AWS en preemptible VMs op GCP kosten 60–80% minder dan on-demand, maar de cloudprovider kan ze terugvorderen met soms maar 30 seconden waarschuwing. Kubernetes-workloads veilig op spot draaien vereist interruption handlers, PodDisruptionBudgets, de juiste taints en gediversifieerde instance pools. Deze guide doorloopt elke laag van de setup op zowel EKS als GKE.

Wat je hebt aan het einde

Een Kubernetes-cluster waar niet-kritieke workloads draaien op spot of preemptible nodes met 60–80% kostenreductie, met interruption handlers die pods graceful drainen voor reclamatie, PodDisruptionBudgets die beschikbaarheid beschermen, en instance type diversificatie die interruptionpercentages onder de 5% houdt.

Vereisten

  • kubectl verbonden met een EKS (1.28+) of GKE (1.25+) cluster
  • helm lokaal geinstalleerd voor handler-deployments
  • Bekend met Karpenter NodePool-configuratie (voor het Karpenter-pad) of Cluster Autoscaler (voor managed node groups)
  • PodDisruptionBudgets geconfigureerd op productie-workloads
  • IAM-rechten om SQS-queues en EventBridge-rules aan te maken (AWS) of node pool-beheerrechten (GCP)

Hoe interruption notices werken

De cloudprovider besluit je spot-capaciteit terug te vorderen. Wat er daarna gebeurt verschilt per provider.

AWS: 2 minuten waarschuwing via IMDS

AWS stuurt een Spot Instance interruption notice precies 2 minuten voor het stoppen of termineren van de instance. De notice is beschikbaar op het IMDS-endpoint:

http://169.254.169.254/latest/meta-data/spot/instance-action

Bij een geplande interruption retourneert het endpoint:

{"action": "terminate", "time": "2026-04-09T14:22:00Z"}

Zonder geplande interruption krijg je HTTP 404. Dat is de normale toestand.

AWS stuurt ook een aparte rebalance recommendation wanneer een spot instance verhoogd risico loopt. Die komt voor de 2-minuten-notice (soms flink eerder) en geeft je tijd om workloads proactief te verplaatsen.

GCP: 30 seconden ACPI-signaal

GCP stuurt een ACPI G2 Soft Off-signaal dat de VM maximaal 30 seconden geeft om graceful af te sluiten. Lukt dat niet binnen het venster, dan volgt een hard kill-signaal.

Die 30 seconden zijn krap. Hou er rekening mee in je terminationGracePeriodSeconds.

GCP-terminologie: preemptible VMs hebben een maximale runtime van 24 uur en Google raadt aan te migreren naar Spot VMs, die geen inherente runtime-limiet hebben. Beide delen hetzelfde 30-seconden-interruptionvenster.

Stap 1: taint spot nodes en configureer tolerations

Spot nodes hebben een taint nodig om te voorkomen dat workloads die niet spot-tolerant zijn erop terechtkomen. Zonder taint kan je database-primary op een spot node belanden.

Op EKS met Karpenter stel je de taint in via de NodePool spec (zie stap 3).

Op GKE pas je de taint toe bij het aanmaken van de spot node pool:

gcloud container node-pools create spot-pool \
  --cluster=production-main \
  --spot \
  --node-taints=cloud.google.com/gke-spot="true":NoSchedule

GKE labelt spot nodes automatisch met cloud.google.com/gke-spot: "true" en cloud.google.com/gke-provisioning: "spot" (GKE 1.25.5+).

Workloads die op spot mogen draaien hebben een bijpassende toleration nodig:

# Voor GKE spot nodes
tolerations:
  - key: cloud.google.com/gke-spot
    operator: Equal
    value: "true"
    effect: NoSchedule
# Voor Karpenter-managed spot nodes (custom taint)
tolerations:
  - key: "spot"
    operator: "Equal"
    value: "true"
    effect: "NoSchedule"

Stap 2: installeer een interruption handler

Er moet iets het reclamatiesignaal van de cloudprovider opvangen en pods drainen voordat de node verdwijnt. De juiste tool hangt af van je setup.

Optie A: Karpenter native handling (EKS, aanbevolen)

Draai je al Karpenter, dan heb je geen aparte handler nodig. Karpenter handelt Spot interruption notifications native af sinds v0.19.0 via een SQS-queue en EventBridge.

Bij een interruption:

  1. EventBridge stuurt het event door naar de SQS-queue
  2. Karpenter's interruption controller taint de node NoSchedule en begint pods te drainen
  3. Tegelijkertijd provisiont Karpenter een vervangende node vanuit de NodePool
  4. De vervanging is doorgaans klaar voor het 2-minuten-venster verloopt

Schakel het in door de queue-naam aan Karpenter mee te geven:

karpenter --interruption-queue=karpenter-interruption-queue

Een beperking: Karpenter publiceert wel events voor Spot rebalance recommendations, maar handelt er niet proactief op. Wil je rebalance-triggered vervanging, dan heb je nog steeds NTH in Queue mode nodig naast Karpenter.

Optie B: aws-node-termination-handler (EKS, zonder Karpenter)

De aws-node-termination-handler (NTH) draait in twee wederzijds exclusieve modes:

IMDS mode (DaemonSet): pollt het IMDS-endpoint elke 5 seconden. Geen extra AWS-infrastructuur nodig. Ideaal voor eenvoudige spot-setups.

helm install aws-node-termination-handler \
  eks/aws-node-termination-handler \
  --set enableSpotInterruptionDraining=true \
  --set enableRebalanceMonitoring=true \
  --set enableScheduledEventDraining=true

Queue mode (Deployment): monitort een SQS-queue gevoed door EventBridge. Ondersteunt ASG lifecycle hooks met grace periods tot 48 uur via RecordLifecycleActionHeartbeat. Noodzakelijk voor langlopende batchjobs.

helm install aws-node-termination-handler \
  eks/aws-node-termination-handler \
  --set enableSqsTerminationDraining=true \
  --set queueURL=https://sqs.eu-west-1.amazonaws.com/123456789012/nth-queue
Factor IMDS mode Queue mode
Infrastructuur nodig Geen SQS + EventBridge + IAM
ASG lifecycle hooks Nee Ja (tot 48u)
Langlopende batchjobs Nee Ja
Simpele spot nodes Aanbevolen Overkill

Optie C: GKE kubelet graceful shutdown

GKE 1.20+ handelt preemptie automatisch af. De kubelet vangt het ACPI-signaal op en termineert pods graceful. Geen aparte handler nodig.

Voor GKE-clusters ouder dan 1.20 deploy je de k8s-node-termination-handler van GoogleCloudPlatform.

Stap 3: configureer Karpenter voor spot (EKS)

Deze NodePool vertelt Karpenter om spot te prefereren, terug te vallen op on-demand als er geen capaciteit is, en te diversifieren over instance types om het interruptionrisico te verkleinen:

apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: spot-workloads
spec:
  template:
    spec:
      taints:
        - key: "spot"
          value: "true"
          effect: NoSchedule
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot", "on-demand"]   # spot preferred, on-demand fallback
        - key: kubernetes.io/arch
          operator: In
          values: ["amd64"]
        - key: node.kubernetes.io/instance-type
          operator: In
          values:
            - m5.xlarge        # diversifieer over families en generaties
            - m5a.xlarge
            - m6i.xlarge
            - m6a.xlarge
            - m5d.xlarge
            - m5n.xlarge
            - m4.xlarge
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 30s
    budgets:
      - nodes: "20%"
        reasons: ["Empty", "Drifted"]
      - nodes: "5"            # max 5 nodes tegelijk disrupted voor overige redenen

Karpenter prioriteert capacity types in volgorde: reserved > spot > on-demand. Als spot-capaciteit niet beschikbaar is, cachet Karpenter de InsufficientCapacityError voor 3 minuten en valt terug op on-demand.

SpotToSpotConsolidation

Karpenter kan een draaiende spot node vervangen door een goedkopere spot node. Schakel het in via Helm:

helm upgrade karpenter oci://public.ecr.aws/karpenter/karpenter \
  --set settings.featureGates.spotToSpotConsolidation=true

Dit triggert pas wanneer er 15+ instance type opties met lagere prijzen beschikbaar zijn, zodat je niet convergeert op een enkel type met hoge interruption rates. Het gebruikt de price-capacity-optimized strategie. Ik zou aanraden om consolidatiepatronen eerst te valideren in een staging-omgeving.

Stap 4: stel PDBs en graceful termination periods in

Spot interruptions triggeren de Eviction API, die PodDisruptionBudgets respecteert. Een goed geconfigureerde PDB voorkomt dat alle replica's tegelijk worden geevict.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-pdb
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app: web
  unhealthyPodEvictionPolicy: AlwaysAllow  # voorkomt vastgelopen drains

Stel unhealthyPodEvictionPolicy: AlwaysAllow in zodat unhealthy pods de eviction niet blokkeren wanneer het PDB-budget al verbruikt is.

Termination grace periods

De terminationGracePeriodSeconds dekt zowel de preStop hook als de SIGTERM-handler van de container. Stem het af op het interruptionvenster van je cloudprovider:

  • AWS (2-minuten-venster): stel terminationGracePeriodSeconds: 90 in voor de meeste workloads. Batchjobs op Queue mode NTH met lifecycle hooks kunnen hoger.
  • GCP (30-seconden-venster): stel terminationGracePeriodSeconds: 25 in om binnen de harde cutoff te blijven.

Een minimale preStop hook die load balancer deregistratie toestaat:

lifecycle:
  preStop:
    exec:
      command: ["/bin/sh", "-c", "sleep 5"]   # wacht op endpoint-verwijdering

Productieservices achter load balancers zouden moeten verifieren dat deregistratie compleet is in plaats van te leunen op een vaste sleep.

Minimaal aantal replica's

Draai minstens 2 replica's van elke spot-tolerante Deployment. Een enkele replica op een spot node betekent een volledige outage bij interruption. Geen PDB redt je daarvan.

Stap 5: selecteer instance types met lage interruption rates

Interruption is niet willekeurig. De AWS Spot Instance Advisor toont per type de interruptionfrequentie in banden: <5%, 5–10%, 10–15%, 15–20%, >20%. Over alle types heen ligt het historisch gemiddelde onder de 5%.

Drie regels voor instance type selectie:

  1. Diversifieer over 5–10 types van dezelfde vCPU- en geheugenklasse (bijv. m5.xlarge, m5a.xlarge, m6i.xlarge, m6a.xlarge). Hoe meer pools Karpenter of de ASG kan benutten, hoe kleiner de kans dat alle pools tegelijk uitgeput raken.
  2. Spreid over meerdere Availability Zones. Hetzelfde instance type kan sterk verschillende interruption rates hebben per AZ.
  3. Gebruik capacity-optimized of price-capacity-optimized allocatie. Deze strategieen selecteren uit de diepste capaciteitspools in plaats van puur de laagste prijs.

De spotinfo CLI laat je interruption rates en besparingspercentages per instance type en regio browsen vanuit de terminal.

Verifieer de setup

Na het deployen, bevestig dat elke laag werkt:

# Controleer of spot nodes draaien en getaint zijn
kubectl get nodes -l karpenter.sh/capacity-type=spot -o wide
kubectl describe node <spot-node> | grep -A2 Taints

# Controleer of NTH of Karpenter interruption controller draait
kubectl get pods -n kube-system | grep -E 'node-termination|karpenter'

# Controleer PDBs
kubectl get pdb --all-namespaces

Verwachte staat: spot nodes dragen de correcte taint, de interruption handler pod is Running, en PDBs tonen disruptionsAllowed > 0 voor workloads met meerdere replica's.

Geschiktheid per workloadtype

Niet elke workload hoort op spot. Gebruik deze checklist:

  • Goed geschikt: stateless webservers, API workers, CI/CD runners, batch dataverwerking, non-productieomgevingen, ML-training met checkpointing
  • Mogelijk met zorg: StatefulSets met persistent volumes wanneer de applicatie graceful checkpoint/restore ondersteunt (Kafka, Redis met RDB-snapshots, gedistribueerde ML-training)
  • Niet geschikt: single-node databases (PostgreSQL, MySQL als primary), workloads die geen 2-minuten (AWS) of 30-seconden (GCP) herstart aankunnen, latencygevoelige services zonder on-demand fallback

De vuistregel: als je applicatie een process restart overleeft zonder dataverlies, kan die op spot draaien.

Wanneer escaleren

Verzamel het volgende voordat je hulp inschakelt:

  • kubectl get events --field-selector reason=Eviction -A output
  • kubectl describe node <getroffen-node> (let op conditions en taints)
  • NTH- of Karpenter-controllerlogs: kubectl logs -n kube-system deploy/aws-node-termination-handler of kubectl logs -n kube-system deploy/karpenter
  • De PDB-status: kubectl get pdb -A -o wide
  • Instance type en AZ van de interrupted node
  • Of workloads succesvol herstarten op vervangende nodes

Terugkerende server- of deploymentproblemen?

Ik help teams productie betrouwbaar maken met CI/CD, Kubernetes en cloud—zodat fixes blijven en deploys geen stress meer zijn.

Bekijk DevOps consultancy

Doorzoek deze site

Begin met typen om te zoeken, of blader door de kennisbank en blog.