Kubernetes provides powerful abstractions to deploy and manage containerized applications at scale. Understanding these building blocks is essential for effectively running applications in a Kubernetes cluster.
Goal: learn how to deploy a Java Spring (Spring Boot) microservice on Kubernetes, step by step. This post is organized in three blocks: Pods, Deployments & ReplicaSets, and Services & Networking.
1. Prerequisites & Sample Spring Boot App
- JDK 17+ and Maven or Gradle
- Docker or another OCI image builder
- A Kubernetes cluster (Kind/Minikube/K3d/AKS/EKS/GKE) and
kubectl
Minimal REST app (Maven)
// src/main/java/com/example/demo/DemoApplication.java
@SpringBootApplication
@RestController
public class DemoApplication {
@GetMapping("/healthz") public String health() { return "ok"; }
@GetMapping("/hello") public Map<String,String> hello() {
return Map.of("message", "Hello, Kubernetes!");
}
public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }
}
Dockerfile (multi-stage, small image):
# Dockerfile
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn -q -e -DskipTests dependency:go-offline
COPY src ./src
RUN mvn -q -DskipTests package
FROM eclipse-temurin:17-jre
ENV JAVA\_OPTS="-XX\:MaxRAMPercentage=75 -XX:+UseG1GC"
WORKDIR /app
COPY --from=build /app/target/\*SNAPSHOT.jar app.jar
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=2s --retries=3 CMD curl -fs [http://localhost:8080/healthz](http://localhost:8080/healthz) || exit 1
ENTRYPOINT \["sh", "-c", "java \$JAVA\_OPTS -jar app.jar"]
Build & (optionally) push:
docker build -t demo-svc:1.0.0 .
# If using a remote registry:
# docker tag demo-svc:1.0.0 <REGISTRY>/demo-svc:1.0.0
# docker push <REGISTRY>/demo-svc:1.0.0
2. Creating & Managing Pods
A Pod is the smallest deployable unit in Kubernetes. It usually runs one container (your app), optionally sidecars (like an agent).
Pod manifest (for learning only; in production use Deployments):
apiVersion: v1
kind: Pod
metadata:
name: demo-pod
labels:
app: demo
spec:
containers:
- name: app
image: demo-svc:1.0.0 # or <REGISTRY>/demo-svc:1.0.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
readinessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 15
periodSeconds: 20
Create & inspect:
kubectl apply -f pod.yaml
kubectl get pods -o wide
kubectl logs -f demo-pod
kubectl exec -it demo-pod -- curl -s http://localhost:8080/hello
Why not Pods alone? Pods are ephemeral and not self-healing. For rollouts and replicas, use Deployments.
3. Deployments & ReplicaSets
A Deployment manages desired state, rollouts, and rollback via underlying ReplicaSets.
ConfigMap & Secret (externalize config):
apiVersion: v1
kind: ConfigMap
metadata: { name: demo-config }
data:
SPRING_PROFILES_ACTIVE: "prod"
WELCOME_MESSAGE: "Hello from ConfigMap"
---
apiVersion: v1
kind: Secret
metadata: { name: demo-secret }
type: Opaque
stringData:
DB\_USERNAME: "appuser"
DB\_PASSWORD: "s3cr3t"
Deployment (3 replicas, rolling updates, resource limits, probes):
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
labels: { app: demo }
spec:
replicas: 3
revisionHistoryLimit: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels: { app: demo }
template:
metadata:
labels: { app: demo }
spec:
serviceAccountName: demo-sa
containers:
- name: app
image: demo-svc:1.0.0 # bump version to roll out
ports: [{ containerPort: 8080 }]
envFrom:
- configMapRef: { name: demo-config }
- secretRef: { name: demo-secret }
readinessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet: { path: /healthz, port: 8080 }
initialDelaySeconds: 15
periodSeconds: 20
resources:
requests: { cpu: "200m", memory: "256Mi" }
limits: { cpu: "500m", memory: "512Mi" }
# Optional tolerations/affinity can be added here
Basic rollout operations:
# Create / update
kubectl apply -f config.yaml -f secret.yaml -f deployment.yaml
# Watch status
kubectl rollout status deployment/demo-deployment
# History & rollback
kubectl rollout history deployment/demo-deployment
kubectl rollout undo deployment/demo-deployment --to-revision=1
4. Services & Networking
A Service provides a stable virtual IP and DNS for a set of Pods (selected by labels). Common types:
- ClusterIP (default): internal-only
- NodePort: exposes a port on each node (dev/test)
- LoadBalancer: requests a cloud LB (prod on managed clouds)
ClusterIP Service for the app:
apiVersion: v1
kind: Service
metadata:
name: demo-svc
spec:
type: ClusterIP
selector: { app: demo }
ports:
- name: http
port: 80
targetPort: 8080
Ingress (optional; requires an Ingress Controller like NGINX):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- host: demo.local # map in DNS or /etc/hosts for local
http:
paths:
- path: /(.*)
pathType: Prefix
backend:
service:
name: demo-svc
port: { number: 80 }
Local testing without Ingress:
# Port-forward to your laptop
kubectl port-forward svc/demo-svc 8080:80
curl -s http://localhost:8080/hello
5. Probes, Resources & Autoscaling
- Liveness restarts crashed or stuck containers.
- Readiness gates traffic until the app is ready.
- Resource requests/limits enable fair scheduling and stability.
- Horizontal Pod Autoscaler (HPA) adjusts replicas based on metrics.
HPA (CPU-based autoscaling 1–5 replicas):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: demo-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: demo-deployment
minReplicas: 1
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
6. Apply & Verify
Put the above manifests in one file (separated by ---
) or multiple files, then:
# Create a dedicated namespace (recommended)
kubectl create namespace demo
kubectl config set-context --current --namespace=demo
# (Optional) service account and simple RBAC
kubectl apply -f - <\
7. Troubleshooting Cheatsheet
- Image pull issues: check image name, registry auth (
imagePullSecrets
), andkubectl describe pod
events. - CrashLoopBackOff: view
kubectl logs POD -p
(previous), inspect probes and env vars. - Readiness failing: hit the pod directly (
kubectl exec
+curl
), confirm port/path. - Service not routing: labels must match
spec.selector
; verify withkubectl get endpoints demo-svc
. - Ingress 404: ensure controller is installed and rule host/path matches, check
kubectl logs
of the ingress controller. - Resources: too low memory causes OOMKill; tune
requests/limits
and JVM heap (JAVA_OPTS
).
Takeaways: Start with Pods to understand the basics, then use Deployments for rollouts and resilience, and wire traffic through Services (and optional Ingress). Add probes, resource limits, and HPA for production-grade behavior.
8. Architecture Diagram
The following PlantUML diagram summarizes the full deployment workflow of our Java Spring Boot microservice on Kubernetes. It shows how developers push code, CI/CD builds and publishes container images, and how Kubernetes orchestrates Pods, Deployments, Services, and Ingress with support from ConfigMaps, Secrets, Autoscaling, Observability, and Logging.
0 Comments