0Pricing
Linux Command Line & Bash Scripting Mastery · Leçon

Sondes d’état, barrières de disponibilité et boucles d’attente

Implémentez des boucles d’attente des dépendances et des sondes de vivacité pour que les services conteneurisés démarrent de manière fiable.

Sondes d’état, barrières de disponibilité et boucles d’attente est une leçon Linux Command Line & Bash Scripting Mastery gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Linux Command Line & Bash Scripting Mastery, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Linux Command Line & Bash Scripting Mastery comprend 4 leçons au total.

Pourquoi les services échouent au démarrage

Dans les environnements conteneurisés, les services démarrent rarement de manière isolée. Une application web a besoin que sa base de données soit prête, un microservice que son courtier de messages soit disponible et un processus de traitement que son cache soit alimenté avant de pouvoir traiter les tâches.

Sans coordination, les conteneurs démarrent en parallèle et les premières requêtes arrivent avant que les dépendances soient opérationnelles. Il en résulte des erreurs de refus de connexion, des exceptions de pointeur nul et un état de démarrage corrompu qui nécessitent des redémarrages manuels.

  • Sonde de vivacité — le processus s’exécute-t-il toujours et n’est-il pas bloqué ?
  • Sonde de disponibilité — le service est-il prêt à accepter du trafic ?
  • Boucle d’attente — un script de démarrage qui bloque jusqu’à ce que les dépendances soient accessibles

Kubernetes fournit des mécanismes de sondage intégrés, mais les scripts shell qui alimentent les initContainers, les enveloppes entrypoint.sh et les vérifications de santé autonomes sont écrits en Bash. Les maîtriser constitue une compétence fondamentale en DevOps.

Le modèle d’attente

La boucle d’attente de dépendance la plus simple interroge une cible à intervalles fixes jusqu’à ce qu’elle devienne accessible. La forme canonique utilise une boucle while avec nc (netcat) ou curl pour sonder un port TCP ou un point de terminaison HTTP.

Principales décisions de conception :

  • Délai d’expiration — abandonnez après N secondes afin qu’une dépendance défaillante ne bloque pas le pod indéfiniment
  • Intervalle entre les tentatives — attendez entre les sondages pour éviter de surcharger un service en cours de rétablissement
  • Code de sortie — quittez avec le code 1 en cas de dépassement du délai afin que le conteneur redémarre, ou que le conteneur d’initialisation échoue explicitement

L’extrait ci-dessous attend jusqu’à 60 secondes qu’un port TCP accepte les connexions avant de lancer le processus 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 "$@"

Sondes de disponibilité HTTP avec curl

Une connexion TCP indique uniquement que le port est ouvert, et non que l’application qui se trouve derrière est prête à traiter des requêtes. De nombreux services exposent un point de terminaison dédié /health ou /readyz qui renvoie HTTP 200 uniquement lorsque tous les sous-systèmes internes sont initialisés.

Utilisez curl --fail --silent --output /dev/null pour sonder un point de terminaison HTTP de disponibilité. L’option --fail fait quitter curl avec le code 22 pour les réponses 4xx/5xx, ce qui pilote proprement la logique de nouvelle tentative.

Options importantes à connaître :

  • --fail — traiter les erreurs HTTP comme des erreurs curl, avec une sortie différente de zéro
  • --silent — supprimer la sortie de progression
  • --max-time N — délai d’expiration par requête, en secondes
  • --retry N --retry-delay S — propre mécanisme de nouvelle tentative de curl, utile dans les cas 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 "$@"

Temporisation exponentielle dans les boucles d’attente

Une boucle de nouvelle tentative à intervalle fixe surcharge un service en cours de rétablissement à un rythme constant. La temporisation exponentielle double le temps d’attente à chaque tentative, ce qui réduit la charge pendant le rétablissement tout en convergeant rapidement lorsque le service revient vite.

La formule standard est la suivante : sleep_time = min(base * 2^attempt, max_sleep). Un composant d’aléa, c’est-à-dire un décalage fractionnaire aléatoire, évite les problèmes de ruée simultanée lorsque de nombreux conteneurs redémarrent en même temps.

Ce modèle est utilisé par des outils de production tels que wait-for-it, les nouvelles tentatives du AWS SDK et les boucles de réconciliation des contrôleurs 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 1

Sonder plusieurs dépendances

Les applications réelles ont plusieurs dépendances : une base de données, un cache, un courtier de messages et parfois une API externe. Les sonder séquentiellement gaspille du temps au démarrage. Une meilleure approche consiste à sonder toutes les dépendances en parallèle et à attendre leur réussite à toutes.

L’opérateur Bash & exécute chaque sonde en arrière-plan, et wait collecte leurs codes de sortie. Si une sonde échoue, le point d’entrée quitte avec un code différent de zéro, ce qui déclenche le redémarrage du conteneur.

Technique essentielle : capturez les PID des processus en arrière-plan avec $! et transmettez-les explicitement à wait afin de pouvoir vérifier les codes de sortie individuels.

#!/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 "$@"

Vivacité ou disponibilité : des scripts différents pour des sondes différentes

Kubernetes distingue les sondes de vivacité et de disponibilité, qui doivent effectuer des tâches différentes :

  • Sonde de vivacité — répond à la question : le processus est-il toujours actif et n’est-il pas bloqué ? Elle doit être rapide et vérifier uniquement l’état interne, par exemple l’existence d’un fichier PID de processus ou le renvoi de 200 par un point de terminaison de santé local. Une sonde de vivacité défaillante arrête et redémarre le conteneur.
  • Sonde de disponibilité — répond à la question : ce pod doit-il recevoir du trafic ? Elle peut vérifier les dépendances en aval. Une sonde de disponibilité défaillante retire le pod de l’équilibreur de charge du service, mais ne le redémarre pas.

Ne placez jamais de vérifications lentes de dépendances dans les sondes de vivacité. Une brève panne de base de données redémarrerait à tort tous vos pods d’application et aggraverait le problème.

#!/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 0

Sondes de démarrage et initContainers

Kubernetes propose un troisième type de sonde : startupProbe. Elle s’exécute à la place des sondes de vivacité et de disponibilité jusqu’à sa première réussite, ce qui laisse aux applications dont le démarrage est lent, notamment lors du réchauffement de la JVM ou des migrations de base de données, le temps de s’initialiser sans provoquer de faux échecs de vivacité.

Pour attendre les dépendances, les initContainers sont souvent plus propres que les scripts de point d’entrée. Ils s’exécutent avant le démarrage des conteneurs d’application, et Kubernetes gère automatiquement la logique de nouvelle tentative et de redémarrage. L’image du conteneur d’initialisation n’a besoin que de sh, nc ou curl ; vous pouvez utiliser une image minimale busybox ou alpine.

Exemple de spécification initContainer dans le manifeste d’un 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-secrets

Script de vérification de l’état de santé avec sortie JSON

Les systèmes de production agrègent souvent l’état de santé de plusieurs sous-systèmes et l’exposent sous la forme d’une charge utile JSON structurée. Celle-ci est utilisée par les équilibreurs de charge, les orchestrateurs et les tableaux de bord de supervision.

Un script Bash de vérification de l’état de santé peut construire directement la sortie JSON avec printf ou jq. Le code de sortie continue de piloter l’automatisation ; le corps JSON est destiné aux opérateurs humains et aux systèmes de supervision.

Convention : renvoyer HTTP 200 avec {"status":"ok"} lorsque le système est sain, et HTTP 503 avec {"status":"degraded", "checks":{...}} lorsqu’il ne l’est pas. Le script ci-dessous est destiné à être servi par une enveloppe HTTP légère telle que socat, ou à être appelé directement par des sondes d’exécution 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 1

Utilitaire de délai d’expiration avec le modèle d’échéance

La commande GNU timeout enveloppe n’importe quelle commande et l’arrête si elle ne se termine pas dans la durée indiquée. C’est la manière la plus propre d’imposer une échéance stricte à une boucle d’attente ou à une sonde de santé sans gérer manuellement des tâches en arrière-plan.

timeout DURATION COMMAND [ARGS...]

Codes de sortie de timeout :

  • 0 — la commande a réussi dans le délai imparti
  • Code de sortie de la commande — la commande s’est exécutée, mais a renvoyé un code différent de zéro
  • 124 — le délai de la commande a expiré, et SIGTERM a été envoyé
  • 137 — la commande a été arrêtée avec SIGKILL, après --kill-after

La détection du code de sortie 124 permet d’afficher un message clair de dépassement du délai plutôt qu’une erreur générique.

#!/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 "$@"

Implémentation autonome de wait-for-it en Bash pur

De nombreuses images de conteneurs minimales ne contiennent pas nc (netcat). Bash pur peut ouvrir des connexions TCP au moyen de la substitution de processus vers /dev/tcp, une fonctionnalité intégrée non standard mais largement prise en charge par Bash, qui ne nécessite aucun outil externe.

Syntaxe : /dev/tcp/HOST/PORT — Bash ouvre une connexion TCP lorsque vous redirigez vers ce chemin ou depuis celui-ci. Une erreur, avec une sortie différente de zéro, est générée si la connexion est refusée ou si le délai expire.

C’est la technique utilisée par le script populaire wait-for-it.sh, fourni avec de nombreuses configurations Docker Compose. Le script ci-dessous est une implémentation complète et autonome que vous pouvez COPY dans n’importe quel 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
fi

Intégrer des sondes à entrypoint.sh

Le modèle du point d’entrée est la méthode standard pour combiner les boucles d’attente, la validation de l’environnement et le démarrage du processus dans un seul script facile à maintenir. Le ENTRYPOINT de Docker appelle ce script, qui se termine par exec "$@" pour transmettre le contrôle au CMD avec le même PID, ce qui permet une transmission propre des signaux.

Un entrypoint.sh de niveau production effectue généralement les opérations suivantes :

  • Valider rapidement les variables d’environnement obligatoires
  • Exécuter les boucles d’attente des dépendances
  • Exécuter les migrations de base de données, le cas échéant
  • Effectuer une dernière auto-vérification
  • Transférer le contrôle avec exec "$@"

L’utilisation de exec est essentielle : elle remplace le processus shell afin que l’application devienne le PID 1 et reçoive directement SIGTERM de Docker ou Kubernetes lors de l’arrêt progressif.

#!/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 "$@"

Contrôle des connaissances : sondes de vivacité et de disponibilité

Testez votre compréhension des concepts clés abordés dans cette leçon.

Récapitulatif : sondes de santé, barrières de disponibilité et boucles d’attente

Dans cette leçon, vous avez créé la boîte à outils Bash nécessaire pour coordonner de manière fiable le démarrage des conteneurs. Voici les notions abordées :

  • Modèle d’attente — une boucle until nc -z HOST PORT avec un délai d’expiration strict et une vérification du temps écoulé empêche tout blocage infini causé par des dépendances défaillantes.
  • Sondes de disponibilité HTTP — curl --fail --max-time vérifie qu’une application est réellement prête, et pas seulement que son port est ouvert.
  • Temporisation exponentielle — le doublement de l’intervalle d’attente entre les tentatives réduit la charge liée aux redémarrages simultanés et permet aux services en cours de rétablissement de se stabiliser.
  • Sondage parallèle des dépendances — l’exécution des sondes en arrière-plan avec & et la collecte des résultats avec wait $PID réduisent la latence de démarrage lorsque plusieurs dépendances sont présentes.
  • Vivacité ou disponibilité — les sondes de vivacité doivent être rapides et limitées à l’état local ; les sondes de disponibilité peuvent vérifier les systèmes en aval. Ne les confondez jamais.
  • startupProbe — protège les applications dont le démarrage est lent contre les faux échecs de vivacité pendant l’initialisation.
  • Sonde /dev/tcp — une vérification TCP en Bash pur ne nécessite aucun outil externe, ce qui est idéal pour les images de conteneurs minimales.
  • entrypoint.sh — la validation de l’environnement, les boucles d’attente, les migrations et exec "$@" constituent le modèle standard de démarrage d’un service conteneurisé.

Ces modèles constituent le fondement des déploiements de conteneurs auto-réparateurs et de niveau production avec Docker Compose, Kubernetes et ECS.

Questions Fréquemment Posées

La leçon « Sondes d’état, barrières de disponibilité et boucles d’attente » est-elle gratuite ?

Oui — le texte complet de « Sondes d’état, barrières de disponibilité et boucles d’attente » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Linux Command Line & Bash Scripting Mastery, passe à CoddyKit PRO. Le cours Linux Command Line & Bash Scripting Mastery comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Sondes d’état, barrières de disponibilité et boucles d’attente » ?

Implémentez des boucles d’attente des dépendances et des sondes de vivacité pour que les services conteneurisés démarrent de manière fiable. Tu pratiques Linux Command Line & Bash Scripting Mastery avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Linux Command Line & Bash Scripting Mastery ?

Aucune expérience préalable n'est requise. Linux Command Line & Bash Scripting Mastery sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.

Combien de temps prend la leçon « Sondes d’état, barrières de disponibilité et boucles d’attente » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Linux Command Line & Bash Scripting Mastery ?

Oui. Chaque leçon Linux Command Line & Bash Scripting Mastery inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Écrire des Dockerfiles légers et des points d’entrée Shell
  2. Créer des modèles de configuration avec envsubst et des heredocs
  3. Script­er des ressources cloud avec la CLI et jq
  4. Sondes d’état, barrières de disponibilité et boucles d’attente
← Retour à Linux Command Line & Bash Scripting Mastery