Sondagens de integridade, barreiras de prontidão e loops de espera
Implemente loops de espera por dependências e sondagens de atividade que façam serviços em contêineres iniciarem de forma confiável.
Sondagens de integridade, barreiras de prontidão e loops de espera é uma aula grátis de Linux Command Line & Bash Scripting Mastery no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Linux Command Line & Bash Scripting Mastery, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Linux Command Line & Bash Scripting Mastery inclui 4 aulas no total.
Por que os serviços falham na inicialização
Em ambientes conteinerizados, os serviços raramente iniciam de forma isolada. Uma aplicação web precisa que seu banco de dados esteja pronto, um microsserviço precisa que seu sistema de mensageria esteja disponível e um trabalhador precisa que seu cache esteja preenchido antes de poder processar tarefas.
Sem coordenação, os contêineres iniciam em paralelo e as primeiras solicitações chegam antes que as dependências estejam saudáveis. O resultado são erros de conexão recusada, exceções de ponteiro nulo e estado de inicialização corrompido, que exigem reinicializações manuais.
- Verificação de atividade — o processo ainda está em execução e não está bloqueado?
- Verificação de prontidão — o serviço está pronto para aceitar tráfego?
- Laço de espera — um script de inicialização que bloqueia até que as dependências estejam acessíveis
O Kubernetes fornece mecanismos integrados de verificação, mas os scripts de shell que alimentam initContainers, os invólucros de entrypoint.sh e as verificações de integridade independentes são escritos em Bash. Dominá-los é uma habilidade essencial de DevOps.
O padrão de espera
O laço de espera de dependência mais simples consulta um destino em um intervalo fixo até que ele se torne acessível. A estrutura clássica usa um laço while com nc (netcat) ou curl para verificar uma porta TCP ou um endpoint HTTP.
Principais decisões de projeto:
- Limite de tempo — encerre após N segundos para que uma dependência com falha não deixe o pod bloqueado para sempre
- Intervalo entre tentativas — aguarde entre as verificações para evitar sobrecarregar um serviço em recuperação
- Código de saída — encerre com o código 1 ao atingir o limite de tempo para que o contêiner seja reiniciado ou o contêiner de inicialização falhe de forma explícita
O trecho abaixo aguarda até 60 segundos para que uma porta TCP aceite conexões antes de iniciar o processo 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 "$@"Verificações de prontidão HTTP com curl
Uma conexão TCP apenas informa que a porta está aberta — não que a aplicação por trás dela está pronta para atender solicitações. Muitos serviços expõem um endpoint dedicado /health ou /readyz que retorna HTTP 200 somente quando todos os subsistemas internos são inicializados.
Use curl --fail --silent --output /dev/null para verificar um endpoint de prontidão HTTP. O sinalizador --fail faz o curl sair com o código 22 para respostas 4xx/5xx, o que conduz corretamente a lógica de novas tentativas.
Sinalizadores importantes:
--fail— tratar erros HTTP como erros do curl, com saída diferente de zero--silent— suprimir a saída de progresso--max-time N— limite de tempo por solicitação, em segundos--retry N --retry-delay S— camada própria de novas tentativas do curl, útil em casos simples
#!/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 "$@"Espera exponencial em laços de espera
Um laço de novas tentativas em intervalo fixo bombardeia um serviço em recuperação a uma taxa constante. A espera exponencial dobra o tempo de espera a cada tentativa, reduzindo a carga durante a recuperação e ainda convergindo rapidamente quando o serviço volta a funcionar.
A fórmula padrão é: sleep_time = min(base * 2^attempt, max_sleep). Um componente de variação aleatória, representado por um deslocamento fracionário, evita problemas de efeito manada quando muitos contêineres são reiniciados simultaneamente.
Esse padrão é usado por ferramentas de produção como wait-for-it, pelas novas tentativas do AWS SDK e pelos laços de reconciliação dos controladores do 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 1Verificando várias dependências
Aplicações reais têm várias dependências: um banco de dados, um cache, um sistema de mensageria e talvez uma API externa. Verificá-las em sequência desperdiça tempo de inicialização. Uma abordagem melhor verifica todas as dependências em paralelo e aguarda que todas sejam bem-sucedidas.
O operador Bash & executa cada verificação em segundo plano, e wait coleta seus códigos de saída. Se alguma verificação falhar, o ponto de entrada será encerrado com um código diferente de zero, acionando o reinício do contêiner.
Técnica principal: capture os identificadores de processo em segundo plano com $! e passe-os explicitamente para wait para que você possa verificar os códigos de saída individualmente.
#!/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 "$@"Atividade versus prontidão: scripts diferentes para verificações diferentes
O Kubernetes distingue entre verificações de atividade e de prontidão, e elas devem fazer coisas diferentes:
- Verificação de atividade — responde: o processo ainda está ativo e não está bloqueado? Deve ser rápida e verificar apenas o estado interno, por exemplo, se o arquivo PID do processo existe ou se um endpoint de integridade local retorna 200. Uma verificação de atividade malsucedida encerra e reinicia o contêiner.
- Verificação de prontidão — responde: este pod deve receber tráfego? Pode verificar dependências externas. Uma verificação de prontidão malsucedida remove o pod do balanceador de carga do serviço, mas não o reinicia.
Nunca coloque verificações lentas de dependências em verificações de atividade. Uma breve interrupção do banco de dados reiniciaria incorretamente todos os seus pods de aplicação, agravando o 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 0Verificações de inicialização e initContainers
O Kubernetes oferece um terceiro tipo de verificação: startupProbe. Ela é executada no lugar das verificações de atividade e prontidão até ser bem-sucedida uma vez, dando tempo para que aplicações de inicialização lenta, como as que exigem aquecimento da JVM ou migrações de banco de dados, sejam inicializadas sem acionar falsos erros de atividade.
Para aguardar dependências, initContainers costumam ser mais simples do que scripts de ponto de entrada. Eles são executados antes que os contêineres da aplicação sejam iniciados, e o Kubernetes gerencia automaticamente a lógica de novas tentativas e reinicialização. A imagem do contêiner de inicialização precisa apenas de sh, nc ou curl — você pode usar uma imagem mínima de busybox ou alpine.
Exemplo de especificação de initContainer em um manifesto 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 verificação de integridade com saída JSON
Sistemas de produção geralmente agregam o status de integridade de vários subsistemas e o expõem como um objeto JSON estruturado. Isso é consumido por balanceadores de carga, orquestradores e painéis de monitoramento.
Um script Bash de verificação de integridade pode montar a saída JSON diretamente usando printf ou jq. O código de saída ainda controla a automação; o corpo JSON é destinado aos operadores e aos sistemas de monitoramento.
Convenção: retorne HTTP 200 com {"status":"ok"} quando estiver saudável e HTTP 503 com {"status":"degraded", "checks":{...}} quando estiver insalubre. O script abaixo foi projetado para ser disponibilizado por um invólucro HTTP leve, como socat, ou chamado diretamente pelas verificações exec do 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 1Utilitário de limite de tempo com o padrão de prazo
O comando timeout do GNU envolve qualquer comando e o encerra se ele não terminar dentro da duração especificada. É a forma mais simples de impor um prazo rígido a um laço de espera ou a uma verificação de integridade sem gerenciar manualmente tarefas em segundo plano.
timeout DURATION COMMAND [ARGS...]
Códigos de saída de timeout:
- 0 — o comando foi concluído com sucesso dentro do prazo
- Código de saída do comando — o comando foi executado, mas retornou um valor diferente de zero
- 124 — o comando excedeu o limite de tempo, e SIGTERM foi enviado
- 137 — o comando foi encerrado com SIGKILL após
--kill-after
Detectar o código de saída 124 permite exibir uma mensagem clara de limite de tempo, em vez de um erro 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 autocontido em Bash puro
Muitas imagens mínimas de contêiner não incluem nc (netcat). O Bash puro pode abrir conexões TCP usando substituição de processos para /dev/tcp, um recurso interno não padrão, mas amplamente compatível do Bash, que não exige nenhuma ferramenta externa.
Sintaxe: /dev/tcp/HOST/PORT — o Bash abre uma conexão TCP quando você redireciona dados para esse caminho ou a partir dele. Ele gera um erro, com saída diferente de zero, se a conexão for recusada ou exceder o limite de tempo.
Essa é a técnica usada pelo popular script wait-for-it.sh, incluído em muitas configurações do Docker Compose. O script abaixo é uma implementação completa e independente que você pode copiar com COPY para qualquer 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
fiIntegrando verificações em entrypoint.sh
O padrão de ponto de entrada é a forma padrão de combinar laços de espera, validação do ambiente e inicialização do processo em um único script de fácil manutenção. O ENTRYPOINT do Docker chama esse script, que termina com exec "$@" para transferir o controle ao CMD com o mesmo PID, permitindo o encaminhamento correto de sinais.
Um entrypoint.sh pronto para produção normalmente:
- Valida antecipadamente as variáveis de ambiente obrigatórias, falhando rapidamente
- Executa laços de espera pelas dependências
- Executa migrações do banco de dados, quando aplicável
- Executa uma autoverificação final
- Transfere o controle com
exec "$@"
Usar exec é essencial — ele substitui o processo do interpretador de comandos, fazendo com que a aplicação se torne o PID 1 e receba diretamente o SIGTERM do Docker/Kubernetes durante o encerramento normal.
#!/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 "$@"Verificação de conhecimentos: verificações de atividade versus prontidão
Teste seus conhecimentos sobre os principais conceitos abordados nesta lição.
Recapitulação: verificações de integridade, barreiras de prontidão e laços de espera
Nesta lição, você criou o conjunto de ferramentas Bash para coordenar de forma confiável a inicialização de contêineres. Veja o que foi abordado:
- Padrão de espera — um laço
until nc -z HOST PORTcom um limite de tempo rígido e uma verificação do tempo decorrido evita o bloqueio infinito causado por dependências com falha. - Verificações de prontidão HTTP —
curl --fail --max-timevalida que uma aplicação está realmente pronta, e não apenas que a porta está aberta. - Espera exponencial — dobrar o intervalo de espera entre as novas tentativas reduz a carga do efeito manada e permite que os serviços em recuperação se estabilizem.
- Verificação paralela de dependências — executar verificações em segundo plano com
&e coletar os resultados comwait $PIDreduz a latência de inicialização quando existem várias dependências. - Atividade versus prontidão — as verificações de atividade devem ser rápidas e apenas locais; as verificações de prontidão podem consultar sistemas externos. Nunca as confunda.
- startupProbe — protege aplicações de inicialização lenta contra falsos erros de atividade durante a inicialização.
- Verificação por /dev/tcp — a verificação TCP em Bash puro não exige ferramentas externas, sendo ideal para imagens mínimas de contêiner.
- entrypoint.sh — a validação do ambiente, os laços de espera, as migrações e
exec "$@"formam o padrão de inicialização de serviços conteinerizados.
Esses padrões formam a base de implantações de contêineres autorrecuperáveis e prontas para produção em Docker Compose, Kubernetes e ECS.
Perguntas Frequentes
A aula “Sondagens de integridade, barreiras de prontidão e loops de espera” é grátis?
Sim — o texto completo de “Sondagens de integridade, barreiras de prontidão e loops de espera” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Linux Command Line & Bash Scripting Mastery, atualize para CoddyKit PRO. O curso de Linux Command Line & Bash Scripting Mastery inclui 4 aulas no total.
O que vou aprender em “Sondagens de integridade, barreiras de prontidão e loops de espera”?
Implemente loops de espera por dependências e sondagens de atividade que façam serviços em contêineres iniciarem de forma confiável. Você pratica Linux Command Line & Bash Scripting Mastery com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Linux Command Line & Bash Scripting Mastery?
Nenhuma experiência prévia é necessária. Linux Command Line & Bash Scripting Mastery no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Sondagens de integridade, barreiras de prontidão e loops de espera”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Linux Command Line & Bash Scripting Mastery?
Sim. Cada aula de Linux Command Line & Bash Scripting Mastery inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Dockerfiles enxutos e pontos de entrada do Shell
- Criação de modelos de configurações com envsubst e heredocs
- Criação de scripts para recursos de nuvem com CLI e jq
- Sondagens de integridade, barreiras de prontidão e loops de espera