前言
Kubernetes 是容器编排的事实标准。Spring Boot 应用可以方便地部署到 K8s 集群。本文将介绍 Spring Boot Kubernetes 部署的完整方案。
基础概念
1. K8s 架构
┌─────────────────────────────────────────┐
│ Control Plane │
│ ┌─────────┬─────────┬─────────────┐ │
│ │ API │ Scheduler│ Controller │ │
│ │ Server │ │ Manager │ │
│ └─────────┴─────────┴─────────────┘ │
│ │ etcd │
└──────────────┼──────────────────────────┘
│
┌──────────┼──────────┐
│ │ │
┌───▼────┐ ┌───▼────┐ ┌───▼────┐
│ Node 1 │ │ Node 2 │ │ Node 3 │
│ ┌────┐ │ │ ┌────┐ │ │ ┌────┐ │
│ │Pod │ │ │ │Pod │ │ │ │Pod │ │
│ └────┘ │ │ └────┘ │ │ └────┘ │
└────────┘ └────────┘ └────────┘
2. 核心资源
| 资源 | 说明 | 用途 |
|---|---|---|
| Pod | 最小调度单元 | 运行容器 |
| Deployment | 无状态应用 | 管理 Pod |
| Service | 服务发现 | 负载均衡 |
| ConfigMap | 配置管理 | 外部化配置 |
| Secret | 敏感信息 | 密码、证书 |
| Ingress | 入口规则 | HTTP 路由 |
Deployment
1. 基础配置
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
labels:
app: demo
spec:
replicas: 3
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: demo
image: demo:1.0.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
protocol: TCP
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: JAVA_OPTS
value: "-Xms512m -Xmx512m"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
2. 部署命令
# 创建 Deployment
kubectl apply -f deployment.yaml
# 查看状态
kubectl get deployments
kubectl get pods
kubectl get pods -l app=demo
# 查看日志
kubectl logs -f deployment/demo-app
kubectl logs -f deployment/demo-app -c demo
# 扩缩容
kubectl scale deployment demo-app --replicas=5
# 滚动更新
kubectl set image deployment/demo-app demo=demo:1.1.0
# 回滚
kubectl rollout undo deployment/demo-app
# 查看历史
kubectl rollout history deployment/demo-app
Service
1. ClusterIP(内部访问)
# service-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
name: demo-service
spec:
selector:
app: demo
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
2. NodePort(外部访问)
# service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: demo-service
spec:
selector:
app: demo
ports:
- protocol: TCP
port: 80
targetPort: 8080
nodePort: 30080
type: NodePort
3. LoadBalancer(云厂商)
# service-loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
name: demo-service
spec:
selector:
app: demo
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
ConfigMap 和 Secret
1. ConfigMap
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: demo-config
data:
application.yml: |
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://mysql:3306/demo
username: root
redis:
host: redis
port: 6379
logging:
level:
com.example.demo: INFO
# 使用 ConfigMap
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
spec:
template:
spec:
containers:
- name: demo
image: demo:1.0.0
envFrom:
- configMapRef:
name: demo-config
volumeMounts:
- name: config
mountPath: /app/config
volumes:
- name: config
configMap:
name: demo-config
2. Secret
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: demo-secret
type: Opaque
stringData:
DB_PASSWORD: "complex-password"
JWT_SECRET: "jwt-secret-key"
API_KEY: "api-key-value"
# 创建 Secret
kubectl create secret generic demo-secret \
--from-literal=DB_PASSWORD=complex-password \
--from-literal=JWT_SECRET=jwt-secret-key
# 使用 Secret
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
spec:
template:
spec:
containers:
- name: demo
image: demo:1.0.0
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: demo-secret
key: DB_PASSWORD
Ingress
1. 基础 Ingress
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: demo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-service
port:
number: 80
2. TLS 配置
# ingress-tls.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- demo.example.com
secretName: demo-tls
rules:
- host: demo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-service
port:
number: 80
自动扩缩容
1. HPA(水平扩缩容)
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: demo-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: demo-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
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
- type: Pods
value: 4
periodSeconds: 15
selectPolicy: Max
2. VPA(垂直扩缩容)
# vpa.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: demo-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: demo-app
updatePolicy:
updateMode: Auto
resourcePolicy:
containerPolicies:
- containerName: demo
minAllowed:
cpu: 100m
memory: 256Mi
maxAllowed:
cpu: 1
memory: 2Gi
存储管理
1. PersistentVolume
# pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: demo-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: standard
hostPath:
path: /mnt/data/demo
2. PersistentVolumeClaim
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: demo-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: standard
# 使用 PVC
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
spec:
template:
spec:
containers:
- name: demo
image: demo:1.0.0
volumeMounts:
- name: data
mountPath: /app/data
volumes:
- name: data
persistentVolumeClaim:
claimName: demo-pvc
监控和日志
1. Prometheus 监控
# service-monitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: demo-monitor
labels:
release: prometheus
spec:
selector:
matchLabels:
app: demo
endpoints:
- port: http
path: /actuator/prometheus
interval: 30s
namespaceSelector:
matchNames:
- default
2. 日志收集
# 使用 Fluentd 收集日志
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-config
data:
fluent.conf: |
<source>
@type tail
path /var/log/containers/*.log
pos_file /var/log/fluentd-containers.log.pos
tag kubernetes.*
<parse>
@type json
</parse>
</source>
<match kubernetes.**>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
logstash_prefix kubernetes
</match>
最佳实践
1. 资源限制
# ✅ 推荐
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
# ❌ 不推荐
# 不设置资源限制
2. 健康检查
# ✅ 推荐
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
# ❌ 不推荐
# 不设置健康检查
3. 优雅关闭
# application.yml
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
# Deployment 配置
spec:
template:
spec:
terminationGracePeriodSeconds: 30
4. 多环境部署
# 命名空间隔离
kubectl create namespace dev
kubectl create namespace test
kubectl create namespace prod
# 不同环境使用不同配置
kubectl apply -f deployment.yaml -n dev
kubectl apply -f deployment.yaml -n prod
5. Helm Chart
# Chart.yaml
apiVersion: v2
name: demo
description: Demo Application
type: application
version: 1.0.0
appVersion: "1.0.0"
# values.yaml
replicaCount: 3
image:
repository: demo
tag: "1.0.0"
pullPolicy: IfNotPresent
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
总结
Kubernetes 部署要点:
- ✅ Deployment - 无状态应用管理
- ✅ Service - 服务发现和负载均衡
- ✅ ConfigMap/Secret - 配置管理
- ✅ Ingress - HTTP 路由
- ✅ HPA - 自动扩缩容
- ✅ 最佳实践 - 资源限制、健康检查
Kubernetes 是云原生部署的标准平台。