有状态应用往往是 Kubernetes 从“会发服务”走向“真正在生产里负责任”时的分水岭。数据库、消息队列、复制型存储这些系统,不只是需要 Pod 跑起来,它们还要求身份稳定、卷稳定、拓扑稳定,出事后还能恢复得回来。

有状态工作负载通常需要什么

  • 稳定身份
  • 持久化存储
  • 有序启动和停止
  • 可预期的复制行为
  • 真实可执行的备份与恢复流程

这几个条件只要缺一个,系统可能仍然能启动,但运维过程会很快变得痛苦。

最常见的一套基础组合

对大多数有状态系统来说,最典型的组合是:

  • StatefulSet
  • 基于 PVC 的持久存储
  • 通过 StorageClass 提供存储策略
  • 用 Headless Service 提供稳定 DNS
  • 集群外或集群级的备份恢复流程

所以这篇应该和 StatefulSet、PV 与 PVC、StorageClass 和 Headless Service 一起看。

为什么有状态比无状态难很多

无状态系统更关心的是“副本坏了能不能替换掉”。

有状态系统更关心的是:

  • 哪个副本是谁
  • 数据到底在哪
  • 恢复顺序是什么
  • 发布时拓扑会不会被打乱

所以真正难点通常落在恢复时间和操作正确性。

用一个可删除重建的实验验证身份和卷

下面不运行数据库,只验证 StatefulSet 最基础的两个承诺:Pod 名称稳定,每个副本绑定自己的 PVC。保存为 stateful-demo.yaml:

apiVersion: v1
kind: Service
metadata:
  name: stateful-demo
spec:
  clusterIP: None
  selector:
    app: stateful-demo
  ports:
    - name: peer
      port: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: stateful-demo
spec:
  serviceName: stateful-demo
  replicas: 2
  selector:
    matchLabels:
      app: stateful-demo
  template:
    metadata:
      labels:
        app: stateful-demo
    spec:
      containers:
        - name: app
          image: busybox:1.36
          command: ["sh", "-c"]
          args:
            - 'echo "$(hostname)" > /data/identity; while true; do sleep 3600; done'
          volumeMounts:
            - name: data
              mountPath: /data
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 1Gi

默认 StorageClass 可用时,执行:

kubectl apply -f stateful-demo.yaml
kubectl get pod,pvc -l app=stateful-demo
kubectl exec stateful-demo-0 -- sh -c 'date -Iseconds > /data/marker'
kubectl delete pod stateful-demo-0
kubectl wait --for=condition=Ready pod/stateful-demo-0 --timeout=5m
kubectl exec stateful-demo-0 -- cat /data/identity /data/marker

最后一条命令应同时看到稳定的 stateful-demo-0 身份和删除前写入的 marker。这个实验只证明 Pod 重建后重新挂载原 PVC,不证明跨节点存储可用、数据库复制正确或备份能够恢复。

一开始就该问清楚的拓扑问题

在把一个有状态系统当生产级别对待之前,最好先回答这些问题:

  • 它是单主还是多主?
  • 客户端怎么发现主节点?
  • 副本怎么追数据?
  • 节点挂了以后会发生什么?
  • 新旧版本能不能在升级期共存?

Kubernetes 能把工作负载跑起来,但不会替你解决数据库复制和共识问题。

存储规划本身就是应用设计的一部分

多数有状态系统都应该是一副本一 PVC。共享卷看起来省事,但除非应用本身明确支持,否则很容易引发性能或一致性问题。

这也意味着容量规划最好按副本来做,而不是只按“整套系统大概需要多少盘”来想。

备份不能只是挂在嘴上

PVC 不等于备份。

你仍然需要:

  • 逻辑备份或快照
  • 恢复演练
  • 备份保留策略
  • 把备份放到集群外

很多团队虽然有备份文件,却没有演练过真实恢复时的顺序和时机。

升级节奏必须更慢一点

有状态升级通常更慢,因为你要保护更多东西:

  • 成员关系
  • 复制延迟
  • 卷 attach / mount 时间
  • 预热和 readiness 时间

如果你的发布流程只适合无状态 Deployment,那它往往还不够成熟去承载真正的有状态系统。

几个特别值钱的操作习惯

  • 先单副本跑顺,再扩多副本
  • readiness 必须能代表真实可用性
  • 尽量把副本分散到不同节点
  • 监控磁盘使用率和复制延迟
  • 在事故发生前写好 failover / restore 步骤

很多所谓“高可用”,最终依赖这些习惯持续积累。

三个上线判断

Q: 数据库真的适合跑在 Kubernetes 上吗? A: 可以,但前提是你把存储、复制、恢复和升级都当成一等公民来看,而不是只把它当一个能起起来的 Pod。

Q: 有状态应用最大的风险是什么? A: 常见情况是 workload 能启动,但恢复比团队预期慢很多,或者恢复步骤从未真正演练过。

Q: 为什么恢复演练要提前做? A: 因为关键在于能否按正确顺序、在可接受时间内把服务恢复回来。

把控制器和存储链路接起来

最低验收线

至少完成 Pod 删除、节点维护、卷重新挂载和备份恢复四类演练,并记录恢复时间。Kubernetes 能组织身份、调度和持久化,应用复制与数据恢复仍由应用、Operator 和运维流程负责。

参考链接