CephFS 是 Ceph 生态中的分布式文件系统,与块存储 RBD 不同,它可以同时被多个节点挂载读写,这一点恰好契合 Kubernetes 中跨节点调度 Pod 的场景。当 StatefulSet 副本需要迁移、多个 Pod 需要共享同一目录的数据,或者需要为 AI 训练任务提供海量小文件的读写能力时,CephFS 搭配 CSI 驱动都能提供一个相对省心的动态供给方案。本文结合一次完整的落地过程,从原理、部署到踩坑排查,把关键环节逐一展开。

CephFS 的架构原理与适用场景
先看清楚 CephFS 内部长什么样,后面的排错会轻松很多。CephFS 由三个角色协同工作:Metadata Server(MDS)负责管理文件系统的元数据,比如目录树、inode、文件权限等;Ceph OSD 集群负责存储实际的文件数据;Monitor 集群则维护整个集群的拓扑和状态。MDS 是有状态的,可以部署多个形成 active-standby 结构,元数据请求才会被正确处理,如果所有 MDS 都处于 standby 状态,客户端挂载会直接失败。
CephFS 与 RBD 最核心的区别在于访问模式。RBD 卷同一时刻只能被一个节点以读写方式附加,对应 Kubernetes 里的 ReadWriteOnce;而 CephFS 天然支持多客户端并发挂载,对应 ReadWriteMany 和 ReadOnlyMany。所以选型时可以简单判断:数据库这类独占磁盘 IO 的负载用 RBD,共享目录、日志汇总、模型训练数据集这类多 Pod 共读共写的负载用 CephFS。另外 CephFS 的容量是弹性伸缩的,不像 RBD 卷需要预先指定大小,不过通过 CSI 创建时仍要声明大小,这主要是为了配合配额管理。
客户端访问方式有两种:内核客户端(kernel mount)和 FUSE 客户端(ceph-fuse)。内核客户端性能更好,延迟更低,是 CSI 驱动的默认选择;FUSE 客户端兼容性好,适合内核版本过旧无法支持新特性(比如 quota)的环境。ceph-csi 会优先尝试内核挂载,遇到不支持的特性时可以显式指定为 fuse 模式。
Ceph 集群侧的准备工作
假设你已经有了一套 Luminous 或更新版本的 Ceph 集群(本文以 Pacific 版本为例),第一步是创建文件系统并保证有活跃的 MDS:
ceph fs volume create k8sfs ceph fs ls ceph mds stat
fs volume create 会自动创建存储池和 MDS 服务,省去手工建 pool 的步骤。执行 ceph mds stat 后必须确认输出里有 active 状态的 rank,如果全部是 standby,说明没有激活,需要检查 max_mds 配置。接下来为 CSI 驱动创建专用的认证用户,权限最小化原则很重要:
ceph fs authorize k8sfs client.k8scsi / rwps ceph auth get client.k8scsi
拿到用户密钥后,把它导入到 Kubernetes 的 Secret 里。CSI 驱动同时需要 admin 或者具备完整 caps 权限的用户来创建子卷(subvolume),所以一般会准备两组凭据:一组用于 Provisioner 创建卷,一组用于 NodeStageVolume 阶段挂载。简单点可以直接用 admin 密钥,但生产环境建议单独建一个仅授予必要权限的用户。Secret 的关键内容如下:
apiVersion: v1 kind: Secret metadata: name: csi-cephfs-secret namespace: kube-system stringData: userID: k8scsi userKey: AQxxxxxxxxxxxxxxxxxxxxx== adminID: admin adminKey: AQyyyyyyyyyyyyyyyyyyyy==
部署 ceph-csi 并配置动态供给
ceph-csi 的部署文件可以直接从官方仓库获取,需要部署三个组件:运行在各节点上的 cephfs 插件 DaemonSet、负责监听 PVC 并调用 Ceph 创建子卷的 Provisioner Deployment,以及 CSIDriver 对象。把仓库里 deploy/cephfs 目录下的 yaml 依次 apply 即可,注意把 ConfigMap 中的 clusterID 改成自己集群的 fsid,这个值可以通过 ceph fsid 查到。
组件就绪后,创建 StorageClass,这里的关键参数是 clusterID、fsName 和 Secret 引用:
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: cephfs-sc provisioner: cephfs.csi.ceph.com parameters: clusterID: 1234abcd-5678-ef90-1234-abcdef123456 fsName: k8sfs csi.storage.k8s.io/provisioner-secret-name: csi-cephfs-secret csi.storage.k8s.io/provisioner-secret-namespace: kube-system csi.storage.k8s.io/node-stage-secret-name: csi-cephfs-secret csi.storage.k8s.io/node-stage-secret-namespace: kube-system reclaimPolicy: Delete allowVolumeExpansion: true
接下来创建 PVC 验证整条链路:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: shared-data
spec:
accessModes:
- ReadWriteMany
storageClassName: cephfs-sc
resources:
requests:
storage: 100Gi
Pod 挂载这个 PVC 时,CSI 驱动会在 Ceph 侧创建一个 subvolume,实际上是一组带配额的子目录。可以用 ceph fs subvolume ls k8sfs 查看已创建的子卷。验证共享能力很简单:起两个调度到不同节点的 Pod 挂同一个 PVC,在一边写入文件另一边立刻可见,就说明 RWX 生效了。
权限控制、常见报错与生产建议
多租户场景下建议在 StorageClass 中指定 subvolumeGroup,把不同业务团队 PVC 生成的子卷隔离到不同分组,再结合 ceph fs authorize 按子目录授权,避免 A 团队的应用有机会触达 B 团队的数据。Pod 内文件权限方面,可以在 Pod spec 的 securityContext 里设置 fsGroup,让挂载目录的属组符合进程的运行用户,避免出现文件不可写的尴尬。
几个高频报错值得提前了解。挂载卡在 ContainerCreating 并提示加载密钥后仍然挂载失败,多半是内核版本太低不支持 quota 特性,去掉 quota 相关选项或改用 fuse 模式即可。Provisioner 报错 exit code 13 通常是认证失败,检查 Secret 里的密钥是否正确、用户 caps 是否齐全。还有一种情况是节点上残留了失效挂载点导致 Pod 无法启动,处理办法是在对应节点手工 umount 清理,必要时重启 csi 插件 Pod。
生产环境还有三条经验:一是 MDS 至少部署两个实例做高可用,元数据池建议放在 SSD 上,MDS 内存要给足,因为元数据基本全靠内存缓存加速;二是开启 allowVolumeExpansion 后扩容是在线生效的,但只支持扩大不支持缩小;三是做好 Prometheus 对 ceph-exporter 和 CSI 容器指标的采集,MDS 的 CPU 飙升往往是客户端元数据请求模式异常的前兆,提前告警比事后救火成本低得多。
整体来看,CephFS 对接 Kubernetes 的门槛主要在于 Ceph 侧的认证和 CSI 参数配置,一旦跑通一次,后续新增业务只需要创建 PVC 即可。对于已经用 Ceph 做块存储的团队来说,复用同一套集群补齐共享文件系统能力,是很自然也很有性价比的选择。
CephFSKubernetesStorageClass修改时间:2026-09-08 13:29:54