OOMKilled signifie qu’un pod a dépassé sa limite de mémoire et le kernel l’a terminé (code 137). C’est parce que soit votre limite est trop basse, soit votre app a une fuite mémoire. Ce guide vous montre comment diagnostiquer, dimensionner correctement les limites, et comprendre les classes QoS.
Symptôme
Le pod redémarre à plusieurs reprises et kubectl describe pod affiche :
Name: example-app-5f7c9d4bfb-7x8kq
Namespace: default
Status: Running
Restarts: 12
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
Signal: 9
Last Probe: 2m30s ago
Reason: None
Alternativement, le pod n’affiche pas la raison OOMKilled mais se termine constamment avec le code 137 :
Exit Code: 137
Signal: 9
Dans kubectl get events :
pod-name OOMKilled The container was OOMKilled
L’utilisation mémoire peut être visible dans kubectl top pod :
NAME CPU(cores) MEMORY(bytes)
example-app-5f7c9d4bfb 100m 1536Mi # À ou près de la limite
Pourquoi ça arrive
Classées par probabilité :
- La limite mémoire est trop basse pour le workload normal de l’app — L’app a besoin de plus de mémoire pour stocker les données, mettre en cache, ou traiter les requêtes, mais la limite ne le permet pas.
- Fuite mémoire dans l’app — L’app alloue de la mémoire au fil du temps sans la libérer, finissant par atteindre la limite même si le besoin initial était plus petit.
- Heap JVM ou runtime non configuré — Java, Node.js, ou Python utilise toute la mémoire système disponible au lieu de respecter une taille de heap explicite.
- Pic soudain dans le traitement des données — Un travail batch ou la génération de rapport nécessite temporairement plus de mémoire que la limite ne le permet.
- Aucune memory request définie — Le pod a une limite mais pas de request, donc Kubernetes l’ordonnance sur un nœud qui semble libre mais est réellement sous pression.
- La classe QoS est Burstable, pas Guaranteed — Le pod peut être expulsé si le nœud manque de mémoire, même s’il n’a pas techniquement dépassé la limite.
Correction
1. La limite mémoire est trop basse
Tout d’abord, mesurez combien de mémoire votre app utilise réellement :
# Installer metrics-server si absent (généralement pré-installé sur les clusters cloud)
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# Attendre 1-2 minutes pour que les métriques soient collectées
sleep 120
# Vérifier l'utilisation en temps réel
kubectl top pod <pod-name>
# Surveiller au fil du temps (regarder les pics)
kubectl top pod <pod-name> --containers
Enregistrez l’utilisation mémoire maximale. Puis augmentez la limite de 20–50% comme marge de sécurité :
# Si le pic est 512 Mi, définir la limite à 768 Mi
kubectl set resources deployment <deployment-name> \
--limits=memory=768Mi \
--requests=memory=512Mi
Ou éditez directement le Deployment :
kubectl edit deployment <deployment-name>
Et mettez à jour la section resources :
containers:
- name: app
resources:
requests:
memory: "512Mi"
cpu: "100m"
limits:
memory: "768Mi" # Augmenté de 512 Mi
cpu: "500m"
Appliquez et vérifiez :
kubectl rollout restart deployment/<deployment-name>
kubectl top pod <pod-name> # Vérifier que le nouveau pod a assez de mémoire
2. Fuite mémoire dans l’app
Si la mémoire de l’app grandit au fil du temps jusqu’à atteindre la limite, il y a une fuite. Surveillez la mémoire sur plusieurs heures ou jours :
# Collecter l'utilisation mémoire toutes les 30 secondes et sauvegarder dans un fichier
watch -n 30 "kubectl top pod <pod-name> >> memory-log.txt"
Si la mémoire grandit constamment, l’app a une fuite. Corrigez la fuite dans le code de l’application :
- Java: Utilisez des heap dumps pour trouver les objets retenus :
jmap -dump:live,format=b,file=heap.bin <pid> - Node.js: Utilisez clinic.js ou des heap snapshots :
node --inspect=0.0.0.0:9229 app.js # Puis connectez DevTools et prenez des heap snapshots - Python: Utilisez memory_profiler ou objgraph pour tracer les allocations.
Pour une solution de contournement rapide en attendant de corriger la fuite, définissez une limite mémoire plus basse et augmentez la policy de redémarrage :
restartPolicy: Always
containers:
- name: app
resources:
limits:
memory: "512Mi" # Limite plus basse force un redémarrage plus rapide
Ce n’est pas une correction mais gagne du temps pendant que vous debuggez la fuite réelle.
3. Heap JVM ou runtime non configuré
Si votre app s’exécute sur une JVM, Node.js, ou Python, configurez explicitement le heap ou la limite mémoire :
Java/JVM :
Définissez la taille du heap dans votre Dockerfile ou spec pod :
FROM openjdk:17-slim
ENV JAVA_OPTS="-Xmx512m -Xms256m"
COPY app.jar /app.jar
ENTRYPOINT ["java", "$JAVA_OPTS", "-jar", "/app.jar"]
Ou passez-la comme variable d’environnement :
containers:
- name: app
image: myapp:jvm
env:
- name: JAVA_OPTS
value: "-Xmx512m -Xms256m"
Node.js :
Définissez max-old-space-size :
FROM node:20-alpine
ENV NODE_OPTIONS="--max-old-space-size=512"
COPY . /app
WORKDIR /app
CMD ["node", "server.js"]
Ou :
containers:
- name: app
env:
- name: NODE_OPTIONS
value: "--max-old-space-size=512"
Python :
Certains frameworks pré-allouent de la mémoire. Vérifiez s’il y a une configuration mémoire :
containers:
- name: app
env:
- name: PYTHONUNBUFFERED
value: "1" # Aide avec la journalisation dans les conteneurs
Redéployez :
kubectl rollout restart deployment/<deployment-name>
4. Pic soudain dans le traitement des données
Pour les travaux batch ou la génération de rapports, utilisez une classe de ressource séparée avec des limites plus hautes :
apiVersion: batch/v1
kind: Job
metadata:
name: heavy-report
spec:
template:
spec:
containers:
- name: report
image: myapp:latest
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "3Gi"
cpu: "1500m"
restartPolicy: Never
Pour les apps de longue durée qui pic occasionnellement, envisagez d’utiliser un Vertical Pod Autoscaler (VPA) pour ajuster automatiquement les limites basé sur l’utilisation historique :
# Installer VPA (s'il n'est pas déjà installé)
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
./hack/vpa-up.sh
# Puis annoter votre Deployment
kubectl annotate deployment <deployment-name> \
vpa.k8s.io/updateMode=auto \
vpa.k8s.io/updatePolicy.updateMode=auto
5. Pas de memory request définie
Si vous définissez une limite mais pas de request, Kubernetes traite la request comme égale à la limite. Cela cause des problèmes de scheduling. Définissez toujours les deux :
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
La règle du pouce :
– Request = mémoire minimale nécessaire (pour l’ordonnancement)
– Limit = maximum que l’app devrait jamais utiliser (pour l’application)
– Ratio : la limite devrait être 1.5–2x la request
Mettez à jour votre Deployment :
kubectl set resources deployment <deployment-name> \
--requests=memory=256Mi,cpu=100m \
--limits=memory=512Mi,cpu=500m
6. Classe QoS est Burstable au lieu de Guaranteed
Vérifiez la classe QoS :
kubectl get pod <pod-name> -o yaml | grep qosClass
Trois classes QoS existent :
– Guaranteed: requests == limits (priorité la plus haute, jamais expulsé pour la mémoire)
– Burstable: requests < limits (priorité moyenne, peut être expulsé)
– BestEffort: pas de requests ni de limits (priorité la plus basse, expulsé en premier)
Pour les apps de production, utilisez Guaranteed :
resources:
requests:
memory: "512Mi"
cpu: "100m"
limits:
memory: "512Mi" # Même que la request
cpu: "100m"
Appliquez et redémarrez :
kubectl apply -f deployment.yaml
kubectl rollout restart deployment/<deployment-name>
Éviter la récidive
-
Mesurez toujours avant de définir les limites :
kubectl top pod <pod-name>Définissez les requests et limites basé sur l’utilisation réelle observée, pas sur des suppositions.
-
Définissez toujours les requests et limites, jamais juste une :
resources: requests: memory: "256Mi" limits: memory: "512Mi" -
Configurez explicitement le heap JVM/runtime :
ENV JAVA_OPTS="-Xmx512m -Xms256m" ENV NODE_OPTIONS="--max-old-space-size=512" -
Activez metrics-server pour surveiller l’utilisation mémoire :
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -
Utilisez un Vertical Pod Autoscaler (VPA) pour le dimensionnement automatique :
apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: example-vpa spec: targetRef: apiVersion: apps/v1 kind: Deployment name: example-app updatePolicy: updateMode: "Auto" -
Profilez votre app pour les fuites mémoire pendant le développement :
– Java: heap dumps
– Node.js: clinic.js ou DevTools
– Python: memory_profiler -
Définissez les startup probes pour donner du temps à l’app d’allouer la mémoire initiale :
startupProbe: httpGet: path: /health port: 8080 failureThreshold: 30 periodSeconds: 10
FAQ
Comment savoir si un pod a été OOMKilled ?
Exécutez kubectl describe pod <pod> et cherchez ‘Reason: OOMKilled’ dans Last State, ou le code 137. Aussi vérifiez kubectl get events.
Pourquoi la limite de mémoire de mon pod est-elle différente de ce que j’ai défini ?
Kubernetes définit les limites et les requests séparément. Si vous définissez uniquement une limite, la request par défaut est la limite. Voyez les deux avec kubectl get pod -o yaml | grep -A 3 resources:.
Comment mesurer combien de mémoire mon app utilise réellement ?
Utilisez kubectl top pod <pod> pour voir l’utilisation de mémoire en temps réel. Si metrics-server n’est pas installé, installez-le d’abord (généralement pré-installé sur les clusters cloud).
Quelle est la différence entre requests et limits ?
Les requests sont des garanties (pour l’ordonnancement) ; les limits sont des plafonds (pour l’application). Si un pod dépasse sa limite, il est OOMKilled. Définissez toujours les deux, avec une limite 1.5–2x la request.
Comment ce guide a été vérifié
Rédigé à la main à partir de la documentation officielle, chaque commande revérifiée, puis relu une seconde fois avant publication. Dernière relecture le .
Sources officielles consultées pour cet article

