Incidentes y lecciones aprendidas
Registro de lo que ha fallado, por qué y qué regla se ha sacado de cada caso. Lo más reciente va primero.
2026-10-21 · Retirada del Actions Runner Controller (summerwind)
No fue un incidente, sino una limpieza. El ARC de summerwind (v0.27.6, proyecto abandonado, con 10 CVE CRITICAL y 239 HIGH según Trivy) solo tenía un RunnerDeployment para example-org/Bingx. Ese proyecto ya no se usa (BingX no está disponible en España ni en la UE), el runner estaba offline desde abril, y en los values de Helm había un token personal de GitHub en texto plano.
Pasos, en este orden para no dejar recursos bloqueados: borrar el RunnerDeployment con el controlador aún vivo (se dio de baja él mismo en GitHub) → helm uninstall arc (controlador, webhooks, RBAC, certificado de cert-manager) → borrar las 5 CRDs *.actions.summerwind.dev → borrar el namespace arc-systems. Comprobado después: API ok, ningún pod con problemas y Argo CD con las 4 aplicaciones en Synced.
El token personal de GitHub que usaba había caducado en mayo de 2026, y por eso el runner estaba offline desde entonces. Ya no es válido, así que no hay riesgo. Es el mismo patrón que la credencial de Argo CD: credenciales que caducan sin que nadie se entere (→ alertas, R2).
Si en el futuro hacen falta runners propios (despliegues dentro de la LAN, minutos ilimitados, builds ARM), instalar el ARC oficial (runner scale sets, GitHub App, pods efímeros) y nunca usarlo en repos públicos.
2026-10-21 · Homepage se queda sin la lista de nodos a ratos
Causa. La tarjeta Nodos sale de kube-state-metrics, que corría en master-1 (WiFi). En la última hora, Prometheus (en un nodo cableado) solo pudo leer lo que hay en el master-1 el 83-92 % del tiempo; lo cableado, el 100 %. Cuando falla varias lecturas, las series caducan y la tarjeta se queda vacía.
Solución. kube-state-metrics se movió a un worker cableado (parche de affinity: nunca en master-1, mejor en un worker) y se quitó la release duplicada. No se usó helm upgrade porque habría reiniciado Grafana, que no tiene persistencia.
Lección. Mientras master-1 vaya por WiFi, no poner en él piezas de las que dependa la monitorización (Prometheus, kube-state-metrics, exporters centrales). Desde el 2026-10-27 master-1 va por cable: la restricción ya no es necesaria por la red, aunque sigue siendo un master con mucha carga.
2026-10-18 → 21 · Jira y Confluence caídos desde internet durante 3 días (túnel de Cloudflare)
Síntoma. jira.example.com y confluence.example.com respondían 530 / error 1033 (Cloudflare no encuentra ningún conector activo del túnel). Se descubrió el 2026-10-21 de casualidad, al revisar otra cosa.
Causa. cloudflared (en master-3) estaba en bucle: registraba sus 4 conexiones con Cloudflare y a los 10-20 s se le caían todas (timeout: no recent network activity, por QUIC/UDP). 1.000-1.700 reconexiones al día desde el 2026-10-18, ninguna antes. Ese día coincidieron dos cosas: apt actualizó cloudflared de la 2026.3.0 a la 2026.10.1 (13:51) y empezaron los cortes de la red cableada. El túnel usaba QUIC (UDP), el protocolo por defecto.
Solución (2026-10-21 18:48). Forzar HTTP/2 (TCP), que aguanta mucho mejor la red inestable:
sudo sed -i 's|tunnel run --token|tunnel --protocol http2 run --token|' /etc/systemd/system/cloudflared.service
sudo systemctl daemon-reload && sudo systemctl restart cloudflared
Desde entonces: 4 conexiones protocol=http2 y ninguna caída. Jira y Confluence responden de nuevo.
Lecciones
- Tercera avería silenciosa en una semana, después de Argo CD sin sincronizar y Velero sin copias: hacen falta alertas (R2) y un comprobador externo de
*.example.com(R8). cloudflaredcorre en un solo nodo y se actualiza conapt: vigilar sus logs después de cada actualización y valorar desplegarlo en el clúster con 2 réplicas (pendiente n.º 18).
2026-10-20 · Argo CD llevaba meses sin sincronizar
Síntoma. Al desplegar Homepage, Argo CD no hacía nada. Las 4 aplicaciones estaban en Unknown con ComparisonError: authentication required: Invalid username or token.
Causa. La credencial con la que Argo CD lee example-org/GitOps (Secret repo-*, creado el 2026-01-18) había caducado. Las aplicaciones seguían funcionando con lo último que se desplegó, así que nadie lo notó: cualquier cambio en GitOps se habría quedado sin aplicar, en silencio.
Solución. Se regeneró la credencial y se actualizó en Argo CD (Settings → Repositories). Antes de reconectar, se comprobó con kubectl diff que el repo y lo desplegado coincidían, para evitar cambios sorpresa al volver a sincronizar.
Lecciones
- Una credencial con caducidad sin alerta es una avería programada. Opciones: conectar Argo CD con una GitHub App de solo lectura (no caduca) y añadir una alerta por aplicaciones en
UnknownoOutOfSync(R2). - Mismo patrón que Velero (copias que fallan sin que nadie se entere): hacen falta alertas que lleguen a alguien.
2026-10-18/19 · Control-plane en bucle de reinicios (etcd perdiendo líder)
Síntoma. k3s se reiniciaba una y otra vez en los masters: etcd perdía el líder, el controller-manager registraba leaderelection lost y k3s salía.
Causas encontradas
- filebeat (DaemonSet de kube-system) con límite de 200 Mi en bucle OOM. Releía
/var/loga ~490 MB/s en master-1, con un 70 % de presión de I/O. Además, Elasticsearch llevaba ~104 días caído, así que filebeat no tenía a dónde enviar nada. → Pausado connodeSelector paused-by=claude-etcd-incident. Para deshacerlo, quitar ese nodeSelector. - master-3 con
--flannel-iface=wlo1con la WiFi desconectada. k3s salía cada ~30 s (196 reinicios) y forzaba elecciones de etcd. → Cambiado aenp2s0. Más tarde, master-2 también se pasó dewlp2s0aenp1s0. - Disco de master-3 al 97 % de presión de I/O. SSD de gama baja, con reconstrucciones de Longhorn y trivy-operator escaneando a la vez.
→ trivy-operator escalado a 0. Reactivado el 2026-10-26 con poca carga (servidor propio con la base de datos en el disco local de worker-2, un escaneo a la vez, preferentemente en worker-2/worker-1):
seguridad/trivy-operator.yaml. - Cortes de red entre el router y todos los equipos cableados: ~11 s cada ~33 s, con un 32 % de pérdida de master-2 al router. Los cableados se ven entre ellos sin pérdidas. etcd en master-1 (WiFi) pierde al líder (master-2) cada ~30 s (
tls: bad record MAC/EOF). Se sospecha del cable entre el router y el switch. → Resuelto el 2026-10-27: cable router ↔ switch sustituido por uno S/FTP. Después, desde master-2, 0 % de pérdida al router y a todos los nodos, con enlaces a 1000 Mb/s, y desde master-1 (WiFi) 0 % en 90 s con un máximo de 91 ms. Durante el cambio, master-3 y worker-2 se quedaron sin red (hubo que reconectarlos y reiniciar worker-2) y Longhorn reconstruyó 4 volúmenes sin incidencias. Con la red estable, keepalived pasó al cable en master-2 y master-3, e master-1 pasó entero al cable conscripts/master1_a_cable.sh(un primer intento enchufando el cable sin más dejó al master-1 con dos IP en la misma red, .112 por WiFi y .185 por cable, y cortó la SSH: hay que hacerlo con el script).
Daños colaterales
- Los volúmenes de n8n y Portainer quedaron faulted porque su única réplica estaba en el worker-1, que había cambiado de IP (
.168a.128). Se recuperaron al volver a unir el worker-1. - Daños en el sistema de ficheros del volumen de Elasticsearch (
input/output errorbajoindices/*/_state). - La imagen de MinIO fijada en
quay.ioempezó a devolver 401. Solo quedaba en caché en master-1 y worker-2, así que se fijó MinIO a worker-2. - keepalived quedó en FAULT en master-2 y master-3 (VRRP por una WiFi caída). Se descubrió el 2026-10-20 y se resolvió el 2026-10-27, tras cambiar el cable, pasando el VRRP al cable.
- Se retiró definitivamente el nodo
worker-4.
Lecciones
- En un master no deben correr cargas con mucho I/O sin límites. etcd es muy sensible a la latencia de disco.
--flannel-ifacetiene que apuntar a una interfaz que esté siempre levantada. Si cambia la red física de un nodo, hay que revisar k3s y keepalived.- Las IPs de los nodos tienen que estar reservadas en el router.
2026-07-04 · n8n pierde sus datos al quitarle el nodeSelector
Qué pasó. El Deployment de n8n tenía un init-container clean-n8n (rm -rf /data/*) que montaba el mismo PVC que el contenedor principal, aunque en otra ruta. Nadie lo veía porque el pod llevaba 132 días sin reiniciarse. Al quitar el nodeSelector, el primer reinicio borró el PVC.
Por qué no se pudo recuperar. Velero solo guardaba manifiestos, no el contenido de los volúmenes. Había un snapshot de Longhorn de 9 h antes, pero se decidió empezar de cero porque los workflows no eran críticos.
Lecciones
- Antes de forzar el primer reinicio de un pod que lleva mucho tiempo arrancado, revisa sus
initContainersy sidecars por si tienen comandos destructivos. - Una copia de Velero sin node-agent no protege datos (ver backups.md).
2026-07-04 · La VIP rompe la red overlay
Qué pasó. keepalived añadía la VIP como IP secundaria, con preferred_lft forever, en la misma interfaz que la IP real del nodo, que era dinámica. flannel prefiere las direcciones permanentes, así que eligió la VIP como public-ip del nodo y como local del túnel VXLAN. Se rompieron NodePort, el DNS interno y el tráfico entre pods en todos los nodos que habían tenido la VIP. Pasar --node-ip y --flannel-iface no bastó, porque flannel repite la detección periódicamente.
Solución. Mover la VIP a una interfaz dummy0 dedicada (nodes/masters/vip-dummy0.service). Durante el cambio, poner la VIP con /24 creó una ruta para toda la subred por dummy0 y rompió el raft de etcd durante unos 2 minutos. Tiene que ser /32.
Lección. En k3s con flannel, una IP flotante siempre va en una interfaz dummy y con /32. Después de cualquier cambio de red, comprueba que ip -d link show flannel.1 muestra la IP real.
2026-07-03 · Migración a HA: pérdida de datos en local-path
Qué pasó. Para convertir master-2 y master-3 de agent a server se ejecutó k3s-agent-uninstall.sh, que borra /var/lib/rancher/k3s entero, incluido el almacenamiento de local-path. Se perdieron los datos de Kafka, Jenkins, Confluence y Portainer.
Otros problemas de la migración de SQLite a etcd
--cluster-initcon undb/etcda medias de un intento anterior falla conno bootstrap data found in datastore. Solución: moverdb/etcdy repetir.- Un
state.dbgrande daetcdserver: request is too large. Solución:--etcd-arg=max-request-bytes=52428800 --etcd-arg=quota-backend-bytes=2147483648. - Instalar sin
INSTALL_K3S_VERSIONmete la última versión stable, que puede ir 2 minor por delante del resto. - Con 2 miembros de etcd, si el tercero no termina de unirse, se pierde el quórum y con él la API. Para recuperarla,
--cluster-reset.
Lección. Ver operacion.md.
2026-10-26 · Grafana: «database is locked» y rebuild en bucle de su volumen
Qué pasó. Grafana pasó a un PVC de Longhorn (grafana_persistente.sh) y corría en master-1, que va por WiFi. Primero, un corte con la réplica de master-3 dejó las escrituras colgadas y SQLite dio database is locked. Después, grafana_local.sh (2 réplicas + dataLocality: best-effort) pidió una réplica local en el master-1: Longhorn no conseguía reconstruirla por la WiFi, el volumen entró en un bucle de AutoSalvaged / Detached / Attached y el kubelet llegó a lanzar mkfs.ext4 sobre el volumen (falló con Input/output error).
Daños. Ninguno: Grafana se paró a 0 réplicas y, montado de solo lectura en worker-2, el sistema de ficheros estaba intacto y grafana.db pasaba PRAGMA integrity_check (31 paneles y 2 usuarios, último cambio a las 22:01). Además había copia en /root/.config/grafana-copia/.
Arreglo. Grafana fijado en worker-2 (monitoring/grafana-persistente.yaml, nodeSelector), cableado y con la réplica sana: la réplica local ya existe y no hay que copiar nada por WiFi.
Lección. Ningún volumen de Longhorn con localidad, ni cargas con SQLite sobre Longhorn, en un nodo por WiFi (el master-1 lo era; desde el 2026-10-27 va por cable). Ver operación.
2026-10-28 · Falco no arranca con el kernel 7.0.0-42
Qué pasó. Tras reiniciar worker-1 y worker-2 con el kernel nuevo (7.0.0-42-generic), Falco 0.45.0 entró en CrashLoopBackOff en esos dos nodos: libbpf: failed to load BPF skeleton 'bpf_probe': -13 / libpman: failed to load BPF object (errno: 13 | message: Permission denied). En los nodos con 7.0.0-38 seguía funcionando: el verificador de BPF del kernel nuevo rechaza la sonda modern_ebpf.
Arreglo. Falco 0.45.1 (libs 0.26.1: fix signal_deliver verifier failure on kernels exposing the siginfo argument as a scalar) con scripts/falco_actualizar.sh. Se hizo una excepción a la regla de las dos semanas (publicada el 2026-10-19) por ser el arreglo de la herramienta de seguridad.
Lección. Tras una actualización de kernel, comprobar Falco en los nodos reiniciados antes de reiniciar el resto: un kernel nuevo puede dejar sin la sonda eBPF a todo el clúster.
2026-10-28 · El drain de worker-2 se queda colgado por tickets huérfanos de Longhorn
Qué pasó. Al actualizar los workers, kubectl drain de worker-2 no terminaba: el PDB de instance-manager no dejaba desalojarlo. Los volúmenes de MinIO, Confluence y es-snapshots tenían en spec.attachmentTickets tickets snapshot-controller-* de snapshots que ya no existían, restos de la caída del 27/10. Longhorn los daba por adjuntos y protegía el instance-manager.
Arreglo. Quitar solo los tickets huérfanos, uno a uno (operación). Después el drain terminó y worker-2 se reinició con el kernel nuevo. No se perdió ningún dato.
Lección. Antes de un drain, buscar tickets snapshot-controller-* cuyo snapshot no exista.