Spring Boot: Triển Khai với Docker & Kubernetes

Spring Boot application của bạn chạy ngon lành trên máy. Giờ ship lên production. Nghĩa là containers, orchestration, health checks, scaling, và configuration management — mà không biến deployment thành ngôi nhà xếp bằng bài.
Bài này đưa bạn từ một Spring Boot app hoạt động đến container production-ready chạy trên Kubernetes. Chúng ta sẽ cover Docker best practices cho Java/Spring Boot, rồi deploy lên Kubernetes với proper health probes, configuration, và auto-scaling.
Những Gì Bạn Sẽ Học
✅ Multi-stage Dockerfiles — images nhỏ, an toàn cho Spring Boot
✅ Layered JARs — builds nhanh hơn với Spring Boot layer extraction
✅ Docker Compose — chạy app cùng PostgreSQL và Redis locally
✅ Kubernetes Deployments — declarative manifests cho application
✅ Services và Ingress — expose app ra bên ngoài
✅ ConfigMaps và Secrets — externalize configuration an toàn
✅ Health probes — liveness và readiness với Spring Boot Actuator
✅ Resource limits — ngăn một pod chiếm hết resources của cluster
✅ Horizontal Pod Autoscaling — scale dựa trên CPU và memory
✅ Local development — minikube và kind để test Kubernetes locally
Prerequisites
- Spring Boot cơ bản (Getting Started)
- Database integration (JPA & PostgreSQL)
- Docker cơ bản (Docker Fundamentals) hoặc kiến thức Docker chung
- Java 17+ và Docker đã cài
kubectlđã cài (cho phần Kubernetes)
1. Tại Sao Containerize Spring Boot?
Spring Boot applications là self-contained — chúng embed Tomcat và tạo ra single JAR. Nghe có vẻ deployment-friendly cho đến khi bạn nhận ra:
- "Works on my machine" — JDK version, environment variables, và OS trên máy bạn khác production
- Dependency conflicts — nhiều apps trên cùng server tranh giành ports, libraries, system resources
- Scaling — thêm instances nghĩa là provision servers, configure load balancers, quản lý state
Containers giải quyết cả ba. Đóng gói app với JDK chính xác, dependencies, và configuration vào image. Chạy image đó ở bất kỳ đâu — laptop, staging, production. Scale bằng cách chạy thêm bản sao.
| Không Có Containers | Có Containers |
|---|---|
| Cài JDK trên mỗi server | JDK đóng gói trong image |
| Cấu hình env vars thủ công | Env vars khai báo trong manifests |
| Port conflicts giữa apps | Mỗi container có networking riêng |
| Snowflake servers | Images giống nhau ở mọi nơi |
| Scale bằng mua server lớn hơn | Scale bằng chạy thêm containers |
2. Multi-Stage Dockerfile
Dockerfile đơn giản copy source code và build trong image. Kết quả: image 600MB+ với build tools, source code, và JDK compiler — không cái nào cần ở runtime.
Multi-stage builds fix điều này. Build ở stage một, copy chỉ JAR sang stage cuối:
# Stage 1: Build
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app
# Copy build files trước (cache dependencies)
COPY pom.xml mvnw ./
COPY .mvn .mvn
RUN ./mvnw dependency:go-offline -B
# Copy source và build
COPY src src
RUN ./mvnw package -DskipTests -B
# Stage 2: Runtime
FROM eclipse-temurin:21-jre AS runtime
WORKDIR /app
# Tạo non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser
# Copy chỉ JAR
COPY --from=builder /app/target/*.jar app.jar
# Set ownership
RUN chown -R appuser:appuser /app
USER appuser
# JVM flags cho containers
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]Được Gì
| Khía Cạnh | Dockerfile Đơn Giản | Multi-Stage |
|---|---|---|
| Image size | ~600MB (JDK + build tools + source) | ~280MB (chỉ JRE) |
| Security | Build tools, source code bị lộ | Chỉ runtime artifacts |
| Build cache | Rebuild mọi thứ khi code thay đổi | Dependencies cached riêng |
| User | Chạy root | Chạy non-root user |
Chi Tiết Quan Trọng
eclipse-temurin:21-jre — dùng JRE, không phải JDK, cho runtime stage. Bạn không cần compiler ở production.
-XX:+UseContainerSupport — bảo JVM respect container memory limits. Không có flag này, JVM thấy toàn bộ RAM của host và có thể allocate quá nhiều.
-XX:MaxRAMPercentage=75.0 — dùng tối đa 75% memory limit của container cho heap. Để 25% cho non-heap memory (metaspace, threads, native memory).
Non-root user — chạy root trong container là security risk. Nếu attacker escape container, họ có root trên host.
3. Layered JARs (Spring Boot Optimization)
Spring Boot 2.3+ hỗ trợ layered JARs. Thay vì single fat JAR, application được chia thành layers mà Docker cache độc lập:
Layer 1: dependencies (hiếm khi thay đổi — cached)
Layer 2: spring-boot-loader (hiếm khi thay đổi — cached)
Layer 3: snapshot-dependencies (đôi khi thay đổi)
Layer 4: application (thay đổi mỗi build)Khi bạn đổi code, chỉ Layer 4 rebuild. Layers 1-3 lấy từ cache. Build nhanh hơn đáng kể.
Bật Layered JARs
Thêm vào pom.xml:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
</plugins>
</build>Layered Dockerfile
# Stage 1: Build
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app
COPY pom.xml mvnw ./
COPY .mvn .mvn
RUN ./mvnw dependency:go-offline -B
COPY src src
RUN ./mvnw package -DskipTests -B
# Extract layers
RUN java -Djarmode=layertools -jar target/*.jar extract --destination extracted
# Stage 2: Runtime
FROM eclipse-temurin:21-jre AS runtime
WORKDIR /app
RUN groupadd -r appuser && useradd -r -g appuser appuser
# Copy layers theo thứ tự (ít thay đổi nhất trước để tận dụng cache)
COPY --from=builder /app/extracted/dependencies/ ./
COPY --from=builder /app/extracted/spring-boot-loader/ ./
COPY --from=builder /app/extracted/snapshot-dependencies/ ./
COPY --from=builder /app/extracted/application/ ./
RUN chown -R appuser:appuser /app
USER appuser
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS org.springframework.boot.loader.launch.JarLauncher"]Lưu ý: Với layered extraction, entry point dùng JarLauncher thay vì -jar app.jar.
So Sánh Build Time
Build đầu tiên: ~90 giây
Code thay đổi (không layers): ~90 giây (rebuild mọi thứ)
Code thay đổi (có layers): ~15 giây (chỉ application layer rebuild)4. Docker Compose cho Development
Đừng cài PostgreSQL, Redis, và app riêng biệt. Docker Compose chạy mọi thứ cùng nhau:
# docker-compose.yml
version: "3.8"
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
SPRING_PROFILES_ACTIVE: dev
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/taskdb
SPRING_DATASOURCE_USERNAME: postgres
SPRING_DATASOURCE_PASSWORD: password
SPRING_DATA_REDIS_HOST: redis
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: taskdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redisdata:/data
volumes:
pgdata:
redisdata:Sử Dụng
# Start mọi thứ
docker compose up -d
# Xem logs
docker compose logs -f app
# Rebuild sau khi đổi code
docker compose up -d --build app
# Stop mọi thứ
docker compose down
# Stop và xóa volumes (bắt đầu lại)
docker compose down -vDevelopment vs Production Compose
Cho development, thêm hot-reload bằng cách mount source code:
# docker-compose.dev.yml (override)
version: "3.8"
services:
app:
build:
context: .
target: builder # Dừng ở build stage
command: ./mvnw spring-boot:run
volumes:
- ./src:/app/src # Mount source cho hot reload
environment:
SPRING_PROFILES_ACTIVE: dev
SPRING_DEVTOOLS_RESTART_ENABLED: "true"# Development với hot-reload
docker compose -f docker-compose.yml -f docker-compose.dev.yml up5. Build và Push Images
Build Locally
# Build với tag
docker build -t my-app:1.0.0 .
# Build với nhiều tags
docker build -t my-app:1.0.0 -t my-app:latest .
# Build cho platform cụ thể (ví dụ ARM Apple Silicon → x86 cho production)
docker build --platform linux/amd64 -t my-app:1.0.0 .Push lên Container Registry
# Docker Hub
docker tag my-app:1.0.0 username/my-app:1.0.0
docker push username/my-app:1.0.0
# GitHub Container Registry
docker tag my-app:1.0.0 ghcr.io/username/my-app:1.0.0
docker push ghcr.io/username/my-app:1.0.0
# AWS ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com
docker tag my-app:1.0.0 123456789.dkr.ecr.us-east-1.amazonaws.com/my-app:1.0.0
docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/my-app:1.0.0Spring Boot Maven Plugin (Không Cần Dockerfile)
Spring Boot có thể build OCI images trực tiếp không cần Dockerfile dùng Cloud Native Buildpacks:
./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=my-app:1.0.0Tạo image tối ưu, layered tự động. Tốt cho quick builds; dùng custom Dockerfile nếu cần kiểm soát nhiều hơn.
6. Kubernetes Cơ Bản cho Spring Boot
Trước khi deploy, hiểu các Kubernetes objects chính:
| Object | Mục Đích | Liên Quan Tới Spring Boot |
|---|---|---|
| Pod | Đơn vị deploy nhỏ nhất (chạy container) | Một pod = một instance app |
| Deployment | Quản lý pod replicas và rolling updates | Khai báo bao nhiêu instances và cách update |
| Service | Network endpoint ổn định cho pods | Load balance qua các app instances |
| Ingress | HTTP routing từ ngoài cluster | Map domain names tới Service |
| ConfigMap | Configuration không nhạy cảm | Giá trị application.properties |
| Secret | Configuration nhạy cảm | Database passwords, API keys |
| HPA | Horizontal Pod Autoscaler | Scale pods dựa trên CPU/memory |
7. Kubernetes Deployment Manifest
Tạo Deployment chạy Spring Boot application:
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-app
labels:
app: spring-app
spec:
replicas: 3
selector:
matchLabels:
app: spring-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Tối đa 1 pod dư trong lúc update
maxUnavailable: 0 # Tất cả pods cũ vẫn chạy trong lúc update
template:
metadata:
labels:
app: spring-app
spec:
containers:
- name: spring-app
image: ghcr.io/username/spring-app:1.0.0
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: spring-app-config
- secretRef:
name: spring-app-secrets
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 15
periodSeconds: 5
failureThreshold: 3
startupProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 30 # 30 * 5s = 150s max startup timeRolling Update Strategy
maxSurge: 1, maxUnavailable: 0Nghĩa là: khi update, Kubernetes tạo một pod mới trước, đợi cho tới khi ready, rồi mới terminate một pod cũ. Lặp lại cho đến khi tất cả pods được update. Zero downtime.
Resource Requests vs Limits
resources:
requests:
cpu: "250m" # Đảm bảo: 0.25 CPU cores
memory: "512Mi" # Đảm bảo: 512MB RAM
limits:
cpu: "1000m" # Tối đa: 1 CPU core
memory: "1Gi" # Tối đa: 1GB RAM (OOM-killed nếu vượt)- Requests — pod được đảm bảo có. Kubernetes dùng để scheduling.
- Limits — giới hạn tối đa. Vượt CPU → bị throttle. Vượt memory → bị OOM-killed.
Cho Spring Boot, 512Mi-1Gi là typical. JVM cần heap + metaspace + thread stacks + native memory. Monitor usage thực tế trước khi set limits.
8. Health Probes với Spring Boot Actuator
Kubernetes dùng ba loại probes để quản lý pods:
| Probe | Mục Đích | Khi Fail |
|---|---|---|
| Startup | App còn đang start? | Tiếp tục đợi (chưa restart) |
| Liveness | App còn sống? | Restart pod |
| Readiness | App xử lý traffic được? | Rút khỏi load balancer |
Bật Actuator Health Probes
Thêm Actuator dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>Cấu hình trong application.yaml:
management:
endpoints:
web:
exposure:
include: health,info,prometheus
endpoint:
health:
show-details: always
probes:
enabled: true # Bật /actuator/health/liveness và /readiness
health:
livenessState:
enabled: true
readinessState:
enabled: true
db:
enabled: true # Kiểm tra database connectivity
redis:
enabled: true # Kiểm tra Redis connectivityCách Probes Phối Hợp
Phân biệt quan trọng:
- Database down → readiness fail (ngừng gửi traffic), liveness pass (không restart — restart chẳng fix được database outage)
- App deadlocked → liveness fail (restart pod)
- App đang start → startup probe tiếp tục chạy, liveness và readiness đợi
Custom Health Indicators
@Component
public class ExternalServiceHealthIndicator implements HealthIndicator {
private final ExternalServiceClient client;
public ExternalServiceHealthIndicator(ExternalServiceClient client) {
this.client = client;
}
@Override
public Health health() {
try {
client.ping();
return Health.up()
.withDetail("service", "external-api")
.withDetail("status", "reachable")
.build();
} catch (Exception e) {
return Health.down()
.withDetail("service", "external-api")
.withDetail("error", e.getMessage())
.build();
}
}
}9. ConfigMaps và Secrets
Đừng bao giờ hardcode configuration vào Docker image. Dùng ConfigMaps cho giá trị không nhạy cảm và Secrets cho credentials.
ConfigMap
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: spring-app-config
data:
SPRING_PROFILES_ACTIVE: "prod"
SPRING_DATASOURCE_URL: "jdbc:postgresql://postgres-service:5432/taskdb"
SPRING_DATA_REDIS_HOST: "redis-service"
SERVER_PORT: "8080"
MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: "health,info,prometheus"Secret
# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: spring-app-secrets
type: Opaque
data:
SPRING_DATASOURCE_USERNAME: cG9zdGdyZXM= # base64 encoded "postgres"
SPRING_DATASOURCE_PASSWORD: cGFzc3dvcmQ= # base64 encoded "password"
JWT_SECRET: bXktc3VwZXItc2VjcmV0LWtleQ== # base64 encodedQuan trọng: Base64 là encoding, không phải encryption. Cho secrets management thực sự, dùng Sealed Secrets, External Secrets Operator, hoặc secrets manager của cloud provider (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager).
Tạo Secrets Từ Command Line
# An toàn hơn đặt base64 trong YAML
kubectl create secret generic spring-app-secrets \
--from-literal=SPRING_DATASOURCE_USERNAME=postgres \
--from-literal=SPRING_DATASOURCE_PASSWORD=password \
--from-literal=JWT_SECRET=my-super-secret-keySpring Boot Configuration Hierarchy
Spring Boot resolve configuration theo thứ tự (priority cao nhất trước):
- Environment variables (từ ConfigMap/Secret) — priority cao nhất
application-{profile}.yaml— profile-specificapplication.yaml— defaults trong JAR@ConfigurationPropertiesdefaults — priority thấp nhất
Nghĩa là ConfigMap/Secret values override mọi thứ trong application.yaml đóng gói. Bạn ship JAR với sensible defaults, và Kubernetes override những gì cần thay đổi theo environment.
10. Service và Ingress
Service
Service cung cấp cho pods network identity ổn định:
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: spring-app-service
spec:
type: ClusterIP
selector:
app: spring-app
ports:
- port: 80
targetPort: 8080
protocol: TCP| Service Type | Truy Cập Từ | Use Case |
|---|---|---|
| ClusterIP | Chỉ trong cluster | Internal services, microservices |
| NodePort | Ngoài qua node IP:port | Development, testing |
| LoadBalancer | Ngoài qua cloud LB | Production trên cloud providers |
Cho production, dùng ClusterIP + Ingress (linh hoạt nhất) hoặc LoadBalancer (đơn giản nhất trên cloud).
Ingress
Route external HTTP traffic tới Service:
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: spring-app-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls-cert
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: spring-app-service
port:
number: 80Map https://api.example.com tới Spring Boot application với automatic TLS qua cert-manager.
11. Horizontal Pod Autoscaling
Scale Spring Boot application dựa trên CPU và memory usage:
# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: spring-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: spring-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # Scale up khi CPU > 70%
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80 # Scale up khi memory > 80%
behavior:
scaleUp:
stabilizationWindowSeconds: 60 # Đợi 60s trước khi scale up lần nữa
policies:
- type: Pods
value: 2
periodSeconds: 60 # Thêm tối đa 2 pods mỗi phút
scaleDown:
stabilizationWindowSeconds: 300 # Đợi 5 phút trước khi scale down
policies:
- type: Pods
value: 1
periodSeconds: 120 # Bớt tối đa 1 pod mỗi 2 phútCách Hoạt Động
Scale-down thận trọng (5 phút stabilization, 1 pod mỗi lần) vì scale down quá nhanh có thể gây cascading failures. Scale-up nhanh hơn vì latency spikes ảnh hưởng users ngay lập tức.
Prerequisites
HPA yêu cầu Metrics Server:
# Cài Metrics Server (nếu chưa có)
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# Kiểm tra
kubectl top pods12. Database trên Kubernetes
Cho production databases, dùng managed service (AWS RDS, Cloud SQL, Azure Database). Chạy PostgreSQL trên Kubernetes có thể nhưng thêm operational complexity.
Cho development và testing, đây là PostgreSQL deployment:
# k8s/postgres.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16-alpine
ports:
- containerPort: 5432
envFrom:
- secretRef:
name: postgres-secrets
volumeMounts:
- name: pgdata
mountPath: /var/lib/postgresql/data
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
volumes:
- name: pgdata
persistentVolumeClaim:
claimName: postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
name: postgres-service
spec:
type: ClusterIP
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Secret
metadata:
name: postgres-secrets
type: Opaque
data:
POSTGRES_DB: dGFza2Ri # taskdb
POSTGRES_USER: cG9zdGdyZXM= # postgres
POSTGRES_PASSWORD: cGFzc3dvcmQ= # password13. Complete Deployment
Cấu Trúc Thư Mục
k8s/
├── namespace.yaml
├── configmap.yaml
├── secret.yaml
├── deployment.yaml
├── service.yaml
├── ingress.yaml
├── hpa.yaml
└── postgres.yaml # Chỉ dev/testNamespace
# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: spring-appDeploy Mọi Thứ
# Tạo namespace
kubectl apply -f k8s/namespace.yaml
# Deploy theo thứ tự
kubectl apply -f k8s/configmap.yaml -n spring-app
kubectl apply -f k8s/secret.yaml -n spring-app
kubectl apply -f k8s/postgres.yaml -n spring-app # Chỉ dev
kubectl apply -f k8s/deployment.yaml -n spring-app
kubectl apply -f k8s/service.yaml -n spring-app
kubectl apply -f k8s/ingress.yaml -n spring-app
kubectl apply -f k8s/hpa.yaml -n spring-app
# Hoặc deploy tất cả cùng lúc
kubectl apply -f k8s/ -n spring-appKiểm Tra
# Check pods
kubectl get pods -n spring-app
# NAME READY STATUS RESTARTS AGE
# spring-app-6d4f8b7c9d-abc12 1/1 Running 0 30s
# spring-app-6d4f8b7c9d-def34 1/1 Running 0 30s
# spring-app-6d4f8b7c9d-ghi56 1/1 Running 0 30s
# postgres-7f8d9e6c5b-xyz78 1/1 Running 0 45s
# Check services
kubectl get svc -n spring-app
# Check logs
kubectl logs -f deployment/spring-app -n spring-app
# Check health
kubectl exec -it deployment/spring-app -n spring-app -- curl localhost:8080/actuator/health
# Check HPA status
kubectl get hpa -n spring-app
# NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS
# spring-app-hpa Deployment/spring-app 25%/70%, 40%/80% 2 10 314. Local Kubernetes Development
Không cần cloud cluster để test Kubernetes manifests. Dùng minikube hoặc kind locally.
minikube
# Start local cluster
minikube start --cpus=4 --memory=4096
# Bật ingress
minikube addons enable ingress
minikube addons enable metrics-server
# Build image bên trong Docker của minikube
eval $(minikube docker-env)
docker build -t spring-app:latest .
# Deploy (dùng imagePullPolicy: Never cho local images)
kubectl apply -f k8s/ -n spring-app
# Truy cập app
minikube service spring-app-service -n spring-app
# Dashboard
minikube dashboardkind (Kubernetes in Docker)
# Tạo cluster
kind create cluster --name spring-dev
# Load image vào kind
docker build -t spring-app:latest .
kind load docker-image spring-app:latest --name spring-dev
# Deploy
kubectl apply -f k8s/ -n spring-app
# Port forward để truy cập
kubectl port-forward svc/spring-app-service 8080:80 -n spring-app15. Production Checklist
Trước khi deploy lên production, kiểm tra mỗi item:
| Category | Item | Lý Do |
|---|---|---|
| Docker | Multi-stage Dockerfile | Images nhỏ hơn, không build tools ở production |
| Docker | Non-root user | Security — giảm thiệt hại nếu container bị escape |
| Docker | JRE, không phải JDK | Image nhỏ, attack surface giảm |
| Docker | UseContainerSupport flag | JVM respect container memory limits |
| Docker | Layered JARs | Builds nhanh với layer caching |
| K8s | Resource requests và limits | Ngăn resource starvation |
| K8s | Liveness probe | Restart dead pods |
| K8s | Readiness probe | Không gửi traffic tới unready pods |
| K8s | Startup probe | Cho apps khởi động chậm thời gian boot |
| K8s | ConfigMap cho config | Không bake configuration vào images |
| K8s | Secrets cho credentials | Không lưu passwords trong ConfigMaps |
| K8s | HPA | Scale khi tải cao |
| K8s | Rolling update strategy | Zero-downtime deployments |
| K8s | Managed database | Không chạy production databases trên K8s |
| K8s | Ingress với TLS | HTTPS cho external traffic |
Tổng Kết
✅ Multi-stage Dockerfiles tạo images nhỏ, an toàn — chỉ JRE, không build tools
✅ Layered JARs làm rebuilds nhanh — chỉ application layer thay đổi khi update code
✅ Docker Compose đơn giản hóa local development — app, database, cache trong một lệnh
✅ Kubernetes Deployments khai báo desired state — replicas, updates, resource limits
✅ Health probes là thiết yếu — startup, liveness, readiness phục vụ mục đích khác nhau
✅ ConfigMaps và Secrets externalize configuration — không bao giờ bake credentials vào images
✅ HPA scale app tự động — scale up nhanh, scale down thận trọng
✅ Rolling updates với maxSurge: 1, maxUnavailable: 0 cho zero-downtime deploys
✅ Dùng managed databases ở production — đừng thêm database operations vào K8s workload
✅ Local K8s tools (minikube, kind) cho phép test manifests trước khi deploy lên cloud
Tiếp Theo?
Spring Boot app đã containerized và chạy trên Kubernetes, khám phá tiếp:
Tiếp tục Spring Boot series:
- CI/CD với GitHub Actions - Tự động hóa builds, tests, và deployments
- Cloud Deployment (AWS, Azure, GCP) - Deploy lên managed Kubernetes services
- Structured Logging & Centralized Logging - Observability trong containers
Bài Spring Boot Liên Quan:
- Performance Optimization & Profiling - Tune trước khi containerize
- Testing Guide với JUnit & Mockito - Test trước khi deploy
- Spring Boot Learning Roadmap - Lộ trình học đầy đủ
Infrastructure Cơ Bản:
- Docker Fundamentals - Container basics
- Load Balancing Explained - Phân phối traffic
- Reverse Proxy Explained - Nginx, Traefik, SSL termination
Thuộc series Spring Boot Learning Roadmap.
📬 Đăng ký nhận bản tin
Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.
Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.
💬 Comments
Đăng nhập để bình luận
Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.