Skip to main content

Runbook: Pod OOM Killed

Severita: Critical Alert Prometheus: PodOOMKilled

Descrizione

Questo alert si attiva quando il kernel Linux termina un container perche ha superato il limite di memoria configurato (resources.limits.memory). Il container riceve il segnale SIGKILL (exit code 137) e Kubernetes lo riavvia automaticamente, portando potenzialmente a un CrashLoopBackOff se il problema persiste.

Regola Prometheus:

kube_pod_container_status_last_terminated_reason{namespace="pos-enterprise", reason="OOMKilled"} == 1

Impatto

  • Terminazione improvvisa del processo: nessun graceful shutdown, le transazioni in corso vengono perse.
  • Potenziale corruzione dati: operazioni di scrittura interrotte a meta (mitigato dalla natura transazionale di MongoDB).
  • CrashLoop: se il consumo di memoria supera il limite gia al boot, il pod entra in CrashLoopBackOff.
  • Degradazione del servizio: perdita temporanea del nodo Moleculer con redistribuzione del carico.

Diagnosi

1. Identificare il pod OOM Killed

# Pod con OOMKilled recente
kubectl get pods -n pos-enterprise -o json | \
jq -r '.items[] | select(.status.containerStatuses[]?.lastState.terminated.reason == "OOMKilled") |
"\(.metadata.name) - Restart: \(.status.containerStatuses[0].restartCount)"'

# Dettaglio del pod
kubectl describe pod <POD_NAME> -n pos-enterprise | grep -A 10 "Last State"

2. Verificare i limiti di memoria configurati

kubectl get pod <POD_NAME> -n pos-enterprise -o jsonpath='{.spec.containers[0].resources}' | jq .

Output tipico:

{
"requests": { "memory": "256Mi", "cpu": "100m" },
"limits": { "memory": "512Mi", "cpu": "500m" }
}

3. Analizzare il consumo di memoria attuale

# Consumo attuale dei pod
kubectl top pods -n pos-enterprise --sort-by=memory

# Consumo per container specifico
kubectl top pod <POD_NAME> -n pos-enterprise --containers

4. Verificare lo storico in Grafana

Aprire la dashboard Grafana su https://grafana.sellogic.cloud:

# Memoria utilizzata vs limite per un deployment
container_memory_working_set_bytes{namespace="pos-enterprise", pod=~"orders-node.*"}
/ on(pod) kube_pod_container_resource_limits{namespace="pos-enterprise", resource="memory", pod=~"orders-node.*"}
# Trend di crescita della memoria (possibile memory leak)
rate(container_memory_working_set_bytes{namespace="pos-enterprise", container="moleculer"}[1h])

5. Analisi dei log prima dell'OOM

# Log dell'istanza precedente (prima del kill)
kubectl logs <POD_NAME> -n pos-enterprise --previous --tail=200

6. Heap snapshot per Node.js (memory leak detection)

# Abilitare il profiling con --inspect (solo in staging)
kubectl set env deployment/<DEPLOY_NAME> -n pos-enterprise NODE_OPTIONS="--max-old-space-size=384 --inspect=0.0.0.0:9229"

# Port-forward per connessione Chrome DevTools
kubectl port-forward <POD_NAME> -n pos-enterprise 9229:9229

# Aprire chrome://inspect in Chrome, connettere e acquisire heap snapshot

In alternativa, generare un heap dump programmatico:

# Inviare SIGUSR2 al processo Node.js (se configurato con heapdump)
kubectl exec <POD_NAME> -n pos-enterprise -- kill -USR2 1

# Copiare il file di dump
kubectl cp pos-enterprise/<POD_NAME>:/app/heapdump-*.heapsnapshot ./heapdump.heapsnapshot

Risoluzione

Soluzione rapida: aumentare i limiti di memoria

Determinare il consumo reale e impostare il limite al 150% del picco osservato:

# Verificare il picco storico in Grafana
# max_over_time(container_memory_working_set_bytes{pod=~"orders-node.*"}[7d])

# Aggiornare il deployment
kubectl patch deployment <DEPLOY_NAME> -n pos-enterprise --type='json' -p='[
{"op": "replace", "path": "/spec/template/spec/containers/0/resources/limits/memory", "value": "768Mi"},
{"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/memory", "value": "384Mi"}
]'

# Verificare il rollout
kubectl rollout status deployment/<DEPLOY_NAME> -n pos-enterprise

Riferimento limiti consigliati per tipo di nodo

Nodo MoleculerRequestsLimitsNote
api-gateway256Mi512MiGestisce connessioni HTTP
orders-node256Mi512MiCarico moderato
products-node128Mi256MiPrevalentemente letture
sync-node256Mi512MiOperazioni batch
warehouse-node256Mi512MiQuery complesse
bookings-node128Mi256MiBasso carico
printer-node128Mi256MiBuffer di stampa
cash-node128Mi256MiCalcoli aggregati
knowledge-node384Mi768MiEmbedding e LLM
support-node128Mi256MiSessioni supporto
scheduler-node256Mi512MiCron e batch job
realtime-node256Mi512MiWebSocket/MQTT
platform-node256Mi512MiServizi piattaforma

Soluzione memory leak: tuning GC Node.js

# Impostare opzioni GC ottimizzate
kubectl set env deployment/<DEPLOY_NAME> -n pos-enterprise \
NODE_OPTIONS="--max-old-space-size=384 --gc-interval=100 --expose-gc"

Soluzione memory leak: identificare la causa

Cause comuni di memory leak nei servizi Moleculer:

  1. Cache non limitata: verificare che il cacher Redis sia configurato e non si accumulino cache locali.
  2. Event listener non rimossi: controllare this.broker.on() senza corrispondente off() nel lifecycle stopped.
  3. Mongoose connection pool: connessioni MongoDB non rilasciate.
  4. Buffer di stampa: nel printer-node, buffer ESC/POS non liberati dopo l'invio.
# Verificare il numero di connessioni MongoDB attive
kubectl exec <POD_NAME> -n pos-enterprise -- node -e "
const mongoose = require('mongoose');
console.log('Pool size:', mongoose.connection.client?.topology?.s?.pool?.totalConnectionCount);
"

Prevenzione

  • Impostare --max-old-space-size al 75% del memory limit del container per permettere al GC di agire prima che il kernel intervenga.
  • Monitorare il trend di memoria: alert proattivo quando la memoria supera l'80% del limite.
  • Load testing: eseguire test di carico con k6/Locust simulando traffico di punta per validare i limiti.
  • Vertical Pod Autoscaler (VPA): considerare l'adozione di VPA in modalita recommend per suggerire limiti basati sull'uso reale.
  • Profiling periodico: eseguire heap snapshot settimanali in staging per individuare leak precoci.
# Alert proattivo al 80% del limite
container_memory_working_set_bytes{namespace="pos-enterprise"}
/ on(pod, container) kube_pod_container_resource_limits{namespace="pos-enterprise", resource="memory"}
> 0.8

Escalation

LivelloCondizioneContatto
L1 - OpsSingolo pod OOM, restart risolveTeam Ops - aumentare limite
L2 - DevOOM ricorrente, sospetto memory leakTeam Dev - analisi heap
L3 - ArchProblema sistemico su piu nodiTech Lead + DevOps Lead

Tempo massimo di risposta: 15 minuti. Se il pod entra in CrashLoopBackOff, trattare come P1.

Questa pagina ti è stata utile?