跳到内容

第 7 卷 / 第 11 章 / 第 2 课

11.2 Kubernetes 有状态工作负载:身份、存储与恢复 ​

把数据库 YAML 改成 StatefulSet 不等于获得了高可用数据库。Kubernetes 能维护 Pod 身份、挂载声明和编排顺序;复制协议、一致性、备份校验和故障切换仍由应用、operator 或托管服务负责。

PV、PVC 与 StorageClass 分工 ​

text
Pod → PersistentVolumeClaim → PersistentVolume → 存储系统
                         ↑
                   StorageClass 动态供应策略
  • PVC 表达工作负载需要多大的卷、访问模式和存储类;
  • PV 表示集群中可绑定的存储资源;
  • StorageClass 描述 provisioner、参数、回收策略和绑定方式。

ReadWriteOnce 允许单个节点读写,同一节点上的多个 Pod 仍可能访问该卷。需要集群范围单 Pod 使用时,可评估 CSI 支持的 ReadWriteOncePod。两者都不替应用实现事务或成员 fencing,也不说明存储故障域和性能保证。

对拓扑受限的块存储,volumeBindingMode: WaitForFirstConsumer 可以结合 Pod 调度约束再绑定或供应卷,减少“Pod 在 A 区、卷在 B 区”造成的不可调度。但已绑定的 zonal 卷不会因此跨区搬家;区域故障后的恢复仍需存储复制或备份恢复。

StatefulSet 提供稳定身份 ​

以下清单展示网络身份和卷绑定,尚不是数据库部署方案。两个对象应放在同一 namespace;fast-zonal StorageClass 必须已配置,镜像占位符要替换为实际 digest,应用自身还要实现成员协议并配置合适的探针。

yaml
apiVersion: v1
kind: Service
metadata:
  name: ledger-headless
spec:
  clusterIP: None
  selector:
    app: ledger
  ports:
    - name: peer
      port: 7000
      targetPort: peer
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: ledger
spec:
  serviceName: ledger-headless
  replicas: 3
  persistentVolumeClaimRetentionPolicy:
    whenDeleted: Retain
    whenScaled: Retain
  selector:
    matchLabels:
      app: ledger
  template:
    metadata:
      labels:
        app: ledger
    spec:
      terminationGracePeriodSeconds: 60
      containers:
        - name: ledger
          image: registry.example/ledger@sha256:REPLACE_ME
          ports:
            - name: peer
              containerPort: 7000
          volumeMounts:
            - name: data
              mountPath: /var/lib/ledger
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: [ReadWriteOnce]
        storageClassName: fast-zonal
        resources:
          requests:
            storage: 100Gi

Pod 会获得 ledger-0、ledger-1 这样的稳定名字;例如 ledger-0 使用 data-ledger-0 PVC。替换 Pod 会换 UID,但仍关联原 PVC。默认 OrderedReady 按序创建,并在缩容时逆序终止;直接删除整个 StatefulSet 不保证这个终止顺序。

StatefulSet 需要 headless Service 提供网络身份,但 DNS 名称稳定不代表进程一定健康。成员发现还要处理 readiness 与启动依赖:若第一个 Pod 只有等齐三个成员才能 Ready,OrderedReady 就可能让后两个永远无法创建。应用支持并行引导时才选择 Parallel,需要发现未就绪 peer 时另行评估 Service 的 publishNotReadyAddresses。

失联不等于进程已经停止。强制删除未知状态的 Pod 后,旧进程可能仍在写入;重建前应通过 fencing 确认旧成员不能再访问存储或对外服务,不能仅凭同名 Pod 已重建推断无脑裂。

PVC 生命周期默认偏向保数据 ​

示例明确保留缩容和删除 StatefulSet 时的 PVC。删除 PVC 是另一层操作:PV 的 reclaimPolicy: Delete 通常会连带删除底层存储,Retain 则保留以待人工回收。删除 namespace 会删除其中的 PVC,不受 StatefulSet 保留设置兜底;这几层删除权限和备份必须分别确认。

扩容前先验证应用是否支持增加成员;缩容前先让应用层安全移除副本,再改变 replicas。直接把三副本数据库缩成一副本,控制器不会替你维护 quorum。

探针不能只检查端口打开 ​

有状态服务常需要区分:

  • startup:恢复日志、重放 WAL 或加入集群是否完成;
  • readiness:该副本此刻能否安全接收目标类型的流量;
  • liveness:进程是否已进入只能重启恢复的状态。

把“暂时落后”写成 liveness 失败会触发重启循环,反而永远追不上。把主库专属写能力与普通存活混在一个 readiness 中,也会让读流量无端中断。

快照不是完整备份策略 ​

CSI VolumeSnapshot 需要快照 CRD、controller 和驱动支持,通常用于快速捕获单卷状态。是否应用一致取决于存储实现和应用冻结流程;多个卷各拍一张,并不自动构成同一时刻的一组快照。生产恢复方案要回答:

  1. 是 crash-consistent 还是 application-consistent?
  2. 数据库日志和多卷如何获得同一恢复点?
  3. 备份是否复制到独立账户、区域或故障域?
  4. 加密密钥、schema、配置和 operator 版本是否一起保留?
  5. 最近一次从空集群恢复演练花了多久,恢复到哪个时间点?

没有成功恢复记录的备份,只是尚未验证的一组对象。RPO 是可接受的数据恢复点落后时间,RTO 是可接受的服务恢复时间。例如 RPO 为 5 分钟、RTO 为 30 分钟,要求恢复点距故障最多 5 分钟,并在 30 分钟内恢复约定服务。两者共同约束备份频率、复制方式和演练周期。

Operator 的价值与边界 ​

成熟数据库 operator 可以编码成员变更、滚动升级、备份和故障切换知识,但它也引入 CRD、webhook、controller 和版本兼容风险。采用前应检查:

  • operator 与数据库版本升级矩阵;
  • controller 不可用时数据面是否继续服务;
  • CRD 删除和 finalizer 卡住时如何处置;
  • 自动故障切换在网络分区下的 fencing 机制;
  • 备份能否脱离原集群恢复。

若团队无法承担这些操作责任,托管数据库往往比“把数据库塞进 Kubernetes”更可靠。

上线检查 ​

  • 每个副本的身份和卷绑定是否可预测?
  • 节点、可用区和存储是否共享同一故障域?
  • 资源不足时是否会同时驱逐多数副本?
  • 升级、缩容、节点 drain 和区域故障是否演练?
  • PVC、快照、备份与密钥的删除权限是否分离?
  • RPO/RTO 是否通过真实恢复数据验证?

下一课把资源、调度、扩缩容和中断预算组合起来,控制共享集群中的容量与故障半径。

参考资料 ​

Built with VitePress | Software Systems Atlas