Runbook: Pod Not Ready
Severita: Warning
Alert Prometheus: PodNotReady
Descrizione
Questo alert si attiva quando un pod nel namespace pos-enterprise rimane nello stato NotReady per piu di 5 minuti. Il pod e in esecuzione ma non supera la readiness probe, quindi Kubernetes lo rimuove dagli endpoint del Service e non riceve traffico.
Ogni nodo Moleculer della piattaforma espone un health server sulla porta 3001 che verifica:
- Stato del transporter NATS (connesso/disconnesso)
- Stato del cacher Redis (connesso/disconnesso)
- Stato del service broker (started/stopped)
Regola Prometheus:
kube_pod_status_ready{namespace="pos-enterprise", condition="false"} == 1
con for: 5m per evitare falsi positivi durante i deploy.
Impatto
- Capacita ridotta: il pod non riceve traffico ma consuma risorse del cluster.
- Carico sbilanciato: le repliche sane ricevono piu traffico del previsto.
- Scaling inefficace: l'HPA potrebbe non scalare correttamente se conta i pod NotReady.
- Se tutti i pod di un deployment sono NotReady: equivale a un outage del servizio.
Diagnosi
1. Identificare i pod NotReady
kubectl get pods -n pos-enterprise | grep -v "Running.*1/1"
# Dettaglio delle condizioni
kubectl get pods -n pos-enterprise -o json | jq -r '
.items[] |
select(.status.conditions[]? | select(.type == "Ready" and .status == "False")) |
"\(.metadata.name) - Phase: \(.status.phase)"
'
2. Verificare il motivo del NotReady
kubectl describe pod <POD_NAME> -n pos-enterprise
Cercare nella sezione Conditions e Events:
Readiness probe failed: connection refused→ il servizio non e ancora avviatoReadiness probe failed: HTTP probe failed with statuscode: 503→ dipendenza non disponibileReadiness probe failed: dial tcp ... connect: connection refused→ porta 3001 non in ascolto
3. Testare l'health check manualmente
# Port-forward alla porta health del pod
kubectl port-forward <POD_NAME> -n pos-enterprise 3001:3001
# Testare l'endpoint health
curl -v http://localhost:3001/health
# Risposta attesa (200 OK):
# {
# "status": "ok",
# "transporter": { "connected": true },
# "cacher": { "connected": true },
# "services": 15,
# "uptime": 3600
# }
# Risposta problematica (503):
# {
# "status": "error",
# "transporter": { "connected": false },
# "cacher": { "connected": true }
# }
4. Verificare la connessione NATS (transporter)
kubectl logs <POD_NAME> -n pos-enterprise --tail=50 | grep -i "nats\|transporter\|connect"
# Se NATS e disconnesso, vedere runbook NATS Down
5. Verificare la connessione Redis (cacher)
kubectl logs <POD_NAME> -n pos-enterprise --tail=50 | grep -i "redis\|cacher\|connect"
# Test diretto
kubectl exec <POD_NAME> -n pos-enterprise -- \
node -e "
const Redis = require('ioredis');
const r = new Redis(process.env.REDIS_URL);
r.ping().then(v => { console.log('Redis:', v); process.exit(0); })
.catch(e => { console.error('Redis error:', e.message); process.exit(1); });
"
6. Verificare la configurazione della probe
kubectl get pod <POD_NAME> -n pos-enterprise -o jsonpath='{.spec.containers[0].readinessProbe}' | jq .
Output tipico:
{
"httpGet": {
"path": "/health",
"port": 3001,
"scheme": "HTTP"
},
"initialDelaySeconds": 10,
"periodSeconds": 10,
"timeoutSeconds": 5,
"successThreshold": 1,
"failureThreshold": 3
}
7. Verificare se e un problema di avvio lento
# Tempo di avvio del broker Moleculer
kubectl logs <POD_NAME> -n pos-enterprise | grep -i "broker started\|service started"
# Se il servizio carica molti dati all'avvio (es. cache warmup),
# l'initialDelaySeconds potrebbe essere insufficiente
Risoluzione
Causa: Transporter NATS disconnesso
# Verificare prima lo stato di NATS (vedi runbook nats-down.md)
# Se NATS e ok ma il pod non si connette, riavviare il pod
kubectl delete pod <POD_NAME> -n pos-enterprise
Causa: Cacher Redis non raggiungibile
# Verificare lo stato di Redis Cloud
kubectl run redis-test --rm -it --image=redis:7-alpine -n pos-enterprise -- \
redis-cli -u "$REDIS_URL" ping
# Se Redis e down, i servizi possono funzionare senza cache
# Disabilitare temporaneamente il cacher
kubectl set env deployment/<DEPLOY_NAME> -n pos-enterprise CACHER=""
# ATTENZIONE: senza cacher Redis, le performance degradano significativamente
Causa: Startup lento (initialDelaySeconds insufficiente)
# Aumentare il delay iniziale della readiness probe
kubectl patch deployment <DEPLOY_NAME> -n pos-enterprise --type='json' -p='[
{"op": "replace",
"path": "/spec/template/spec/containers/0/readinessProbe/initialDelaySeconds",
"value": 30},
{"op": "replace",
"path": "/spec/template/spec/containers/0/readinessProbe/failureThreshold",
"value": 6}
]'
Causa: Startup probe mancante
Per servizi con avvio lento, aggiungere una startup probe separata:
kubectl patch deployment <DEPLOY_NAME> -n pos-enterprise --type='json' -p='[
{"op": "add",
"path": "/spec/template/spec/containers/0/startupProbe",
"value": {
"httpGet": {"path": "/health", "port": 3001},
"periodSeconds": 5,
"failureThreshold": 30
}}
]'
Questo da al servizio fino a 150 secondi (5s x 30) per avviarsi, senza influire sulla readiness probe post-startup.
Causa: Risoluzione DNS lenta
# Verificare la risoluzione DNS dal pod
kubectl exec <POD_NAME> -n pos-enterprise -- nslookup nats.sellogic.cloud
# Se la risoluzione e lenta, verificare il CoreDNS
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=20
Riavvio selettivo
# Riavviare solo i pod NotReady di un deployment
kubectl get pods -n pos-enterprise -l app=<APP_LABEL> | \
grep -v "1/1.*Running" | awk 'NR>1{print $1}' | \
xargs -I {} kubectl delete pod {} -n pos-enterprise
Prevenzione
- Startup probe su tutti i deployment: separare la verifica di avvio dalla readiness continua.
- Dipendenze resilienti: il health check non dovrebbe fallire per dipendenze non critiche (es. cache miss non deve rendere il pod NotReady).
- Timeout adeguati:
timeoutSecondsdella probe deve essere > tempo massimo di risposta dell'health endpoint. - Monitorare il tempo di avvio: tracciare la metrica
moleculer_broker_started_secondsper ogni nodo.
# Configurazione probe raccomandata
readinessProbe:
httpGet:
path: /health
port: 3001
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
startupProbe:
httpGet:
path: /health
port: 3001
periodSeconds: 5
failureThreshold: 30
livenessProbe:
httpGet:
path: /health
port: 3001
initialDelaySeconds: 30
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 5
Escalation
| Livello | Condizione | Contatto |
|---|---|---|
| L1 - Ops | Singolo pod NotReady, altre repliche sane | Monitorare 10 min, poi riavviare |
| L1 - Ops | Pod NotReady post-deploy | Verificare probe config, rollback se necessario |
| L2 - Ops | Tutti i pod di un deployment NotReady | Diagnosi dipendenze (NATS, Redis) |
| L3 - Dev | NotReady causato da bug nell'health check | Team Dev |
Tempo massimo di risposta: 15 minuti. Se tutti i pod di un deployment critico (api-gateway, orders-node) sono NotReady, trattare come Critical.