存储与 CSI¶
容器根文件系统随 Pod 重建而变化。日志缓存等临时数据可以跟随 Pod 消失,数据库文件、上传附件和队列数据则需要持久存储。Kubernetes 把“应用需要多大的卷”与“底层由 NFS、Ceph、云盘还是本地盘提供”分离开。
使用 RBD CSI 或 CephFS CSI 前,应先理解底层 Pool、PG、CRUSH 和故障域,参见 Ceph。
Ceph CSI 的部署方式、Controller/Node Plugin 架构以及 PVC 完整挂载流程参见 Rook、Ceph 与 Kubernetes CSI。
对象关系¶
Pod / StatefulSet
→ PVC:应用提出容量、访问模式等要求
→ StorageClass:选择存储类型和动态制备参数
→ CSI Driver:调用实际存储系统创建、挂载、扩容卷
→ PV:集群中已制备的持久卷
| 对象 | 作用 | 运维关注点 |
|---|---|---|
| Volume | Pod 内可挂载的卷定义 | 生命周期由卷类型决定 |
| PV | 集群级持久卷资源 | 容量、访问模式、回收策略、后端标识 |
| PVC | Namespace 内的存储申请 | 是否 Bound、使用哪个 StorageClass |
| StorageClass | 动态制备模板 | CSI provisioner、参数、绑定和回收策略 |
| CSI | Kubernetes 与存储系统的标准接口 | 控制器、节点插件及其日志 |
emptyDir 适合临时文件和容器间共享,Pod 被删除后数据消失。configMap、secret 和 projected 卷用于配置,不是业务数据盘。hostPath 绑定节点目录,Pod 漂移后无法保证数据仍可用,生产业务应谨慎使用。
动态申请持久卷¶
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
namespace: app
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-block
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: app
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: registry.example.com/app:1.0.0
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: app-data
PVC 只有处于 Bound 后,Pod 才能正常挂载。ReadWriteOnce 表示卷可被一个节点读写,不等于只能被一个 Pod 使用;底层存储是否支持跨节点、多写,必须看 CSI 驱动和存储产品能力。
StorageClass 的关键策略¶
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-block
provisioner: csi.example.com
reclaimPolicy: Retain
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete:PVC 删除后通常连同后端卷删除,适合可重建数据,但风险高。reclaimPolicy: Retain:保留 PV 和后端数据,需人工清理或重新绑定。WaitForFirstConsumer:等 Pod 调度时再按可用区/节点拓扑制备卷,避免卷建在错误故障域。allowVolumeExpansion:允许扩 PVC;文件系统是否在线扩容仍取决于驱动和卷类型。
StatefulSet 与独立数据卷¶
有状态副本通常需要稳定名称和每副本独立 PVC:
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-block
resources:
requests:
storage: 50Gi
生成的 PVC 类似 data-mysql-0、data-mysql-1。缩容或删除 StatefulSet 通常不会自动删除这些 PVC,这是为了保护数据,也意味着扩缩容和清理前必须核对卷归属。
扩容、快照与备份¶
扩容先修改 PVC 请求容量,再观察 PVC、PV 和文件系统状态:
kubectl patch pvc app-data -n app \
-p '{"spec":{"resources":{"requests":{"storage":"40Gi"}}}}'
kubectl get pvc,pv -n app
kubectl describe pvc app-data -n app
VolumeSnapshot 依赖 CSI 快照能力和快照控制器。快照不天然等于应用一致性备份:数据库仍需配合停写、日志归档或数据库原生备份。生产恢复方案至少要明确备份对象、异地保存、保留周期、RPO/RTO 和定期恢复演练。
排障顺序¶
kubectl get storageclass
kubectl get pvc -A
kubectl describe pvc app-data -n app
kubectl get pv
kubectl describe pod <pod> -n app
kubectl get events -n app --sort-by=.lastTimestamp
kubectl get pods -A | grep -i csi
| 现象 | 重点检查 |
|---|---|
| PVC 一直 Pending | StorageClass 名称、provisioner、容量/访问模式、CSI 控制器事件、拓扑限制 |
| Pod Pending | PVC 未 Bound,或 WaitForFirstConsumer 与调度条件互相限制 |
| FailedMount/Attach | CSI 节点插件、卷是否仍挂在旧节点、节点网络和权限 |
| 挂载成功但无法写 | 容器用户、fsGroup、目录权限、只读挂载、存储端 ACL |
| 扩容无变化 | StorageClass 是否允许扩容、驱动能力、PVC condition、文件系统扩容状态 |
| 节点故障后卷无法迁移 | 后端存储可达性、VolumeAttachment、故障域和强制解绑风险 |
不要为了解决挂载失败直接删除 PVC/PV。先记录 PV 的后端卷标识、回收策略和备份状态,再决定解绑或重建。
官方参考:Persistent Volumes、CSI。