SpringNotes

第 28 章:容器与云原生

zjc 于 2026-01-28 发布

这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 容器化让 Spring Boot 应用以一致环境运行,Kubernetes 提供调度、扩缩容、发布和服务发现能力。云原生不是把 jar 放进容器,而是围绕健康检查、资源、配置、弹性和可观测重新设计运行方式。

28.1 Dockerfile

简单示例:

FROM eclipse-temurin:21-jre-alpine

WORKDIR /app
COPY target/app.jar /app/app.jar

ENV TZ=Asia/Shanghai \
    JAVA_OPTS=""

EXPOSE 8080
ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar /app/app.jar"]

最佳实践:

  1. 使用多阶段构建;
  2. 镜像最小化;
  3. 使用非 root 用户;
  4. 不把 secret 写入镜像;
  5. exec 确保 Java 是 PID 1;
  6. 固定基础镜像版本;
  7. 扫描漏洞。

28.2 JVM 容器参数

java -XX:+UseContainerSupport \
  -XX:InitialRAMPercentage=60.0 \
  -XX:MaxRAMPercentage=60.0 \
  -XX:ActiveProcessorCount=4 \
  -jar /app/app.jar

内存预算:

容器 limit
  >= Heap
   + Metaspace
   + Thread stacks
   + Direct memory
   + CodeCache
   + Native
   + 余量

MaxRAMPercentage=75 且 Netty、压缩缓存、大量线程同时存在,可能仍会 OOMKilled,需要按应用实测。

28.3 Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    spec:
      containers:
        - name: order
          image: registry.example.com/order-service:1.8.0
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: "1"
              memory: "2Gi"
            limits:
              memory: "2Gi"

内存 request 和 limit 通常保持一致,避免调度与实际限制不一致。

28.4 探针

startupProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  failureThreshold: 30
  periodSeconds: 2
livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080

区别:

探针 失败结果
startup 延长启动判断
liveness 重启容器
readiness 摘除流量

liveness 检查不应包含外部依赖,否则下游故障会引发服务重启风暴。

28.5 ConfigMap 与 Secret

ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: order-config
data:
  SPRING_PROFILES_ACTIVE: "prod"
  PAY_TIMEOUT: "3s"

生产建议使用平台密钥系统注入,而不是明文 Secret YAML。配置变更要有版本和回滚能力。

28.6 Service 与 Ingress

apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector:
    app: order-service
  ports:
    - port: 80
      targetPort: 8080

Ingress 或 Gateway API 负责外部路由、TLS 和流量策略。服务间调用可选择 Kubernetes Service、注册中心或服务网格,保持一种主模型,避免策略分裂。

28.7 HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: order-service
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: order-service
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 65

HPA 依赖指标可用。扩容还需考虑数据库连接总量、下游容量、冷启动、缓存预热、第三方限流和成本。

28.8 Service Mesh

网格提供 mTLS、流量拆分、重试超时、故障注入、遥测和权限策略。

Spring Cloud 与网格能力可能重叠:

能力 Spring Cloud Mesh
服务发现 可用 平台提供
熔断 应用内 sidecar 策略
灰度 网关/注册元数据 流量规则
mTLS 按需实现 常见能力

选择时避免同一能力双层配置且语义不一致。

28.9 资源与调度

CPU:

  1. request 影响调度;
  2. limit 触发 throttling;
  3. GC/JIT 线程受 CPU 感知影响;
  4. 突发流量要评估毛刺。

常见问题:

现象 原因
OOMKilled 内存超限
延迟毛刺 CPU throttling
Pod Pending 资源不足
反复重启 liveness 失败
发布卡住 PDB 或探针
调度不均 request 设置不合理

28.10 云原生清单

1. 镜像不可变且可追溯
2. 配置外部化
3. secret 独立管理
4. 健康探针正确
5. 优雅停机
6. 资源 request/limit 合理
7. HPA 策略验证
8. 日志输出 stdout 或挂载卷
9. 指标和 trace 接入
10. 发布可回滚
11. PDB 保证最小可用
12. 网络策略和安全策略

本章小结

容器与云原生部署要关注镜像、JVM 资源感知、探针、配置注入、自动扩缩容和优雅停机。Kubernetes 提供调度和发布能力,但应用仍需暴露正确健康状态并控制资源。引入服务网格时要明确与 Spring Cloud 治理能力的边界。

思考题

  1. 为什么 ENTRYPOINT 中要使用 exec java
  2. 内存 request 和 limit 为什么常保持一致?
  3. liveness 为什么不应检查外部依赖?
  4. HPA 扩容有哪些隐藏限制?
  5. Spring Cloud 和 Service Mesh 能力如何分工?