实验:ceph-csi 与 gpfs-csi 接入
把后端存储真正接进 K8s,并跑通快照。
学完这节你能做到
- 部署 ceph-csi 并创建可用的 StorageClass
- 接入 gpfs-csi 并挂载已有文件系统
- 配置 VolumeSnapshot 并完成一次恢复
这一节要做什么
把后端存储真正接进 K8s,跑通「PVC → Pod → 写数据 → 快照 → 恢复」的完整闭环。分三部分:ceph-csi、gpfs-csi、VolumeSnapshot。
一、ceph-csi-rbd
1. Ceph 侧准备
# 创建专用池
ceph osd pool create kube
ceph osd pool application enable kube rbd
rbd pool init kube
# 创建 CSI 专用用户(注意 pool 名要和 StorageClass 一致)
ceph auth get-or-create client.kubernetes \
mon 'profile rbd' \
osd 'profile rbd pool=kube' \
mgr 'profile rbd pool=kube'
# 记下这三样东西
ceph fsid # clusterID
ceph mon dump # monitors 地址
ceph auth get-key client.kubernetes # userKey
2. K8s 侧配置
# ConfigMap:告诉 CSI 去连哪个集群
apiVersion: v1
kind: ConfigMap
metadata:
name: ceph-csi-config
data:
config.json: |-
[{
"clusterID": "8d2f1c3a-7b41-4c9e-9f2a-5c6d8e0a1b23",
"monitors": ["10.0.20.1:6789", "10.0.20.2:6789", "10.0.20.3:6789"]
}]
---
apiVersion: v1
kind: Secret
metadata:
name: csi-rbd-secret
stringData:
userID: kubernetes
userKey: AQBxxxxxxxxxxxxxxxxxxxxxxxxxx==
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: block-ceph
provisioner: rbd.csi.ceph.com
parameters:
clusterID: 8d2f1c3a-7b41-4c9e-9f2a-5c6d8e0a1b23
pool: kube # ← 必须与用户 caps 里的池一致
imageFeatures: layering
csi.fstype: xfs
csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
csi.storage.k8s.io/provisioner-secret-namespace: default
csi.storage.k8s.io/controller-expand-secret-name: csi-rbd-secret
csi.storage.k8s.io/controller-expand-secret-namespace: default
csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
csi.storage.k8s.io/node-stage-secret-namespace: default
reclaimPolicy: Retain
allowVolumeExpansion: true
volumeBindingMode: Immediate
ceph osd pool create创建的池名ceph auth里 caps 限定的pool=- StorageClass 的
parameters.pool
任何一处不一致,结果就是上一节演练里那个 Permission denied。接入时把这三处抄在一起对照一遍,能省掉一次排障。
3. CephFS 的差别
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: shared-ceph
provisioner: cephfs.csi.ceph.com # ← 驱动不同
parameters:
clusterID: 8d2f1c3a-7b41-4c9e-9f2a-5c6d8e0a1b23
fsName: prod # ← 文件系统名,RBD 没有这项
pool: cephfs.prod.data
csi.storage.k8s.io/provisioner-secret-name: csi-cephfs-secret
csi.storage.k8s.io/provisioner-secret-namespace: default
reclaimPolicy: Retain
allowVolumeExpansion: true
CephFS 的用户权限也不一样:
ceph fs authorize prod client.kubernetes-cephfs / rw
用这个 SC 创建的 PVC 支持 ReadWriteMany。
二、gpfs-csi
GPFS CSI 的架构和 ceph-csi 有个关键差别:它通过 GPFS GUI 的 REST API 来操作存储,而不是直连存储协议。所以前置条件多一项:
前置条件:
1. GPFS 存储集群已部署
2. GPFS GUI 服务已部署(CSI 依赖它的 REST API)
3. 所有 K8s 节点已挂载 GPFS 文件系统(例如 /mnt/gpfs1)
部署方式有两种,选择会影响后续复杂度:
| 方式 | 要求 | 特点 |
|---|---|---|
| 所有客户端加入服务端集群 | 客户端节点加入存储集群、hosts 全一致、客户端与服务端 quorum 节点免密、共用一套 GUI | 简单,适合中小环境 |
| 服务端与客户端集群分离 | 两个集群各自部署、各自 hosts 一致、客户端有独立 quorum、两侧 quorum 互相免密、各自独立 GUI | 复杂,但隔离性好,是多租户方案的基础 |
GPFS 的管理操作都是 mm* 命令,需要在集群节点上以 root 执行。CSI 驱动跑在容器里,不方便直接调这些命令,于是 IBM 让 CSI 走 GUI 提供的 REST API。
这带来两个运维影响:
- GUI 挂了 CSI 就不能创建卷(已挂载的卷不受影响)—— GUI 成了一个需要监控的关键组件
- GUI 的权限模型不能按文件系统维度细分,这正是上一节多租户方案里"独立文件系统方案用不了原生 GPFS CSI"的原因
三、VolumeSnapshot
快照是跨驱动的通用能力,需要额外部署两个组件。
# 1. 部署 CRD 和 snapshot-controller(集群级,一次性)
kubectl apply -k volumesnapshots/crd/
kubectl apply -k volumesnapshots/snapshot-controller/
# 2. CSI 驱动的 controller 里要有 csi-snapshotter sidecar
kubectl -n <ns> get deploy <csi-controller> -o jsonpath='{.spec.template.spec.containers[*].name}'
# 3. VolumeSnapshotClass
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: csi-rbdplugin-snapclass
driver: rbd.csi.ceph.com
parameters:
clusterID: 8d2f1c3a-7b41-4c9e-9f2a-5c6d8e0a1b23
csi.storage.k8s.io/snapshotter-secret-name: csi-rbd-secret
csi.storage.k8s.io/snapshotter-secret-namespace: default
deletionPolicy: Delete
端到端验证
这套流程建议亲手跑一遍,它能一次性验证所有环节:
# 1. 创建 PVC 并写入数据
kubectl apply -f pvc.yaml
kubectl apply -f pod.yaml
kubectl exec -it test-pod -- sh -c 'echo "hello 2026-08-13" > /data/marker.txt'
kubectl exec -it test-pod -- cat /data/marker.txt
# 2. 打快照
cat <<'EOF' | kubectl apply -f -
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: data-snap-1
spec:
volumeSnapshotClassName: csi-rbdplugin-snapclass
source:
persistentVolumeClaimName: data-pvc
EOF
kubectl get volumesnapshot
# READYTOUSE 必须是 true,否则看 snapshot-controller 日志
# 3. 破坏数据
kubectl exec -it test-pod -- sh -c 'echo "corrupted" > /data/marker.txt'
# 4. 从快照恢复成新 PVC
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: restored-pvc
spec:
storageClassName: block-ceph
dataSource:
name: data-snap-1
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 20Gi
EOF
# 5. 挂上新 PVC 验证内容
kubectl exec -it restore-pod -- cat /data/marker.txt
# 应该看到 "hello 2026-08-13"
K8s 的 VolumeSnapshot 恢复语义是:用快照作为数据源创建一个新卷。原来的 PVC 不受影响。
所以恢复流程通常是:从快照建新 PVC → 修改 Deployment 指向新 PVC → 重启 Pod。这个流程要提前演练并写进 SOP,不要等真出事时才第一次做。
另外提醒和前面几节一致:快照与源数据在同一套存储上,不能替代异地备份。
不管什么驱动,症状对应的排查位置是固定的:
| 症状 | 看哪里 |
|---|---|
| PVC Pending | controller 的 csi-provisioner sidecar 日志 |
| Pod 卡在 ContainerCreating | 该 Pod 所在节点的 node plugin 日志 |
| 扩容不生效 | csi-resizer sidecar 日志 + SC 的 allowVolumeExpansion |
| 快照 READYTOUSE 为 false | csi-snapshotter sidecar + snapshot-controller 日志 |
| 删了 PVC 但后端空间没释放 | reclaimPolicy 是不是 Retain(那就是预期行为) |
部署 gpfs-csi 前,除了 GPFS 存储集群本身,还必须准备什么?
Pod 一直卡在 ContainerCreating,PVC 已经是 Bound。该看哪里的日志?
关于 K8s 快照恢复,下面哪些说法正确?(多选)
这节课的落点
- ceph-csi 的三处 pool 名(建池 / caps / StorageClass)必须完全一致
- CephFS 的 SC 多一项
fsName,且支持 RWX - GPFS CSI 依赖 GUI 的 REST API,GUI 是需要监控的关键组件
- 快照需要 CRD + snapshot-controller + csi-snapshotter sidecar 三件套
- K8s 快照恢复出的是新 PVC,要提前演练切换流程
- 通用排障分界:创建卷看 controller,挂载卷看对应节点的 node plugin
延伸资料
- ·k8s-in-action
storage/ceph-csi-rbd/ - ·k8s-in-action
storage/gpfs-csi/ - ·k8s-in-action
storage/volumesnapshots/ - ·k8s-in-action
storage/ceph-snapshot/