Skip to content

Kubernetes PV、PVC、StorageClass与CSI存储全过程

Pod和容器是可替换计算实例,容器可写层跟随容器/Pod生命周期,不能承载需要长期保存的数据。Kubernetes通过Volume、PersistentVolume、PersistentVolumeClaim、StorageClass和CSI,把应用的存储需求与云盘、SAN、NFS、分布式文件系统等具体实现解耦。

“PVC Bound”只说明申请和PV已经绑定,不代表磁盘已附加到目标Node、文件系统已挂载、权限正确、数据库数据一致或已经有备份。本页从PVC提交开始,追踪动态供应、调度拓扑、Attach、Stage、Publish、扩容、快照和故障恢复。

学习目标

学完后应能:

  1. 区分容器可写层、emptyDir、hostPath、Local PV和持久PVC。
  2. 解释Volume、PV、PVC、StorageClass、VolumeAttachment和CSI职责。
  3. 解释静态供应与动态供应的绑定全过程。
  4. 区分Immediate与WaitForFirstConsumer的拓扑语义。
  5. 解释Scheduler怎样把Pod约束和存储Zone共同纳入决策。
  6. 解释CSI Controller/Node插件及Create、Attach、Stage、Publish调用链。
  7. 准确区分RWO、ROX、RWX和RWOP,而不是把RWO误解为单Pod。
  8. 区分Filesystem与Block VolumeMode。
  9. 解释ReclaimPolicy、Finalizer、Released和底层磁盘生命周期。
  10. 设计在线扩容、文件系统扩容和不支持缩容的处理。
  11. 区分快照、数据库一致性备份、PITR和灾备复制。
  12. 解释StatefulSet volumeClaimTemplates和PVC保留边界。
  13. 根据PVC/PV/VolumeAttachment/Event/CSI日志排查Pending、Multi-Attach和FailedMount。
  14. 设计容量、性能、加密、权限、监控和恢复演练。

一、数据究竟写到哪里

text
应用写文件
├── 容器可写层:容器删除通常丢失
├── emptyDir:Pod删除丢失,容器重启通常仍在
├── hostPath:绑定某Node路径,跨Node不可移植
├── Local PV:受控本地盘,带Node拓扑和PV生命周期
├── PVC/PV:由存储系统提供持久卷
└── 对象存储/数据库:应用通过网络API访问的外部系统

不是所有持久数据都应该写PVC:

  • 用户图片/视频常适合对象存储。
  • 应用日志适合stdout并集中采集。
  • Session适合Redis/数据库等共享系统。
  • 数据库数据目录才可能使用块存储PVC。
  • 临时排序/解压缓存可使用emptyDir并设置容量。

二、核心对象关系

mermaid
flowchart TD
    A["Pod声明volume使用PVC"] --> B["PVC表达容量、访问模式和StorageClass"]
    B --> C["PVC绑定PV"]
    C --> D["PV描述底层Volume Handle和策略"]
    D --> E["StorageClass定义Provisioner和供应参数"]
    E --> F["CSI Driver管理真实磁盘/文件系统"]
对象Namespace主要消费者/维护者含义
Pod VolumeNamespaced PodSpec内应用/kubelet容器看到的挂载/设备声明
PVCNamespaced应用对存储容量和能力的申请
PVCluster-scoped控制器/管理员已供应存储资源的K8s表示
StorageClassCluster-scoped平台团队动态供应的类别和参数
VolumeAttachmentCluster-scopedAttach/Detach与CSI卷与Node附加关系
VolumeSnapshotNamespaced CRD应用/备份系统某PVC/卷的快照请求

PVC只能被同Namespace的Pod直接引用;PV和StorageClass是集群级对象。

三、为什么需要PV和PVC解耦

应用只声明:

text
需要100Gi
需要单Node读写
需要fast-rwo存储类别
需要Filesystem

平台决定:

  • 使用哪家云盘/阵列。
  • IOPS和吞吐规格。
  • 加密Key。
  • Zone。
  • ReclaimPolicy。
  • CSI Driver和升级。

这样应用清单不直接携带云盘ID和厂商API,但StorageClass名称/能力仍是平台契约,不能完全忽略存储差异。

四、PVC完整结构

yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: order-db-data
  namespace: data
  labels:
    app: order-db
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  storageClassName: fast-rwo
  resources:
    requests:
      storage: 100Gi

主要字段:

字段作用
accessModes请求挂载访问语义
volumeModeFilesystem或Block
storageClassName指定存储类别
requests.storage最低申请容量
selector静态供应时筛选PV,可能阻止动态供应
volumeName预绑定指定PV,需谨慎

PVC请求100Gi,实际PV可能大于100Gi;绑定不是从一个大PV切一部分给PVC,一个PV通常绑定一个PVC。

五、PV完整结构

CSI PV简化示例:

yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pvc-2f9c-example
spec:
  capacity:
    storage: 100Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: fast-rwo
  claimRef:
    namespace: data
    name: order-db-data
  csi:
    driver: block.csi.example.com
    volumeHandle: provider-volume-id-12345
    fsType: ext4

volumeHandle是CSI Driver识别真实卷的ID,通常不能随意修改。PV对象删除与真实磁盘删除是否联动,由ReclaimPolicy、CSI和Finalizer共同决定。

六、StorageClass不是一块磁盘

yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-rwo
provisioner: block.csi.example.com
parameters:
  type: premium-ssd
  encrypted: "true"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer

它是供应模板/类别,字段解释:

  • provisioner:哪一个CSI/存储插件处理。
  • parameters:厂商特有磁盘类型、加密等参数。
  • reclaimPolicy:动态PV默认回收策略。
  • allowVolumeExpansion:是否允许PVC请求扩容。
  • volumeBindingMode:何时供应/绑定以及是否等待调度拓扑。
  • mountOptions:平台可设置挂载选项,错误值可能导致Mount失败。

业务团队通常只引用平台审核后的StorageClass,不应自行创建带任意加密Key、NFS地址或危险Mount Option的Class。

七、默认StorageClass和空字符串边界

PVC未写 storageClassName 时,集群可能通过默认StorageClass供应。显式写:

yaml
storageClassName: ""

通常表示不使用StorageClass动态供应,尝试绑定无Class的静态PV。未写和空字符串不是同一语义。

多个默认StorageClass或默认Class变更可能让同一清单在不同集群供应不同磁盘。生产PVC应显式选择受控类别。

八、静态供应全过程

管理员先创建PV,应用再创建PVC:

mermaid
flowchart TD
    A["管理员创建带容量/模式/Class的PV"] --> B["应用创建PVC"]
    B --> C["PV Controller查找Available PV"]
    C --> D["匹配StorageClass、AccessMode、VolumeMode、容量和Selector"]
    D --> E{"是否找到唯一适合PV"}
    E -- "否" --> F["PVC保持Pending并记录Event"]
    E -- "是" --> G["写claimRef并双向绑定"]
    G --> H["PVC/PV进入Bound"]

静态供应适合已有磁盘、迁移数据或特殊硬件。预绑定时PVC volumeName和PV claimRef必须一致,且不能绕过Node Affinity、Class、VolumeMode等必要约束。

九、动态供应全过程

当PVC引用StorageClass且没有现成PV,External Provisioner观察PVC并调用CSI:

mermaid
flowchart TD
    A["创建PVC storageClass=fast-rwo"] --> B["External Provisioner观察未绑定PVC"]
    B --> C["读取StorageClass和拓扑要求"]
    C --> D["调用CSI Controller CreateVolume"]
    D --> E["存储系统创建真实卷"]
    E --> F["Provisioner创建对应PV"]
    F --> G["PV与PVC绑定"]
    G --> H["PVC状态Bound"]

如果存储API超时,Provisioner通常重试,CreateVolume需要幂等,避免一次请求创建多个孤儿磁盘。排查应同时看PVC Event、Provisioner日志和云存储事件。

十、CSI架构

CSI把Kubernetes与存储厂商实现分离。典型部署:

mermaid
flowchart TD
    A["CSI Controller Deployment"] --> A1["External Provisioner"]
    A --> A2["External Attacher"]
    A --> A3["External Resizer"]
    A --> A4["Snapshotter等Sidecar"]
    A --> A5["Driver Controller Plugin"]
    B["每Node的CSI Node DaemonSet"] --> B1["Node Driver Registrar"]
    B --> B2["Driver Node Plugin"]
    C["Kubernetes Controller/kubelet"] --> A
    C --> B

Controller侧常见能力

  • Create/Delete Volume。
  • ControllerPublish/Unpublish,即Attach/Detach。
  • Expand Volume。
  • Snapshot/Create Clone(若支持)。

Node侧常见能力

  • NodeStage/Unstage。
  • NodePublish/Unpublish。
  • NodeExpand。
  • 获取Node拓扑和能力。

不是所有存储都需要Attach:NFS等网络文件系统可能直接由Node Mount;云块盘通常需要先附加到虚拟机。

十一、Immediate绑定模式

yaml
volumeBindingMode: Immediate

PVC创建后立即供应和绑定,哪怕还没有Pod。对于有Zone限制的块盘,可能先在Zone A创建卷,而Pod因CPU、Affinity等只能调度到Zone B,最终无法同时满足。

Immediate适合无拓扑限制的共享存储或平台已确保拓扑一致的场景。

十二、WaitForFirstConsumer为什么重要

yaml
volumeBindingMode: WaitForFirstConsumer

PVC先保持Pending,直到有Pod引用它,Scheduler同时考虑:

  • Pod CPU/内存。
  • NodeSelector/Affinity。
  • Taint/Toleration。
  • Pod拓扑分散。
  • 存储允许的Zone/Node拓扑。
mermaid
flowchart TD
    A["创建PVC,暂不供应卷"] --> B["创建引用PVC的Pod"]
    B --> C["Scheduler筛选计算与存储可行Node"]
    C --> D["选定候选Zone/Node拓扑"]
    D --> E["Provisioner在对应拓扑CreateVolume"]
    E --> F["PV/PVC绑定"]
    F --> G["Pod完成Node绑定"]

此时PVC Pending可能是正常等待首个消费者,不能看到Pending就手工创建随机PV。

十三、Scheduler的Volume Binding阶段

Scheduler插件会检查:

  • PVC是否Bound。
  • 未绑定PVC是否可在候选Node拓扑供应。
  • 已绑定PV Node Affinity是否匹配。
  • 访问模式/Attach限制。
  • 同一Pod多个PVC能否在一个Node同时满足。

调度失败Event可能显示Volume Node Affinity Conflict、未绑定Immediate PVC等。必须读完整FailedScheduling消息,不能只归因“节点资源不足”。

十四、从Bound到容器可读写还有多远

PVC Bound之后,Pod在Node侧仍要经过:

mermaid
flowchart TD
    A["Pod绑定目标Node"] --> B["Attach/Detach Controller确认卷需要附加"]
    B --> C["External Attacher调用ControllerPublishVolume"]
    C --> D["云/存储系统把卷Attach到Node"]
    D --> E["VolumeAttachment状态Attached"]
    E --> F["kubelet调用NodeStageVolume"]
    F --> G["必要时格式化并挂载到Node全局Stage路径"]
    G --> H["kubelet调用NodePublishVolume"]
    H --> I["Bind Mount/设备映射到Pod路径"]
    I --> J["容器启动并访问Volume"]

所以:

text
PVC=Bound
但Pod=ContainerCreating/FailedMount

完全可能。问题在Attach、Node插件、文件系统、权限或Mount,而不是PVC绑定。

十五、ControllerPublish与NodePublish区别

  • ControllerPublish:让存储系统把卷附加到某Node,例如云盘挂到虚拟机。
  • NodeStage:在Node上准备设备/文件系统到全局Stage路径,可被多个Pod挂载复用。
  • NodePublish:把Stage后的Volume发布到具体Pod的目标路径。

文件存储可能没有传统Attach;块存储则需处理设备出现、格式化和Mount。CSI调用应幂等,因为Controller/kubelet可能重试。

十六、VolumeMode:Filesystem与Block

Filesystem

yaml
volumeMode: Filesystem

Node插件将块设备格式化为ext4/xfs等并Mount,容器看到目录。首次格式化是高风险动作,Driver必须识别已有文件系统,不能误格式化有数据卷。

Block

yaml
volumeMode: Block

Pod通过 volumeDevices看到原始块设备:

yaml
volumeDevices:
  - name: data
    devicePath: /dev/xvda

应用负责文件系统/数据库原始设备管理。使用门槛高,错误写入可直接破坏数据;不要把Block设备当普通目录mountPath。

PVC和PV的VolumeMode必须兼容。

十七、四种AccessMode准确语义

模式准确语义常见实现
RWO ReadWriteOnce单个Node读写挂载云块盘
ROX ReadOnlyMany多Node只读挂载共享文件/镜像数据
RWX ReadWriteMany多Node读写挂载NFS/分布式文件系统
RWOP ReadWriteOncePod集群范围单Pod读写支持该能力的CSI

17.1 RWO不等于单Pod

RWO主要限制单Node读写。同一Node上的多个Pod在驱动和工作负载允许时可能同时挂载同一PVC。需要严格单Pod写入时评估RWOP,并确认CSI Sidecar/Driver和Kubernetes版本支持。

17.2 AccessMode不是应用锁

即使RWX允许多个Pod写,文件系统也不会自动解决:

  • 多进程并发写同一文件。
  • 数据库锁和缓存一致性。
  • 文件重命名/锁语义差异。
  • NFS缓存和故障恢复。

应用必须支持共享文件系统语义。

17.3 驱动是否强制执行

AccessMode用于匹配、调度和CSI请求,但底层是否严格阻止越权挂载取决于Driver/存储。安全性不能只依赖PVC YAML。

十八、emptyDir原理和风险

yaml
volumes:
  - name: cache
    emptyDir:
      sizeLimit: 2Gi

生命周期:

  • Pod分配到Node时创建。
  • 同Pod容器重启时通常保留。
  • Pod删除/重新调度时删除。
  • Node故障可能丢失。

适合临时文件、Sidecar共享和可重建缓存,不适合数据库持久数据。

Memory emptyDir

yaml
emptyDir:
  medium: Memory
  sizeLimit: 512Mi

通常使用tmpfs,写入会占用Node/容器内存记账,可能导致内存压力或OOM。sizeLimit不等于额外免费内存。默认磁盘emptyDir还会消耗Node Ephemeral Storage和inode。

十九、hostPath为什么危险

yaml
hostPath:
  path: /var/lib/example
  type: Directory

风险:

  • Pod换Node看到不同数据。
  • Node磁盘损坏即数据丢失。
  • 绕过存储配额/生命周期。
  • 挂载宿主机敏感目录可权限突破。
  • 多租户Pod访问Node文件。

hostPath适合必须访问Node文件的受控DaemonSet等特殊组件,不是生产业务持久化的默认选择。尽量只读、限制路径、禁止privileged并使用Admission策略。

二十、Local PersistentVolume

Local PV比hostPath多了PV/PVC、容量和Node Affinity管理:

yaml
nodeAffinity:
  required:
    nodeSelectorTerms:
      - matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values: ["worker-storage-01"]

性能好但绑定Node故障域。Pod不能随意漂移到其他Node继续使用同一盘;需要应用复制、多副本、备份和Local Volume Manager等运维能力。

Local PV通常应配WaitForFirstConsumer,避免PVC先绑定到与Pod约束冲突的Node。

二十一、Generic/CSI Ephemeral Volume

除emptyDir外,Kubernetes支持通用临时PVC模板和CSI Ephemeral等模式,Volume随Pod创建/删除但可使用存储插件能力。适合每Pod临时空间,不等于长期持久数据。

字段成熟度和Driver支持随版本变化,使用前验证回收、配额和Pod删除行为。

二十二、ReclaimPolicy

Delete

PVC删除后,动态PV和底层卷通常由Controller删除。优点是自动清理,风险是误删PVC可能快速删除真实数据。

Retain

PVC删除后PV进入Released并保留底层卷,需要人工审计、清理claimRef、恢复或删除。优点是防误删,风险是孤儿盘、费用和敏感数据残留。

Recycle

旧式策略,现代集群基本不推荐/不支持。

生产数据库不能只凭“Retain更安全”结束设计;仍需备份、权限和恢复流程。Delete也可通过平台删除保护、快照和审批治理。

二十三、删除PVC时发生什么

mermaid
flowchart TD
    A["用户删除PVC"] --> B["PVC保护Finalizer检查是否仍被Pod使用"]
    B --> C["解除PVC与PV绑定"]
    C --> D{"PV ReclaimPolicy"}
    D -- "Delete" --> E["Provisioner调用CSI DeleteVolume"]
    E --> F["底层卷和PV删除"]
    D -- "Retain" --> G["PV变Released,底层卷保留"]
    G --> H["人工恢复、再绑定或安全擦除"]

实际异步步骤、Finalizer名称和顺序受版本/CSI影响。对象长期Terminating时不要直接清Finalizer;先确认Pod使用、VolumeAttachment和底层API状态,强删可能留下正在挂载的卷或孤儿资源。

二十四、PV Released为什么不能自动给新PVC用

Released PV仍带旧claimRef和旧数据,Controller不会随便绑定给其他租户。恢复流程通常需要:

  1. 确认底层数据和旧PVC身份。
  2. 将ReclaimPolicy设为Retain,防止误删。
  3. 按受控流程移除/调整claimRef或创建预绑定对象。
  4. 新PVC volumeName指向该PV。
  5. 验证Namespace、容量、AccessMode、VolumeMode和Node Affinity。
  6. 以只读/隔离方式检查数据后再开放。

不要在不知数据归属时把Released PV交给新业务。

二十五、PVC删除保护不等于数据保护

PVC Protection Finalizer可阻止正在被Pod使用的PVC立即删除,但不能防:

  • 删除整个Namespace后的最终清理。
  • 有权限用户强删Finalizer。
  • 底层云盘被云控制台删除。
  • 应用逻辑删除/覆盖数据。
  • 勒索/凭据泄露。

它是对象生命周期保护,不是备份。

二十六、StatefulSet volumeClaimTemplates

yaml
volumeClaimTemplates:
  - metadata:
      name: data
  spec:
    accessModes: ["ReadWriteOnce"]
    storageClassName: fast-rwo
    resources:
      requests:
        storage: 100Gi

每个Ordinal得到独立PVC:

text
data-mysql-0
data-mysql-1
data-mysql-2

Pod mysql-1重建后重新使用 data-mysql-1。StatefulSet默认通常保留PVC,缩容/删除不会自动清理数据;较新版本支持PVC保留策略,需按目标版本验证。

StatefulSet不会自动实现数据库复制、备份和选主。三个PVC只是三块存储。

二十七、强制删除有状态Pod的风险

强制删除API对象不保证旧Node进程已停止。Controller可能创建同名替代Pod并尝试挂载同一卷,出现:

  • Multi-Attach。
  • 两个实例声称同一身份。
  • 文件系统并发写损坏。
  • 数据库脑裂。

先隔离旧Node、确认进程和Attach状态,再按数据库/存储恢复协议处理。

二十八、在线扩容全过程

前提:StorageClass allowVolumeExpansion: true,Driver和文件系统支持。

修改PVC只能增大:

yaml
resources:
  requests:
    storage: 200Gi
mermaid
flowchart TD
    A["PVC请求从100Gi改到200Gi"] --> B["External Resizer观察PVC"]
    B --> C["调用CSI ControllerExpandVolume"]
    C --> D["底层卷容量扩大"]
    D --> E["PV Capacity更新"]
    E --> F{"是否还需Node/文件系统扩容"}
    F -- "是" --> G["kubelet调用NodeExpandVolume/扩文件系统"]
    F -- "否" --> H["PVC容量完成"]
    G --> H

块设备变大不等于文件系统自动可用全部空间。检查PVC Conditions、PV Capacity、Node扩容和容器内 df/文件系统工具。

二十九、为什么PVC不能直接缩容

Kubernetes通常不支持把PVC请求从200Gi缩到100Gi,因为底层块设备和文件系统缩容可能截断数据,很多文件系统也不支持在线缩小。

需要缩容时:

  1. 创建较小新PVC。
  2. 停写或建立一致性点。
  3. 使用数据库/文件级工具迁移数据。
  4. 校验完整性。
  5. 切换应用。
  6. 经过回滚窗口后删除旧卷。

不能只修改PV/PVC Capacity字段假装底层磁盘已缩小。

三十、扩容失败为什么会反复重试

如果请求超过配额/存储最大值,Resizer会持续尝试。检查:

  • PVC Event和Condition。
  • StorageClass expansion能力。
  • CSI Resizer/Controller日志。
  • 云盘最大容量和配额。
  • Zone/卷类型限制。
  • 文件系统支持。

不要手工编辑PV Capacity掩盖失败,这会造成控制面与真实设备不一致。

三十一、VolumeSnapshot对象链路

Snapshot API通常由CRD、Snapshot Controller、CSI Snapshotter和Driver共同实现:

mermaid
flowchart TD
    A["创建VolumeSnapshot引用PVC"] --> B["Snapshot Controller处理"]
    B --> C["External Snapshotter调用CSI CreateSnapshot"]
    C --> D["存储系统创建快照"]
    D --> E["VolumeSnapshotContent记录Handle"]
    E --> F["VolumeSnapshot ReadyToUse"]
    F --> G["从Snapshot创建新PVC恢复"]

没有安装CRD/Controller或Driver不支持时,写YAML不会自动获得快照。

三十二、快照不是备份

快照可能:

  • 与源卷位于同一存储系统/账号。
  • 依赖源卷元数据。
  • 被同一管理员凭据删除。
  • 只保证Crash Consistent。
  • 不包含数据库日志/PITR链。

备份应有:

  • 独立故障域/账号/区域。
  • 不可变或防删除策略。
  • 数据库一致性/PITR。
  • 加密和密钥备份。
  • 定期恢复演练。
  • RPO/RTO验收。

三十三、Crash Consistent与Application Consistent

Crash Consistent

像突然断电时的磁盘状态。文件系统/数据库依赖Journal、redo/WAL恢复,可能丢失内存中尚未落盘的数据。

Application Consistent

快照前由应用:

  • Flush/Checkpoint。
  • 暂停或冻结写入。
  • 锁定备份点。
  • 记录WAL/binlog位置。
  • 多卷建立一致性组。

然后拍快照并恢复写入。不能仅因为存储快照状态Ready就声明数据库可恢复。

三十四、数据库备份为什么不能只复制挂载目录

运行中数据库的数据页、redo/WAL、元数据和内存状态可能不在同一时间点。普通文件复制会跨越写入,得到逻辑不一致集合。

正确选择:

  • 数据库逻辑备份。
  • 数据库物理热备工具。
  • 数据库配合的存储快照。
  • 主从/PITR归档日志。

所有方案必须做恢复验证,而不是只看备份Job成功。

三十五、从Snapshot恢复PVC

标准PVC dataSource引用同Namespace中的VolumeSnapshot。下面示例假设平台已经把受控快照对象导入/创建在 restore-lab;不能直接用名称跨Namespace引用原业务Snapshot。某些新版本支持更丰富的跨Namespace DataSource机制,但需要显式授权和Driver支持,应按目标集群验证。

示意:

yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: order-db-restore-test
  namespace: restore-lab
spec:
  storageClassName: fast-rwo
  dataSource:
    name: order-db-snapshot-20260715
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 100Gi

恢复到隔离Namespace/网络,验证:

  • PVC Bound和Mount。
  • 文件系统一致性。
  • 数据库能启动恢复。
  • 表数量/行数/校验。
  • binlog/WAL位置。
  • 应用关键查询。
  • 数据脱敏和访问控制。

不要直接覆盖生产PVC做首次恢复实验。

三十六、Volume Clone

部分CSI支持PVC作为dataSource克隆新卷,适合测试副本/快速环境。Clone不是长期备份,通常同StorageClass/Namespace/拓扑有约束,且会复制敏感数据。需要脱敏、配额和生命周期清理。

三十七、存储加密和密钥

存储加密可能包括:

  • 云盘/阵列静态加密。
  • 文件系统/应用层加密。
  • 传输加密(NFS/TLS等)。
  • 数据库TDE。

StorageClass参数声明 encrypted: "true" 只在对应Driver实现中有意义。还需验证:

  • 实际卷加密状态。
  • 使用哪一个KMS Key。
  • Key权限和审计。
  • Key轮换。
  • Snapshot/备份是否同样加密。
  • 灾备时Key是否可恢复。

删除KMS Key可能让所有卷和备份不可读,比删除PVC影响更大。

三十八、文件权限和fsGroup

Pod以非root运行时,挂载卷可能由root拥有。Pod SecurityContext:

yaml
securityContext:
  runAsNonRoot: true
  runAsUser: 10001
  runAsGroup: 10001
  fsGroup: 10001
  fsGroupChangePolicy: OnRootMismatch

kubelet/CSI可能递归调整所有权,大卷包含百万文件时启动会很慢。OnRootMismatch可减少不必要递归,但支持和行为按版本/Driver验证。

不要用Init Container无条件 chmod -R 777:慢、不安全,并可能每次启动重扫全盘。

三十九、性能不只看容量

100Gi卷可能有不同:

  • IOPS。
  • Throughput。
  • P50/P99 Latency。
  • Burst Credit。
  • Queue Depth。
  • 单卷/单Node上限。
  • 多租户噪声。

数据库慢时同时看:

  • 应用/数据库IO等待。
  • Node设备延迟。
  • CSI/云盘指标。
  • 文件系统容量和inode。
  • Throttle/配额。
  • Snapshot/备份争用。

把PVC从100Gi扩到200Gi是否提高IOPS取决于存储类型,不能一概而论。

四十、容量和inode耗尽

bash
df -h /var/lib/mysql
df -i /var/lib/mysql

可能出现:

  • 字节空间充足但inode用尽,无法创建新文件。
  • 文件已删除但进程仍持有FD,空间未释放。
  • 容器看到的Filesystem与云盘指标口径不同。
  • Thin Provisioning后端池耗尽。

监控PVC使用率、增长速率、inode、预测耗尽时间和存储后端容量。不能等95%才第一次告警。

四十一、完整StatefulSet商业结构Demo

前提:平台已提供 fast-rwo StorageClass和备份能力。示例只展示存储结构,不等于完整数据库高可用。

yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: order-db
  namespace: data
spec:
  serviceName: order-db
  replicas: 3
  selector:
    matchLabels:
      app: order-db
  template:
    metadata:
      labels:
        app: order-db
    spec:
      terminationGracePeriodSeconds: 120
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        runAsGroup: 10001
        fsGroup: 10001
        fsGroupChangePolicy: OnRootMismatch
      containers:
        - name: database
          image: registry.example.com/order-db@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
          volumeMounts:
            - name: data
              mountPath: /var/lib/order-db
          resources:
            requests:
              cpu: "1"
              memory: 2Gi
            limits:
              memory: 4Gi
  volumeClaimTemplates:
    - metadata:
        name: data
        labels:
          app: order-db
      spec:
        accessModes: ["ReadWriteOnce"]
        volumeMode: Filesystem
        storageClassName: fast-rwo
        resources:
          requests:
            storage: 100Gi

镜像必须支持非root和目标数据目录权限。数据库复制、选主、配置、Secret、Probe和备份需要额外完整设计。

四十二、PVC Pending Runbook

bash
kubectl get pvc -n data
kubectl describe pvc order-db-data -n data
kubectl get storageclass
kubectl get pv
kubectl get events -n data --sort-by=.metadata.creationTimestamp

检查:

  • StorageClass是否存在/默认。
  • Provisioner名称与CSI Controller是否运行。
  • Immediate还是WaitForFirstConsumer。
  • 是否有引用PVC的Pod。
  • AccessMode/VolumeMode支持。
  • 容量和配额。
  • Selector是否阻止动态供应。
  • Zone/Topology和AllowedTopologies。
  • 云API权限、配额和错误。

WaitForFirstConsumer下没有Pod时Pending可能正常。

四十三、Pod Pending的Volume调度故障

bash
kubectl describe pod <pod> -n data

常见Event:

  • unbound immediate PersistentVolumeClaims。
  • volume node affinity conflict。
  • no available topology。
  • exceeded attach volume limit。

检查Pod Affinity/NodeSelector、PV Node Affinity、StorageClass拓扑和Node每实例最大Attach数。不能只扩CPU Node数量,如果新Node仍在错误Zone或Attach上限已满。

四十四、Multi-Attach Error

RWO云盘仍附加在Node A,却要在Node B挂载:

text
Multi-Attach error for volume ...

原因:

  • 旧Pod/Node未正常终止。
  • VolumeAttachment状态陈旧。
  • 云API Detach慢。
  • 强制删除Pod造成控制面与真实设备不一致。
  • 两个Workload错误引用同PVC。

检查:

bash
kubectl get volumeattachment
kubectl describe volumeattachment <name>
kubectl get pod -A -o wide
kubectl describe node <node>

确认旧进程停止、文件系统已安全卸载后,再按CSI/云厂商Runbook强制Detach。对数据库盲目Detach可能造成写入中断和文件系统损坏。

四十五、FailedAttachVolume

检查:

  • CSI Attacher/Controller日志。
  • VolumeAttachment状态和错误。
  • 云盘是否在同Zone。
  • Node实例是否达到Attach上限。
  • 云IAM权限和API配额。
  • 卷是否被锁定/正在快照或迁移。
  • Node ID是否与云实例匹配。

PVC Bound不排除这些问题。

四十六、FailedMount

常见原因:

  • CSI Node插件未运行/Socket异常。
  • NodeStage/NodePublish失败。
  • 文件系统损坏或fsType错误。
  • Mount Option不支持。
  • NFS/DNS/网络/权限失败。
  • Secret/凭据缺失。
  • 目标路径冲突。
  • fsGroup递归耗时。

证据:

bash
kubectl describe pod <pod> -n data
kubectl get pod -n <csi-namespace> -o wide
kubectl logs -n <csi-namespace> <csi-node-pod> -c <driver-container>

CSI Namespace和容器名按平台实际值。不要随意在Node手工mount生产卷,可能与kubelet状态冲突。

四十七、挂载后Permission Denied

检查:

  • 容器UID/GID。
  • fsGroup和fsGroupChangePolicy。
  • 文件系统现有Owner/Mode/ACL。
  • NFS root_squash。
  • SELinux/AppArmor。
  • CSI是否处理Mount Group。
  • 应用写入路径是否正确。

先在隔离副本验证权限变更。对TB级卷执行递归chown可能造成长时间不可用。

四十八、Node故障后的卷恢复

流程可能包括:

  1. Node心跳丢失,Pod状态未知。
  2. 控制器等待Node/Pod驱逐超时。
  3. 确认旧Node不会继续写盘(Fencing)。
  4. Detach旧Node。
  5. 在同Zone新Node调度替代Pod。
  6. Attach、Mount和数据库恢复。
  7. 验证数据一致性和业务。

Fencing是有状态恢复关键:如果旧Node只是网络分区但仍运行并写磁盘,新Node同时挂载/启动会脑裂。存储和应用必须有单写保护。

四十九、Volume满了怎么处理

先判断:

  • 数据增长还是日志/临时文件异常。
  • inode还是字节容量。
  • 删除文件是否仍被进程打开。
  • 存储后端是否允许扩容。
  • 数据库是否支持在线扩容。
  • 备份/快照是否占用配额。

紧急止损可能包括清理可重建临时数据、受控扩容和限流。不要直接删除数据库文件或redo/WAL。

五十、存储监控

对象层:

  • PVC Phase/Condition和请求容量。
  • PV Phase/ReclaimPolicy。
  • VolumeAttachment错误。
  • CSI Controller/Node可用性。
  • Provision/Attach/Mount/Resize延迟与失败。

数据层:

  • 使用量、inode、增长速度。
  • IOPS、Throughput、Latency、Queue。
  • 错误/重试/Throttle。
  • Snapshot/Backup成功与年龄。
  • 恢复演练RPO/RTO。
  • 存储费用和孤儿卷。

PVC Bound不是健康指标的终点。

五十一、常见误区

  1. PVC Bound等于可用:后续还有Attach、Stage、Publish和权限。
  2. RWO等于单Pod:它主要是单Node读写。
  3. RWX自动解决并发一致性:应用仍需并发协议。
  4. StorageClass是一块磁盘:它是动态供应模板。
  5. Pending一定故障:WaitForFirstConsumer可能在等Pod。
  6. Delete策略一定不安全、Retain一定安全:都需生命周期和备份治理。
  7. Finalizer等于备份:只保护对象删除顺序。
  8. 快照等于备份:可能同故障域且只有Crash Consistent。
  9. 卷扩大后文件系统自动全部可用:可能还需NodeExpand。
  10. 可以直接缩小PVC:多数场景不支持,需迁移。
  11. StatefulSet自动提供数据库HA:它只提供身份和PVC。
  12. 强制Detach能快速修复:未Fencing可能损坏数据。

五十二、面试标准回答

PV、PVC和StorageClass关系

PVC是Namespace内应用的容量、AccessMode、VolumeMode和Class申请;PV是集群级底层卷表示;StorageClass定义Provisioner、参数、Reclaim、扩容和绑定模式。动态供应时External Provisioner调用CSI创建真实卷和PV,再与PVC绑定。

PVC Bound后Pod为什么仍ContainerCreating

Bound只表示PVC与PV绑定。Pod调度后,块盘还要ControllerPublish附加Node、NodeStage准备文件系统、NodePublish挂到Pod路径,再处理权限;任一步都可能FailedAttach/FailedMount,应查VolumeAttachment、Pod Event和CSI Controller/Node日志。

RWO和RWOP区别

RWO是单Node读写,同Node多个Pod在某些场景仍可共享;RWOP才表达集群范围单Pod读写,并要求CSI和Kubernetes版本支持。AccessMode也不是应用并发锁,底层强制能力依Driver。

Immediate和WaitForFirstConsumer区别

Immediate在PVC创建时立即供应,拓扑卷可能先落在错误Zone;WaitForFirstConsumer等待引用PVC的Pod,Scheduler把计算约束和存储Zone一起决策,再在选定拓扑供应卷,PVC在没有消费者时Pending可能正常。

Delete和Retain区别

Delete通常在PVC删除后由Provisioner删除PV和底层卷;Retain保留底层数据并让PV Released,需人工恢复或清理。两者都不是备份:Delete要防误删,Retain要防孤儿盘、费用和数据泄露。

快照为什么不等于备份

快照可能与源卷同系统/账号、可被同凭据删除,且通常只Crash Consistent。备份要独立故障域、不可变/防删除、数据库一致性或PITR,并定期恢复验证;Snapshot Ready不能证明数据库能恢复。

Multi-Attach怎么处理

先确认旧Pod/Node是否仍运行并完成Fencing,再查VolumeAttachment、云盘真实附加和CSI日志;安全卸载后按厂商Runbook Detach并在同拓扑重建。不能直接强制Detach仍在写入的数据库卷,否则可能脑裂或文件系统损坏。

五十三、学习实验与验收

实验一:动态供应

创建PVC,观察Provisioner日志、PV、claimRef、volumeHandle和Bound过程;删除前记录ReclaimPolicy,确认底层卷结果。

实验二:WaitForFirstConsumer

创建WFFC PVC但不创建Pod,观察Pending;再创建带Zone Affinity的Pod,观察选定拓扑、供应和调度。

实验三:Attach/Mount故障

在隔离环境制造错误fsType/Mount Option或停止测试CSI Node插件,保存VolumeAttachment、Event和CSI日志,恢复后验证幂等重试。

实验四:RWO边界

分别在同Node/不同Node引用测试RWO PVC,观察Driver行为;再用支持的RWOP验证单Pod约束。不得对生产数据库实验。

实验五:扩容

把测试PVC从10Gi扩到20Gi,记录ControllerExpand、PV Capacity、PVC Condition、NodeExpand和容器内文件系统容量。

实验六:快照恢复

写入校验数据,创建Snapshot,恢复到隔离PVC,启动只读验证应用并记录RPO/RTO;再说明为什么这仍不等于数据库PITR备份。

验收清单

  1. 能画出PVC动态供应和CSI组件图。
  2. 能解释WFFC与Scheduler拓扑。
  3. 能画出Attach、Stage、Publish全过程。
  4. 能区分四种AccessMode和两种VolumeMode。
  5. 能说明emptyDir、hostPath、Local PV和PVC边界。
  6. 能解释Delete、Retain、Released和Finalizer。
  7. 能完成扩容并验证文件系统。
  8. 能区分快照、备份、PITR和复制。
  9. 能解释StatefulSet PVC保留和强删风险。
  10. 能排查Pending、Multi-Attach、FailedAttach、FailedMount。
  11. 能设计容量、性能、权限、加密和监控。
  12. 能在隔离环境完成真实恢复演练。

关联知识点