Kubernetes for Java Developers

Deploy, Scale, and Manage Your Java Applications in the Cloud

Why Kubernetes?

Imagine you're running a pizza restaurant. On a quiet Monday, one chef is enough. But on Super Bowl Sunday, you need 10 chefs, more ovens, and extra delivery drivers. What if your restaurant could automatically scale up and down based on demand?

That's exactly what Kubernetes (K8s) does for your applications. It automatically manages your containers - starting new ones when traffic increases, restarting crashed ones, and distributing load across servers.

Auto-Scaling

Automatically add more containers when traffic spikes and reduce when it's quiet. Pay only for what you use.

Self-Healing

If a container crashes, Kubernetes automatically restarts it. Your app stays running 24/7.

Zero-Downtime Deployments

Roll out updates gradually. If something goes wrong, automatically roll back to the previous version.

Industry Standard

Used by Google, Amazon, Netflix, and most modern companies. Essential skill for cloud-native development.

Prerequisites: Docker First

Before Kubernetes, you need to understand Docker. Your Java app must be containerized first.

Quick Docker Recap

# Dockerfile for Spring Boot app
FROM eclipse-temurin:17-jdk-alpine

WORKDIR /app

# Copy the JAR file
COPY target/myapp-0.0.1-SNAPSHOT.jar app.jar

# Expose port
EXPOSE 8080

# Run the application
ENTRYPOINT ["java", "-jar", "app.jar"]
# Build and run locally
docker build -t myapp:1.0 .
docker run -p 8080:8080 myapp:1.0

# Push to registry (Docker Hub, ECR, GCR, etc.)
docker tag myapp:1.0 username/myapp:1.0
docker push username/myapp:1.0

Kubernetes Core Concepts

Think of Kubernetes like managing a fleet of delivery trucks:

Pod

The smallest unit - like a single truck. Contains one or more containers that work together.

Deployment

Manages a fleet of identical pods. "I want 3 trucks running at all times."

Service

The dispatch center - routes requests to available pods. Provides a stable address.

ConfigMap & Secret

Configuration storage - like driver instructions and security codes.

Visual Overview

                    Internet
                        |
                    Ingress (optional - routing rules)
                        |
                    Service (load balancer)
                   /    |    \
                Pod   Pod   Pod  (your app instances)
                 |     |     |
            Container Container Container
            (Java app) (Java app) (Java app)

Your First Kubernetes Deployment

Step 1: Create a Deployment

A Deployment tells Kubernetes how to run your app:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
  labels:
    app: myapp
spec:
  replicas: 3  # Run 3 instances of your app
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: username/myapp:1.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 30

Understanding the YAML

  • replicas: 3 - Keep 3 pods running at all times
  • image - Your Docker image from a registry
  • resources - Memory and CPU limits
  • readinessProbe - Is the app ready to receive traffic?
  • livenessProbe - Is the app still alive? Restart if not.

Step 2: Create a Service

A Service provides a stable way to access your pods:

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:
    app: myapp  # Match pods with this label
  ports:
  - protocol: TCP
    port: 80        # External port
    targetPort: 8080  # Container port
  type: LoadBalancer  # Creates external IP (cloud providers)

Step 3: Apply to Kubernetes

# Apply configurations
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

# Check status
kubectl get deployments
kubectl get pods
kubectl get services

# View pod logs
kubectl logs -f myapp-deployment-abc123

# Describe for debugging
kubectl describe pod myapp-deployment-abc123

Configuration: ConfigMaps and Secrets

Don't hardcode configuration in your Docker image. Use ConfigMaps for regular config and Secrets for sensitive data.

ConfigMap for Application Properties

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
data:
  SPRING_PROFILES_ACTIVE: "production"
  SERVER_PORT: "8080"
  LOG_LEVEL: "INFO"
  application.properties: |
    spring.datasource.url=jdbc:postgresql://db-service:5432/mydb
    spring.jpa.hibernate.ddl-auto=validate
    server.tomcat.max-threads=200

Secrets for Sensitive Data

# Create secret from command line (values are base64 encoded)
kubectl create secret generic myapp-secrets \
  --from-literal=DB_USERNAME=admin \
  --from-literal=DB_PASSWORD=supersecret123

# Or from YAML (values must be base64 encoded)
# echo -n 'admin' | base64 → YWRtaW4=
apiVersion: v1
kind: Secret
metadata:
  name: myapp-secrets
type: Opaque
data:
  DB_USERNAME: YWRtaW4=
  DB_PASSWORD: c3VwZXJzZWNyZXQxMjM=

Using Config in Deployment

# Updated deployment.yaml
spec:
  containers:
  - name: myapp
    image: username/myapp:1.0
    ports:
    - containerPort: 8080

    # Environment variables from ConfigMap
    envFrom:
    - configMapRef:
        name: myapp-config

    # Environment variables from Secret
    env:
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: myapp-secrets
          key: DB_USERNAME
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: myapp-secrets
          key: DB_PASSWORD

    # Mount config file
    volumeMounts:
    - name: config-volume
      mountPath: /app/config

  volumes:
  - name: config-volume
    configMap:
      name: myapp-config
      items:
      - key: application.properties
        path: application.properties

Scaling Your Application

Manual Scaling

# Scale to 5 replicas
kubectl scale deployment myapp-deployment --replicas=5

# Check the scaling
kubectl get pods -w  # Watch pods come up

Auto-Scaling (HPA)

# hpa.yaml - Horizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp-deployment
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70  # Scale when CPU > 70%
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80  # Scale when Memory > 80%
# Apply and monitor
kubectl apply -f hpa.yaml
kubectl get hpa -w

# Output:
# NAME        REFERENCE                 TARGETS   MINPODS   MAXPODS   REPLICAS
# myapp-hpa   Deployment/myapp-deploy   45%/70%   2         10        3

Zero-Downtime Deployments

Kubernetes can update your app without any downtime using rolling updates.

# Update the image to a new version
kubectl set image deployment/myapp-deployment myapp=username/myapp:2.0

# Or edit the deployment
kubectl edit deployment myapp-deployment

# Watch the rollout
kubectl rollout status deployment/myapp-deployment

# View rollout history
kubectl rollout history deployment/myapp-deployment

# Rollback if something goes wrong!
kubectl rollout undo deployment/myapp-deployment
kubectl rollout undo deployment/myapp-deployment --to-revision=2

Deployment Strategy

# In deployment.yaml
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # Max pods over desired count during update
      maxUnavailable: 0  # Never have less than desired count

Spring Boot Kubernetes Best Practices

1. Enable Actuator Health Endpoints

# pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

# application.properties
management.endpoints.web.exposure.include=health,info,prometheus
management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true

2. Graceful Shutdown

# application.properties
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30s

3. Optimized Dockerfile

# Use layered JAR for faster builds
FROM eclipse-temurin:17-jdk-alpine as builder
WORKDIR /app
COPY target/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract

FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./

# Run as non-root user
RUN addgroup -S spring && adduser -S spring -G spring
USER spring

EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

4. Resource Limits

# Set JVM memory based on container limits
ENTRYPOINT ["java", \
  "-XX:+UseContainerSupport", \
  "-XX:MaxRAMPercentage=75.0", \
  "-jar", "app.jar"]

Essential kubectl Commands

# Cluster info
kubectl cluster-info
kubectl get nodes

# Viewing resources
kubectl get pods
kubectl get pods -o wide  # More details
kubectl get all           # All resource types
kubectl get pods -n my-namespace  # Specific namespace

# Debugging
kubectl logs pod-name
kubectl logs -f pod-name  # Follow logs
kubectl logs pod-name -c container-name  # Specific container
kubectl describe pod pod-name
kubectl exec -it pod-name -- /bin/sh  # Shell into container

# Port forwarding (for local testing)
kubectl port-forward pod-name 8080:8080
kubectl port-forward service/myapp-service 8080:80

# Resource management
kubectl apply -f file.yaml
kubectl delete -f file.yaml
kubectl delete pod pod-name

# Namespaces
kubectl create namespace production
kubectl get pods -n production
kubectl config set-context --current --namespace=production

Local Kubernetes Development

Local K8s Options

  • Minikube - Full K8s cluster in a VM (most popular)
  • Docker Desktop - Built-in K8s (easiest for Mac/Windows)
  • Kind - Kubernetes in Docker (lightweight)
  • K3s - Lightweight K8s (great for learning)
# Minikube setup
minikube start
minikube dashboard  # Opens web UI

# Docker Desktop
# Enable Kubernetes in Docker Desktop settings

# Use local images with Minikube
eval $(minikube docker-env)
docker build -t myapp:1.0 .
# Now minikube can use the local image

Master Cloud-Native Java Development

Learn Kubernetes, Docker, and cloud deployment with hands-on projects and expert mentorship.