Kubernetes Deployments die daadwerkelijk werken in productie
Praktische patronen voor Kubernetes deployments: rolling updates, health checks, resource limits en de valkuilen waar teams keer op keer intrappen.
Jean-Pierre Broeders
Freelance DevOps Engineer
Kubernetes Deployments die daadwerkelijk werken in productie
De meeste Kubernetes tutorials stoppen bij kubectl apply -f deployment.yaml en noemen het een dag. Maar tussen een werkende demo en een stabiele productie-omgeving zit een wereld van verschil. Hier de patronen die het verschil maken.
Rolling updates zonder downtime
Een standaard Deployment in Kubernetes doet al rolling updates. Maar de defaults zijn te agressief voor de meeste workloads. Dit is een configuratie die beter aansluit bij de realiteit:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api
image: registry.example.com/api:v2.4.1
ports:
- containerPort: 8080
maxUnavailable: 0 is de sleutel. Hiermee draait er altijd minstens het gewenste aantal pods. Duurt de rollout iets langer? Ja. Maar gebruikers merken er niks van.
Health checks: niet optioneel
Zonder health checks weet Kubernetes niet of een container echt klaar is om verkeer te ontvangen. Het resultaat: requests die tegen een nog-niet-opgestarte applicatie aanlopen. Twee checks zijn essentieel:
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 2
De readinessProbe bepaalt wanneer een pod verkeer krijgt. De livenessProbe herstart een pod die vastloopt. Een veelgemaakte fout: dezelfde endpoint voor beide gebruiken. Dat werkt, totdat een pod even traag reageert op de liveness check en onnodig herstart wordt — midden in een piekmoment.
Maak /healthz simpel: process draait, klaar. Maak /ready strenger: database-connectie werkt, caches zijn warm, dependencies bereikbaar.
Resource limits instellen
Geen resource limits is vragen om problemen. Eén container die memory lekt, trekt het hele node onderuit.
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
De requests bepalen scheduling — hoeveel ruimte reserveert Kubernetes. De limits zijn het plafond. Een gangbare aanpak: start met ruime limits, monitor het werkelijke verbruik een week lang met tools als Prometheus of kubectl top pods, en stel ze daarna bij.
Let op met CPU limits. Er is een groeiende stroming die zegt: stel geen CPU limits in, alleen requests. De redenering is dat CPU throttling meer schade aanricht dan een pod die even piekt. Geheugen limits zijn wél altijd nodig — een OOMKill is beter dan een node die volledig vastloopt.
Graceful shutdown
Kubernetes stuurt een SIGTERM naar de container en wacht dan terminationGracePeriodSeconds (default: 30 seconden) voordat het SIGKILL stuurt. De applicatie moet dat SIGTERM signaal afvangen en lopende requests afmaken.
spec:
terminationGracePeriodSeconds: 60
containers:
- name: api
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"]
Die preStop sleep van 5 seconden geeft de load balancer tijd om de pod uit de rotation te halen. Zonder die sleep komen er nog requests binnen terwijl de pod al aan het afsluiten is.
Pod Disruption Budgets
Bij cluster-onderhoud (node upgrades, autoscaling) verwijdert Kubernetes pods. Een PDB voorkomt dat alle pods tegelijk verdwijnen:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-server-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: api-server
Met 3 replicas en minAvailable: 2 kan Kubernetes maximaal één pod tegelijk verwijderen tijdens onderhoud. Simpel, maar het voorkomt onverwachte downtime bij cluster-operaties.
Wat teams vergeten
Een paar dingen die regelmatig over het hoofd worden gezien:
Image pull policy. Gebruik altijd specifieke tags, nooit :latest in productie. En zet imagePullPolicy: IfNotPresent zodat nodes niet bij elke pod-start het image opnieuw pullen.
Anti-affinity regels. Zonder pod anti-affinity kunnen alle replicas op dezelfde node terechtkomen. Gaat die node down, dan is alles weg.
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- api-server
topologyKey: kubernetes.io/hostname
Namespace isolation. Draai productie en staging in gescheiden namespaces met ResourceQuotas. Een staging-deployment die op hol slaat, mag nooit productie-resources opeten.
Tot slot
Kubernetes deployment-configuratie is geen eenmalig iets. Het is een proces van uitrollen, monitoren, bijstellen. De configuratie hierboven is een startpunt — geen eindpunt. Meet wat de applicatie daadwerkelijk nodig heeft en pas aan op basis van echte data. Dat levert meer op dan welke best practice lijst dan ook.
