一句话总结
把前七篇的网络与存储知识, 落到一个真实问题上: 有状态应用怎么在 K8s 里安全跑起来?
StatefulSet 用 volumeClaimTemplates 给每个副本一块独立持久盘; 存储选型在本地盘(快但绑死节点), 网络盘(灵活可漂移), 分布式存储(高可用但重)间权衡; 而"数据库该不该上 K8s"--答案是"看情况", 但要先想清备份与运维这两道坎.
前置回顾 · 全系列收束
这是云原生网络与存储的最后一篇, 也是把全部知识落地的一篇. 前面我们打通了网络(01-05: 从 veth 到 CNI 到零信任)和存储(06-07: 从存储抽象到 CSI 三段式).
现在回到最初的动机--让有状态应用在这个为"无状态, 易逝"而生的系统里安全地保存数据. 本篇把网络, 存储, 调度的知识拧成真实的选型决策.
有状态应用为什么是 K8s 的"硬骨头"
K8s 天生为无状态应用设计: Deployment 管理一堆完全对等, 可随意替换的 Pod--挂一个补一个, 谁是谁无所谓. 但有状态应用(数据库, 消息队列, 分布式存储)有三个 Deployment 满足不了的诉求:
|
这三个诉求--稳定身份, 有序操作, 独立存储--正是 StatefulSet 这个工作负载(Kubernetes 笔记里见过)存在的理由. 本篇聚焦它和存储结合的那一面.
StatefulSet + volumeClaimTemplates: 每个副本一块独立盘
关键机制是 volumeClaimTemplates(卷申领模板). 普通 Deployment 如果引用一个 PVC, 所有副本会共用同一个 PVC(对数据库是灾难--多个实例写同一份数据文件会损坏). StatefulSet 则用模板为每个副本自动创建一个独立的 PVC:
|
这个模板的效果是: K8s 给每个副本生成一块专属, 稳定绑定的盘:
|
稳定身份 + 专属存储 = 有状态的根基
StatefulSet 的精髓: Pod 有稳定的序号身份(postgres-0/1/2, 而非 Deployment 那种随机后缀), 每个身份终身绑定自己的 PVC.
于是 postgres-0 不管重建多少次, 漂移到哪个节点(只要存储能跟过去, 第 07 篇), 都还是"那个有自己数据的 0 号实例". 这就把第 06 篇"数据活过容器"从单个 Pod 推广到了"一组有身份的实例各自的数据都不丢, 不乱"--这是跑数据库集群的地基.
存储选型: 本地 vs 网络 vs 分布式
给有状态应用选什么底层存储(即第 06 篇 StorageClass 背后是什么), 是核心决策. 三类选择, 又是一组经典权衡:
| 类型 | 是什么 | 优点 | 致命短板 | 适合 |
|---|---|---|---|---|
| 本地存储(Local PV) | 直接用节点本机的磁盘 | 性能最高(本地 NVMe, 无网络开销) | 盘绑死在那台节点, Pod 不能漂移到别的节点; 节点挂了数据就危险 | 对性能极致敏感, 且应用层自己做了多副本(如 Kafka, ES) |
| 网络块存储(云盘等) | 云厂商的 EBS/PD 等, 通过网络挂载 | 可跟随 Pod 漂移(detach/attach, 第 07 篇); 云厂商保证盘本身的可靠性 | 性能不如本地; 仍是 RWO 单节点 | 绝大多数有状态应用的默认选择 |
| 分布式存储(Ceph/Rook 等) | 跨多节点的存储集群, 数据多副本 | 高可用, 支持 RWX, 容量弹性, 能在自建环境提供"类云盘"能力 | 重: 自己运维一套分布式存储系统, 复杂度高 | 自建机房, 需要 RWX 或统一存储平台 |
本地存储的诱惑与陷阱
本地 NVMe 性能诱人, 但它和云原生的"Pod 可漂移"理念根本冲突: 数据绑死在节点 X 上, 那个 Pod 就只能调度回节点 X(第 07 篇: 本地盘没法跨节点 attach). 一旦节点 X 宕机, 这个实例和它的数据就一起趴窝.
所以用本地存储的前提是--应用层自己有多副本和数据冗余(像 Kafka/ES/Cassandra 这种本就在应用层做了分片复制的), 单个节点挂了不影响整体. 如果是单实例数据库用本地盘, 等于把鸡蛋全放一个会随时摔碎的篮子里.
Rook: 在 K8s 上跑 Ceph 的'运维大脑'
自建环境想要"类云盘"的网络/分布式存储, 常用 Ceph(强大的开源分布式存储). 但 Ceph 运维复杂, 于是有了 Rook--它是一个 K8s Operator, 用声明式方式把 Ceph 的部署, 扩容, 故障恢复自动化(声明"我要一个 Ceph 集群", Rook 帮维护).
Rook 让 Ceph 通过 CSI(第 07 篇)给集群提供块/文件/对象三种存储. 这又是声明式 + Operator 模式(Kubernetes 笔记)的应用--把复杂运维知识沉淀进控制器.
灵魂拷问: 数据库到底该不该上 Kubernetes
这是有状态领域最有争议的问题. 先给结论: 没有标准答案, 取决于运维能力和数据库类型, 但要先想清两道坎.
两种立场, 各有道理:
|
务实的判断框架
与其纠结立场, 不如问自己几个问题:
- 有没有靠谱的 Operator?(如 CloudNativePG, Zalando postgres-operator--它们把主从复制, 故障转移, 备份自动化了, 没有 Operator 裸跑数据库风险很大)
- 团队的备份/恢复演练做到位了吗?(见下节)
- 是核心交易库还是边缘库?(核心库求稳, 可以先用云托管 RDS; 边缘库, 开发测试库上 K8s 试水)
趋势是成熟 Operator 让"数据库上 K8s"越来越可行, 但它要求的运维成熟度, 远高于跑无状态应用. 别因为"云原生"就盲目把核心数据库搬上去.
绕不开的最后一道坎: 备份
无论数据库在不在 K8s 上, 有状态应用都有一条铁律: 持久化(Persistence)不等于备份(Backup). 这是最致命的误区之一:
PV 把数据存下来了 ≠ 数据安全了
PV/PVC 解决的是"数据活过容器重建"(第 06 篇), 但它救不了这些: 误删了一张表(数据还在盘上, 但内容错了), 误删了 PVC 且回收策略是 Delete(第 06 篇, 盘连数据一起没了), 存储后端整体故障, 勒索软件加密.
这些都需要"另一份独立的, 时间点的副本"--也就是备份. 持久化是"防止容器重启丢数据", 备份是"防止数据本身出错或丢失", 两者解决完全不同的问题, 缺一不可.
K8s 环境下的备份手段, 正好用上前面的知识:
- 卷快照(第 07 篇 CSI Snapshot): 给数据盘拍时间点快照, 可快速恢复或克隆. 注意快照通常和原卷在同一存储后端, 不能替代异地备份.
- 应用层逻辑备份: 如
pg_dump导出数据库, 存到对象存储(第 06 篇, S3 这类异地, 便宜, 耐久). - 专用工具: 如 Velero(备份整个 K8s 资源 + PV 数据), 各数据库 Operator 自带的备份能力.
没演练过恢复的备份, 等于没有备份
这是 SRE 的血泪经验(呼应可观测性系列第 08 篇的复盘文化): 很多团队"有备份", 但从没试过恢复--真出事时才发现备份是坏的, 流程没人会, 恢复要十几个小时.
备份的价值不在"备了", 而在"能在可接受时间内恢复". 定期做恢复演练, 把它写进 runbook, 才是真正的数据安全.
全系列收束: 两套对称的可插拔体系
回望这八篇, 云原生的网络与存储其实讲的是同一个故事的两半:
|
贯穿始终的是那条主线: Kubernetes 自己不实现网络和存储, 而是定义 CNI / CSI 标准接口, 把实现外包给可插拔的插件. 正是这套"机制与策略分离"的设计, 让 K8s 核心保持精简, 而网络与存储生态各自繁荣, 自由竞争与替换. 所有眼花缭乱的选择(Flannel/Calico/Cilium, EBS/Ceph/NFS), 本质都是同一个接口下的不同实现--抓住接口, 就抓住了不变的骨架.
快速回顾
- 有状态的三诉求: 稳定身份, 有序操作, 独立存储--Deployment 满足不了, 要用 StatefulSet
- volumeClaimTemplates: 为每个副本自动建独立 PVC, 实例序号与数据盘终身绑定(postgres-0 永远绑 data-postgres-0), 数据不乱
- 存储选型: 本地盘(最快但绑死节点, 需应用层多副本) / 网络块存储(可漂移, 默认选择) / 分布式如 Ceph+Rook(高可用支持 RWX 但运维重)
- 数据库上 K8s: 无标准答案, 关键看有无成熟 Operator, 备份演练是否到位, 是否核心库; Operator 让它越来越可行但运维门槛高
- 持久化 ≠ 备份: PV 防容器重启丢数据, 救不了误删/逻辑错误/后端故障; 需独立时间点备份(CSI 快照/pg_dump 到对象存储/Velero)
- 没演练过恢复的备份等于没有; 全系列主线--CNI/CSI 把网络与存储外包给可插拔插件, 机制/策略分离
StatefulSet 缩容(比如 3 副本减到 1)时, 那些 PVC 和数据会被自动删掉吗?
默认不会--这是个贴心的安全设计. StatefulSet 缩容时, 删掉的 Pod 对应的 PVC 默认保留(不自动删除), 数据还在. 这样误缩容, 或之后想扩回来时, 数据能复用, 不丢.
代价是会留下"孤儿 PVC"占用存储, 需要确认数据不要了之后手动清理.(较新版本 K8s 提供了 persistentVolumeClaimRetentionPolicy 可配置缩容/删除时是否自动清 PVC, 但默认仍偏保守保留.) 对有状态应用, "宁可留着也别自动删数据"是正确的默认.
既然数据库上 K8s 这么麻烦, 为什么不直接都用云托管数据库(RDS)?
云托管(RDS/CloudSQL 等)确实是很多场景的省心之选--云厂商替我们搞定高可用, 备份, 补丁. 该不该用它, 权衡点是:
- 用云托管省运维, 但成本通常更高, 被云厂商绑定, 灵活性受限(版本, 插件, 参数)
- 自己在 K8s 上跑则相反--更可控, 可移植(混合云/自建), 能和应用同栈管理, 但要自担运维
常见策略: 核心生产库用云托管求稳, 非核心库/有多云诉求/自建机房的场景用 K8s. 没有绝对优劣, 这又是一道"省心 vs 可控"的权衡--和本系列(乃至 CI/CD, 可观测性)反复出现的选型逻辑一脉相承.
动手练习
- 验证每副本独立 PVC: 用 StatefulSet 部署一个 3 副本的有状态应用(如 PostgreSQL 或简单的 nginx+PVC), 观察
kubectl get pvc: 确认每个副本各有一个独立 PVC(data-xxx-0/1/2) - 验证身份与数据绑定: 往 xxx-0 的数据目录写点东西, 删除 xxx-0 这个 Pod, 等它重建后确认数据还在, 且仍绑回 data-xxx-0--验证"身份与数据终身绑定"
- 判断存储类型: 查看集群默认 StorageClass 背后是哪类存储(本地/网络/分布式), 用本篇的权衡表判断它适合什么场景
- 亲手验证备份: 用 CSI 快照(第 07 篇)给数据盘拍一个快照, 然后故意删掉数据, 再从快照恢复--亲手验证"备份能救命", 并体会"持久化 ≠ 备份"
- 实际选型分析: 写一段话回答: 团队现在的(或计划中的)数据库, 该上 K8s 还是用云托管? 用本篇的判断框架(Operator, 备份演练, 核心与否)给出理由