#!/usr/bin/env bash # A rolling restart under load, four ways. Each run: 20 concurrent clients calling /work?ms=300 # through the Service for 45 s, rollout restart at t=8 s. METHOD=POST (default) or GET. # -> docs/output/shutdown--.txt set -uo pipefail source "$(dirname "$0")/env.sh" export METHOD="${METHOD:-POST}" RUNS="${RUNS:-3}" M=$(echo "$METHOD" | tr 'A-Z' 'a-z') D="deploy/orders" PRESTOP_PATH=/spec/template/spec/containers/0/lifecycle variant() { # name, description - runs it $RUNS times local base="$1" desc="$2" r for r in $(seq 1 "$RUNS"); do run_once "$base-run$r" "$desc (run $r of $RUNS)"; done } run_once() { # name, description local name="$1" desc="$2" kubectl -n "$NS" rollout status "$D" --timeout=180s > /dev/null kubectl -n "$NS" delete events --all > /dev/null 2>&1 sleep 3 "$MODULE_DIR/scripts/loadgen.sh" "lg-$M-$name" "http://orders:8080/work?ms=300" 20 45 kubectl -n "$NS" wait --for=condition=Ready "pod/lg-$M-$name" --timeout=60s > /dev/null sleep 8 kubectl -n "$NS" rollout restart "$D" > /dev/null kubectl -n "$NS" rollout status "$D" --timeout=180s > /dev/null kubectl -n "$NS" wait --for=jsonpath='{.status.phase}'=Succeeded "pod/lg-$M-$name" --timeout=120s > /dev/null { echo "# $desc" echo "# 20 clients, $METHOD /work?ms=300 via Service orders, 45 s; kubectl rollout restart at ~t=8 s" echo kubectl -n "$NS" logs "lg-$M-$name" echo echo "# Events:" kubectl -n "$NS" get events --sort-by=.lastTimestamp | grep -E 'PreStop|Killing' | awk '{$1=""; print}' | sed 's/^ //' | sort | uniq -c | head -6 } > "$OUT/shutdown-$M-$name.txt" grep -E '^TOTAL|^LATENCY' "$OUT/shutdown-$M-$name.txt" | sed "s/^/$M $name: /" kubectl -n "$NS" delete pod "lg-$M-$name" --wait=false > /dev/null } # 1. server.shutdown=immediate, no preStop kubectl -n "$NS" patch "$D" --type=json -p "[{\"op\":\"remove\",\"path\":\"$PRESTOP_PATH\"}]" > /dev/null 2>&1 kubectl -n "$NS" set env "$D" SERVER_SHUTDOWN=immediate > /dev/null variant 1-immediate-no-prestop "server.shutdown=immediate, no preStop hook" # 2. graceful shutdown (the Boot default), no preStop kubectl -n "$NS" set env "$D" SERVER_SHUTDOWN- > /dev/null variant 2-graceful-no-prestop "graceful shutdown (default), no preStop hook" # 3. graceful + native sleep preStop (Kubernetes 1.32+) - the baseline manifest kubectl -n "$NS" patch "$D" --type=json -p "[{\"op\":\"add\",\"path\":\"$PRESTOP_PATH\",\"value\":{\"preStop\":{\"sleep\":{\"seconds\":5}}}}]" > /dev/null variant 3-graceful-prestop-sleep "graceful shutdown + preStop: sleep: {seconds: 5}" # 4. graceful + exec preStop that needs a shell - on a distroless image kubectl -n "$NS" patch "$D" --type=json -p "[{\"op\":\"replace\",\"path\":\"$PRESTOP_PATH\",\"value\":{\"preStop\":{\"exec\":{\"command\":[\"sh\",\"-c\",\"sleep 5\"]}}}}]" > /dev/null variant 4-graceful-prestop-exec-sh "graceful shutdown + preStop: exec: [sh, -c, sleep 5] on a distroless image" # summary across runs { echo "# $METHOD /work?ms=300, 20 clients, rolling restart of 2 replicas. Failed requests per run (successful in brackets)." echo for f in "$OUT"/shutdown-$M-*-run1.txt; do v=$(basename "$f" -run1.txt); v=${v#shutdown-$M-} printf '%-32s' "$v" for r in $(seq 1 "$RUNS"); do t=$(grep '^TOTAL' "$OUT/shutdown-$M-$v-run$r.txt") ok=$(echo "$t" | grep -o 'ok=[0-9]*' | cut -d= -f2) fails=$(echo "$t" | grep -o '[A-Za-z_0-9]*=[0-9]*' | grep -v '^ok=' | tr '\n' ' ') printf ' | run %s: %-38s' "$r" "${fails:-0 failures} [$ok]" done echo done } | tee "$OUT/shutdown-$M-summary.txt" # restore the baseline kubectl -n "$NS" patch "$D" --type=json -p "[{\"op\":\"replace\",\"path\":\"$PRESTOP_PATH\",\"value\":{\"preStop\":{\"sleep\":{\"seconds\":5}}}}]" > /dev/null