有状态应用往往是 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: 因为关键在于能否按正确顺序、在可接受时间内把服务恢复回来。
把控制器和存储链路接起来
- 接着读 StatefulSet,理解控制器层面的行为。
- 再读 PV 与 PVC 和 StorageClass,把存储链路看完整。
- 数据库案例可继续看 Kubernetes 部署 MySQL 和 MySQL 复制。
最低验收线
至少完成 Pod 删除、节点维护、卷重新挂载和备份恢复四类演练,并记录恢复时间。Kubernetes 能组织身份、调度和持久化,应用复制与数据恢复仍由应用、Operator 和运维流程负责。