Kubernetes
Kubernetes (K8s) orchestrates Docker containers across a cluster — scheduling Java services as Pods, managing releases with Deployments, exposing traffic via Services, and injec…
Introduction
Kubernetes (K8s) orchestrates Docker containers across a cluster — scheduling Java services as Pods, managing releases with Deployments, exposing traffic via Services, and injecting config through ConfigMaps and Secrets.
Spring Boot on Kubernetes uses actuator health probes, graceful shutdown on SIGTERM, and service discovery via DNS (payment-service.default.svc.cluster.local). This lesson maps K8s primitives to how Java microservices actually run in production — not abstract YAML trivia.
Business problem
Manual container management doesn't scale:
- Node failure: Single VM dies — payment service down until manual restart.
- Rolling deploy: Zero-downtime deploy of 20 Java pods — manual script error causes outage.
- Config sprawl: JDBC URL baked in image — rebuild to change database endpoint.
- Secret leakage: DB password in git — compliance failure.
- Resource hog: One Spring Boot pod OOMs node — no isolation between services.
Why this topic exists
Kubernetes automates container lifecycle at fleet scale:
- Pod: Smallest deploy unit — one or more containers sharing network/IP (usually one Java app per pod).
- Deployment: Declarative desired state — 3 replicas, rolling update strategy, rollback history.
- Service: Stable ClusterIP/DNS load balances to healthy pod endpoints.
- ConfigMap: Non-sensitive config — application.yml overrides, feature flags.
- Secret: Base64-encoded sensitive data — mount as env or files for JDBC password.
Core concepts
K8s concepts for Java engineers:
- Deployment → ReplicaSet → Pods: Deployment manages ReplicaSet manages Pods — scale with
replicas: N. - Service types: ClusterIP (internal), NodePort, LoadBalancer, Ingress (HTTP routing).
- Probes: liveness (restart if dead), readiness (remove from Service if not ready), startup (slow Spring boot).
- ConfigMap mount: Volume mount
/config/application.yml— Springspring.config.additional-location. - Secret as env:
SPRING_DATASOURCE_PASSWORDfrom secretKeyRef — never in image. - Resource requests/limits: requests for scheduling; limits for cgroup cap — align with JVM MaxRAMPercentage.
Internal architecture
Java payment service on Kubernetes:
Internet ──▶ Ingress ──▶ Service (payment-svc:80)│ClusterIP load balance│┌───────────────┼───────────────┐▼ ▼ ▼Pod (payment) Pod (payment) Pod (payment)Spring Boot Spring Boot Spring Boot:8080 :8080 :8080│ │ │└───────────────┼───────────────┘▼ConfigMap: app-configSecret: db-credentials│▼PostgreSQL (StatefulSet or managed RDS)Deployment spec highlights:replicas: 3strategy: RollingUpdate (maxSurge 1, maxUnavailable 0)template.spec.containers[0]:resources.requests.memory: 512Miresources.limits.memory: 512MilivenessProbe: /actuator/health/livenessreadinessProbe: /actuator/health/readiness
Pods, Deployments, Services, and config injection:
Code walkthrough
Deployment + Service + ConfigMap + Secret for Spring Boot:
- image digest pin: Immutable deploy — same artifact verified in staging.
- secretKeyRef: Password injected at runtime — not in ConfigMap plaintext.
- liveness vs readiness: Liveness restart pod; readiness remove from Service endpoints during slow startup.
- preStop sleep: Allow load balancer to deregister before SIGTERM — graceful drain.
# deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata:name: payment-servicespec:replicas: 3selector:matchLabels:app: payment-servicetemplate:metadata:labels:app: payment-servicespec:containers:- name: paymentimage: registry.example.com/payment:1.2.3@sha256:abc...ports:- containerPort: 8080envFrom:- configMapRef:name: payment-configenv:- name: SPRING_DATASOURCE_PASSWORDvalueFrom:secretKeyRef:name: db-credentialskey: passwordresources:requests:memory: "512Mi"cpu: "250m"limits:memory: "512Mi"cpu: "1000m"livenessProbe:httpGet:path: /actuator/health/livenessport: 8080initialDelaySeconds: 60readinessProbe:httpGet:path: /actuator/health/readinessport: 8080initialDelaySeconds: 30lifecycle:preStop:exec:command: ["sh", "-c", "sleep 5"] # drain in-flight requests---apiVersion: v1kind: Servicemetadata:name: payment-servicespec:selector:app: payment-serviceports:- port: 80targetPort: 8080type: ClusterIP
Production example
ConfigMap + Spring Boot externalized config:
- graceful shutdown: server.shutdown=graceful + terminationGracePeriodSeconds ≥ 30 — in-flight payments complete.
- application.yaml in ConfigMap: Mount as volume or use spring.cloud.kubernetes for hot reload.
- HPA: Scale replicas on CPU — each pod needs headroom for JVM GC spikes.
- Sealed Secrets: Encrypt Secret for git storage — decrypt only in cluster.
# configmap.yamlapiVersion: v1kind: ConfigMapmetadata:name: payment-configdata:SPRING_PROFILES_ACTIVE: "production"SPRING_KAFKA_BOOTSTRAP_SERVERS: "kafka:9092"LOGGING_LEVEL_ROOT: "INFO"application.yaml: |server:shutdown: gracefulspring:lifecycle:timeout-per-shutdown-phase: 30s---# secret.yaml (apply with sealed-secrets or external-secrets in prod)apiVersion: v1kind: Secretmetadata:name: db-credentialstype: OpaquestringData:password: "REPLACE_VIA_CI_NOT_GIT"# Spring Boot 3 actuator probes — application.propertiesmanagement.endpoint.health.probes.enabled=truemanagement.health.livenessState.enabled=truemanagement.health.readinessState.enabled=true# HPA — scale Java pods on CPUapiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata:name: payment-hpaspec:scaleTargetRef:apiVersion: apps/v1kind: Deploymentname: payment-serviceminReplicas: 3maxReplicas: 20metrics:- type: Resourceresource:name: cputarget:type: UtilizationaverageUtilization: 70
Enterprise case study
Uber — Kubernetes at scale: Uber runs thousands of microservices on Kubernetes — Java and Go services share platform primitives. Key lesson for Java teams: readiness probe must reflect real readiness — Spring actuator readiness includes DB/Kafka health; premature ready → traffic to starting pod → 503 storm. Set initialDelaySeconds and startupProbe for slow Spring context.
- Before: Mesos custom scheduler — inconsistent deploy patterns per team.
- After: Standard Deployment + HPA + platform ConfigMaps — paved road for Java Spring Boot.
- Java-specific: memory limit = heap (MaxRAMPercentage) + 128MB buffer minimum.
- Incident: Missing preStop — rolling deploy dropped in-flight requests during SIGTERM.
Performance considerations
K8s performance for Java workloads:
- Right-size requests: Over-requested memory strands cluster capacity; under-requested → eviction.
- CPU requests affect GC: JVM sees AvailableProcessors from CPU limit — too low slows GC.
- Pod density: Don't pack heap-heavy Java pods — leave node headroom for OS and kubelet.
- Anti-affinity: Spread payment pods across nodes — single node failure doesn't take all replicas.
- Startup probe: Long initialDelay on liveness causes slow start kill — use startupProbe instead.
Security considerations
K8s security for Java services:
- RBAC: Service account per app — minimal permissions; no cluster-admin for apps.
- NetworkPolicy: Payment pod talks only to postgres and kafka — deny all else.
- Secret management: External Secrets Operator, Vault — not plaintext in git.
- Pod security: runAsNonRoot, readOnlyRootFilesystem, drop ALL capabilities.
- Image pull secrets: Private registry auth for Java app images.
Scalability considerations
Scaling Java on Kubernetes:
- HPA on CPU/custom metrics: Kafka consumer lag metric for worker deployments.
- VPA (Vertical): Recommend memory requests after observing JVM heap usage — caution with Java.
- Cluster autoscaler: Add nodes when pods pending — Java services memory-heavy.
- Multi-zone: podAntiAffinity + topologySpreadConstraints — AZ failure tolerance.
Production challenges
Real K8s + Java challenges:
- CrashLoopBackOff: Spring fails startup — check logs, missing Secret, wrong ConfigMap key.
- Probe misconfiguration: Liveness kills during GC pause — tune or use startupProbe.
- Config drift: ConfigMap updated but pods not restarted — stale config until rollout.
- Memory limit too tight: OOMKilled during peak — monitor JVM heap vs container limit.
- DNS resolution delay: Service name fails on first boot — retry logic in Java client or init container.
Common mistakes
- Same value for liveness and readiness with short timeout — restart loop on slow DB connect.
- Secrets in ConfigMap — base64 is not encryption; use Secret resource properly.
- No resource limits — noisy neighbor Java pod consumes entire node.
- Hardcoding localhost in Spring — use K8s Service DNS names.
- Ignoring terminationGracePeriodSeconds — Spring graceful shutdown cut mid-request.
Debugging guide
Debug Java pods on Kubernetes:
- Pod logs:
kubectl logs payment-service-xxx -f --previousfor crash. - Describe pod: Events show OOMKilled, FailedMount, probe failures.
- Exec debug:
kubectl exec -it pod -- wget -qO- localhost:8080/actuator/health. - Port forward:
kubectl port-forward svc/payment-service 8080:80local test.
# Pod status and eventskubectl describe pod -l app=payment-service# Check endpoints registeredkubectl get endpoints payment-service# ConfigMap mounted correctly?kubectl exec payment-pod -- env | grep SPRING# Rollout statuskubectl rollout status deployment/payment-servicekubectl rollout undo deployment/payment-service # rollback
Best practices
- One Java application per Pod — sidecar for mesh/logging only when needed.
- Deployment with rolling update maxUnavailable=0 for payment services.
- Service ClusterIP for internal Java-to-Java — Ingress for external HTTP.
- Externalize config via ConfigMap; credentials via Secret.
- Enable Spring Boot actuator probes — separate liveness and readiness.
- Set memory requests = limits for Java (Guaranteed QoS) — predictable JVM.
- preStop hook + graceful shutdown — drain before SIGTERM.
Anti-patterns
- Deploying without probes — broken pods receive traffic.
- Latest image tag in Deployment — use digest or immutable semver.
- Giant ConfigMap with secrets mixed in — split ConfigMap and Secret.
- Single replica production Java service — no HA during node drain.
- Manual kubectl edit prod Deployment — GitOps (ArgoCD/Flux) for audit trail.
Staff engineer notes
- Staff engineers define "paved road" Deployment template for all Java services — probes, resources, labels standard.
- Memory limit for Java pod is a JVM tuning parameter — coordinate with MaxRAMPercentage, don't copy from Node.js template.
- Readiness must gate traffic — liveness must not depend on external DB (use separate health groups in Spring Boot 3).
- ConfigMap change = rolling restart — automate in GitOps pipeline; document which keys require restart.
- Kubernetes is not your CI — build image in CI, K8s pulls and runs; no kubectl apply from laptop to prod.
Interview questions
Interview preparation
15 questions grouped by difficulty — expand any card for a model answer and follow-up probe.
Beginner
1What is a Kubernetes Pod?
BeginnerModel answer
- Smallest deployable unit
- one or more containers sharing network namespace and IP. Usually one application container per pod. Ephemeral
- replaced not repaired. Scheduled onto nodes by kube-scheduler.
Follow-up probe
Multi-container pod?
2What is a Deployment?
BeginnerModel answer
Declarative controller managing ReplicaSet to maintain desired pod replicas.
Supports rolling updates and rollbacks.
Change pod template → rolling replacement of pods.
kubectl rollout undo for rollback.
Follow-up probe
vs StatefulSet?
3What is a Kubernetes Service?
BeginnerModel answer
Stable network endpoint for pods.
Selector matches pod labels.
ClusterIP provides internal DNS name and load balancing across ready endpoints.
Pods come and go; Service DNS stable.
Follow-up probe
Service types?
4ConfigMap vs Secret?
BeginnerModel answer
- ConfigMap: non-sensitive config data (URLs, feature flags, yaml). Secret: sensitive data (passwords, tokens)
- base64 encoded at rest, can mount as files or env vars. Neither replaces vault for enterprise secret rotation.
Follow-up probe
Secret encryption at rest?
5Liveness vs readiness probe?
BeginnerModel answer
Liveness: is app alive?
Fail → restart pod.
Readiness: ready for traffic?
Fail → remove from Service endpoints but don't restart.
Spring Boot: /actuator/health/liveness and /readiness.
Follow-up probe
Startup probe?
Intermediate
6How Spring Boot runs on Kubernetes?
IntermediateModel answer
Containerized jar in Deployment.
ConfigMap/Secret for config.
Actuator health probes.
Service DNS for discovery.
MaxRAMPercentage for heap.
Graceful shutdown on SIGTERM.
Optional Spring Cloud Kubernetes for config reload.
Follow-up probe
Service discovery without Eureka?
7Explain rolling update strategy.
IntermediateModel answer
- Deployment maxSurge/maxUnavailable control rollout. Default replaces pods gradually
- new pods ready before old terminated if maxUnavailable 0. Zero downtime if readiness probe correct and preStop drain configured.
Follow-up probe
Blue-green on K8s?
8How inject DB password securely?
IntermediateModel answer
Kubernetes Secret with secretKeyRef env var or volume mount.
Sealed Secrets or External Secrets Operator for gitops.
Never in ConfigMap or image.
Rotate via secret update + rolling restart.
Follow-up probe
Spring binds SPRING_DATASOURCE_PASSWORD?
9Resource requests vs limits?
IntermediateModel answer
- Requests: guaranteed resources for scheduling
- pod placed on node with capacity. Limits: maximum allowed
- cgroup enforced. Java: set memory request=limit for predictable JVM. CPU limit throttles JVM GC threads.
Follow-up probe
QoS classes?
10Why Java pod OOMKilled?
IntermediateModel answer
- Container memory limit exceeded
- heap + metaspace + native + direct buffers. JVM MaxRAMPercentage too high for limit. Increase limit or lower percentage. ExitOnOutOfMemoryError for clean restart.
Follow-up probe
JVM sees limit how?
Advanced
11Design payment service on K8s.
AdvancedModel answer
Deployment 3 replicas, anti-affinity, memory 512Mi limit MaxRAMPercentage 75, actuator probes, Service ClusterIP, Ingress external, ConfigMap for kafka URL, Secret for DB password, HPA CPU 70%, PDB minAvailable 2, NetworkPolicy to postgres/kafka only, graceful shutdown 30s.
Follow-up probe
PodDisruptionBudget?
12K8s service discovery for Feign?
AdvancedModel answer
Feign URL http://payment-service (short DNS within namespace).
local.
Spring Cloud K8s discovery optional.
No Eureka needed on K8s native.
Follow-up probe
Cross-namespace?
13GitOps vs kubectl apply?
AdvancedModel answer
- GitOps (ArgoCD/Flux): git is source of truth, cluster reconciles to manifest
- audit, rollback, PR review. kubectl apply from laptop
- no audit, drift. Production Java deploys via GitOps pipeline promoting image digest.
Follow-up probe
Helm vs raw YAML?
14When NOT use Kubernetes?
AdvancedModel answer
- Small team single service, no ops capacity, simple VPS sufficient. K8s complexity overhead
- need 10+ services or multi-team to justify. Managed platforms (ECS, Cloud Run) middle ground.
Follow-up probe
Managed K8s?
15HPA scaling Java services?
AdvancedModel answer
- HorizontalPodAutoscaler on Deployment
- scale replicas on CPU, memory, or custom metrics (Kafka lag). Each new pod = new JVM warmup
- consider minReplicas for baseline. Memory HPA tricky with JVM
- prefer CPU or custom.
Follow-up probe
Scale-to-zero Java?
Hands-on exercise
Lab: Kubernetes YAML concepts:
- Run playground — review probe and memory checklist output.
- Sketch Deployment with replicas=3 and payment-service labels.
- Write readinessProbe path for Spring actuator.
- Map ConfigMap key to SPRING_KAFKA_BOOTSTRAP_SERVERS env.
- Bonus: explain rolling update with maxUnavailable=0.
JavaKubernetes for Java
Press Run to execute. Real javac + JVM via remote API. Falls back to simulator on network failure.Architecture trade-offs
- Deployment vs StatefulSet: Deployment wins stateless Java APIs; StatefulSet wins embedded identity (Kafka broker).
- ClusterIP vs Ingress: ClusterIP wins internal; Ingress wins HTTP routing and TLS termination.
- ConfigMap mount vs env: Mount wins large yaml; env wins simple key-value.
- Eureka vs K8s DNS: K8s DNS wins on cluster; Eureka wins multi-cluster legacy.
Summary
Kubernetes runs Java microservices at fleet scale — Deployments for rollouts, Services for discovery, ConfigMaps and Secrets for externalized config. Wire Spring Boot actuator probes and graceful shutdown before claiming production-ready. Next: OpenTelemetry for observing requests across these pods.
Key takeaways
- Pod = run unit; Deployment = desired replicas + rolling updates.
- Service = stable DNS load balancer to ready pods.
- ConfigMap for config; Secret for credentials — never in image.
- Spring actuator liveness/readiness probes — gate traffic correctly.
- Memory limit + MaxRAMPercentage — coordinate JVM and K8s resources.