Sondas de estado, puertas de disponibilidad y bucles de espera
Implemente bucles de espera para dependencias y sondas de actividad que permitan iniciar servicios contenedorizados de forma fiable.
Sondas de estado, puertas de disponibilidad y bucles de espera es una lección gratuita de Linux Command Line & Bash Scripting Mastery en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Linux Command Line & Bash Scripting Mastery, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.
Por qué fallan los servicios al iniciarse
En los entornos contenerizados, los servicios rara vez se inician de forma aislada. Una aplicación web necesita que su base de datos esté lista, un microservicio necesita que su agente de mensajería esté disponible y un worker necesita que su caché esté poblada antes de poder procesar trabajos.
Sin coordinación, los contenedores se inician en paralelo y las primeras solicitudes llegan antes de que las dependencias estén en buen estado. El resultado son errores de conexión rechazada, excepciones de puntero nulo y un estado de inicio corrupto que requiere reinicios manuales.
- Sonda de actividad — ¿el proceso sigue ejecutándose y no está bloqueado?
- Sonda de preparación — ¿el servicio está listo para aceptar tráfico?
- Bucle de espera — un script de inicio que se bloquea hasta que se puede acceder a las dependencias
Kubernetes proporciona mecanismos de sondeo integrados, pero los scripts de shell que impulsan los initContainers, los wrappers de entrypoint.sh y las comprobaciones de estado independientes se escriben en Bash. Dominarlos es una competencia fundamental de DevOps.
El patrón wait-for
El bucle de espera de dependencias más sencillo consulta un objetivo a intervalos fijos hasta que se puede acceder a él. La estructura canónica utiliza un bucle while con nc (netcat) o curl para sondear un puerto TCP o un endpoint HTTP.
Decisiones de diseño clave:
- Tiempo de espera — abandone después de N segundos para que una dependencia averiada no mantenga el pod bloqueado indefinidamente
- Intervalo de backoff — espere entre sondeos para evitar sobrecargar un servicio que se está recuperando
- Código de salida — salga con el código 1 al agotarse el tiempo de espera para que el contenedor se reinicie o el init container falle de forma explícita
El fragmento siguiente espera hasta 60 segundos a que un puerto TCP acepte conexiones antes de iniciar el proceso principal.
#!/usr/bin/env bash
set -euo pipefail
HOST="${DB_HOST:-postgres}"
PORT="${DB_PORT:-5432}"
TIMEOUT=60
INTERVAL=2
ELAPSED=0
echo "[wait-for] Waiting for ${HOST}:${PORT}..."
until nc -z "$HOST" "$PORT" 2>/dev/null; do
if (( ELAPSED >= TIMEOUT )); then
echo "[wait-for] Timed out after ${TIMEOUT}s waiting for ${HOST}:${PORT}" >&2
exit 1
fi
echo "[wait-for] ${HOST}:${PORT} not ready — retrying in ${INTERVAL}s (${ELAPSED}s elapsed)"
sleep "$INTERVAL"
(( ELAPSED += INTERVAL ))
done
echo "[wait-for] ${HOST}:${PORT} is up — continuing"
exec "$@"Sondas de preparación HTTP con curl
Una conexión TCP solo indica que el puerto está abierto, no que la aplicación que se encuentra detrás esté lista para atender solicitudes. Muchos servicios exponen un endpoint dedicado /health o /readyz que devuelve HTTP 200 únicamente cuando todos los subsistemas internos se han inicializado.
Utilice curl --fail --silent --output /dev/null para sondear un endpoint de preparación HTTP. La opción --fail hace que curl salga con el código 22 ante respuestas 4xx/5xx, lo que permite gestionar limpiamente la lógica de reintento.
Opciones importantes:
--fail— trata los errores HTTP como errores de curl (salida distinta de cero)--silent— suprime la salida de progreso--max-time N— tiempo de espera por solicitud, en segundos--retry N --retry-delay S— capa de reintentos propia de curl, útil en casos sencillos
#!/usr/bin/env bash
set -euo pipefail
HEALTH_URL="${HEALTH_URL:-http://localhost:8080/healthz}"
TIMEOUT=90
INTERVAL=3
ELAPSED=0
echo "[probe] Polling readiness at ${HEALTH_URL}"
until curl --fail --silent --output /dev/null \
--max-time 2 "${HEALTH_URL}"; do
if (( ELAPSED >= TIMEOUT )); then
echo "[probe] Service not ready after ${TIMEOUT}s" >&2
exit 1
fi
printf '[probe] Not ready yet (%ds elapsed)\n' "$ELAPSED"
sleep "$INTERVAL"
(( ELAPSED += INTERVAL ))
done
echo "[probe] Service is ready"
exec "$@"Backoff exponencial en bucles de espera
Un bucle de reintento con intervalos fijos sobrecarga un servicio en recuperación a un ritmo constante. El backoff exponencial duplica el tiempo de espera en cada intento, lo que reduce la carga durante la recuperación y permite converger rápidamente cuando el servicio se inicia pronto.
La fórmula estándar es: sleep_time = min(base * 2^attempt, max_sleep). Un componente de jitter (desfase fraccionario aleatorio) evita los problemas del efecto manada cuando muchos contenedores se reinician simultáneamente.
Este patrón se utiliza en herramientas de producción como wait-for-it, en los reintentos de los SDK de AWS y en los bucles de reconciliación de los controladores de Kubernetes.
#!/usr/bin/env bash
set -euo pipefail
HOST="${HOST:-redis}"
PORT="${PORT:-6379}"
MAX_ATTEMPTS=8
BASE_SLEEP=1
MAX_SLEEP=30
for attempt in $(seq 1 "$MAX_ATTEMPTS"); do
if nc -z "$HOST" "$PORT" 2>/dev/null; then
echo "[backoff] Connected to ${HOST}:${PORT} on attempt ${attempt}"
exec "$@"
fi
# Exponential backoff with jitter
raw=$(( BASE_SLEEP * (2 ** (attempt - 1)) ))
capped=$(( raw < MAX_SLEEP ? raw : MAX_SLEEP ))
jitter=$(( RANDOM % 3 ))
sleep_time=$(( capped + jitter ))
echo "[backoff] Attempt ${attempt}/${MAX_ATTEMPTS} failed — sleeping ${sleep_time}s"
sleep "$sleep_time"
done
echo "[backoff] ${HOST}:${PORT} unreachable after ${MAX_ATTEMPTS} attempts" >&2
exit 1Sondeo de varias dependencias
Las aplicaciones reales tienen varias dependencias: una base de datos, una caché, un agente de mensajería y quizá una API externa. Sondearlas secuencialmente desperdicia tiempo de inicio. Un enfoque mejor consiste en sondearlas todas en paralelo y esperar a que todas tengan éxito.
El operador de Bash & ejecuta cada sondeo en segundo plano, y wait recopila sus códigos de salida. Si algún sondeo falla, el entrypoint termina con un código distinto de cero, lo que provoca el reinicio del contenedor.
Técnica clave: capture los PID de los procesos en segundo plano con $! y páselos explícitamente a wait para poder comprobar los códigos de salida individuales.
#!/usr/bin/env bash
set -euo pipefail
wait_tcp() {
local host="$1" port="$2" timeout="${3:-30}"
local elapsed=0
until nc -z "$host" "$port" 2>/dev/null; do
(( elapsed >= timeout )) && { echo "TIMEOUT ${host}:${port}" >&2; return 1; }
sleep 2; (( elapsed += 2 ))
done
echo "[ok] ${host}:${port}"
}
# Launch all probes in parallel
wait_tcp postgres 5432 60 & PID_PG=$!
wait_tcp redis 6379 30 & PID_RD=$!
wait_tcp rabbitmq 5672 45 & PID_RQ=$!
# Collect results — fail fast if any probe failed
FAILED=0
for pid in $PID_PG $PID_RD $PID_RQ; do
wait "$pid" || (( FAILED++ ))
done
if (( FAILED > 0 )); then
echo "[entrypoint] ${FAILED} dependency probe(s) failed — aborting" >&2
exit 1
fi
echo "[entrypoint] All dependencies ready"
exec "$@"Actividad frente a preparación: scripts distintos para sondas distintas
Kubernetes distingue entre sondas de actividad y de preparación, y cada una debe hacer algo diferente:
- Sonda de actividad — responde a la pregunta: ¿el proceso sigue activo y no está bloqueado? Debe ser rápida y comprobar únicamente el estado interno, por ejemplo, que exista el archivo PID del proceso o que un endpoint de estado local devuelva 200. Si falla una sonda de actividad, el contenedor se termina y se reinicia.
- Sonda de preparación — responde a la pregunta: ¿debe recibir tráfico este pod? Puede comprobar las dependencias posteriores. Si falla una sonda de preparación, el pod se elimina del equilibrador de carga del Service, pero no se reinicia.
No incluya nunca comprobaciones lentas de dependencias en las sondas de actividad. Una breve interrupción de la base de datos reiniciaría incorrectamente todos los pods de la aplicación y agravaría el problema.
#!/usr/bin/env bash
# liveness.sh — fast local-only check
# Used in: livenessProbe.exec.command
set -euo pipefail
PID_FILE="/var/run/app/app.pid"
HEALTH_URL="http://127.0.0.1:8080/internal/live"
# Check 1: process is running
[[ -f "$PID_FILE" ]] || { echo "PID file missing" >&2; exit 1; }
kill -0 "$(cat "$PID_FILE")" 2>/dev/null || { echo "Process dead" >&2; exit 1; }
# Check 2: local endpoint responds (2s timeout — never block)
curl --fail --silent --max-time 2 --output /dev/null "$HEALTH_URL" || {
echo "Liveness endpoint unresponsive" >&2
exit 1
}
echo "live"
exit 0Sondas de arranque e initContainers
Kubernetes ofrece un tercer tipo de sonda: startupProbe. Se ejecuta en lugar de las sondas de actividad y preparación hasta completarse correctamente una vez, lo que da tiempo para inicializarse a las aplicaciones que arrancan lentamente, como las que requieren el calentamiento de la JVM o migraciones de base de datos, sin provocar falsos fallos de actividad.
Para esperar a las dependencias, los initContainers suelen ser más limpios que los scripts de entrypoint. Se ejecutan antes de que se inicien los contenedores de la aplicación, y Kubernetes gestiona automáticamente la lógica de reintento y reinicio. La imagen del init container solo necesita sh, nc o curl; puede utilizar una imagen mínima de busybox o alpine.
Ejemplo de especificación de initContainer en un manifiesto de Pod:
# kubernetes/pod-with-init.yaml (illustrative — not runnable as bash)
# initContainers run sequentially before app containers
initContainers:
- name: wait-for-postgres
image: busybox:1.36
command:
- sh
- -c
- |
set -e
echo 'Waiting for postgres...'
until nc -z postgres 5432; do
echo 'postgres not ready — sleeping 2s'
sleep 2
done
echo 'postgres is up'
- name: run-migrations
image: myapp:latest
command: ['python', 'manage.py', 'migrate', '--noinput']
envFrom:
- secretRef:
name: app-secretsScript de comprobación de estado con salida JSON
Los sistemas de producción suelen agregar el estado de salud de varios subsistemas y exponerlo como una carga útil JSON estructurada. Los equilibradores de carga, los orquestadores y los paneles de monitorización utilizan esta información.
Un script de comprobación de estado de Bash puede generar directamente la salida JSON mediante printf o jq. El código de salida sigue controlando la automatización; el cuerpo JSON está destinado a los operadores y a los sistemas de monitorización.
Convención: devuelva HTTP 200 con {"status":"ok"} cuando el sistema esté en buen estado, y HTTP 503 con {"status":"degraded", "checks":{...}} cuando no lo esté. El script siguiente está pensado para ser servido mediante un wrapper HTTP ligero como socat o llamado directamente por las sondas exec de Kubernetes.
#!/usr/bin/env bash
# health_check.sh — composite health with JSON output
set -uo pipefail
check_postgres() {
pg_isready -h "${DB_HOST:-postgres}" -p "${DB_PORT:-5432}" \
-U "${DB_USER:-app}" -t 2 &>/dev/null
}
check_redis() {
redis-cli -h "${REDIS_HOST:-redis}" -p "${REDIS_PORT:-6379}" \
PING 2>/dev/null | grep -q PONG
}
check_disk() {
local usage
usage=$(df / | awk 'NR==2{gsub(/%/,"",$5); print $5}')
(( usage < 90 ))
}
PG_OK=0; RD_OK=0; DSK_OK=0
check_postgres && PG_OK=1
check_redis && RD_OK=1
check_disk && DSK_OK=1
OVERALL=$(( PG_OK && RD_OK && DSK_OK ))
STATUS=$( (( OVERALL )) && echo 'ok' || echo 'degraded' )
printf '{"status":"%s","checks":{"postgres":%s,"redis":%s,"disk":%s}}\n' \
"$STATUS" "$PG_OK" "$RD_OK" "$DSK_OK"
(( OVERALL )) && exit 0 || exit 1Utilidad timeout con el patrón deadline
El comando GNU timeout envuelve cualquier comando y lo termina si no finaliza dentro de la duración especificada. Es la forma más sencilla de imponer un límite estricto a un bucle de espera o una sonda de estado sin gestionar manualmente trabajos en segundo plano.
timeout DURATION COMMAND [ARGS...]
Códigos de salida de timeout:
- 0 — el comando se completó correctamente dentro del límite
- Código de salida del comando — el comando se ejecutó, pero devolvió un valor distinto de cero
- 124 — se agotó el tiempo de espera del comando (se envió SIGTERM)
- 137 — el comando terminó mediante SIGKILL (después de
--kill-after)
Detectar el código de salida 124 permite mostrar un mensaje claro de tiempo de espera agotado en lugar de un error genérico.
#!/usr/bin/env bash
set -euo pipefail
HOST="${DB_HOST:-postgres}"
PORT="${DB_PORT:-5432}"
DEADLINE=60 # seconds
# Inline poll loop, wrapped by timeout
timeout "$DEADLINE" bash -c "
until nc -z '$HOST' '$PORT' 2>/dev/null; do
echo '[wait] ${HOST}:${PORT} not ready...'
sleep 2
done
" && echo "[wait] ${HOST}:${PORT} is up" || {
code=$?
if (( code == 124 )); then
echo "[wait] Timed out after ${DEADLINE}s waiting for ${HOST}:${PORT}" >&2
else
echo "[wait] Probe failed with exit code ${code}" >&2
fi
exit "$code"
}
exec "$@"wait-for-it autocontenido en Bash puro
Muchas imágenes de contenedor mínimas no incluyen nc (netcat). Bash puro puede abrir conexiones TCP mediante la sustitución de procesos hacia /dev/tcp, una función integrada de Bash no estándar, pero ampliamente compatible, que no requiere herramientas externas.
Sintaxis: /dev/tcp/HOST/PORT — Bash abre una conexión TCP al redirigir hacia esta ruta o desde ella. Genera un error (salida distinta de cero) si se rechaza la conexión o se agota el tiempo de espera.
Esta es la técnica utilizada por el popular script wait-for-it.sh, incluido en muchas configuraciones de Docker Compose. El script siguiente es una implementación completa e independiente que puede copiar con COPY en cualquier Dockerfile.
#!/usr/bin/env bash
# wait-for-it.sh (pure bash, no nc/curl required)
set -uo pipefail
usage() { echo "Usage: $0 HOST:PORT [-t TIMEOUT] [-- COMMAND]"; exit 1; }
parse_hostport() {
HOST="${1%%:*}"
PORT="${1##*:}"
[[ -n "$HOST" && "$PORT" =~ ^[0-9]+$ ]] || usage
}
[[ $# -ge 1 ]] || usage
parse_hostport "$1"; shift
TIMEOUT=30
[[ "${1:-}" == "-t" ]] && { TIMEOUT="$2"; shift 2; }
[[ "${1:-}" == "--" ]] && shift
wait_for() {
local elapsed=0
while (( elapsed < TIMEOUT )); do
# Pure bash TCP probe — no nc, no curl
if (exec 3<>"/dev/tcp/${HOST}/${PORT}") 2>/dev/null; then
exec 3>&-
return 0
fi
sleep 1
(( elapsed++ ))
done
return 1
}
echo "Waiting for ${HOST}:${PORT} (timeout ${TIMEOUT}s)..."
if wait_for; then
echo "${HOST}:${PORT} is available"
[[ $# -gt 0 ]] && exec "$@"
else
echo "Timed out waiting for ${HOST}:${PORT}" >&2
exit 1
fiIntegración de sondas en entrypoint.sh
El patrón de entrypoint es la forma estándar de combinar bucles de espera, validación del entorno e inicio del proceso en un único script fácil de mantener. ENTRYPOINT de Docker llama a este script, que termina con exec "$@" para transferir el control a CMD con el mismo PID y permitir el reenvío correcto de señales.
Un entrypoint.sh de nivel de producción suele:
- Validar pronto las variables de entorno requeridas (fallar rápido)
- Ejecutar los bucles de espera de dependencias
- Ejecutar las migraciones de base de datos, si procede
- Realizar una comprobación final del propio estado
- Transferir el control mediante
exec "$@"
Utilizar exec es fundamental: reemplaza el proceso del shell para que la aplicación se convierta en el PID 1 y reciba directamente SIGTERM de Docker o Kubernetes durante el apagado ordenado.
#!/usr/bin/env bash
# docker/entrypoint.sh
set -euo pipefail
# ── 1. Validate required env vars ────────────────────────────────────
for var in DATABASE_URL REDIS_URL SECRET_KEY; do
[[ -n "${!var:-}" ]] || { echo "FATAL: ${var} is not set" >&2; exit 1; }
done
# ── 2. Parse DB host/port from DATABASE_URL ──────────────────────────
DB_HOST=$(echo "$DATABASE_URL" | sed -E 's|.*@([^:/]+).*|\1|')
DB_PORT=$(echo "$DATABASE_URL" | sed -E 's|.*:([0-9]+)/.*|\1|')
# ── 3. Wait for dependencies ─────────────────────────────────────────
timeout 60 bash -c "
until nc -z '${DB_HOST}' '${DB_PORT}' 2>/dev/null; do sleep 2; done
" || { echo "Database unreachable" >&2; exit 1; }
# ── 4. Run migrations ────────────────────────────────────────────────
echo "[entrypoint] Running migrations..."
python manage.py migrate --noinput
# ── 5. Hand off to CMD (exec preserves PID 1 for signal handling) ────
echo "[entrypoint] Starting application: $*"
exec "$@"Comprobación de conocimientos: sondas de actividad y preparación
Compruebe su comprensión de los conceptos clave tratados en esta lección.
Repaso: sondas de estado, puertas de preparación y bucles de espera
En esta lección ha creado el conjunto de herramientas de Bash para coordinar de forma fiable el inicio de contenedores. Estos son los conceptos cubiertos:
- Patrón wait-for — un bucle
until nc -z HOST PORTcon un tiempo de espera estricto y una comprobación del tiempo transcurrido evita el bloqueo infinito ante dependencias averiadas. - Sondas de preparación HTTP —
curl --fail --max-timevalida que una aplicación esté realmente lista, no solo que el puerto esté abierto. - Backoff exponencial — duplicar el intervalo de espera entre reintentos reduce la carga provocada por el efecto manada y permite que los servicios en recuperación se estabilicen.
- Sondeo paralelo de dependencias — ejecutar sondas en segundo plano con
&y recopilar los resultados conwait $PIDreduce la latencia de inicio cuando existen varias dependencias. - Actividad frente a preparación — las sondas de actividad deben ser rápidas y comprobar únicamente el estado local; las sondas de preparación pueden comprobar sistemas posteriores. Nunca las confunda.
- startupProbe — protege las aplicaciones de inicio lento frente a falsos fallos de actividad durante la inicialización.
- Sondeo de /dev/tcp — la comprobación TCP en Bash puro no requiere herramientas externas, por lo que resulta ideal para imágenes de contenedor mínimas.
- entrypoint.sh — la validación del entorno, los bucles de espera, las migraciones y
exec "$@"forman el patrón estándar de inicio de servicios contenerizados.
Estos patrones forman los cimientos de los despliegues de contenedores autorrecuperables y de nivel de producción en Docker Compose, Kubernetes y ECS.
Preguntas frecuentes
¿La lección «Sondas de estado, puertas de disponibilidad y bucles de espera» es gratis?
Sí — el texto completo de «Sondas de estado, puertas de disponibilidad y bucles de espera» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Linux Command Line & Bash Scripting Mastery, actualiza a CoddyKit PRO. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.
¿Qué aprenderé en «Sondas de estado, puertas de disponibilidad y bucles de espera»?
Implemente bucles de espera para dependencias y sondas de actividad que permitan iniciar servicios contenedorizados de forma fiable. Practicas Linux Command Line & Bash Scripting Mastery con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Linux Command Line & Bash Scripting Mastery?
No se requiere experiencia previa. Linux Command Line & Bash Scripting Mastery en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.
¿Cuánto tiempo toma la lección «Sondas de estado, puertas de disponibilidad y bucles de espera»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Linux Command Line & Bash Scripting Mastery?
Sí. Cada lección de Linux Command Line & Bash Scripting Mastery incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Creación de Dockerfiles compactos y entrypoints de shell
- Creación de plantillas de configuración con envsubst y heredocs
- Scripting de recursos en la nube mediante CLI y jq
- Sondas de estado, puertas de disponibilidad y bucles de espera