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
kubectlverbonden met een EKS (1.28+) of GKE (1.25+) clusterhelmlokaal 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:
- EventBridge stuurt het event door naar de SQS-queue
- Karpenter's interruption controller taint de node
NoScheduleen begint pods te drainen - Tegelijkertijd provisiont Karpenter een vervangende node vanuit de NodePool
- 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: 90in voor de meeste workloads. Batchjobs op Queue mode NTH met lifecycle hooks kunnen hoger. - GCP (30-seconden-venster): stel
terminationGracePeriodSeconds: 25in 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:
- 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. - Spreid over meerdere Availability Zones. Hetzelfde instance type kan sterk verschillende interruption rates hebben per AZ.
- Gebruik
capacity-optimizedofprice-capacity-optimizedallocatie. 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 -Aoutputkubectl describe node <getroffen-node>(let op conditions en taints)- NTH- of Karpenter-controllerlogs:
kubectl logs -n kube-system deploy/aws-node-termination-handlerofkubectl 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