This comprehensive guide covers Kubernetes deployment configurations specifically optimized for Java applications, including best practices, resource management, and production-ready configurations.
Basic Java Application Deployment
1. Simple Spring Boot Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-boot-app
namespace: java-apps
labels:
app: spring-boot-app
version: v1
component: backend
spec:
replicas: 3
selector:
matchLabels:
app: spring-boot-app
template:
metadata:
labels:
app: spring-boot-app
version: v1
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/actuator/prometheus"
spec:
containers:
- name: spring-boot-app
image: my-registry/java-app:1.0.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: http
protocol: TCP
env:
- name: SPRING_PROFILES_ACTIVE
value: "production"
- name: JAVA_OPTS
value: "-Xmx512m -Xms256m -XX:+UseG1GC"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 90
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3
startupProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 30
volumeMounts:
- name: tmp-volume
mountPath: /tmp
volumes:
- name: tmp-volume
emptyDir: {}
terminationGracePeriodSeconds: 60
---
apiVersion: v1
kind: Service
metadata:
name: spring-boot-service
namespace: java-apps
labels:
app: spring-boot-app
spec:
selector:
app: spring-boot-app
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
type: ClusterIP
Advanced Java-Specific Configurations
2. Microservices Deployment with Multiple JVM Options
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
namespace: microservices
spec:
replicas: 2
selector:
matchLabels:
app: user-service
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: user-service
tier: backend
annotations:
sidecar.istio.io/inject: "true"
spec:
containers:
- name: user-service
image: my-registry/user-service:2.1.0
ports:
- containerPort: 8080
env:
- name: JAVA_TOOL_OPTIONS
value: >-
-Xmx1024m
-Xms512m
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:+UnlockExperimentalVMOptions
-XX:+UseContainerSupport
-Djava.security.egd=file:/dev/./urandom
-Dspring.profiles.active=kubernetes
- name: SPRING_APPLICATION_NAME
value: "user-service"
- name: MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE
value: "health,info,metrics,prometheus"
- name: LOGGING_LEVEL_COM_EXAMPLE
value: "INFO"
resources:
requests:
memory: "768Mi"
cpu: "300m"
ephemeral-storage: "1Gi"
limits:
memory: "1536Mi"
cpu: "800m"
ephemeral-storage: "2Gi"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
httpHeaders:
- name: Custom-Header
value: "Liveness-Check"
initialDelaySeconds: 120
periodSeconds: 30
timeoutSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 45
periodSeconds: 20
timeoutSeconds: 5
startupProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 30
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- |
curl -X POST http://localhost:8080/actuator/shutdown || true
sleep 30
volumeMounts:
- name: config-volume
mountPath: /app/config
- name: logs-volume
mountPath: /app/logs
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
volumes:
- name: config-volume
configMap:
name: user-service-config
- name: logs-volume
emptyDir: {}
imagePullSecrets:
- name: registry-credentials
nodeSelector:
node-type: application
tolerations:
- key: "app"
operator: "Equal"
value: "java"
effect: "NoSchedule"
3. Stateful Java Application (e.g., with Database)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: order-processing-service
namespace: java-apps
spec:
serviceName: "order-service"
replicas: 2
selector:
matchLabels:
app: order-processing-service
template:
metadata:
labels:
app: order-processing-service
version: v2
spec:
containers:
- name: order-service
image: my-registry/order-service:3.0.0
ports:
- containerPort: 8080
env:
- name: JAVA_OPTS
value: "-Xmx2g -Xms1g -XX:+UseG1GC -XX:MaxRAMPercentage=75.0"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
resources:
requests:
memory: "2Gi"
cpu: "500m"
limits:
memory: "3Gi"
cpu: "1000m"
volumeMounts:
- name: data-volume
mountPath: /app/data
- name: temp-volume
mountPath: /tmp
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: order-data-pvc
- name: temp-volume
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: order-data-pvc
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "fast-ssd"
resources:
requests:
storage: 10Gi
Complete Microservices Stack
4. Multi-Service Deployment with ConfigMaps and Secrets
--- # ConfigMap for application configuration apiVersion: v1 kind: ConfigMap metadata: name: java-app-config namespace: java-apps data: application.yml: | server: port: 8080 shutdown: graceful spring: datasource: url: jdbc:postgresql://postgres-service:5432/mydb jpa: hibernate: ddl-auto: validate show-sql: false redis: host: redis-service port: 6379 logging: level: com.example: INFO org.hibernate.SQL: WARN management: endpoints: web: exposure: include: health,info,metrics endpoint: health: show-details: always probes: enabled: true --- # Secret for sensitive data apiVersion: v1 kind: Secret metadata: name: app-secrets namespace: java-apps type: Opaque data: database-password: cG9zdGdyZXNfcGFzc3dvcmQ= # base64 encoded api-key: eW91cl9hcGlfa2V5 # base64 encoded --- # Deployment using ConfigMap and Secret apiVersion: apps/v1 kind: Deployment metadata: name: api-gateway namespace: java-apps spec: replicas: 2 selector: matchLabels: app: api-gateway template: metadata: labels: app: api-gateway version: v1.2.0 spec: containers: - name: api-gateway image: my-registry/api-gateway:1.2.0 ports: - containerPort: 8080 env: - name: JAVA_OPTS value: "-Xmx1g -Xms512m -XX:+UseG1GC -Dspring.config.location=file:/app/config/application.yml" - name: DB_PASSWORD valueFrom: secretKeyRef: name: app-secrets key: database-password - name: API_KEY valueFrom: secretKeyRef: name: app-secrets key: api-key volumeMounts: - name: config-volume mountPath: /app/config readOnly: true resources: requests: memory: "768Mi" cpu: "300m" limits: memory: "1536Mi" cpu: "800m" livenessProbe: httpGet: path: /actuator/health/liveness port: 8080 initialDelaySeconds: 60 periodSeconds: 30 readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 30 periodSeconds: 15 volumes: - name: config-volume configMap: name: java-app-config items: - key: application.yml path: application.yml --- # Service for API Gateway apiVersion: v1 kind: Service metadata: name: api-gateway-service namespace: java-apps annotations: prometheus.io/scrape: "true" prometheus.io/port: "8080" spec: selector: app: api-gateway ports: - name: http port: 80 targetPort: 8080 protocol: TCP type: LoadBalancer externalTrafficPolicy: Local
Horizontal Pod Autoscaling
5. Auto-scaling Configuration for Java Apps
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: java-app-hpa namespace: java-apps spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: spring-boot-app minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80 - type: Pods pods: metric: name: http_requests_per_second target: type: AverageValue averageValue: "500" behavior: scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 50 periodSeconds: 60 - type: Pods value: 2 periodSeconds: 60 selectPolicy: Min scaleUp: stabilizationWindowSeconds: 60 policies: - type: Percent value: 100 periodSeconds: 30 - type: Pods value: 4 periodSeconds: 30 selectPolicy: Max
Job and CronJob for Batch Processing
6. Spring Batch Job Deployment
--- # CronJob for scheduled batch processing apiVersion: batch/v1 kind: CronJob metadata: name: data-processor-job namespace: java-apps spec: schedule: "0 2 * * *" # Run daily at 2 AM concurrencyPolicy: Forbid startingDeadlineSeconds: 600 jobTemplate: spec: template: spec: containers: - name: batch-processor image: my-registry/batch-processor:1.0.0 env: - name: JAVA_OPTS value: "-Xmx2g -Xms1g -XX:+UseG1GC -Dspring.batch.job.names=dataMigrationJob" - name: SPRING_PROFILES_ACTIVE value: "batch,production" resources: requests: memory: "2Gi" cpu: "500m" limits: memory: "3Gi" cpu: "1000m" volumeMounts: - name: input-data mountPath: /app/input - name: output-data mountPath: /app/output volumes: - name: input-data persistentVolumeClaim: claimName: input-data-pvc - name: output-data persistentVolumeClaim: claimName: output-data-pvc restartPolicy: OnFailure activeDeadlineSeconds: 3600 # 1 hour timeout --- # One-time Job apiVersion: batch/v1 kind: Job metadata: name: database-migration-job namespace: java-apps spec: backoffLimit: 3 activeDeadlineSeconds: 1800 template: spec: containers: - name: migration-job image: my-registry/migration-service:2.1.0 env: - name: JAVA_OPTS value: "-Xmx1g -Xms512m -Dspring.datasource.url=jdbc:postgresql://db-host:5432/mydb" command: ["java"] args: - "-jar" - "/app/app.jar" - "--spring.batch.job.name=dataMigrationJob" resources: requests: memory: "1Gi" cpu: "500m" limits: memory: "2Gi" cpu: "1000m" restartPolicy: Never
Advanced Features and Best Practices
7. Pod Disruption Budget
apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: java-app-pdb namespace: java-apps spec: minAvailable: 2 selector: matchLabels: app: spring-boot-app
8. Network Policies
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: java-app-network-policy namespace: java-apps spec: podSelector: matchLabels: app: spring-boot-app policyTypes: - Ingress - Egress ingress: - from: - namespaceSelector: matchLabels: name: frontend-namespace ports: - protocol: TCP port: 8080 egress: - to: - namespaceSelector: matchLabels: name: database-namespace ports: - protocol: TCP port: 5432 - to: - namespaceSelector: matchLabels: name: redis-namespace ports: - protocol: TCP port: 6379
Monitoring and Observability
9. ServiceMonitor for Prometheus
apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: spring-boot-monitor namespace: java-apps labels: app: spring-boot-app release: prometheus spec: selector: matchLabels: app: spring-boot-app endpoints: - port: http path: /actuator/prometheus interval: 30s scrapeTimeout: 10s relabelings: - sourceLabels: [__meta_kubernetes_pod_name] targetLabel: pod_name
Best Practices Summary
Resource Management
# Good practices for Java apps resources: requests: memory: "512Mi" # Start with reasonable requests cpu: "250m" limits: memory: "1Gi" # Set limits to prevent runaway processes cpu: "500m"
JVM Optimization
env: - name: JAVA_OPTS value: >- -Xmx512m -Xms256m -XX:+UseG1GC -XX:MaxRAMPercentage=75.0 -XX:+UnlockExperimentalVMOptions -XX:+UseContainerSupport -Djava.security.egd=file:/dev/./urandom
Health Checks
livenessProbe: httpGet: path: /actuator/health/liveness port: 8080 initialDelaySeconds: 90 # Give JVM time to start periodSeconds: 30 readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 30 periodSeconds: 15 startupProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 10 periodSeconds: 10 failureThreshold: 30 # Allow up to 5 minutes for startup
Graceful Shutdown
terminationGracePeriodSeconds: 60 lifecycle: preStop: exec: command: - /bin/sh - -c - | # Send graceful shutdown signal to Spring Boot curl -X POST http://localhost:8080/actuator/shutdown || true sleep 30
These Kubernetes deployment configurations provide a solid foundation for running Java applications in production environments, with proper resource management, health monitoring, and scalability considerations specific to JVM-based applications.