¶Runbook: Incidente
Procedimiento ante incidentes en producción de Ciel Agent Framework (despliegue HA).
¶Triaje rápido
- ¿El servicio responde?
curl -s localhost:8080/healthz(liveness) ycurl -s localhost:8080/readyz(readiness)./healthsigue como alias. - ¿Cuántas réplicas vivas?
kubectl -n ciel get pods -l app.kubernetes.io/name=ciel. Si <minAvailabledel PDB, el servicio está degradado pero no caído. - ¿El HPA está disparado?
kubectl -n ciel get hpa— revisarTARGETS/REPLICAS. - Revisar logs:
kubectl -n ciel logs -l app.kubernetes.io/name=ciel --tail=200.
¶Síntomas y acciones
¶1. Healthcheck falla en una réplica (CrashLoopBackOff)
kubectl -n ciel describe pod <pod>para verEventsyLast State.kubectl -n ciel logs <pod> --previouspara el crash anterior.- Si es error de arranque del gateway, aislar con
uv run ciel servelocal con los mismos env (CIEL_TENANT,CIEL_PROVIDER_URL,CIEL_API_KEYvía Secret, y en prodCIEL_STATE_BACKEND=postgres+CIEL_STATE_DSN). /readyzdevuelvenot_readysi elStateBackendno está conectado/migrado (p.ej. Postgres caído). La réplica no recibe tráfico hasta quereadyzseaready.
¶2. HIL (aprobación humana) bloqueada
- Un nodo de grafo con
require_approvalpausa elGraphRunnery persistepaused=True. Si nadie aprueba, el flujo queda detenido (no es un crash). - Verificar rol del aprobadador: debe tener
approve:*(roladmin).uv run ciel rbac check --subject <user> --action approve:deploy. - Reanudar vía
GraphRunner.approve(run_id, approver=..., rbac=..., action=...)desde el código/CLI correspondiente, o denegar condeny.
¶3. Resume multi-réplica tras caída (Fase 14 / F15+F16)
- El state de checkpoint/session vive en un StateBackend compartido
(
PostgresStateBackenden prod,SqliteStateBackendlocal en dev), NO en el PVC efímero de una réplica. Por eso N>=2 réplicas rehidratan el MISMO checkpoint tras caída de nodo: no dependen de RWX. - Para reanudar un
run_iden otra réplica usaclaim_run_lease(lease idempotente por run_id con TTL). Si otra réplica ya tiene el lease vivo, la reanudación es rechazada (evita doble ejecución). Versrc/ciel/runtime/resume.py. - Si una réplica murió sin liberar el lease, espera a que expire (
DEFAULT_LEASE_TTL_SECONDS=300) o libera manualmente víarelease_run_lease(backend, run_id=...). - Los dashboards de Studio (costo/trace) son best-effort por réplica; el state
compartido es solo checkpoint/session/board/audit (ver
deploy.md).
¶4. Spans/auditoría no aparecen
uv run ciel observeconfirma el exporter. Si se usó--otel-endpoint, verificar conectividad al collector (Tempo/Jaeger/OTel Collector).- El audit inmutable (
HashChainAuditSink) escribe JSONL particionado por tenant/session en el PVC de audit (NO es SQLite; verbackup.md).
¶5. Canal de mensajería caído (Teams/Discord/WebUI)
- Verificar health del router:
curl -s localhost:8080/v1/messaging/<channel>/health. - Revisar que el adapter correspondiente esté montado en
make_app(ciel serve) y que las rutas de webhook apunten al endpoint correcto. - Para pruebas offline usar
FakeAdapter(ciel chat --adapter fake).
¶Backup antes de rollback
- Antes de
helm rollback, respalda audit/board/state (verbackup.md/scripts/backup_state.py).