Introducción
Running significa que existe un proceso de contenedor; no demuestra que la aplicación esté lista para recibir tráfico ni que pueda recuperarse por sí sola. Las sondas de Kubernetes convierten las comprobaciones de salud específicas de la aplicación en comportamientos concretos de los controladores y los Services.
Añadirás los tres tipos de sondas, observarás cómo la falta de disponibilidad elimina un Pod de los endpoints del Service, lo repararás y añadirás un mecanismo de apagado gradual.
Mantén claras las responsabilidades: una sonda de inicio protege las inicializaciones lentas, una sonda de disponibilidad determina si un Pod recibe tráfico del Service y una sonda de actividad decide cuándo kubelet debe reiniciar un contenedor bloqueado. Utilizar una sola sonda para todos los fines suele provocar reinicios evitables o enviar tráfico demasiado pronto.
Implementa una aplicación con sondas
En este paso, implementarás NGINX con comprobaciones de inicio, disponibilidad y actividad contra / en su puerto http con nombre.
El manifiesto es más extenso porque las tres sondas se encuentran dentro de la especificación del contenedor. Antes de aplicarlo, léelo en este orden: Deployment y etiquetas; contenedor y puerto con nombre; y, por último, sondas de inicio, disponibilidad y actividad. En esta demostración, las tres comprobaciones utilizan el mismo endpoint HTTP, pero las consecuencias de sus fallos son diferentes.
Algunas asignaciones y listas de una sola línea utilizan el “estilo de flujo” compacto de YAML, como {app: healthy-web}. Las llaves representan un mapa y los corchetes representan una lista; su significado es el mismo que el de sus equivalentes YAML en varias líneas. Úsalos aquí únicamente cuando el valor completo siga siendo fácil de leer.
El umbral de inicio permite hasta 24 segundos (12 × 2) antes de que Kubernetes considere que el inicio ha fallado. Una vez superado el inicio, la sonda de disponibilidad se ejecuta cada tres segundos y la de actividad cada cinco segundos.
cd /home/labex/project/application-health
cat <<'EOF' > healthy-web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: healthy-web
namespace: practice-health
spec:
replicas: 2
selector:
matchLabels: {app: healthy-web}
template:
metadata:
labels: {app: healthy-web}
spec:
containers:
- name: web
image: nginx:1.27-alpine
imagePullPolicy: IfNotPresent
ports:
- {name: http, containerPort: 80, protocol: TCP}
startupProbe:
httpGet: {path: /, port: http}
failureThreshold: 12
periodSeconds: 2
readinessProbe:
httpGet: {path: /, port: http}
periodSeconds: 3
livenessProbe:
httpGet: {path: /, port: http}
periodSeconds: 5
failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
name: healthy-web
namespace: practice-health
spec:
selector: {app: healthy-web}
ports:
- {name: http, port: 80, targetPort: http, protocol: TCP}
EOF
Crea el espacio de nombres y aplica el manifiesto. rollout status espera a que ambas réplicas cumplan la condición de disponibilidad, no solo a que sus procesos se inicien:
kubectl create namespace practice-health
kubectl apply -f healthy-web.yaml
kubectl rollout status deployment/healthy-web -n practice-health --timeout=60s
La sonda de inicio protege una inicialización lenta; la de disponibilidad controla si el Pod puede recibir tráfico; y la de actividad permite reiniciarlo después de un fallo irrecuperable.
Si el despliegue se completa, ya sabes algo más que “los contenedores están ejecutándose”: ambos Pods respondieron correctamente a la solicitud de disponibilidad.
La última línea del despliegue es la primera prueba de salud:
Waiting for deployment "healthy-web" rollout to finish: 0 of 2 updated replicas are available...
Waiting for deployment "healthy-web" rollout to finish: 1 of 2 updated replicas are available...
deployment "healthy-web" successfully rolled out
Las líneas intermedias son normales. Kubernetes informa del progreso a medida que cada Pod supera las sondas de inicio y disponibilidad; solo la última línea significa que ambas réplicas están disponibles.
Conecta la disponibilidad con los endpoints del Service
En este paso, inspeccionarás la condición de disponibilidad y los EndpointSlices.
Empieza con la tabla habitual de Pods y, después, limita la salida a la condición de disponibilidad y al contador de reinicios. La tabla personalizada permite distinguir fácilmente entre “no disponible” y “reiniciándose”:
kubectl get pods -n practice-health -l app=healthy-web
kubectl get pods -n practice-health -l app=healthy-web -o custom-columns='POD:.metadata.name,READY_CONDITION:.status.conditions[?(@.type=="Ready")].status,RESTARTS:.status.containerStatuses[0].restartCount'
kubectl get endpointslices -n practice-health -l kubernetes.io/service-name=healthy-web -o jsonpath='{range .items[].endpoints[*]}{.addresses[0]} ready={.conditions.ready}{"\n"}{end}'
Ambas direcciones deberían mostrar ready=true. Un fallo de disponibilidad deja el contenedor ejecutándose, pero elimina su dirección del enrutamiento normal del Service.
Los EndpointSlices conectan el selector de un Service con las direcciones reales de los Pods. El Service sigue existiendo aunque un Pod no esté disponible; es la condición de su endpoint la que impide enviarle tráfico normal.
Una salida típica de un estado saludable es:
POD READY_CONDITION RESTARTS
healthy-web-... True 0
healthy-web-... True 0
10.244.104.11 ready=true
10.244.104.12 ready=true
Los nombres de los Pods y las direcciones IP serán diferentes. Lo importante es la relación: dos Pods con Ready=True corresponden a dos endpoints con ready=true, y ninguno de los contenedores se ha reiniciado.
Observa un fallo de disponibilidad
En este paso, cambiarás deliberadamente solo la ruta de disponibilidad a una URL inexistente.
Se trata de un fallo controlado. JSON Patch reemplaza un único campo anidado, por lo que las sondas de inicio y actividad permanecen sin cambios. NGINX devuelve HTTP 404 para /not-ready, y la sonda de disponibilidad lo interpreta como un fallo.
kubectl patch deployment healthy-web -n practice-health --type='json' -p='[{"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/path","value":"/not-ready"}]'
kubectl rollout status deployment/healthy-web -n practice-health --timeout=20s || true
kubectl get pods -n practice-health -l app=healthy-web
kubectl get endpointslices -n practice-health -l kubernetes.io/service-name=healthy-web -o jsonpath='{range .items[].endpoints[*]}{.addresses[0]} ready={.conditions.ready}{"\n"}{end}'
|| true marca el tiempo de espera del despliegue como esperado para que el shell continúe. Los nuevos contenedores se ejecutan, pero la disponibilidad falla y el Deployment no puede alcanzar el estado disponible.
La prueba del despliegue produjo un estado similar a este:
error: timed out waiting for the condition
NAME READY STATUS RESTARTS
healthy-web-old-... 1/1 Running 0
healthy-web-old-... 1/1 Running 0
healthy-web-new-... 0/1 Running 0
...
10.244.104.17 ready=false
El nuevo Pod está en estado Running, pero no disponible, y no se ha reiniciado. Los dos Pods antiguos que estaban disponibles permanecen porque las actualizaciones graduales de Deployment protegen la disponibilidad: Kubernetes no sustituirá más réplicas saludables mientras la nueva revisión no pueda alcanzar el estado disponible. Por tanto, el tiempo de espera del despliegue demuestra tanto el comportamiento de la sonda de disponibilidad como el mecanismo de seguridad del controlador. En la salida de EndpointSlice, la nueva dirección muestra ready=false, así que el enrutamiento normal del Service continúa utilizando únicamente los endpoints saludables.
Repara la disponibilidad y verifica el tráfico
En este paso, restaurarás la ruta de disponibilidad y esperarás a que los endpoints del Service vuelvan a estar disponibles.
Utiliza el parche inverso al anterior para que el experimento cambie una sola variable. Ahora rollout status se completa correctamente porque los Pods de reemplazo responden a / con HTTP 200.
kubectl run health-client crea un Pod temporal e independiente para realizar la solicitud. --image selecciona BusyBox, --image-pull-policy=IfNotPresent reutiliza la imagen almacenada en caché y --restart=Never solicita un Pod en lugar de un controlador. --rm -i mantiene el comando conectado y elimina el Pod al finalizar; el segundo -- separa las opciones de kubectl del comando wget que se ejecuta dentro del contenedor.
En wget -qO-, -q oculta los mensajes de progreso y -O- escribe el cuerpo de la respuesta en la salida estándar; la tubería envía ese cuerpo a head, que muestra únicamente sus primeras líneas. La URL utiliza el nombre del Service healthy-web, que el DNS del clúster resuelve dentro del espacio de nombres.
kubectl patch deployment healthy-web -n practice-health --type='json' -p='[{"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/path","value":"/"}]'
kubectl rollout status deployment/healthy-web -n practice-health --timeout=60s
kubectl run health-client -n practice-health --image=busybox:1.36 --image-pull-policy=IfNotPresent --restart=Never --rm -i -- wget -qO- http://healthy-web | head
El cliente temporal utiliza el DNS del clúster. Limitar el HTML mostrado mantiene la evidencia legible, mientras que una respuesta correcta demuestra que la disponibilidad y el enrutamiento del Service coinciden.
La respuesta comienza con HTML reconocible de NGINX:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
Ver HTML después de un despliegue correcto demuestra el recorrido completo: el DNS resolvió el Service, el Service seleccionó un endpoint disponible y NGINX gestionó la solicitud.
Eliminar después el Pod temporal evita dejar clientes de diagnóstico innecesarios en el espacio de nombres.
Añade una terminación gradual
En este paso, añadirás un hook preStop y un periodo de gracia para la terminación. El hook retrasa brevemente el apagado para que la eliminación del endpoint pueda propagarse antes de que finalice el proceso.
Lee por separado las dos operaciones de JSON Patch. La primera añade un límite de 20 segundos para la terminación del Pod. La segunda añade un hook de espera de cinco segundos al contenedor con índice 0. Kubernetes inicia el hook cuando comienza la terminación, mientras el periodo de gracia restante sigue transcurriendo.
kubectl patch deployment healthy-web -n practice-health --type=json -p='[
{"op":"add","path":"/spec/template/spec/terminationGracePeriodSeconds","value":20},
{"op":"add","path":"/spec/template/spec/containers/0/lifecycle","value":{"preStop":{"sleep":{"seconds":5}}}}
]'
kubectl rollout status deployment/healthy-web -n practice-health --timeout=60s
kubectl get deployment healthy-web -n practice-health -o jsonpath='grace={.spec.template.spec.terminationGracePeriodSeconds}s preStop={.spec.template.spec.containers[0].lifecycle.preStop.sleep.seconds}s{"\n"}'
El parche JSON modifica únicamente los dos campos indicados, por lo que las sondas y el resto de la configuración del contenedor permanecen intactos. La terminación gradual complementa la disponibilidad: una controla la admisión de tráfico y la otra da tiempo a Kubernetes para retirar el Pod del tráfico antes de completar el apagado.
La salida final de JSONPath debería ser grace=20s preStop=5s. El hook de cinco segundos cabe dentro del límite de 20 segundos, dejando tiempo para que NGINX finalice con normalidad.
El despliegue puede detenerse temporalmente con mensajes como 1 old replicas are pending termination, porque el nuevo hook preStop añade deliberadamente cinco segundos al apagado. La salida final sigue siendo:
deployment "healthy-web" successfully rolled out
grace=20s preStop=5s
Resumen
Has conectado las comprobaciones de inicio, disponibilidad y actividad con el comportamiento de Kubernetes, has observado cómo la falta de disponibilidad elimina endpoints sin detener los contenedores, has restaurado el tráfico y has añadido una terminación gradual. Estos mecanismos hacen que la salud de la aplicación permita tomar medidas, en lugar de limitarse a mostrar información.


