Kubernetes 持久化存储与 CSI 生产排障完整指南

Kubernetes 存储问题通常跨越多个层次:Pod、PVC、PV、StorageClass、调度器、CSI 控制器、节点插件、操作系统挂载以及真正的存储后端。只盯着 Pod 事件,很容易在错误的层级反复重启。

本文建立一套从声明到数据面的排障路径,并覆盖动态供给、拓扑、扩容、快照、克隆、多可用区和故障恢复。

一、先理解完整链路

Pod
 └─ PVC
     └─ PV
         └─ CSI volumeHandle
             └─ 存储后端中的真实卷

控制面:
external-provisioner / attacher / resizer / snapshotter

节点面:
node-driver-registrar / CSI node plugin / kubelet / mount

常见对象职责:

对象 作用
StorageClass 指定 provisioner、参数、回收策略和绑定模式
PVC 用户对容量、访问模式和存储类的申请
PV 集群中的卷资源及后端句柄
VolumeAttachment 控制器记录卷与节点的附加关系
CSINode 节点可用 CSI 驱动及拓扑信息
VolumeSnapshotClass 定义快照驱动和删除策略
VolumeSnapshot 用户发起的快照请求
VolumeSnapshotContent 快照在集群与后端中的绑定对象

二、StorageClass设计

1. volumeBindingMode

对有可用区、机架或本地性限制的存储,通常应考虑:

volumeBindingMode: WaitForFirstConsumer

它会在 Pod 被调度时再结合节点拓扑创建卷,避免卷先被创建在错误可用区。Immediate 会在 PVC 创建后立即供给,适合后端真正全局可达的场景。

2. reclaimPolicy

  • Delete:删除 PVC/PV 后通常同时删除后端卷;
  • Retain:保留后端数据,需要人工回收。

生产数据库、审计数据等场景应明确选择,不能依赖默认值。Retain 也不等于备份,它只是不自动删除原卷。

3. allowVolumeExpansion

allowVolumeExpansion: true

这只是允许 PVC 发起扩容,CSI 驱动、后端和文件系统还必须分别支持。

三、建立存储资产清单

kubectl get storageclass
kubectl get pvc -A
kubectl get pv
kubectl get csidriver
kubectl get csinode
kubectl get volumeattachment

建议资产表至少包含:

  • 业务、命名空间和负责人;
  • PVC、PV、StorageClass;
  • volumeHandle 与后端卷 ID;
  • 容量、访问模式、卷模式;
  • 可用区和节点拓扑;
  • 快照与备份策略;
  • RPO、RTO;
  • 回收策略和数据保留要求。

没有后端卷 ID 映射表时,紧急恢复很容易误删或误挂载。

四、PVC一直Pending

1. 看事件

kubectl describe pvc -n <namespace> <pvc>
kubectl get events -n <namespace> --sort-by=.lastTimestamp

2. 判断绑定模式

如果 StorageClass 使用 WaitForFirstConsumer,PVC 在没有消费者 Pod 时保持 Pending 可能是正常行为。

3. 检查调度与拓扑

kubectl describe pod -n <namespace> <pod>
kubectl get csinode -o yaml
kubectl get nodes -L topology.kubernetes.io/zone

常见原因:

  • Pod 的 nodeSelector、亲和性或污点容忍把可选节点限制为空;
  • CSI 驱动未向节点注册拓扑键;
  • 目标区域容量不足;
  • 多个 PVC 的拓扑需求无法在同一节点满足;
  • StorageClass provisioner 名称错误。

4. 检查 provisioner

kubectl get pods -A | grep -i csi
kubectl logs -n <csi-namespace> <controller-pod> -c csi-provisioner --since=30m

检查凭据、API 限流、后端配额、容量、参数和网络。

五、Pod卡在ContainerCreating

Pod 已调度但无法启动,通常进入 attach、stage 或 publish 阶段。

kubectl describe pod -n <namespace> <pod>
kubectl get volumeattachment
kubectl describe volumeattachment <name>
kubectl logs -n <csi-namespace> <node-plugin-pod> --all-containers --since=30m
journalctl -u kubelet --since '-30 min' --no-pager

常见错误与方向:

错误 排查方向
Multi-Attach 卷仍附加在旧节点,或访问模式不允许多节点写入
timeout waiting for attach 控制器、云 API、存储网络或后端任务超时
mount failed 文件系统、设备、权限、挂载参数
rpc error CSI 插件、凭据、连接或版本问题
device busy 残留挂载、进程占用或非正常断开
no space left 节点目录、inode、后端卷或配额不足

六、VolumeAttachment卡住

不要在不了解后端状态时直接删除 VolumeAttachment。

安全顺序:

  1. 确认旧 Pod 和旧节点是否仍访问数据;
  2. 确认后端卷是否真正从旧节点分离;
  3. 检查 CSI attacher 日志;
  4. 检查节点是否失联以及是否需要强制脱离;
  5. 按存储厂商流程处理 fencing;
  6. 后端状态一致后,再处理 Kubernetes 残留对象。

块存储被两个节点同时写入可能损坏文件系统,强制分离必须有明确的数据保护措施。

七、节点侧挂载排障

1. 设备与挂载

lsblk -f
findmnt
mount
df -hT
df -ih

2. 内核与文件系统

dmesg -T | tail -200
journalctl -k --since '-30 min' --no-pager

关注 I/O error、超时、只读重挂载、文件系统错误、multipath、iSCSI/NVMe-oF 连接和设备重置。

3. kubelet目录

只用于观察,不要手工删除:

find /var/lib/kubelet/plugins -maxdepth 3 -type d 2>/dev/null
find /var/lib/kubelet/pods -maxdepth 5 -type d -name volumes 2>/dev/null

如果存在残留挂载,先确认对应 Pod、设备和进程。手工 umount 前应 cordon 节点并评估业务。

八、卷扩容

1. 修改PVC

kubectl patch pvc <pvc> -n <namespace> \
  -p '{"spec":{"resources":{"requests":{"storage":"200Gi"}}}}'

PVC 容量一般只能增大,不能直接缩小。

2. 观察控制面扩容

kubectl describe pvc -n <namespace> <pvc>
kubectl get pvc -n <namespace> <pvc> -o yaml
kubectl logs -n <csi-namespace> <controller-pod> -c csi-resizer --since=30m

3. 观察文件系统扩容

后端块设备扩大后,文件系统还需要由 kubelet、CSI 或人工完成扩容。检查 Pod 内部和节点侧实际容量,不要只看 PVC spec。

常见卡点:

  • StorageClass 未允许扩容;
  • 驱动只支持离线扩容;
  • 后端配额不足;
  • 文件系统类型不支持在线扩容;
  • 节点插件未执行 NodeExpandVolume;
  • PVC 长期处于 FileSystemResizePending。

九、快照不是完整备份

Kubernetes VolumeSnapshot 提供统一的快照接口,但快照质量取决于 CSI 驱动和后端。

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: database-before-change
spec:
  volumeSnapshotClassName: production-snapshot
  source:
    persistentVolumeClaimName: database-data

必须明确:

  • 是崩溃一致性还是应用一致性;
  • 快照是否存放在同一存储系统;
  • 删除策略是 Delete 还是 Retain;
  • 能否跨集群、跨账号、跨区域恢复;
  • 是否支持多卷一致性组快照;
  • 加密密钥是否一并可用。

数据库应在快照前执行冻结、刷盘或数据库原生备份协调。快照与源卷位于同一故障域时,不能替代异地备份。

十、从快照恢复

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: database-restore
spec:
  storageClassName: production-block
  dataSource:
    name: database-before-change
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 200Gi

恢复演练必须验证:

  1. 新 PVC 能创建;
  2. Pod 能挂载;
  3. 文件系统能读取;
  4. 应用能够启动;
  5. 数据时间点符合预期;
  6. 业务校验通过;
  7. 原卷没有被误覆盖。

十一、访问模式容易误解

ReadWriteOnce 表示卷可被单个节点读写,不一定意味着只能被一个 Pod 使用。同一节点上的多个 Pod 是否可用,还取决于驱动和应用。

常见模式:

  • RWO:单节点读写;
  • ROX:多节点只读;
  • RWX:多节点读写;
  • RWOP:单个 Pod 读写。

访问模式不等于文件系统并发安全。即使后端支持 RWX,应用也必须支持多实例同时访问同一数据目录。

十二、Local PV与本地盘

本地盘性能高,但卷与节点绑定。必须设计:

  • 节点故障后的数据恢复;
  • 替换磁盘与重新调度;
  • 拓扑感知;
  • 容量碎片;
  • 节点退役;
  • 备份和副本机制。

Local PV 不应被当作普通网络块存储使用。数据库可依靠自身复制获得高可用,但要演练节点永久丢失后的重建流程。

十三、性能诊断

存储性能不能只看吞吐量。至少区分:

  • IOPS;
  • 带宽;
  • 平均与 P95/P99 延迟;
  • 队列深度;
  • 读写比例;
  • 随机与顺序访问;
  • fsync 延迟;
  • 后端降级或重建状态。

节点侧可结合:

iostat -xz 1
pidstat -d 1
lsblk -o NAME,KNAME,TYPE,SIZE,FSTYPE,MOUNTPOINTS

容器内的慢 I/O 可能来自文件系统、块设备、网络、存储后端、限速策略或邻居负载。不要只根据 await 单一指标下结论。

十四、监控与告警

建议覆盖:

  • PVC Pending 时间;
  • CSI RPC 错误率和延迟;
  • provision、attach、mount、resize、snapshot 成功率;
  • VolumeAttachment 长时间未完成;
  • 节点插件不可用;
  • 后端容量、配额、延迟和错误;
  • 卷吞吐、IOPS、延迟和队列;
  • 快照失败和备份过期;
  • 孤立 PV、无主快照和长期 Terminating 对象。

告警必须关联命名空间、PVC、PV、节点、StorageClass、volumeHandle 和后端卷,才能缩短定位时间。

十五、典型故障:节点宕机后卷无法挂到新节点

现象

  • Pod 已被调度到新节点;
  • 事件显示 Multi-Attach;
  • 旧节点 NotReady;
  • 后端仍显示卷附加在旧主机。

处理

  1. 确认旧节点是否确实失联且不会恢复写入;
  2. 对应用执行 fencing,防止双写;
  3. 检查 VolumeAttachment 和 CSI attacher;
  4. 按后端流程强制分离旧连接;
  5. 确认设备已从旧主机释放;
  6. 允许新节点重新附加;
  7. 检查文件系统并验证应用数据;
  8. 复盘节点失联检测和自动分离策略。

跳过 fencing 直接强制挂载,是最危险的处理方式。

十六、生产检查清单

设计

  • [ ] StorageClass 的绑定和回收策略明确;
  • [ ] 多可用区使用拓扑感知;
  • [ ] 驱动、Kubernetes 与 sidecar 版本兼容;
  • [ ] 关键卷有快照、应用备份和异地副本;
  • [ ] RPO/RTO 已通过恢复演练验证。

日常

  • [ ] PVC、PV、VolumeAttachment 无长期异常;
  • [ ] CSI 控制器和节点插件健康;
  • [ ] 后端容量和配额有余量;
  • [ ] 快照与备份按计划完成;
  • [ ] volumeHandle 与资产台账一致。

故障

  • [ ] 先判断失败发生在供给、调度、附加、挂载还是应用层;
  • [ ] 强制分离前已确认旧节点不会继续写入;
  • [ ] 不直接删除 kubelet 插件目录和挂载目录;
  • [ ] 恢复后已验证文件系统和业务一致性。

参考资料

Copyright © yan-jian.com 2023 - 2026 · Powered by GitBook更新时间: 2026-10-25 22:22:48

results matching ""

    No results matching ""