Files
spring-boot-demo/kubernetes-deployment/scripts/demo-shutdown.sh
T
asmhatreandClaude Opus 5 a065696478 Add kubernetes-deployment: probes, shutdown, JVM ergonomics, HPA
Companion code for "Deploying Spring Boot 4 on Kubernetes: Probes, Graceful
Shutdown, Limits and JVM Ergonomics". A dependency outage under three
probe-group setups, a rolling restart under load four ways (three runs
each), the JVM's ergonomic choices for nine pod shapes, one GC-heavy load
under five CPU limits with throttling counters, and an HPA driven by a
Micrometer gauge through prometheus-adapter. Measured on k3s v1.36.4.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01C3TETMrqVUWeFkNtz3Jbo3
2026-09-11 17:12:10 +00:00

78 lines
3.7 KiB
Bash

#!/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-<method>-<variant>.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