Modern Architecture
& Coding Solutions

从 Docker 到 K8s:Spring Boot 应用部署与自动伸缩实战

上一篇文章我们详细拆解了 Spring Boot 3.4 Docker 镜像的分层构建技巧,把镜像从 200MB 优化到了 80MB。但容器化只是第一步——如何让应用在 Kubernetes 集群中稳定运行?如何配置探针让 K8s 帮你自动恢复故障?如何在流量洪峰时自动扩容、低谷时自动缩容? 本文从零开始,将同一个 Spring Boot 应用从 Docker 镜像演进到生产级 K8s 部署,涵盖 Deployment 编排、Liveness/Readiness 探针、ConfigMap/Secret 管理、HPA 自动伸缩、滚动更新与回滚,以及基于 Prometheus 指标的 KEDA 事件驱动伸缩。

一、为什么需要 Kubernetes?

Docker 解决了“一次构建,到处运行”的问题,但生产环境还需要解决更多问题:

能力Docker 单机Kubernetes 集群
容器调度手动 docker run自动调度到最优节点
故障恢复容器挂了就挂了自动重启 + 自愈
负载均衡需额外配置反向代理内置 Service + Ingress
弹性伸缩手动改端口映射HPA/KEDA 自动伸缩
滚动更新手动逐个替换零停机滚动更新 + 一键回滚

Kubernetes 的核心价值在于将基础设施能力代码化——通过 YAML 声明期望状态,由 K8s 控制器负责将实际状态收敛到期望状态。

本文将从 Spring Boot 分层镜像出发,逐步构建完整的 K8s 部署体系。建议先阅读本站的 《Spring Boot 3.4 Docker 镜像最佳实践(含分层构建)》 作为前置准备。

二、从镜像到 Deployment:在 K8s 中运行 Spring Boot

2.1 前置条件

  • Kubernetes 集群(本地可用 Minikube 或 Kind,生产推荐云厂商托管集群)
  • kubectl 已配置好集群访问
  • 已构建好的 Spring Boot 镜像(推送到镜像仓库,如 Docker Hub、ACR、ECR 等)

2.2 编写 Deployment YAML

Deployment 是 K8s 中最核心的工作负载控制器,管理 Pod 的副本数、更新策略和生命周期。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
  namespace: production
  labels:
    app: order-service
    version: v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # 更新时最多超出期望副本数 1 个
      maxUnavailable: 0   # 更新期间不允许 Pod 不可用(零停机)
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
      - name: order-service
        image: your-registry/order-service:1.0.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
          name: http
        # 资源限制(HPA 依赖此项)
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
        # === 探针配置(生产环境必备)===
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 5
          failureThreshold: 3
        # === 环境变量 ===
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        - name: JAVA_OPTS
          value: "-Xms512m -Xmx768m"

2.3 探针配置详解

Liveness Probe(存活探针) :检测容器是否“活着”。如果探针失败,K8s 会重启容器
Readiness Probe(就绪探针) :检测容器是否“准备好接收流量”。如果探针失败,K8s 会从 Service 的负载均衡中摘除该 Pod

Spring Boot Actuator 提供了两组专用端点:

# application-prod.yml
management:
  endpoint:
    health:
      show-details: always
      probes:
        enabled: true   # 启用 /actuator/health/liveness 和 /actuator/health/readiness
  health:
    livenessstate:
      enabled: true
    readinessstate:
      enabled: true

为什么不能只用同一个 /actuator/health

端点失败后果包含检查项
/actuator/health/liveness重启 Pod仅 JVM 是否存活、OutOfMemory 等致命问题
/actuator/health/readiness摘除流量数据库连接、消息队列、下游依赖是否就绪

如果 /actuator/health 因数据库暂时不可用而失败,Liveness Probe 会直接重启 Pod,可能让问题更糟。正确做法是:Liveness 只检查“是否还活着”,Readiness 检查“是否能干活”

2.4 资源限制(Requests & Limits)

resources:
  requests:   # 调度依据:K8s 保证至少分配这么多资源
    memory: "512Mi"
    cpu: "500m"
  limits:     # 运行时上限:Pod 不能超过这个值
    memory: "1Gi"
    cpu: "1000m"

核心原则requests 是调度器决策的依据,limits 是运行时 Cgroups 的硬上限。建议 requests 设为 limits 的 50%-70%,既保证资源预留,又提高节点利用率。

三、Service:暴露服务与负载均衡

apiVersion: v1
kind: Service
metadata:
  name: order-service
  namespace: production
spec:
  selector:
    app: order-service
  ports:
  - port: 8080
    targetPort: 8080
    name: http
  type: ClusterIP   # 集群内部访问,外部通过 Ingress 暴露

部署命令:

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

# 查看部署状态
kubectl get pods -n production
kubectl get deployment -n production
kubectl get svc -n production

四、配置管理:ConfigMap 与 Secret

环境相关的配置不应硬编码在镜像中,而应通过 K8s 的 ConfigMap 和 Secret 注入。

4.1 ConfigMap(非敏感配置)

apiVersion: v1
kind: ConfigMap
metadata:
  name: order-service-config
  namespace: production
data:
  application.yml: |
    spring:
      datasource:
        url: jdbc:mysql://mysql-service:3306/order_db
        hikari:
          maximum-pool-size: 10
    logging:
      level:
        com.example: INFO

挂载到 Pod 中:

spec:
  containers:
  - name: order-service
    volumeMounts:
    - name: config
      mountPath: /config
  volumes:
  - name: config
    configMap:
      name: order-service-config

启动时通过 --spring.config.additional-location=/config/ 加载外部配置。

4.2 Secret(敏感配置,Base64 编码)

apiVersion: v1
kind: Secret
metadata:
  name: order-service-secret
  namespace: production
type: Opaque
data:
  db-password: <base64-encoded-password>
  api-key: <base64-encoded-api-key>

在 Deployment 中引用:

env:
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: order-service-secret
      key: db-password

⚠️ 重要:Secret 的 Base64 编码不是加密。生产环境应配合外部密钥管理服务(如 HashiCorp Vault、云厂商 KMS)使用。

五、滚动更新与回滚

5.1 触发滚动更新

更新镜像版本:

kubectl set image deployment/order-service order-service=your-registry/order-service:2.0.0 -n production

或修改 YAML 后重新 apply:

kubectl apply -f deployment.yaml

5.2 滚动更新策略

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1        # 更新时最多超出期望副本数 1 个
    maxUnavailable: 0   # 不允许有 Pod 不可用(保证零停机)

默认行为maxSurge=25%maxUnavailable=25%。对于生产环境的关键服务,建议 maxUnavailable: 0 + maxSurge: 1 确保零停机。

5.3 查看与回滚

# 查看滚动更新历史
kubectl rollout history deployment/order-service -n production

# 查看特定版本详情
kubectl rollout history deployment/order-service --revision=3 -n production

# 回滚到上一个版本
kubectl rollout undo deployment/order-service -n production

# 回滚到指定版本
kubectl rollout undo deployment/order-service --to-revision=2 -n production

# 暂停/恢复更新(用于金丝雀发布)
kubectl rollout pause deployment/order-service -n production
kubectl rollout resume deployment/order-service -n production

六、HPA 自动伸缩:基于 CPU/内存

Horizontal Pod Autoscaler(HPA)根据资源利用率自动调整 Pod 副本数。

6.1 前置条件:安装 Metrics Server

HPA 依赖 Metrics Server 提供 Pod 的资源指标:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

6.2 创建 HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: order-service-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: order-service
  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   # 缩容冷静期 5 分钟
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0     # 扩容无延迟
      policies:
      - type: Percent
        value: 100
        periodSeconds: 60
      - type: Pods
        value: 4
        periodSeconds: 60
        selectPolicy: Max

关键参数解读

参数含义推荐值
averageUtilization: 70CPU 平均使用率超过 70% 时扩容60%-80%
scaleDown.stabilizationWindowSeconds缩容前的观察窗口,防止抖动300s(5分钟)
scaleUp.stabilizationWindowSeconds扩容观察窗口,建议 0 快速响应0

6.3 部署与验证

kubectl apply -f hpa.yaml

# 查看 HPA 状态
kubectl get hpa -n production

# 实时监控伸缩
kubectl get hpa order-service-hpa -n production --watch

⚠️ 常见陷阱:如果 Pod 没有设置 resources.requests,HPA 无法计算利用率,将无法工作。

七、KEDA:事件驱动的自动伸缩

HPA 只能基于 CPU/内存等基础指标,对于消息队列积压、自定义业务指标等场景无能为力。KEDA(Kubernetes Event-Driven Autoscaling)填补了这个空白。

7.1 KEDA vs HPA

维度HPAKEDA
指标来源CPU / Memory(Metrics Server)Prometheus、Kafka、RabbitMQ、Redis 等 50+ 数据源
缩容到零❌ 不支持支持(Serverless 场景)
伸缩粒度基于平均值基于具体事件数量(如队列深度、每秒请求数)
适用场景通用负载均衡事件驱动型应用(消息消费、批处理任务)

7.2 安装 KEDA

# Helm 安装
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda --namespace keda --create-namespace

7.3 基于 Prometheus 指标自动伸缩

延续本站 《Java 应用接入 Prometheus + Grafana 全记录》 的监控体系,我们可以让 KEDA 直接读取 Prometheus 中的业务指标进行伸缩。

Step 1:在 Spring Boot 中暴露自定义指标

@RestController
public class OrderMetricsController {

    private final Counter orderCounter = Counter.builder("orders_total")
            .description("Total number of orders")
            .register(Metrics.globalRegistry);

    private final Gauge pendingOrdersGauge = Gauge.builder("orders_pending", 
            () -> pendingOrderService.count()).register(Metrics.globalRegistry);

    @PostMapping("/order")
    public Order createOrder(@RequestBody OrderRequest request) {
        orderCounter.increment();
        // 业务逻辑...
    }
}

Step 2:创建 KEDA ScaledObject

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: order-service-scaler
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: order-service
  minReplicaCount: 1
  maxReplicaCount: 20
  fallback:            # Prometheus 不可用时的保底策略
    failureThreshold: 3
    replicas: 3
  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.monitoring:9090
      metricName: orders_pending
      threshold: "100"           # 待处理订单超过 100 时扩容
      query: |
        sum(orders_pending{namespace="production"})
      activationThreshold: "10"  # 低于 10 时缩容
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.monitoring:9090
      metricName: orders_per_second
      threshold: "50"
      query: |
        rate(orders_total{namespace="production"}[1m])

Step 3:部署验证

kubectl apply -f scaledobject.yaml

# 查看 KEDA 伸缩状态
kubectl get scaledobject -n production
kubectl describe scaledobject order-service-scaler -n production

7.4 KEDA 适用场景

场景推荐方案理由
消息队列消费者(Kafka/RabbitMQ)KEDA根据队列深度精准伸缩
批处理任务(按需启动)KEDA + Job有任务时启动 Pod,完成后缩容到 0
HTTP 服务(基于 QPS)KEDA HTTP Add-on基于 HTTP 流量伸缩
通用 Web 服务(CPU/内存)HPA简单直接,无需额外组件

八、完整架构总览

九、生产环境最佳实践清单

  1. 资源限制:所有 Pod 必须设置 requestslimits
  2. 探针分离:Liveness 和 Readiness 使用不同端点,配置合理的 initialDelaySeconds
  3. 滚动更新:生产环境设置 maxUnavailable: 0 保证零停机
  4. 配置外置:使用 ConfigMap 和 Secret 管理配置,避免环境差异
  5. HPA 必备:所有生产 Deployment 都应配置 HPA,minReplicas 至少 2 保证高可用
  6. 监控联动:将 KEDA 与 Prometheus + Grafana 结合,实现指标驱动的精准伸缩
  7. 回滚预案:熟悉 kubectl rollout undo 命令,故障时快速回滚
  8. 镜像拉取策略:生产环境使用 imagePullPolicy: IfNotPresent,避免每次都拉取

十、总结

从 Docker 到 Kubernetes 的演进路径清晰:

  • 第一阶段:用 Docker 将应用容器化,通过分层构建优化镜像体积
  • 第二阶段:编写 Deployment + Service YAML,将应用部署到 K8s 集群
  • 第三阶段:配置 Liveness/Readiness 探针,让 K8s 具备自愈能力
  • 第四阶段:接入 ConfigMap/Secret,实现配置与镜像分离
  • 第五阶段:配置 HPA 实现 CPU/内存驱动的自动伸缩
  • 第六阶段:引入 KEDA,实现基于业务指标的事件驱动伸缩

完成这六个阶段,你的 Spring Boot 应用就从“能在 K8s 上跑”进化到了“生产级云原生应用”——具备弹性伸缩、自动恢复、零停机发布的能力。


📌 系列拓展阅读


📚 参考文献

  1. Kubernetes 官方文档. Deployments. https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
  2. Kubernetes 官方文档. Horizontal Pod Autoscaling. https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/
  3. Kubernetes 官方文档. Configure Liveness, Readiness and Startup Probes. https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
  4. Kubernetes 官方文档. Performing a Rolling Update. https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/
  5. KEDA 官方文档. Prometheus Scaler. https://keda.sh/docs/latest/scalers/prometheus/
  6. 华为云社区. Kubernetes 集群部署 Spring Boot 应用最佳实践. https://bbs.huaweicloud.com/blogs/443322
  7. CSDN. HPA 扩缩容总慢半拍?Spring Boot 指标选择的”精准制导”秘籍. https://blog.csdn.net/
赞(0) 打赏
未经允许不得转载:MACS Dev Hub » 从 Docker 到 K8s:Spring Boot 应用部署与自动伸缩实战

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续提供更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫

微信扫一扫

登录

找回密码

注册