Skip to content

云原生 K8s ​

Java 架构师 · 可观测与工程化

Kubernetes 是容器编排的事实标准,把"应用部署、伸缩、自愈"标准化为声明式 API:你描述"期望状态"(我要 3 个副本、用这个镜像),控制器不断把"实际状态"向"期望状态"对齐(Reconcile Loop)。架构师需理解其核心对象与发布模型,才能让 Java 服务在云原生环境稳定弹性运行。本章深入 Pod 调度、Deployment 滚动更新、Service/Ingress、HPA 与 Helm,并剖析探针、资源限制、有状态部署等工程要点。

一、核心对象:从 Pod 到 Ingress ​

  • Pod:最小调度单元,含一个或多个共享网络/存储的容器。一个 Pod 通常跑一个主容器(+ 可选的 Sidecar/初始化容器)。
  • Deployment:声明期望副本数,控制器维持对齐(Pod 挂了自动重建 = 自愈)。
  • Service:稳定的虚拟 IP + 负载均衡,通过 Label 选 Pod,解耦"Pod 变化"与"访问地址"(Pod IP 会变,Service 不变)。类型:ClusterIP(集群内)/ NodePort / LoadBalancer(对外)。
  • Ingress:七层(HTTP)路由,把外部域名/路径映射到 Service,常配合 Ingress Controller(Nginx/ELB)。
  • HPA(Horizontal Pod Autoscaler):基于 CPU/自定义指标水平扩缩副本。

二、声明式与 Reconcile 循环 ​

K8s 的核心范式是声明式:你写 YAML 描述期望,各 Controller 监听实际状态,不断调和(Reconcile)直到一致。这与"命令式(执行脚本)"相反——声明式天然支持自愈(Pod 挂了控制器自动补)与幂等(apply 多次结果一致)。

三、Pod 调度:去哪台机器 ​

调度器(Scheduler)为未绑定 Pod 选节点,考虑:

  • 资源请求(requests):节点剩余资源能否满足。
  • 亲和/反亲和(Affinity):如"订单与缓存放同可用区"(亲和)、"两个副本不在同节点"(反亲和,避免同节点故障全挂)。
  • 污点/容忍(Taint/Tolerate):节点标记"只跑特定 Pod"(如 GPU 节点)。
  • 拓扑分布(TopologySpread):打散到不同机架/可用区,提升容灾。

调度与资源的铁律

  • 必须设 requests/limits:requests 供调度决策(否则节点超卖),limits 防单 Pod 吃满节点("噪声邻居"挤垮同机其他服务)。
  • requests 设太低 → 节点超卖,实际压力过大被驱逐;limits 设太低 → 容器被 OOMKill 频繁重启。需基于压测设定。

四、Deployment 滚动更新:零停机 ​

yaml
apiVersion: apps/v1
kind: Deployment
metadata: { name: order }
spec:
  replicas: 3
  strategy:
    rollingUpdate: { maxSurge: 1, maxUnavailable: 0 }  # 先起1个新, 再停1个旧 = 零停机
  selector: { matchLabels: { app: order } }
  template:
    metadata: { labels: { app: order } }
    spec:
      containers:
        - name: order
          image: registry/order:v1.2.0        # 固定 tag, 不用 latest
          readinessProbe:                      # 就绪才接流量
            httpGet: { path: /actuator/health/readiness, port: 8080 }
            initialDelaySeconds: 10
          livenessProbe:                       # 不健康则重启
            httpGet: { path: /actuator/health/liveness, port: 8080 }
          resources:
            requests: { cpu: 500m, memory: 1Gi }
            limits:   { cpu: 1,   memory: 2Gi }
  • maxSurge / maxUnavailable:控制滚动节奏(如 1/0 = 先增后减,零停机;2/1 = 更快但短暂多资源)。
  • readinessProbe(就绪探针):通过才把 Pod 加入 Service 端点(接流量)。未就绪就接流量 = 用户打到未初始化好的实例。
  • livenessProbe(存活探针):失败则 kubelet 杀死并重启 Pod(适用于死锁/假死)。

readiness vs liveness 错配的灾难

  • 把 liveness 设为依赖外部(如"能连 DB"):DB 抖动 → liveness 失败 → Pod 被不断重启 → 雪崩(重启中实例更不可用)。liveness 只查"进程自身健康",外部依赖用 readiness 表达(不健康就不接流量,但不重启)。
  • 没有 readiness:Pod 一启动就接流量,但 JVM 还在预热/连接池没建好 → 大量超时。
  • 镜像用 latest:无法回滚、不可重现(GitOps 无法对齐状态)。

五、Service / Ingress:稳定入口 ​

  • Service:ClusterIP 在集群内提供稳定访问;选择器(selector)动态维护后端 Pod 列表(EndpointSlice)。即使 Pod 频繁重建,调用方始终访问同一个 Service 名(DNS:order.default.svc.cluster.local)。
  • Ingress:对外暴露 HTTP 服务,按 Host/Path 路由到 Service。示例:
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: { name: shop }
spec:
  rules:
    - host: shop.example.com
      http:
        paths:
          - path: /api/order
            pathType: Prefix
            backend: { service: { name: order, port: { number: 8080 } } }

六、HPA:弹性伸缩 ​

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
  scaleTargetRef: { kind: Deployment, name: order }
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource: { name: cpu, target: { type: Utilization, averageUtilization: 70 } }
    - type: Pods                                    # 自定义指标(如 QPS)
      pods:
        metric: { name: order_qps }
        target: { type: AverageValue, averageValue: 1000 }

HPA 周期性(默认 15s)采集指标,计算期望副本数并在 [min,max] 间调整。配合压测设定阈值——容量随流量自动伸缩,省成本且抗突发。

七、Helm:清单模板化 ​

Helm 把 K8s 清单参数化为 Chart,用 values.yaml 按环境渲染,管理版本与回滚(helm rollback)。避免每个环境复制一堆几乎相同的 YAML。环境差异只体现在 values(镜像 tag、副本数、资源),模板复用。

bash
helm install order ./chart -f values-prod.yaml   # 生产
helm upgrade order ./chart -f values-prod.yaml    # 升级
helm rollback order 2                            # 回滚到版本2

八、有状态服务的部署 ​

Redis、MySQL 等有状态服务不能直接用普通 Deployment(Pod 重建数据丢、无稳定网络标识)。用:

  • StatefulSet:稳定网络标识(固定 Pod 名 redis-0/1/2)、稳定持久化存储(PV 随 Pod)、有序部署/扩缩。
  • PV/PVC:把状态外移到持久卷,Pod 重建数据还在。
  • 或直接用云托管(RDS/Redis 云服务),把有状态交给云厂商,K8s 只跑无状态业务。

本节小结

K8s 用声明式 API 把"部署/伸缩/自愈"标准化:Deployment 维持期望副本(Pod 挂自动补)、Service/Ingress 提供稳定入口与七层路由、HPA 实现指标驱动弹性、Helm 管理版本化清单。落地铁律:无状态化(状态外移 DB/Redis/对象存储)、配齐 readiness/liveness 探针(liveness 只查自身、外部依赖用 readiness)、设 requests/limits 防超卖与噪声邻居、镜像用固定 tag 可回滚、有状态用 StatefulSet+PV 或云托管。架构师应把 K8s 当作"分布式操作系统",按它的范式(声明式、自愈、不可变)设计应用——自愈的前提是实例可随时被杀、可水平扩展。

📚 官方文档与参考资料 ​

✏️ 练习与自测 ​

  1. 设计题:为一个 Java 服务写 Deployment,要求零停机发布、CPU>70% 自动扩到 10 副本、配齐探针与资源限制,并说明 maxSurge/maxUnavailable 的取值含义。
  2. 辨析题:解释 readinessProbe 与 livenessProbe 的区别;若把"能连 DB"放进 livenessProbe,DB 抖动时会发生什么?如何正确设计两个探针?
  3. 设计题:有状态服务(如 Redis)在 K8s 中如何部署?为什么不能用普通 Deployment?给出 StatefulSet + PV 的关键设计或"云托管"的取舍。
  4. 设计题:用 Helm 管理"开发/测试/生产"三环境的订单服务,说明 values 里放什么、模板里放什么,并写出回滚到上一版本的命令与验证步骤。