Cómo Realizar Actualizaciones Continuas Sin Tiempo de Inactividad en Despliegues de Kubernetes

Configura actualizaciones continuas de Kubernetes con sondas de preparación, maxSurge, maxUnavailable y apagado gradual.

Cómo Realizar Actualizaciones Continuas Sin Tiempo de Inactividad en Despliegues de Kubernetes

Las actualizaciones continuas de Kubernetes pueden reemplazar Pods sin una interrupción visible, pero solo si tu Despliegue y tu aplicación están de acuerdo sobre cuándo un Pod está listo y cómo debe apagarse. La estrategia predeterminada ayuda, pero no te salva de sondas de preparación deficientes, versiones incompatibles o solicitudes en curso que se pierden.

Lograr un verdadero tiempo de inactividad cero, sin embargo, requiere más que solo la configuración predeterminada de Kubernetes. Exige una coordinación cuidadosa entre el manifiesto del Despliegue, los endpoints de salud de la aplicación y el proceso de terminación gradual. Esta guía proporciona un enfoque integral y paso a paso para configurar Despliegues de Kubernetes que aseguren que las actualizaciones de la aplicación sean fluidas e invisibles para el usuario final.

Esta guía cubre sondas de preparación, maxSurge, maxUnavailable y terminación gradual para que tu servicio mantenga la capacidad durante un despliegue.

Prerrequisitos para Tiempo de Inactividad Cero

Antes de configurar el manifiesto de Kubernetes, la aplicación subyacente debe adherirse a ciertos principios para soportar despliegues sin tiempo de inactividad:

  1. Compatibilidad hacia atrás de la aplicación: Durante el breve período en que ambas versiones, antigua y nueva, de la aplicación se ejecutan simultáneamente, deben ser compatibles con los recursos compartidos (bases de datos, colas, cachés).
  2. Idempotencia: Las operaciones que podrían ser manejadas por ambas versiones deben ser repetibles sin efectos secundarios negativos.
  3. Terminación gradual: La aplicación debe estar programada para reconocer la señal SIGTERM enviada por Kubernetes y dejar de aceptar nuevas conexiones de manera gradual mientras finaliza las solicitudes en curso antes de salir.

Comprendiendo la Estrategia de Actualización Continua de Kubernetes

Los Despliegues de Kubernetes utilizan por defecto la estrategia RollingUpdate. Este método asegura que la versión antigua de la aplicación no se elimine por completo antes de que la nueva versión esté operativa, gestionando la transición mediante dos parámetros principales:

Parámetro Descripción Impacto en Tiempo de Inactividad Cero
maxSurge Define el número máximo de Pods que se pueden crear por encima del número deseado de réplicas. Puede ser un número absoluto o un porcentaje (predeterminado: 25%). Controla la velocidad del despliegue y asegura que la capacidad aumente temporalmente.
maxUnavailable Define el número máximo de Pods que pueden estar no disponibles durante la actualización. Puede ser un número absoluto o un porcentaje (predeterminado: 25%). Crucial para tiempo de inactividad cero. Establecer esto en 0% significa que no se terminan Pods en servicio hasta que los nuevos Pods estén completamente Ready.

Estrategia Recomendada para Tiempo de Inactividad Cero

Para la máxima disponibilidad, la mejor configuración suele ser asegurar que no haya pérdida de capacidad durante el tiempo de inactividad:

  • maxUnavailable: 0 (Asegura que la capacidad nunca disminuya).
  • maxSurge: 1 o 25% (Permite que la capacidad supere brevemente el objetivo, asegurando que un nuevo Pod esté listo antes de que se elimine uno antiguo).

Paso 1: Implementando Sondas de Preparación

La Sonda de Preparación es el mecanismo más importante para asegurar actualizaciones sin tiempo de inactividad. Kubernetes se basa en esta sonda para determinar si un nuevo Pod está listo para recibir tráfico de usuario y si un Pod antiguo todavía está sirviendo tráfico activamente.

Sonda de Vida vs. Sonda de Preparación

  • Sonda de Vida: Le dice a Kubernetes si el contenedor está saludable y funcional. Si falla, el contenedor se reinicia.
  • Sonda de Preparación: Le dice a Kubernetes si el contenedor está listo para servir solicitudes. Si falla, el Pod se elimina de los endpoints del Servicio asociado, desviando el tráfico lejos de él hasta que esté listo.

Para las actualizaciones continuas, la Sonda de Preparación se utiliza para controlar la transición. Kubernetes no procederá a terminar un Pod antiguo hasta que un Pod recién creado pase exitosamente su verificación de preparación.

# extracto de deployment.yaml
spec:
  containers:
  - name: my-app
    image: myregistry/my-app:v2.0
    ports:
    - containerPort: 8080
    
    # --- Configuración de la Sonda de Preparación ---
    readinessProbe:
      httpGet:
        path: /health/ready
        port: 8080
      initialDelaySeconds: 15  # Tiempo de espera antes del primer intento de sonda
      periodSeconds: 5         # Frecuencia de la verificación
      timeoutSeconds: 3
      failureThreshold: 3      # Número de fallos consecutivos para marcar el Pod como no listo

    # --- Configuración de la Sonda de Vida (Verificación de Salud Estándar) ---
    livenessProbe:
      httpGet:
        path: /health/live
        port: 8080
      initialDelaySeconds: 60
      periodSeconds: 10

Consejo: Asegúrate de que el endpoint /health/ready de tu aplicación devuelva un código de éxito (HTTP 200-299) solo cuando la inicialización, las conexiones a la base de datos y otras dependencias externas estén completamente operativas.

Paso 2: Configurando la Estrategia de Despliegue

Para exigir un verdadero tiempo de inactividad cero, configuramos explícitamente la estrategia de actualización continua para evitar cualquier disminución en el número de réplicas disponibles.

En esta configuración, Kubernetes primero creará un nuevo Pod (maxSurge: 1). Una vez que el nuevo Pod pase su sonda de preparación, solo entonces Kubernetes terminará un Pod antiguo. Dado que maxUnavailable es 0, la capacidad del servicio nunca baja del número objetivo de réplicas.

# extracto de deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-web-deployment
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      # Asegura que la capacidad nunca baje del número deseado de réplicas (4)
      maxUnavailable: 0 
      # Permite que se cree un Pod adicional durante el despliegue
      maxSurge: 1 
  template:
    # ... especificación del contenedor ...

Paso 3: Asegurando la Terminación Gradual

Incluso con sondas de preparación robustas, si la aplicación se apaga instantáneamente al recibir la señal de terminación, corre el riesgo de perder solicitudes en curso.

Kubernetes sigue una secuencia de terminación específica:

  1. El Pod se marca como Terminating.
  2. El Pod se elimina de los endpoints del Servicio (el tráfico deja de enrutarse hacia él).
  3. Se ejecuta el hook previo a la parada (si está definido).
  4. El contenedor recibe la señal SIGTERM.
  5. Kubernetes espera durante la duración definida por terminationGracePeriodSeconds (predeterminado: 30 segundos).
  6. Si el contenedor sigue ejecutándose, recibe un SIGKILL innegociable.

Para asegurar un apagado gradual, la aplicación debe manejar SIGTERM, y terminationGracePeriodSeconds debe ser lo suficientemente largo para que la aplicación finalice las solicitudes existentes.

# extracto de deployment.yaml, dentro de la especificación de la plantilla del Pod
spec:
  terminationGracePeriodSeconds: 45 # Configuración a nivel de Pod
  containers:
  - name: my-app
    image: myregistry/my-app:v2.0
    lifecycle:
      preStop:
        exec:
          # Da tiempo a las actualizaciones de endpoints y balanceadores de carga externos para drenar.
          command: ["/bin/sh", "-c", "sleep 10"]

Mejor Práctica: Tu aplicación debe dejar de aceptar nuevo trabajo cuando reciba SIGTERM, luego finalizar las solicitudes en curso antes de salir. Un terminationGracePeriodSeconds ligeramente más largo, como 45 o 60 segundos, ayuda a prevenir terminaciones forzadas para solicitudes más lentas.

Paso 4: Realizando y Monitoreando la Actualización

Una vez que tu manifiesto de Despliegue incluya la estrategia optimizada y sondas robustas, realizar la actualización es sencillo.

  1. Actualiza la Etiqueta de la Imagen: Modifica tu manifiesto de despliegue para reflejar la nueva versión de la imagen (por ejemplo, v2.0 a v2.1).

  2. Aplica la Configuración:

    kubectl apply -f deployment.yaml
    

    Alternativamente, puedes parchear la imagen directamente:

    kubectl set image deployment/my-web-deployment my-app=myregistry/my-app:v2.1
    
  3. Monitorea el Estado del Despliegue: Observa cómo Kubernetes progresa a través de las etapas, verificando que el número de Pods listos nunca baje del número deseado.

    kubectl rollout status deployment/my-web-deployment
    
  4. Verifica la Disponibilidad de los Pods: Observa el estado de los Pods para confirmar que los Pods antiguos (v2.0) se terminan de manera gradual solo después de que los nuevos Pods (v2.1) estén completamente listos.

    kubectl get pods -l app=my-web-deployment -w
    

Consideraciones Avanzadas

Usando Presupuestos de Disrupción de Pods (PDBs)

Mientras que una estrategia de despliegue gestiona los despliegues, un Presupuesto de Disrupción de Pods (PDB) limita las disrupciones voluntarias como el drenaje de nodos y algunas operaciones de mantenimiento del clúster. No previene todas las fallas no planificadas, pero le da a Kubernetes y a las herramientas de automatización un objetivo mínimo de disponibilidad que respetar.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: my-app-pdb
spec:
  minAvailable: 75%  # Asegura que al menos el 75% de las réplicas estén siempre disponibles
  selector:
    matchLabels:
      app: my-web-deployment

La Importancia del Retraso Inicial

Si tu aplicación necesita tiempo para calentarse, ajusta initialDelaySeconds, periodSeconds y failureThreshold para que la preparación refleje el comportamiento real de inicio. Una sonda de preparación que falla mantiene el Pod fuera de los endpoints del Servicio; una sonda de vida que falla es lo que puede reiniciar el contenedor y crear un bucle de fallos.

Despliega de Manera Segura

Lograr actualizaciones continuas verdaderamente sin tiempo de inactividad en Kubernetes es una combinación de una configuración robusta de la plataforma y un desarrollo disciplinado de la aplicación. Al aprovechar correctamente las Sondas de Preparación para señalar el estado operativo, ajustar la estrategia de Despliegue (maxUnavailable: 0) para mantener la capacidad, e implementar manejadores de terminación gradual, puedes asegurar que las actualizaciones de la aplicación se realicen de manera confiable sin interrumpir el servicio a tus usuarios. Siempre prueba tu proceso de actualización a fondo en un entorno de pruebas para validar el período de gracia de terminación y la lógica de las sondas.