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

23 februari 20265 min. leestijd

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.

Wil je op de hoogte blijven?

Schrijf je in voor mijn nieuwsbrief of neem contact op voor freelance projecten.

Neem Contact Op