Chapter 20 · Azure Kubernetes App Deploy
Subchapter 20.34
knowledge-packs/frameworks/spring-boot.mdMarkdown6 KBView on GitHub
Applies to: Projects detected with
pom.xmlcontainingspring-boot-starter-weborbuild.gradle/ containing
build.gradle.ktsorg.springframework.boot| Property | Value |
|---|---|
| Signal files | pom.xml with spring-boot-starter-web or build.gradle(.kts) with org.springframework.boot |
| Default port | 8080 |
| Health path | /actuator/health/liveness + /actuator/health/readiness |
| Base template | templates/dockerfiles/java.Dockerfile (+ references/base-images.md) |
The base template handles the multi-stage build. One Spring Boot-specific optimization worth applying: use layered JAR extraction (java -Djarmode=layertools -jar app.jar extract) so that dependencies, Spring Boot loader, and application code land in separate Docker layers — only changed layers are rebuilt or pushed on each deployment.
Spring Boot Actuator provides health endpoints out of the box:
| Endpoint | Purpose | Probe Type |
|---|---|---|
/actuator/health | Overall health | General |
/actuator/health/liveness | Liveness group | livenessProbe |
/actuator/health/readiness | Readiness group | readinessProbe |
In application.properties or application.yml:
management.endpoints.web.exposure.include=health
management.endpoint.health.probes.enabled=true
management.endpoint.health.show-details=alwaysThe probes are automatically enabled when running in Kubernetes (detected via the KUBERNETES_SERVICE_HOST env var), but it’s best practice to enable them explicitly.
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
failureThreshold: 30 # allows up to 300s for JVM warmup + Spring context init
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 15
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3Important: Spring Boot apps require a startupProbe. JVM warmup and Spring context initialization typically take 15–60 seconds. Without it, the liveness probe may kill the pod before it finishes starting. The startup probe gives the app up to 300 seconds to become healthy before the liveness probe takes over. Uncomment the startupProbe section in the deployment template.
Spring Boot uses Spring Profiles to switch database configurations:
| Profile | Activation | Typical Config File |
|---|---|---|
default | No profile set | application.properties — usually H2 in-memory |
mysql | SPRING_PROFILES_ACTIVE=mysql | application-mysql.properties |
postgres | SPRING_PROFILES_ACTIVE=postgres | application-postgres.properties |
env:
- name: SPRING_PROFILES_ACTIVE
value: postgres
- name: POSTGRES_URL
value: "jdbc:postgresql://{{PG_SERVER_NAME}}.postgres.database.azure.com:5432/{{DB_NAME}}"
- name: POSTGRES_USER
value: "{{IDENTITY_NAME}}"
- name: SPRING_DATASOURCE_AZURE_PASSWORDLESS_ENABLED
value: "true"With Workload Identity and the spring-cloud-azure-starter-jdbc-postgresql dependency, Spring Boot can authenticate to PostgreSQL without a password using Azure AD tokens.
apiVersion: v1
kind: ConfigMap
metadata:
name: {{APP_NAME}}-config
data:
SPRING_PROFILES_ACTIVE: "postgres"
MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: "health"
MANAGEMENT_ENDPOINT_HEALTH_PROBES_ENABLED: "true"When readOnlyRootFilesystem: true is set, Spring Boot needs /tmp writable:
/tmp/tmp as the staging directory/tmpvolumes:
- name: tmp
emptyDir: {}
containers:
- name: app
volumeMounts:
- name: tmp
mountPath: /tmpNo other writable paths are typically needed for production Spring Boot apps.
Spring Boot apps running on the JVM need more memory than interpreted languages. These are starting-point defaults — tune based on observed usage.
| Resource | Request | Limit |
|---|---|---|
| CPU | 250m | 1000m |
| Memory | 512Mi | 1Gi |
Set -XX:MaxRAMPercentage=75.0 in JAVA_OPTS so the JVM uses at most 75% of the container’s memory limit, leaving headroom for the OS and non-heap memory.
server.port in application.propertiesSERVER_PORT=8080Spring Boot always logs the port on startup: Tomcat started on port(s): 8080 (http)
| Build Tool | Build Command | Output |
|---|---|---|
| Maven | ./mvnw package -DskipTests -B | target/*.jar |
| Gradle | ./gradlew bootJar | build/libs/*.jar |
The -B flag (batch mode) suppresses interactive Maven output — important for CI/CD and Docker builds.
| Issue | Symptom | Fix |
|---|---|---|
| JVM OOM in container | OOMKilled pod status | Set -XX:MaxRAMPercentage=75.0 in JAVA_OPTS and ensure memory limit >= 256Mi |
| Slow startup | Readiness probe fails, pod restarted | Increase initialDelaySeconds to 45-60s, or add a startupProbe with higher failureThreshold |
| H2 in-memory on AKS | Data lost on pod restart | Switch to PostgreSQL profile — H2 is for local dev only |
| Connection refused to PostgreSQL | PSQLException: Connection refused | Verify firewall rules on PostgreSQL Flexible Server allow AKS subnet |
| Image too large (>500MB) | Slow pulls, high ACR storage | Use Alpine base image + layered JAR extraction |