【架构实战】Kubernetes存储实战:从EmptyDir到Ceph CSI的持久化存储选型指南

【架构实战】Kubernetes存储实战:从EmptyDir到Ceph CSI的持久化存储选型指南

上篇聊了K8s故障排查,这篇接上存储------这是K8s里最容易踩坑、也最容易被低估的部分。很多人以为"挂个PV就行了",结果生产环境遇到:数据库被调度到新节点数据丢失、读写分离应用连不到同一个PVC、存储性能瓶颈拖垮整个集群......存储选型不对,轻则发布抖动,重则数据永失。

这篇把K8s存储从原理到选型讲透,帮你避开那些"文档里没写但踩了就炸"的坑。

一、先理解PV/PVC的抽象价值

很多人觉得PV/PVC是"多此一举",直接挂路径不就行了?不对。PV/PVC解决的核心问题是存储供给与消费解耦

  • PV(Persistent Volume):存储管理员视角,描述"我有块存储,100GB,性能XX,类型是NFS/Ceph/iSCSI",不关心谁用;
  • PVC(Persistent Volume Claim):应用开发者视角,声明"我要100GB,读写模式RWO,存储类SSD",不关心底层是谁提供的;
  • StorageClass:存储类,定义"这类存储怎么动态创建PV",把管理员从手动创建PV的苦海中解放出来。

三者的关系:PVC绑定PV,PV由StorageClass动态创建(或管理员手动创建),Pod通过PVC使用存储。一层抽象一层自由------应用开发者不用关心底层是NFS还是Ceph,存储管理员不用关心哪个应用用哪块盘。

二、临时存储:EmptyDir与HostPath

2.1 EmptyDir:Pod内的临时共享空间

EmptyDir在Pod创建时分配,Pod删除时数据随之消失。典型场景:

  • 缓存目录(不需要持久化);
  • 临时计算结果(MapReduce中间文件);
  • InitContainer与主容器共享数据(配置生成、代码拉取)。
yaml 复制代码
volumes:
- name: cache-volume
  emptyDir: {}          # 默认用节点磁盘

# 也可以用内存(更快但大小受Pod内存限制)
volumes:
- name: tmp-volume
  emptyDir:
    medium: Memory
    sizeLimit: 512Mi

注意 :EmptyDir的生命周期绑死Pod,不是容器。Pod里所有容器共享同一个EmptyDir,容器重启数据不丢,Pod删除数据才消失。

2.2 HostPath:挂节点路径(慎用)

HostPath把节点上的路径挂到Pod里,常见于:

  • 节点监控Agent(访问/proc、/sys);
  • 日志采集(访问/var/log);
  • 单机测试环境(绕过网络存储)。
yaml 复制代码
volumes:
- name: node-logs
  hostPath:
    path: /var/log
    type: Directory

致命风险 :HostPath绕过了K8s的存储抽象,数据与节点绑定,Pod被调度到新节点就找不到原数据。生产环境慎用甚至禁用(通过PodSecurityPolicy限制)。我见过多次事故:数据库用HostPath,节点故障迁移后数据"消失",实际还在旧节点上。

三、网络存储:NFS、Ceph、GlusterFS

3.1 NFS:最简单但也最脆弱

NFS是K8s里最常见的共享存储,配置简单,一个PV多个Pod同时读写。

yaml 复制代码
volumes:
- name: nfs-volume
  nfs:
    server: 192.168.1.100
    path: /data/shared
    readOnly: false

优点

  • 部署简单,一台NFS服务器即可;
  • 支持ReadWriteMany(多Pod同时读写);
  • 数据不在节点上,Pod迁移不影响数据。

缺点

  • 单点故障:NFS服务器挂了,所有依赖它的Pod全废;
  • 性能瓶颈:NFS协议本身开销大,高并发写容易卡顿;
  • 文件锁不可靠:某些应用(如SQLite)依赖文件锁,NFS的锁机制在Pod跨节点时有坑。

适用场景 :配置中心、静态资源、日志归档、对性能要求不高的共享数据。数据库、消息队列这种高IO应用别用NFS

3.2 Ceph RBD:高性能块存储

Ceph是分布式存储系统,RBD(RADOS Block Device)是其块存储接口,在K8s里用StorageClass动态创建PV。

yaml 复制代码
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ceph-rbd
provisioner: rbd.csi.ceph.com
parameters:
  pool: rbd
  clusterID: ceph-cluster
  csi.storage.k8s.io/provisioner-secret-name: ceph-secret
  csi.storage.k8s.io/node-stage-secret-name: ceph-secret
reclaimPolicy: Delete
allowVolumeExpansion: true

优点

  • 高性能:块设备直接映射到Pod,接近本地盘性能;
  • 高可用:Ceph多副本,节点故障数据不丢;
  • 动态扩容:PVC扩容后Ceph自动扩展RBD镜像。

缺点

  • ReadWriteOnce:一个PVC只能被一个Pod挂载(块存储特性),多Pod共享需要用CephFS;
  • 部署复杂:Ceph集群本身要搭、要运维,门槛不低;
  • 脑裂风险:网络抖动时可能触发OSD切换,IO短暂中断。

适用场景:数据库(MySQL、PostgreSQL)、消息队列(Kafka、RocketMQ)、单实例有状态应用。

3.3 CephFS:共享文件系统

CephFS是Ceph的文件系统接口,支持ReadWriteMany,多Pod共享数据。

yaml 复制代码
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: cephfs
provisioner: cephfs.csi.ceph.com
parameters:
  clusterID: ceph-cluster
  fsName: myfs
  pool: cephfs-data

优点 :兼具NFS的共享能力和Ceph的高可用。缺点:性能略逊于RBD,MDS(元数据服务器)是瓶颈。

四、云厂商存储:AWS EBS、阿里云盘、腾讯CBS

4.1 云盘的本质

云厂商的块存储(AWS EBS、阿里云盘、腾讯CBS)本质是远程块设备,通过StorageClass自动创建。

yaml 复制代码
# 阿里云盘示例
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: alicloud-disk-ssd
provisioner: diskplugin.csi.alibabacloud.com
parameters:
  type: cloud_ssd
  regionId: cn-hangzhou
reclaimPolicy: Delete
allowVolumeExpansion: true

优点

  • 免运维:云厂商负责底层高可用;
  • 性能分层:SSD、高效云盘、普通云盘可选;
  • 快照备份:云盘快照一键备份恢复。

缺点

  • ReadWriteOnce:一个PVC只能挂一个节点,节点间迁移有短暂IO中断;
  • 跨可用区受限:云盘绑可用区,Pod跨AZ迁移要额外配置。

适用场景:云上生产环境,数据库、中间件等有状态应用,对性能有要求。

4.2 云盘的多挂特性

部分云厂商支持"多挂盘"(Multi-Attach),一个云盘同时挂到多个节点,但只读。适合只读数据共享场景,如机器学习模型文件。

五、本地存储:Local PV与TopoLVM

5.1 Local PV:极高性能但绑定节点

Local PV是节点本地的磁盘或SSD,性能最高(无网络开销),但Pod被调度到其他节点就找不到数据。

yaml 复制代码
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-ssd
spec:
  capacity:
    storage: 500Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /mnt/ssd
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - node-1

关键点 :Local PV必须配nodeAffinity,绑死节点。调度器看到PVC绑定这个PV后,会自动把Pod调度到对应节点。

适用场景 :高性能数据库(TiDB、Cassandra)、对IO延迟极度敏感的应用。前提:应用自己处理跨节点数据同步(Replica)。

5.2 TopoLVM:动态本地存储

Local PV的痛点是"手动创建PV、手动绑定节点"。TopoLVM是开源方案,自动在节点上创建LVM卷、动态分配PV,支持扩容。

yaml 复制代码
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: topolvm-provisioner
provisioner: topolvm.io
parameters:
  "csi.storage.k8s.io/fstype": "xfs"
volumeBindingMode: WaitForFirstConsumer
allowedTopologies:
- matchLabelExpressions:
  - key: topology.kubernetes.io/zone
    values:
    - zone-1

volumeBindingMode: WaitForFirstConsumer是关键:等Pod调度到节点后,再在那个节点上创建PV,避免"提前创建结果Pod调度不到"的尴尬。

六、CSI插件:K8s存储的统一接口

CSI(Container Storage Interface)是K8s存储的标准接口,所有存储厂商实现CSI插件,K8s通过统一API调用。

6.1 CSI核心组件

  • Node Plugin:运行在每个节点上,负责挂载/卸载卷;
  • Controller Plugin:负责创建/删除卷、快照、扩容;
  • External Provisioner:监听PVC创建事件,调用Controller Plugin创建卷;
  • External Attacher:监听卷附加事件,调用Controller Plugin挂载卷到节点。

6.2 CSI插件的选型依据

选择存储方案时,评估CSI插件成熟度:

维度 检查点
功能完整性 是否支持快照、扩容、克隆?
高可用 Controller是否支持多副本?故障切换时间?
性能 延迟、吞吐、IOPS是否满足应用需求?
运维复杂度 部署、升级、监控、故障排查难度?
社区活跃度 Issue响应速度、Release频率、文档质量?

七、数据安全:快照、备份、容灾

7.1 VolumeSnapshot

CSI标准支持快照,一键备份PVC状态。

yaml 复制代码
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: mysql-snapshot-20260814
spec:
  volumeSnapshotClassName: csi-snapclass
  source:
    persistentVolumeClaimName: mysql-pvc

应用场景

  • 发布前快照,出问题秒回滚;
  • 数据库定期快照,替代脚本导出;
  • 开发测试环境克隆生产数据。

7.2 备份方案:Velero与Stash

Velero是K8s备份开源方案,支持PV备份到对象存储(S3、OSS、COS)。

bash 复制代码
# 备份整个命名空间
velero backup create myapp-backup --include-namespaces myapp

# 恢复
velero restore create --from-backup myapp-backup

Stash是另一款工具,支持应用感知备份(MySQL逻辑备份、MongoDB导出等)。

7.3 容灾:跨集群/跨云

关键应用要做跨集群容灾,方案有:

  • 存储层复制:Ceph的RBD Mirror、GlusterFS的Geo-Replication;
  • 应用层复制:MySQL主从、MongoDB ReplicaSet、TiDB多AZ部署;
  • K8s层迁移:Velero跨集群恢复,但PV要重新挂载。

八、常见故障排查

8.1 PVC一直Pending

bash 复制代码
kubectl describe pvc <pvc-name>

常见原因:

  • StorageClass不存在或provisioner错误;
  • 存储后端资源不足(Ceph OSD满、云盘配额用光);
  • VolumeBindingMode是WaitForFirstConsumer,但没有Pod使用这个PVC。

8.2 Pod挂载失败

bash 复制代码
kubectl describe pod <pod-name>

常见错误:

  • FailedMount:节点上CSI插件异常、存储后端不可达;
  • Volume is already exclusively attached:块存储被其他节点占用,ReadWriteOnce限制。

8.3 性能抖动

  • NFS:网络延迟、NFS服务器负载高;
  • Ceph:OSD抖动、PG分布不均;
  • 云盘:底层共享资源竞争(邻居效应)。

排查工具:

bash 复制代码
# 在Pod里测IO性能
kubectl exec -it <pod> -- fio --name=randwrite --ioengine=libaio --direct=1 --bs=4k --size=1G --rw=randwrite

# 在节点上测磁盘
dd if=/dev/zero of=/mnt/test bs=1M count=1000 oflag=direct

九、选型决策树

最后给一个简化版选型决策树:

复制代码
需要持久化吗?
├─ 否 → EmptyDir
└─ 是
    └─ 需要跨Pod共享吗?
        ├─ 是
        │   └─ 性能要求高吗?
        │       ├─ 高 → CephFS
        │       └─ 低 → NFS
        └─ 否(单Pod独占)
            └─ 性能要求极高吗?
                ├─ 极高 → Local PV / TopoLVM(应用自己处理副本)
                └─ 高 → Ceph RBD / 云盘SSD

记住三句话:共享数据用文件存储(NFS/CephFS),独占数据用块存储(RBD/云盘),极致性能用本地盘但自己管副本

十、小结

K8s存储的复杂性在于:抽象层虽好,但底层物理世界的限制无法回避。ReadWriteOnce是块存储的物理约束,网络延迟是NFS的天花板,Local PV的高性能换来的是节点绑定。选型时,先想清楚应用的需求(共享还是独占、性能还是成本、单AZ还是跨AZ),再选对应方案,别被"一个PV走天下"的幻觉坑了。

存储选对了,数据库迁移不丢数据、发布抖动不抖、性能监控不爆表。选错了,轻则天天排查IO慢、重则关键时刻数据永失。把存储当核心架构决策,而不是"顺带挂个盘"。


下篇预告:Kubernetes安全实战------从RBAC到PodSecurityPolicy的权限控制体系。

相关推荐
QX_hao1 小时前
【Kubernetes】 的三个Probe
容器·kubernetes
Chasing__Dreams2 小时前
大模型应用开发--6--Transformer架构介绍
深度学习·架构·transformer
cfm_29143 小时前
基于Binlog实现不停机数据库平滑迁移技术
运维·数据库·架构
迪康Defender3 小时前
AI 重构终端安全运营:智能分析中枢 AI Insight 模块架构与落地场景深度解析
运维·网络·人工智能·其他·安全·重构·架构
这个DBA有点耶5 小时前
从DBA到数据架构师(五):数据架构演进中的技术债务管理
数据库·程序人生·云原生·架构·dba·数据库管理员
沪上企服通5 小时前
高端财税的技术切面:从“可审计级旧账重建“看企业税务合规中台的架构演进
架构
dogstarhuang6 小时前
大模型 API 停服怎么办:用 API 网关实现多模型统一接入与可切换架构
人工智能·后端·架构·大模型·api·数字化转型·ai应用
猫吃了源码6 小时前
k9s简介和安装
linux·docker·kubernetes
过江龙8478 小时前
Java架构师的AI转型之路(中):Agent与编排体系实战
架构