Kubernetes pod eviction: node pressure, disk pressure en Evicted-pods

Evicted pods in kubectl get pods zijn het signaal van de kubelet dat een node geen geheugen, disk of PIDs meer had. De kubelet kiest pods om te beëindigen via een drie-stappen-ranking die QoS-class niet rechtstreeks gebruikt, en zet daarna status.phase=Failed en status.reason=Evicted. Dit artikel laat zien hoe je de eviction-reden leest, de achtergebleven pod-objecten opruimt, vaststelt welke pressure de oorzaak was, en herhaling voorkomt.

Hoe een Evicted-pod eruitziet

Je draaide kubectl get pods en zag regels zoals deze:

NAME                          READY   STATUS    RESTARTS   AGE
api-7d4f9b8c6-xvk2p           0/1     Evicted   0          12m
api-7d4f9b8c6-r9k8t           0/1     Evicted   0          11m
api-7d4f9b8c6-2m4qn           1/1     Running   0          3m

Evicted is geen echte pod-fase. Het is de waarde die de kubelet in status.reason schrijft als hij de pod heeft beëindigd om node-pressure-redenen. De daadwerkelijke status.phase op het achtergebleven pod-object is Failed. De containers van de pod zijn weg. Alleen het API-object blijft achter en bezet een slot in de pods-objectquota van de namespace totdat iets het opruimt.

Als een workload-controller (Deployment, ReplicaSet, StatefulSet) eigenaar is van de pod, wordt er vrijwel direct een vervanger gescheduled. De Evicted-pod is een grafsteen, geen draaiende pod. Bare pods (zonder controller) worden niet vervangen.

Eviction is geen OOMKilled en geen preemption

Deze drie failure-modes lijken op elkaar in dashboards en geven overlappende symptomen, maar het zijn verschillende events met verschillende fixes.

Modus Wie acteert Trigger Resultaat voor pod
Container OOMKilled Linux-kernel cgroup OOM-killer Container overschrijdt zijn resources.limits.memory Eén container exit code 137; pod blijft, kubelet herstart de container volgens restartPolicy
Node-pressure eviction Kubelet eviction-manager Node-breed signaal kruist een eviction-drempel Hele pod beëindigd; status.phase=Failed, status.reason=Evicted
Preemption kube-scheduler Een pending pod met hogere priority heeft ruimte nodig Pod met lagere priority gevict om plaats te maken; opnieuw gescheduled via zijn controller

Drie vuistregels. De kernel killt één container; de kubelet killt de hele pod. OOMKilled vuurt als één container zijn eigen cgroup-limiet overschrijdt; eviction vuurt als de node als geheel onder druk staat. Preemption is een scheduler-beslissing, geen kubelet-beslissing, en staat los van resource-druk op de node.

De eviction-reden lezen in kubectl describe pod

Begin altijd hier:

kubectl describe pod api-7d4f9b8c6-xvk2p -n production

Twee delen van de output zijn belangrijk. De status:

Status:           Failed
Reason:           Evicted
Message:          The node was low on resource: memory. Threshold quantity: 100Mi,
                  available: 87Mi. Container api was using 312Mi, request is 256Mi,
                  has larger consumption of memory.

De Message-regel noemt het eviction-signaal dat afging (memory, ephemeral-storage, pid, etc.) en de threshold-hoeveelheid versus het beschikbare op het moment van eviction. Hij vertelt je ook welke container de kubelet als grootste boosdoener heeft aangewezen ten opzichte van zijn request, en dat is precies de centrale input voor de pod-selection-ranking van de kubelet (zie verderop).

Daarna de events:

Events:
  Type     Reason    Age   From               Message
  ----     ------    ----  ----               -------
  Warning  Evicted   12m   kubelet            The node was low on resource: memory.
  Normal   Killing   12m   kubelet            Stopping container api

Event reason Evicted van kubelet bevestigt het. Zie je in plaats daarvan Preempted van default-scheduler, dan kijk je naar scheduler-preemption en niet naar node-pressure eviction; de oorzaak is een pending pod met hogere priority, niet resource-druk op de node.

Om elke Evicted-pod cluster-breed te vinden:

kubectl get pods --all-namespaces --field-selector=status.phase=Failed -o json \
  | jq -r '.items[] | select(.status.reason=="Evicted") | "\(.metadata.namespace)/\(.metadata.name)"'

Of om ze per node te tellen, wat vaak meteen de problematische node aanwijst:

kubectl get pods -A -o json \
  | jq -r '.items[] | select(.status.reason=="Evicted") | .spec.nodeName' \
  | sort | uniq -c | sort -rn

Een node met tientallen Evicted-pods is de node om te onderzoeken.

Evicted-pods opruimen

Evicted-pods verwijderen zichzelf niet. De kubelet laat het API-object staan zodat je kunt bekijken waarom het gevict is. Lang blijven hangen geeft twee echte problemen.

Het eerste is quota-druk. Failed-pods (waaronder Evicted) tellen mee voor de pods- en count/pods-objectquota, want die quota's zijn fase-onafhankelijk. Ze tellen niet mee voor requests.cpu en requests.memory, want die sommeren alleen pods in een non-terminal state. Een Evicted-pod neemt dus een pod-count-slot in beslag maar geen CPU- of geheugenbudget. In een namespace met pods: 100 blokkeren honderd Evicted-restanten dus nieuwe pod-creatie, ook al heeft het cluster nog ruim CPU en geheugen over.

Het tweede is observability-ruis. Lijsten, dashboards en alerts die filteren op status.phase=Failed of failed-pods tellen, struikelen over de restanten lang nadat de onderliggende druk al weg is.

Om elke Evicted-pod in een namespace te verwijderen:

kubectl get pods -n production --field-selector=status.phase=Failed -o json \
  | jq -r '.items[] | select(.status.reason=="Evicted") | .metadata.name' \
  | xargs -r kubectl delete pod -n production

Of cluster-breed:

kubectl get pods -A --field-selector=status.phase=Failed -o json \
  | jq -r '.items[] | select(.status.reason=="Evicted") | "-n \(.metadata.namespace) \(.metadata.name)"' \
  | xargs -r -L1 kubectl delete pod

Verificatie: draai kubectl get pods -n production --field-selector=status.phase=Failed opnieuw en bevestig dat de lijst leeg is. Het pods-quotaverbruik daalt navenant: kubectl describe resourcequota -n production.

Een opruim-loop is een workaround, geen fix. De fix is de onderliggende druk wegnemen (verderop in het artikel).

Eviction-signalen en node-condities

De kubelet bewaakt een vaste set signalen op elke node en vertaalt die naar node-condities die zichtbaar zijn in kubectl describe node.

Signaal Wat het meet Node-conditie Default hard threshold
memory.available node.status.capacity[memory] minus de working set MemoryPressure < 100Mi
nodefs.available Vrije ruimte op het primaire kubelet-filesystem (/var/lib/kubelet, logs, emptyDir) DiskPressure < 10%
nodefs.inodesFree Vrije inodes op nodefs DiskPressure < 5%
imagefs.available Vrije ruimte op het image-filesystem van de container runtime DiskPressure < 15%
imagefs.inodesFree Vrije inodes op imagefs DiskPressure (geen gedocumenteerde default)
containerfs.available Vrije ruimte op het writable-container-layer filesystem (Linux only, separate-disk feature) DiskPressure (volgt imagefs)
pid.available maxpid minus curproc PIDPressure (geen gedocumenteerde default)

memory.available op Linux komt uit cgroup memory accounting, niet uit free -m. Inactieve file-backed pages tellen mee als reclaimable, dus het getal ligt hoger dan wat free als "free" rapporteert. Dat is bewust zo: het kubelet-signaal weerspiegelt wat onder druk daadwerkelijk recupereerbaar is.

Hard thresholds vuren direct, zonder grace period. Soft thresholds (geconfigureerd via evictionSoft) honoreren een per-signaal evictionSoftGracePeriod en de terminationGracePeriodSeconds van de pod, tot maximaal evictionMaxPodGracePeriod. Hard eviction honoreert nooit terminationGracePeriodSeconds of PodDisruptionBudgets.

Om geflapper tussen pressure en geen-pressure te dempen wanneer een signaal vlak rond een threshold zweeft, houdt de kubelet een node-conditie nog evictionPressureTransitionPeriod lang vast nadat het signaal hersteld is. De default is 5m0s. MemoryPressure: True blijft dus minstens vijf minuten actief nadat de geheugendruk al weg is.

Wanneer MemoryPressure: True of DiskPressure: True actief is, wordt de node.kubernetes.io/memory-pressure:NoSchedule- of node.kubernetes.io/disk-pressure:NoSchedule-taint automatisch op de node gezet. Dat voorkomt dat de scheduler nieuwe pods plaatst op een node die juist load aan het afstoten is. Zie je Pending-pods naast Evicted-pods, dan is dit een waarschijnlijke oorzaak; zie Pod blijft in Pending: waarom Kubernetes je workload niet kan schedulen.

Hoe de kubelet kiest welke pod wordt gevict

Dit is het stuk dat de meeste mensen verkeerd hebben. De pod-selection-ranking voor node-pressure eviction van de kubelet is een drie-staps-sortering, in deze volgorde:

  1. Of het verbruik van de pod voor de schaarse resource zijn request overschrijdt. Pods die meer gebruiken dan ze gerequeststeld hebben, worden eerder gevict dan pods die onder hun request blijven. Een pod met requests.memory: 256Mi en een huidige working set van 312Mi staat hoger op de evictionlijst dan een pod met dezelfde request en 200Mi gebruik.
  2. Pod Priority. Binnen de groep pods die hun request overschrijdt, worden pods met lagere priority eerder gevict dan pods met hogere priority. Priority komt uit de priorityClassName van de pod en lost op tot een integer. De ingebouwde classes system-cluster-critical en system-node-critical hebben zeer hoge waardes en worden als laatste gevict.
  3. Hoe ver het verbruik boven de request ligt. Binnen dezelfde priority-tier worden pods die hun request met een grotere marge overschrijden eerder gevict dan pods die er net overheen zitten.

QoS-class is geen directe input voor de ranking. Het wordt soms samengevat als "BestEffort gaat eerst, dan Burstable, dan Guaranteed", en dat is meestal de uitkomst maar niet het mechanisme. Het mechanisme is de drie-staps-sortering hierboven. De uitkomst lijkt QoS-vormig omdat:

  • BestEffort-pods geen requests hebben, dus elk verbruik overschrijdt hun (nul-)request en zet ze bovenaan in stap 1.
  • Guaranteed-pods requests gelijk aan limits hebben, dus per definitie kan hun verbruik onder normale werking de request niet overschrijden, wat ze onderaan stap 1 plaatst.
  • Burstable-pods landen in het midden, afhankelijk van of ze toevallig boven hun request uitkomen op het moment dat de druk toeslaat.

Praktische implicatie: een Guaranteed-pod met een hoge priority en bescheiden werkelijk gebruik is de veiligste configuratie tegen eviction. Een BestEffort-pod met lage priority is de minst veilige.

Eén uitzondering is goed om te weten. Static pods, mirror pods en pods met priorityClassName: system-node-critical (of system-cluster-critical) doen niet mee in de ranking; de kubelet kiest ze niet voor eviction, ook niet onder druk. Dat beschermt daemonset-workloads zoals kube-proxy, de CNI-plugin en metrics-agents waar de node afhankelijk van is.

Oorzaak A: geheugendruk op de node

De eviction-message noemt memory.available. De node bereikte MemoryPressure en de kubelet zette pods van het eiland.

Geheugendruk op een node heeft twee veelvoorkomende oorzaken. Ten eerste overcommit: de som van container-memory-limits op de node is veel groter dan de node-capaciteit, en meerdere pods bursten tegelijk richting hun limit. De scheduler kijkt niet naar limits maar alleen naar requests, dus een node kan flink overcommitted zijn op limits terwijl de scheduler hem nog als underutilized ziet. Ten tweede één hete pod zonder limit (BestEffort) of met een veel te hoog limit, die sneller groeit dan de kubelet kan reclaimen.

Diagnose

Vind de node die de pressure veroorzaakte:

kubectl describe node <node-name> | grep -A 5 "Conditions"
# Kijk naar MemoryPressure met een recente transition time, of de conditie die nu
# nog op True staat als je nog midden in de pressure zit.

Vergelijk werkelijk geheugengebruik met capaciteit (vereist metrics-server):

kubectl top node
# NAME       CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
# node-3     2400m        60%    14820Mi         92%

Som container-limits op de node om overcommit te zien:

kubectl get pods -A --field-selector=spec.nodeName=<node-name> -o json \
  | jq '[.items[].spec.containers[].resources.limits.memory] | length'

Voor fijnere analyse rapporteert kubectl describe node Allocated resources met zowel Requests als Limits als percentage van node allocatable. Limits van 200% of meer is een serieus overcommit-signaal.

Fix

Drie lagen, afhankelijk van wat de diagnose liet zien.

Per pod. Als de eviction-message één container aanwees met "larger consumption of memory", is de request van die pod te laag en zijn werkelijke working set te hoog. Zet resources.requests.memory omhoog tot het waargenomen verbruik zodat hij niet meer als eviction-doelwit fungeert, of fix de onderliggende geheugengroei. Het OOMKilled-artikel loopt door right-sizing van memory-limits en taalspecifieke valkuilen (JVM, Go, Node.js, Python).

Per node. Is overcommit het probleem, verlaag dan aggregate memory-limits over de workloads op die node, of voeg capaciteit toe. Het artikel over resource requests en limits behandelt de request-versus-limit-relatie en de overcommit-rekensom in detail.

Cluster-breed. Raken meerdere nodes MemoryPressure, dan is het cluster te klein. Voeg nodes toe, zet Cluster Autoscaler aan, of verplaats noisy workloads naar een aparte node-pool met taints.

Verificatie: kubectl describe node <node-name> toont MemoryPressure: False, en in kubectl get events -A | grep Evicted verschijnen de komende tien minuten geen nieuwe Evicted-pods (langer dan evictionPressureTransitionPeriod).

Oorzaak B: diskdruk en ephemeral storage

De eviction-message noemt nodefs.available, nodefs.inodesFree of imagefs.available. De node bereikte DiskPressure.

Diskdruk heeft meer bronnen dan geheugendruk. De meest voorkomende: containerlogs die zonder rotatie groeien, emptyDir-volumes die vollopen, de writable container-laag die data verzamelt die de applicatie buiten een volume schreef, image-cache bloat op imagefs, en inode-uitputting (hopen kleine bestanden in logs of caches). Minder vaak maar wel de moeite van het checken: de werk-directory van de kubelet zelf die volloopt omdat checkpoint-bestanden of state van terminated pods niet zijn opgeruimd.

Local ephemeral storage capacity isolation is GA sinds Kubernetes 1.25. Daarmee kun je resources.requests.ephemeral-storage en resources.limits.ephemeral-storage op een container zetten, en dan zal de kubelet eviction-manager pods beëindigen die hun ephemeral-storage limit overschrijden, ongeacht de algehele diskdruk op de node. Dat zet één luidruchtige pod om in een geïsoleerd probleem in plaats van een node-breed probleem.

Diagnose

Identificeer welk signaal afging:

kubectl describe node <node-name> | grep -B 1 -A 5 "DiskPressure"

Op de node zelf (SSH, debug pod, of kubelet-logs):

df -h /var/lib/kubelet /var/lib/docker /var/lib/containerd 2>/dev/null
df -i /var/lib/kubelet /var/lib/docker /var/lib/containerd 2>/dev/null

Vind de grootste verbruikers:

sudo du -sh /var/lib/kubelet/pods/*/volumes/* 2>/dev/null | sort -h | tail
sudo du -sh /var/log/pods/* 2>/dev/null | sort -h | tail

Voor images op imagefs (containerd):

sudo crictl images --no-trunc | sort -k 6 -h

Voor container-diskgebruik (Docker):

docker system df -v

Voor diepere uitleg over ephemeral-storage accounting en wat de kubelet meet, zie Kubernetes ephemeral storage: limieten, eviction en container-disk beheren.

Fix

Per pod. Zet resources.limits.ephemeral-storage op elke container die naar lokale disk schrijft. Verplaats zware writes naar een PersistentVolume (of een tmpfs emptyDir als de data echt vluchtig en klein is). Zorg dat logrotatie geconfigureerd is.

Per node. Draai image garbage collection: de kubelet doet dit automatisch zodra imagefs imagefs.available < 85% raakt (default imageGCHighThresholdPercent), maar je kunt handmatig prunen met crictl rmi --prune of docker image prune -a -f. Vergroot /var/lib/kubelet en het imagefs-volume als de node simpelweg te krap geprovisioneerd is.

Cluster-breed. Forceer een pod-niveau default voor requests.ephemeral-storage via een LimitRange. Zonder default zijn BestEffort-pods voor ephemeral-storage heel gewoon, en die zijn de eerste die gevict worden zodra een willekeurige pod de disk vult.

Verificatie: kubectl describe node <node-name> toont DiskPressure: False. df -h op de node laat ruim vrije ruimte zien. De image-cache na GC is merkbaar kleiner (crictl images | wc -l daalt).

Oorzaak C: PID-druk

De eviction-message noemt pid.available. De node had geen process IDs meer.

PID-uitputting komt minder vaak voor dan geheugen- of diskdruk, maar laat zich in twee verschijningsvormen zien. De eerste is een proceslek: een applicatie die kindprocessen sneller spawnt dan ze opruimt en zo de node-brede PID-ruimte uitput. De tweede is fork-bombs uit verkeerd geconfigureerde workloads (een slecht geschreven shell-loop, een unbounded subprocess-pool in Python, of een shell die zichzelf recursief aanroept).

De kubelet biedt PID-isolatie via Pod PID-limits (SupportPodPidsLimit) en node-level reservations (SystemPidsLimit). Staan beide niet, dan kan één losgeslagen pod de hele node uithongeren.

Diagnose

kubectl describe node <node-name> | grep -B 1 -A 5 "PIDPressure"

Op de node:

cat /proc/sys/kernel/pid_max          # plafond
ps -e --no-headers | wc -l            # huidig

Procesaantallen per pod (containerd):

sudo crictl ps -q | xargs -I{} sh -c 'echo "$(crictl inspect --output go-template --template '"'"'{{.info.pid}}'"'"' {}) {}"'

Of via kubectl debug met ps:

kubectl debug node/<node-name> -it --image=busybox -- sh -c "ps -ef | wc -l"

Fix

Zet per-pod PID-limits via de kubelet-flag --pod-max-pids (of het bijbehorende kubelet-config-veld), of per-container limits via het resources-veld waar dat ondersteund wordt. Onderzoek de lekkende applicatie: een procesboom die ongebreideld groeit is een bug in de applicatie, geen Kubernetes-probleem. De kubelet-eviction is een vangnet, geen oplossing.

Verificatie: kubectl describe node <node-name> toont PIDPressure: False, en ps -e | wc -l op de node geeft onder belasting een stabiel getal in plaats van een klimmend getal.

Eviction-thresholds tunen

De defaults zijn voorzichtig en redelijk voor de meeste clusters. Tune ze alleen wanneer de diagnose duidelijk wijst op een mismatch met je hardware of workload-patroon.

De kubelet-config (meestal /var/lib/kubelet/config.yaml op een node, of de cluster-brede kubelet-ConfigMap op managed platforms) accepteert deze velden:

# /var/lib/kubelet/config.yaml (Kubernetes 1.29+ KubeletConfiguration)
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
  memory.available: "200Mi"           # eerder evicten op kleine nodes
  nodefs.available: "10%"
  nodefs.inodesFree: "5%"
  imagefs.available: "15%"
evictionSoft:
  memory.available: "500Mi"           # eerder waarschuwen met grace period
evictionSoftGracePeriod:
  memory.available: "2m"
evictionMaxPodGracePeriod: 30          # cap op terminationGracePeriodSeconds bij soft eviction
evictionPressureTransitionPeriod: "5m" # default; verhoog om geflapper te dempen
evictionMinimumReclaim:
  memory.available: "100Mi"            # reclaim minstens dit boven de threshold
  nodefs.available: "500Mi"

Drie tuning-patronen zijn de moeite waard.

Eerder hard evicten op kleine nodes. De default memory.available: 100Mi is piepklein op een node van 64 GiB. Verhogen naar 200Mi of 500Mi geeft de kubelet meer marge om te acteren voordat de kernel-OOM-killer afgaat (die is sneller en minder soepel). De afweging: iets meer bruikbare capaciteit gereserveerd.

Soft thresholds met grace periods. evictionSoft met een grace period van enkele minuten geeft tijdelijke spikes de kans te herstellen zonder eviction. Combineer met evictionMaxPodGracePeriod zodat een soft eviction (een gemaximeerde versie van) terminationGracePeriodSeconds respecteert. Vriendelijker voor stateful workloads.

evictionMinimumReclaim. Zonder deze stopt de kubelet met evicten zodra het signaal één byte boven de threshold uitkomt, en dat geeft een ping-pong-patroon. evictionMinimumReclaim zetten zorgt ervoor dat de kubelet doorgaat tot er een betekenisvolle marge boven de threshold is teruggewonnen. Dit is de tuning met de grootste impact voor clusters die herhaald flapperen tussen MemoryPressure: True en MemoryPressure: False.

Op managed Kubernetes (GKE, EKS, AKS) zijn kubelet-config-aanpassingen platform-specifiek en lopen ze vaak via een node-pool-config-object in plaats van directe edits in /var/lib/kubelet/config.yaml. Raadpleeg de docs van het platform voor de juiste plek.

Wanneer escaleren

Heb je de diagnose doorlopen en blijven de evictions terugkomen, verzamel dan dit voordat je hulp inschakelt:

  • Volledige output van kubectl describe pod <evicted-pod-name> -n <namespace>
  • Output van kubectl describe node <affected-node-name>, in elk geval de secties Conditions, Allocated resources en Events
  • kubectl get events -A --sort-by='.lastTimestamp' | grep -i evict voor het afgelopen uur
  • Cluster-brede telling van Evicted-per-node (de jq-snippet eerder in dit artikel)
  • Kubernetes-versie (kubectl version)
  • Of het cluster managed is (GKE, EKS, AKS, OpenShift, k3s, kubeadm, etc.)
  • Output van df -h en df -i op de getroffen node (of via kubectl debug node/<name>)
  • Output van kubectl top node en kubectl top pod -A | sort -k 4 -h | tail -20
  • De kubelet-config in gebruik: op managed platforms de node-pool-config; op self-managed de inhoud van /var/lib/kubelet/config.yaml
  • Of er een LimitRange of ResourceQuota geldt: kubectl get limitrange,resourcequota -A

Genoeg om de eviction te diagnosticeren zonder vervolgvragen.

Hoe je herhaling voorkomt

Een terugkerend eviction-probleem is een sizing-probleem, geen runtime-probleem. Preventie gebeurt op het moment van workload-definitie en cluster-provisioning, niet bij het opruimen van Evicted-pods.

  • Zet resources.requests.memory op basis van waargenomen p95 working-set, niet op gevoel. Een pod waarvan het werkelijke gebruik zijn request overschrijdt, is het eerste doelwit van de eviction-manager.
  • Zet resources.limits.memory op elke productie-container. BestEffort-QoS is een recept voor eviction zodra een willekeurige buur geheugendruk veroorzaakt.
  • Zet resources.requests.ephemeral-storage en resources.limits.ephemeral-storage op elke container die naar lokale disk schrijft (logs naar stdout, emptyDir, scratchbestanden). Zonder die instellingen sleept een noisy neighbor de hele node-disk-budget mee.
  • Gebruik een LimitRange om defaults voor ephemeral-storage en geheugen-requests/limits op admission-tijd te injecteren, zodat namespaces geen pods zonder die instellingen kunnen draaien.
  • Wijs priorityClassName toe aan kritieke workloads zodat ze de eviction-ranking overleven, ook wanneer hun verbruik boven de request uitkomt.
  • Draai image garbage collection agressief op imagefs-zware workloads: verlaag imageGCHighThresholdPercent (default 85) naar 75 als images zich opstapelen.
  • Zet een Prometheus-alert op kube_pod_status_reason{reason="Evicted"} > 0 zodat je evictions ziet wanneer ze gebeuren, niet wanneer een klant het meldt. Een tweede alert op kubelet_node_condition{condition="MemoryPressure"} == 1 voor langer dan vijf minuten vangt aanhoudende druk.
  • Voor workloads waar eviction onacceptabel is (databases, persistent queues, alles met een stateful disk), gebruik Guaranteed-QoS (request gelijk aan limit), een hoge priorityClassName en een PodDisruptionBudget. Let op: PodDisruptionBudgets beschermen niet tegen hard eviction, alleen tegen voluntary disruption en soft eviction.

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.