Saltar a contenido

Reinicio controlado de un nodo con CNPG y Longhorn

Cómo reiniciar un nodo k3s de forma segura cuando hospeda primarios de CloudNativePG o volúmenes de Longhorn. Es el procedimiento que kured no puede hacer solo: un kubectl drain directo se bloquea con los PDB de los primarios CNPG y del instance-manager de Longhorn (ver incidencia 2026-09-24-heimdall-502).

Cuándo usarlo: el sentinel /var/run/reboot-required está presente (kernel pendiente de reiniciar) y kured no consigue drenar el nodo. También como mantenimiento planificado.

Precondiciones

  • kubectl apuntando al cluster (k3s/ansible/.kubeconfig).
  • Plugin kubectl-cnpg instalado (brew install kubectl-cnpg).
  • SSH al nodo (ubuntu@<ip>, clave inyectada por cloud-init).

Procedimiento (nodo a nodo, en serie)

  1. Identificar los primarios CNPG en el nodo:

    kubectl get cluster -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,PRIMARY:.status.currentPrimary'
    kubectl get pods -A -l cnpg.io/instanceRole=primary -o wide   # primarios y su columna NODE
    

  2. Switchover de cada primario que esté en el nodo hacia su réplica (en otro nodo):

    kubectl cnpg promote <cluster> <instancia-replica> -n <namespace>
    kubectl cnpg status <cluster> -n <namespace>   # esperar "Cluster in healthy state" y nuevo Primary
    

  3. Cordon (desactiva programación; CNPG y Longhorn reaccionan):

    kubectl cordon <nodo>
    

  4. Drain (ahora los primarios ya no están en el nodo):

    kubectl drain <nodo> --ignore-daemonsets --delete-emptydir-data --grace-period=60 --timeout=10m --force
    
    El instance-manager de Longhorn puede tardar en evictarse (evacua réplicas antes); los volúmenes quedan temporalmente degraded hasta el reboot. Verificar que termina con node/<nodo> drained antes de seguir; si falla (error when evicting ... disruption budget), hay un primario u otro PDB sin resolver en el nodo: no rebotar.

  5. Reboot:

    ssh ubuntu@<ip> 'sudo -n systemctl reboot'
    

  6. Esperar a que vuelva (control-plane: ~3–5 min hasta que el API server responde). Verificar que el nodo vuelve Ready (kubectl get nodes) y el nuevo kernel / sentinel:

    kubectl get nodes   # esperar STATUS=Ready
    ssh ubuntu@<ip> 'uname -r; cat /var/run/reboot-required 2>/dev/null && echo PRESENT || echo ABSENT'
    

  7. Uncordon (solo con el nodo Ready) y verificar recuperación:

    kubectl uncordon <nodo>
    kubectl get nodes
    kubectl get cluster -A          # "Cluster in healthy state"
    kubectl get pods -A | grep -vE "Running|Completed"   # sin crashloops
    

Nota: justo tras el reboot, el API server puede devolver http: Handler timeout y el operador de CNPG entrar en CrashLoopBackOff mientras Longhorn reconstruye réplicas (alta carga). Es transitorio; se recupera solo en unos minutos.

Por qué kured no lo hace solo

kured hace cordon + drain sin el switchover previo de CNPG. El PDB del primario CNPG (<cluster>-primary, minAvailable: 1) y el PDB por nodo del instance-manager de Longhorn bloquean la evicción → kured reintenta hasta el drainTimeout y falla, evictando mientras tanto pods sin PDB (p. ej. authentik-server), que es lo que provoca los 502.

La causa de fondo es que kured no añade el taint node.kubernetes.io/unschedulable al hacer cordon (solo spec.unschedulable), y CNPG detecta el drenaje por taint (DRAIN_TAINTS). Fix aplicado: kured lleva blockingPodSelector: [cnpg.io/instanceRole=primary], así que se salta los nodos con primario CNPG y solo reinicia los demás. Este runbook es el procedimiento para reiniciar el nodo con primario cuando toque (p. ej. tras un unattended-upgrades). Ver la incidencia referenciada arriba.