# Spring Boot 4 on Kubernetes: probes, graceful shutdown, limits and JVM ergonomics Companion project for [**Deploying Spring Boot 4 on Kubernetes: Probes, Graceful Shutdown, Limits and JVM Ergonomics**](https://ankurm.com/spring-boot-4-kubernetes-probes-graceful-shutdown-cpu-limits-hpa/) on ankurm.com. A small Spring Boot 4 service, the manifests that deploy it, and one script per experiment - a dependency outage under three probe configurations, a rolling restart under load four ways, the JVM's ergonomic choices for nine pod shapes, the same GC-heavy load under five CPU limits, and a HorizontalPodAutoscaler driven by a Micrometer gauge. Every figure in the article is in [`docs/output/`](docs/output). ## Versions | | | |---|---| | Spring Boot | 4.1.1 | | JDK | Temurin 25.0.4.1 (in `gcr.io/distroless/java25-debian13:nonroot`) | | Kubernetes | k3s v1.36.4+k3s1, single node, 2 vCPU / 8 GB, cgroup v1 ([docs/01-the-lab.md](docs/01-the-lab.md)) | | Prometheus | 3.14.0 | | prometheus-adapter | 0.12.0 | ## Quickstart ```bash export JAVA_HOME=/path/to/jdk-25 mvn -DskipTests package docker build -f docker/Dockerfile -t sbd/k8s-demo:1 . # load the image into your cluster (k3s ctr images import / kind load docker-image / minikube image load) kubectl apply -f k8s/00-namespace.yaml -f k8s/downstream.yaml -f k8s/orders.yaml -f k8s/client.yaml ./scripts/demo-probes.sh # ...and the other demo-*.sh; see the table below ``` ## What is in it | Path | | |---|---| | [`k8s/orders.yaml`](k8s/orders.yaml) | the baseline Deployment: startup/liveness/readiness probes, `preStop: sleep`, `MaxRAMPercentage=75` | | [`k8s/downstream.yaml`](k8s/downstream.yaml) | a dependency to take away | | [`k8s/gc-lab.yaml`](k8s/gc-lab.yaml) | the single pod the CPU-limit experiment reshapes | | [`k8s/hpa/`](k8s/hpa) | Prometheus, prometheus-adapter with one rule, the HPA | | [`WorkController`](src/main/java/com/ankurm/k8s/web/WorkController.java) | `/work` (timed requests, the `app.inflight.requests` gauge), `/alloc` (GC load) | | [`DownstreamHealthIndicator`](src/main/java/com/ankurm/k8s/health/DownstreamHealthIndicator.java) | a health indicator whose probe group is the experiment | | [`JvmController`](src/main/java/com/ankurm/k8s/diag/JvmController.java) | `/diag/jvm`, `/diag/gc` - ergonomics and cgroup throttling counters. Diagnostic; delete before shipping | | [`LoadGen`](src/main/java/com/ankurm/k8s/loadgen/LoadGen.java) | the in-cluster load generator, run from the same image | ## Documentation 1. [The lab](docs/01-the-lab.md) 2. [Probes: which checks belong in which probe](docs/02-probes.md) 3. [JVM ergonomics: what the JVM decides from your pod spec](docs/03-jvm-ergonomics.md) 4. [CPU limits and the garbage collector](docs/04-cpu-limits-and-gc.md) 5. [Graceful shutdown and the rolling-update race](docs/05-graceful-shutdown.md) 6. [HPA on a custom Micrometer metric](docs/06-hpa-custom-metrics.md) ## Captured output | File | Produced by | |---|---| | `probes-*.txt` | `scripts/demo-probes.sh` | | [`startup-probe.txt`](docs/output/startup-probe.txt) | `scripts/demo-startup.sh` | | [`jvm-ergonomics.txt`](docs/output/jvm-ergonomics.txt) | `scripts/demo-ergonomics.sh` | | [`gc-throttling.txt`](docs/output/gc-throttling.txt) + raw GC logs | `scripts/demo-gc-throttling.sh` | | [`shutdown-post-summary.txt`](docs/output/shutdown-post-summary.txt), `shutdown-*.txt` | `scripts/demo-shutdown.sh` (`METHOD=GET` for the `-get-` files) | | [`hpa-custom-metric.txt`](docs/output/hpa-custom-metric.txt), [`hpa-custom-metrics-api.txt`](docs/output/hpa-custom-metrics-api.txt) | `scripts/demo-hpa.sh` |