Linux-kommandolinjen og Bash-scripting på ekspertniveau · Lektion

Health probes, readiness gates og venteløkker

Implementér venteløkker for afhængigheder og liveness probes, så containeriserede tjenester starter pålideligt.

Lektion 4 af 413 trin

Health probes, readiness gates og venteløkker er en gratis Linux-kommandolinjen og Bash-scripting på ekspertniveau-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Linux-kommandolinjen og Bash-scripting på ekspertniveau, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Linux-kommandolinjen og Bash-scripting på ekspertniveau-kurset indeholder 4 lektioner i alt.

Hvorfor tjenester fejler ved opstart

I containeriserede miljøer starter tjenester sjældent isoleret. En webapplikation skal have sin database klar, en mikrotjeneste skal have sin meddelelsesformidler tilgængelig, og en arbejdsproces skal have sin cache udfyldt, før den kan behandle opgaver.

Uden koordinering starter containere parallelt, og de første forespørgsler ankommer, før afhængighederne er klar. Resultatet er fejl med afviste forbindelser, nullpegerundtagelser og beskadiget opstartstilstand, som kræver manuelle genstarter.

  • Livstegnprobe — kører processen stadig, uden at den er gået i hårdknude?
  • Parathedsprobe — er tjenesten klar til at modtage trafik?
  • Venteløkke — et opstartsscript, der blokerer, indtil afhængighederne kan nås

Kubernetes har indbyggede probemekanismer, men de shellscripts, der driver initContainers, indpakninger omkring entrypoint.sh og selvstændige sundhedstjek, skrives i Bash. At beherske dem er en central DevOps-kompetence.

Mønstret til at vente på

Den enkleste venteløkke for en afhængighed kontrollerer et mål med et fast interval, indtil det kan nås. Standardformen bruger en while-løkke sammen med nc (netcat) eller curl til at kontrollere en TCP-port eller et HTTP-endpoint.

Vigtige designvalg:

  • Tidsgrænse — afbryd efter N sekunder, så en defekt afhængighed ikke hænger poden fast for evigt
  • Pauseinterval — sov mellem kontrollerne for at undgå at overbelaste en tjeneste, der er ved at komme sig
  • Afslutningsstatus — afslut med 1 ved tidsudløb, så containeren genstarter (eller init-containeren fejler tydeligt)

Kodestykket nedenfor venter i op til 60 sekunder på, at en TCP-port accepterer forbindelser, før hovedprocessen startes.

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

HTTP-parathedstjek med curl

En TCP-forbindelse fortæller kun, at porten er åben — ikke at applikationen bag den er klar til at besvare forespørgsler. Mange tjenester stiller et dedikeret /health- eller /readyz-endpoint til rådighed, som kun returnerer HTTP 200, når alle interne delsystemer er initialiseret.

Brug curl --fail --silent --output /dev/null til at kontrollere et HTTP-parathedsendpoint. Flaget --fail får curl til at afslutte med status 22 ved 4xx/5xx-svar, hvilket styrer genforsøgslogikken rent.

Vigtige indstillinger:

  • --fail — behandl HTTP-fejl som curl-fejl (afslutning med status forskellig fra nul)
  • --silent — undertryk fremdriftsoutput
  • --max-time N — tidsgrænse i sekunder pr. forespørgsel
  • --retry N --retry-delay S — curls eget lag til genforsøg (nyttigt i enkle tilfælde)
#!/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 "$@"

Eksponentiel ventetidsforøgelse i venteløkker

En genforsøgsløkke med faste intervaller overbelaster en tjeneste, der er ved at komme sig, med en konstant hastighed. Eksponentiel ventetidsforøgelse fordobler ventetiden ved hvert forsøg, hvilket reducerer belastningen under genoprettelsen, samtidig med at løkken stadig hurtigt når frem, når tjenesten kommer op.

Standardformlen er: sleep_time = min(base * 2^attempt, max_sleep). En tilfældig brøkforskydning forhindrer problemer med mange samtidige genforsøg, når mange containere genstarter på samme tid.

Dette mønster bruges af produktionsværktøjer som wait-for-it, genforsøg i AWS SDK og Kubernetes-kontrollernes afstemningsløkker.

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

Kontrol af flere afhængigheder

Virkelige applikationer har flere afhængigheder: en database, en cache, en meddelelsesformidler og måske en ekstern API. Hvis de kontrolleres sekventielt, spildes der opstartstid. En bedre tilgang kontrollerer alle afhængigheder parallelt og venter på, at alle lykkes.

Bash-operatoren & kører hver kontrol i baggrunden, og wait samler deres afslutningsstatusser. Hvis en kontrol mislykkes, afsluttes entrypoint-scriptet med en status forskellig fra nul, hvilket udløser en genstart af containeren.

Vigtig teknik: Gem baggrundsprocessernes PID'er med $!, og giv dem eksplicit til wait, så du kan kontrollere de enkelte afslutningsstatusser.

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

Livstegn kontra parathed: Forskellige scripts til forskellige prober

Kubernetes skelner mellem livstegns- og parathedsprober, og de bør udføre forskellige opgaver:

  • Livstegnprobe — besvarer spørgsmålet: er processen stadig i live og ikke gået i hårdknude? Den bør være hurtig og kun kontrollere intern tilstand (f.eks. om proces-id-filen findes, eller om et lokalt sundhedsendpoint returnerer 200). En fejlslagen livstegnprobe afslutter og genstarter containeren.
  • Parathedsprobe — besvarer spørgsmålet: skal denne pod modtage trafik? Den kan kontrollere afhængigheder nedstrøms. En fejlslagen parathedsprobe fjerner poden fra tjenestens belastningsfordeler, men genstarter den ikke.

Anbring aldrig langsomme kontroller af afhængigheder i livstegnsprober. Et kortvarigt databaseudfald ville fejlagtigt genstarte alle dine applikationspoder og dermed forværre problemet.

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

Opstartsprober og initContainers

Kubernetes tilbyder en tredje probetype: startupProbe. Den kører i stedet for livstegns- og parathedsprober, indtil den lykkes én gang, så langsomt startende applikationer (opvarmning af JVM, databasemigreringer) får tid til at initialisere uden at udløse falske livstegnsfejl.

Når der skal ventes på afhængigheder, er initContainers ofte renere end opstartsscripts. De kører, før app-containerne starter, og Kubernetes håndterer automatisk logikken for genforsøg og genstarter. Init-containerafbildningen behøver kun sh, nc eller curl — du kan bruge en minimal busybox- eller alpine-afbildning.

Eksempel på en initContainer-specifikation i et Pod-manifest:

# 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 til sundhedstjek med JSON-resultat

Produktionssystemer samler ofte sundhedsstatus fra flere delsystemer og stiller den til rådighed som et struktureret JSON-svar. Det bruges af belastningsfordelere, orkestratorer og overvågningspaneler.

Et Bash-script til sundhedstjek kan opbygge JSON-resultatet direkte ved hjælp af printf eller jq. Afslutningsstatussen styrer stadig automatiseringen; JSON-indholdet er beregnet til menneskelige operatører og overvågningssystemer.

Konvention: returnér HTTP 200 med {"status":"ok"}, når systemet er sundt, og HTTP 503 med {"status":"degraded", "checks":{...}}, når det er usundt. Scriptet nedenfor er beregnet til at blive leveret af en letvægts-HTTP-indpakning som socat eller kaldt direkte af Kubernetes' exec-prober.

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

Tidsgrænseværktøj med fristmønstret

GNU-kommandoen timeout omslutter enhver kommando og afslutter den, hvis den ikke bliver færdig inden for det angivne tidsrum. Det er den enkleste måde at håndhæve en fast tidsfrist for en venteløkke eller et sundhedstjek uden selv at skulle håndtere baggrundsjob.

timeout DURATION COMMAND [ARGS...]

Afslutningsstatusser fra timeout:

  • 0 — kommandoen lykkedes inden for tidsfristen
  • Kommandoens afslutningsstatus — kommandoen blev kørt, men returnerede en status forskellig fra nul
  • 124 — kommandoen overskred tidsfristen (SIGTERM blev sendt)
  • 137 — kommandoen blev afsluttet med SIGKILL (efter --kill-after)

Ved at registrere afslutningsstatus 124 kan du udskrive en tydelig besked om tidsgrænsen i stedet for en generisk fejl.

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

Selvstændigt wait-for-it i ren Bash

Mange minimale containerafbildninger mangler nc (netcat). Ren Bash kan åbne TCP-forbindelser ved hjælp af proces-substitution til /dev/tcp, en ikke-standardiseret, men bredt understøttet indbygget funktion i Bash, der ikke kræver eksterne værktøjer.

Syntaks: /dev/tcp/HOST/PORT — Bash åbner en TCP-forbindelse, når du omdirigerer til eller fra denne sti. Den giver en fejl (afslutning med status forskellig fra nul), hvis forbindelsen afvises eller får timeout.

Dette er den teknik, der bruges af det udbredte wait-for-it.sh-script, som følger med mange Docker Compose-opsætninger. Scriptet nedenfor er en komplet, selvstændig implementering, som du kan COPY ind i enhver 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

Integrering af prober i entrypoint.sh

Opstartsmønstret er standardmåden at samle venteløkker, validering af miljøet og processtart i ét script, der er let at vedligeholde. Dockers ENTRYPOINT kalder dette script, og scriptet slutter med exec "$@" for at overdrage kontrollen til CMD med samme PID (så signaler kan videresendes korrekt).

Et entrypoint.sh i produktionskvalitet gør typisk følgende:

  • Validerer påkrævede miljøvariabler tidligt (fejler hurtigt)
  • Kører venteløkker for afhængigheder
  • Udfører databasemigreringer (hvis relevant)
  • Kører en afsluttende selvkontrol
  • Overdrager kontrollen med exec "$@"

Det er afgørende at bruge exec — den erstatter shellprocessen, så appen bliver PID 1 og modtager SIGTERM direkte fra Docker/Kubernetes' kontrollerede nedlukning.

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

Videnstjek: Livstegns- kontra parathedsprober

Tjek din forståelse af de vigtigste begreber fra denne lektion.

Opsummering: Sundhedsprober, parathedsvilkår og venteløkker

I denne lektion opbyggede du Bash-værktøjssættet til pålidelig koordinering af containeropstart. Her er det, du gennemgik:

  • Ventemønster — en until nc -z HOST PORT-løkke med en fast tidsgrænse og kontrol af den forløbne tid forhindrer uendelig blokering på defekte afhængigheder.
  • HTTP-parathedstjek — curl --fail --max-time validerer, at en applikation faktisk er klar, og ikke kun at porten er åben.
  • Eksponentiel ventetidsforøgelse — en fordobling af pauseintervallet mellem genforsøg reducerer belastningen fra mange samtidige genforsøg og giver tjenester, der er ved at komme sig, mulighed for at stabilisere sig.
  • Parallel kontrol af afhængigheder — ved at køre kontroller i baggrunden med & og samle resultaterne med wait $PID reduceres opstartsforsinkelsen, når der er flere afhængigheder.
  • Livstegn kontra parathed — livstegnsprober skal være hurtige og kun kontrollere lokale forhold; parathedsprober må kontrollere systemer nedstrøms. Bland dem aldrig sammen.
  • startupProbe — beskytter langsomt startende apps mod falske livstegnsfejl under initialiseringen.
  • Kontrol med /dev/tcp — en ren Bash-TCP-kontrol kræver ingen eksterne værktøjer og er ideel til minimale containerafbildninger.
  • entrypoint.sh — miljøvalidering, venteløkker, migreringer og exec "$@" udgør standardmønstret for opstart af containeriserede tjenester.

Disse mønstre udgør grundlaget for selvhelende containerudrulninger i produktionskvalitet på tværs af Docker Compose, Kubernetes og ECS.

Gratis at komme i gang

Lær Bash med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
22
Lektioner
88

Ofte stillede spørgsmål

Er lektionen “Health probes, readiness gates og venteløkker” gratis?

Ja — hele teksten til “Health probes, readiness gates og venteløkker” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Linux-kommandolinjen og Bash-scripting på ekspertniveau-kurset, skal du opgradere til CoddyKit PRO. Linux-kommandolinjen og Bash-scripting på ekspertniveau-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Health probes, readiness gates og venteløkker”?

Implementér venteløkker for afhængigheder og liveness probes, så containeriserede tjenester starter pålideligt. Du øver dig i Linux-kommandolinjen og Bash-scripting på ekspertniveau med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Linux-kommandolinjen og Bash-scripting på ekspertniveau?

Der kræves ingen tidligere erfaring. Linux-kommandolinjen og Bash-scripting på ekspertniveau på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.

Hvor lang tid tager lektionen “Health probes, readiness gates og venteløkker”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Linux-kommandolinjen og Bash-scripting på ekspertniveau-lektion?

Ja. Alle Linux-kommandolinjen og Bash-scripting på ekspertniveau-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Skriv slanke Dockerfiles og shell-entrypoints
  2. Skabelondannelse af konfigurationer med envsubst og heredocs
  3. Scripting af cloudressourcer via CLI og jq
  4. Health probes, readiness gates og venteløkker
← Tilbage til Linux-kommandolinjen og Bash-scripting på ekspertniveau