想象你经营一家“云上软件公寓”,公寓里有成百上千个叫 Pod 的灵活小房间。每个房间都可能随时被回收重建,但有些房客(比如 AI 模型服务、MySQL 数据库)要求数据必须永久保存——模型训练到一半,Pod 一重启,训练进度全没了,这谁能忍?答案是 Kubernetes 的三层存储体系:PV(PersistentVolume)是“备好的仓库”,PVC(PersistentVolumeClaim)是“仓库租用需求单”,StorageClass 是“自动仓储调度系统”。三者像物流平台上的备货仓、下单与自动分拣,把“存储”从“拿一块硬盘”变成“下一张订单”。 本篇文章用“仓库租赁平台”这个比喻,带你走通 K8s 存储的核心链路:从 PV/PVC 的绑定机制、StorageClass 动态供给,到存储后端选型、有状态应用配置、性能调优与故障排查、备份恢复与监控告警。读完你就能理解:为什么 Kubernetes 能把“存储”变成像外卖一样按需下单的标准化服务。

岗位关联

对于 AI 从业者:模型训练依赖大量数据与 Checkpoint 持久化,理解 K8s 存储能让你设计高可用的 GPU 训练集群,不再因 Pod 被抢占或重启而丢失宝贵训练进度。

目录
  1. 可视化演示
  2. PV核心概念:云端的“备货仓库”
  3. PVC请求与绑定:提交“仓库需求单”
  4. 绑定匹配与生命周期:三条件合一的“锁仓”流程
  5. StorageClass动态供给:自动建仓的智能调度员
  6. 存储后端选型:云盘、NFS还是对象存储
  7. 有状态应用存储与迁移:给固定住户配固定仓库
  8. 性能调优与故障排查:仓库拥堵与寻仓失败
  9. 备份恢复与监控告警:给仓库上保险、装监控
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

演示K8s存储三层抽象:Pod通过PVC申请存储,StorageClass动态供给PV,PV与PVC完成绑定后挂载使用,展示持久化存储的完整生命周期。

本节配有交互动效(进入视口后自动加载,约 3KB)。

K8s存储抽象 PVC StorageClass PV 挂载 PVC绑定PV后由Pod挂载使用

PV核心概念:云端的“备货仓库”

仓库租赁平台的运营方,会提前在城市各处建好一批编号的仓库。这些仓库不依赖任何一家具体的店铺(Pod),即便店铺倒闭了,仓库还在。这就是 PersistentVolume(PV)——它是 Kubernetes 集群级的一种资源对象,独立于 Pod 存在,由存储管理员提前“建仓”,代表一块真实的存储设备,比如云硬盘、NFS 共享目录或本地盘。

从技术上看,PV 描述了三件关键事情:仓库有多大(capacity)、允许什么样的人进入(访问模式)、退租后怎么办(回收策略)。访问模式决定了这块存储能不能被多个节点同时使用:有的仓库只能给一个租户独占,有的可以很多人同时只读翻阅,有的甚至可以多人同时读写。

回收策略也像仓库退租条款:Retain 表示租户搬走后仓库保留,等待人工盘点;Delete 表示退租时直接拆除仓库(删除底层存储);Recycle(已废弃)曾表示清扫后重新出租。了解了仓库,接下来就看租户如何下单了。

访问模式∈{ReadWriteOnce,ReadOnlyMany,ReadWriteMany}\text{访问模式} \in \{ \text{ReadWriteOnce}, \text{ReadOnlyMany}, \text{ReadWriteMany} \}
  1. ReadWriteOnce(RWO)单节点读写模式,如同“独享仓库”,锁上门只允许一个 Pod 读写,适合数据库等需要强一致的场景。
    ReadWriteOnce→单节点读写\text{ReadWriteOnce} \rightarrow \text{单节点读写}
  2. ReadOnlyMany(ROX)多节点只读模式,如同“公共资料馆”,多个 Pod 可以同时翻阅,但谁也不能修改。
    ReadOnlyMany→多节点只读\text{ReadOnlyMany} \rightarrow \text{多节点只读}
  3. ReadWriteMany(RWX)多节点读写模式,如同“共享创作间”,多个 Pod 同时读写,适合协作型工作负载。
    ReadWriteMany→多节点读写\text{ReadWriteMany} \rightarrow \text{多节点读写}

PVC请求与绑定:提交“仓库需求单”

租户企业不会自己去满城找仓库——他们只填一张“仓库需求单”(PersistentVolumeClaim,PVC),写明需要多大面积、什么访问模式。这张需求单不是仓库本身,而是一份对存储的请求声明。

从技术上讲,PVC 是命名空间(namespace)级别的对象,Pod 通过 volumes.persistentVolumeClaim 字段声明要挂载哪张需求单。Kubernetes 的控制平面会像仓库调度员一样,替这张需求单寻找一个满足条件的 PV,并建立绑定关系。绑定成功后,PVC 状态从 Pending 变为 Bound,Pod 就可以把这块存储挂载为容器内的目录,就像把仓库的货架直接搬到自己的办公室里。

这种“需求单”式设计最大的好处是解耦:应用开发者只关心“我要多少存储”,不用关心底层是云盘还是 NFS,存储细节被 PVC 这个接口完全屏蔽了。

capacity(PV)≥request(PVC)\displaystyle \text{capacity}(\text{PV}) \geq \text{request}(\text{PVC})
  1. request(PVC)需求单上声明的期望容量,例如“我要 100Gi 的空间”,这是租户的主观诉求。
    requestPVC=容量诉求\text{request}_{\text{PVC}} = \text{容量诉求}
  2. capacity(PV)仓库实际可用的总容量,这是运营方建仓时的客观指标。
    capacityPV=仓库实际容量\text{capacity}_{\text{PV}} = \text{仓库实际容量}
  3. ≥ 比较判断调度员拿着需求单去核对仓库面积,真实容量必须不少于诉求,仓库才进入候选名单。
    capacityPV≥requestPVC⇒候选\text{capacity}_{\text{PV}} \geq \text{request}_{\text{PVC}} \Rightarrow \text{候选}

绑定匹配与生命周期:三条件合一的“锁仓”流程

仓库管理员接到需求单后,会核对三件事:仓库够不够大(容量)、开放模式是否匹配(访问模式)、是不是同一套仓储体系(storageClass)。只有三项同时通过,管理员才会“锁仓”绑定。这个链路对应 Kubernetes 中 PV 与 PVC 的绑定机制。

技术细节上有三个匹配条件必须同时满足:PV 的容量 ≥ PVC 的请求容量;PV 的访问模式与 PVC 一致;PV 的 storageClassName 与 PVC 相同(或者两者都没指定,则由集群默认策略处理)。绑定是排他性的:一个 PV 一旦绑定某个 PVC,就不能再绑定其他 PVC,直到解绑。

绑定后,PV 的生命周期会经历 Available → Bound → Released → Failed,PVC 则从 Pending 进入 Bound。如果仓库管理员想人工指定某个仓库给某个租户,可以直接在 PV 上写 claimRef 预绑定——就像在仓库门上提前贴好租户名字。

match=(capPV≥reqPVC)∧(modePV=modePVC)∧(classPV=classPVC)\displaystyle \text{match} = \big(\text{cap}_{\text{PV}} \geq \text{req}_{\text{PVC}}\big) \land \big(\text{mode}_{\text{PV}} = \text{mode}_{\text{PVC}}\big) \land \big(\text{class}_{\text{PV}} = \text{class}_{\text{PVC}}\big)
  1. 容量条件仓库库位必须大于等于需求单上的面积,否则塞不下货物,直接淘汰。
    capPV≥reqPVC\text{cap}_{\text{PV}} \geq \text{req}_{\text{PVC}}
  2. 模式配套租户要求“可多节点读写”,就不能配一个“单节点独占”的仓库,所以必须严格相等。
    modePV=modePVC\text{mode}_{\text{PV}} = \text{mode}_{\text{PVC}}
  3. 存储类一致storageClass 像“仓储体系”的标识,普通仓和冷链仓不能混用,只有同体系的仓库才进入匹配池。
    classPV=classPVC\text{class}_{\text{PV}} = \text{class}_{\text{PVC}}

StorageClass动态供给:自动建仓的智能调度员

前面说的都是“人工备货”:管理员提前建好一堆 PV 等着租户下单。可如果集群里没有匹配的仓库怎么办?总不能每次都让运维手工创建 PV。StorageClass(SC)就像一位智能调度员,它接单后发现没有现成仓库,会立刻联系“仓储建造商”(provisioner)自动新建一间仓库,再完成绑定。这就是动态供给。

StorageClass 是集群级对象,核心字段包括 provisioner(谁来建仓,比如 AWS EBS CSI、NFS CSI、阿里云盘 CSI)、reclaimPolicy(退租后删不删)、volumeBindingMode(立即绑定还是等 Pod 调度后再绑定)。创建 PVC 时写上 storageClassName: fast-ssd,调度员就知道该用哪套建仓模板。

动态供给让“按单生产”成为现实:你不再维护一堆空闲 PV 池,而是让 SC 在 Pod 需要时自动建仓。尤其是把某个 SC 设为默认存储类后,PVC 不写 SC 名称也能自动获得动态供给,新手指南里最常见的操作就是“什么都不填,就有盘用了”。

PVnew=ProvisionerSC(storageClassName,capacity,mode)\displaystyle \text{PV}_{\text{new}} = \text{Provisioner}_{\text{SC}}\big(\text{storageClassName}, \text{capacity}, \text{mode}\big)
  1. Provisioner_SC调度员对应的“建仓服务商”,例如 ebs.csi.aws.com,它真正调用云厂商 API 创建底层存储。
    ProvisionerSC=后端建仓服务\text{Provisioner}_{\text{SC}} = \text{后端建仓服务}
  2. capacity 与 mode从 PVC 需求单上读取的容量和访问模式,作为新建仓库的图纸参数。
    参数:{storageClassName,capacity,mode}\text{参数}: \{\text{storageClassName}, \text{capacity}, \text{mode}\}
  3. 新 PV 产出建仓服务创建成功后,Kubernetes 将新存储封装为 PV 对象,并立即与 PVC 绑定。
    PVnew→Bound\text{PV}_{\text{new}} \rightarrow \text{Bound}

存储后端选型:云盘、NFS还是对象存储

仓库建造的“地段”不同,运输速度和成本天差地别。K8s 里的存储后端选型,本质上是在延迟、共享能力和成本之间做权衡。最常见的三类后端是:云盘、网络文件系统(NFS)、对象存储。

云盘(如 AWS EBS、阿里云 ESSD)像“独立车库”,延迟低、IOPS 高,单节点独占,适合数据库、AI 训练 checkpoint 等对性能敏感的 RWO 场景。NFS 像“小区公共储物间”,支持 RWX 多节点同时读写,适合多个 Pod 共享同一份数据集,但吞吐受网络影响较大。对象存储(如 S3、OSS)像“城市档案馆”,容量近乎无限、成本低,但延迟最高,通常不直接作为容器卷,而是用于备份和归档。

选型建议很直白:追求性能选云盘,需要跨 Pod 共享选 NFS,海量冷数据备份选对象存储。记住一个比喻:你不可能把整个档案馆搬回家里当厨房——但你可以把它当成保险柜。

IOPS云盘≫IOPSNFS≥IOPS对象,成本对象≪成本云盘\displaystyle \text{IOPS}_{\text{云盘}} \gg \text{IOPS}_{\text{NFS}} \geq \text{IOPS}_{\text{对象}}, \qquad \text{成本}_{\text{对象}} \ll \text{成本}_{\text{云盘}}
  1. IOPS 对比云盘单盘可做到数万到百万 IOPS,NFS 依赖网络往往只有数千,对象存储受 API 延迟限制更低。
    IOPS云盘≫IOPSNFS\text{IOPS}_{\text{云盘}} \gg \text{IOPS}_{\text{NFS}}
  2. 成本对比对象存储以“按量付费 + 低单价”取胜,云盘按容量和性能等级计费,通常是最贵的选项。
    成本对象≪成本云盘\text{成本}_{\text{对象}} \ll \text{成本}_{\text{云盘}}

有状态应用存储与迁移:给固定住户配固定仓库

数据库、消息队列、AI 训练任务这类“长期定居”的租户,最怕一件事:搬家。普通 Deployment 的 Pod 被删除重建后名字会变,但如果 StatefulSet 中的 Pod 重建后仍然叫 mysql-0、mysql-1,就像小区里的固定门牌号。配合 volumeClaimTemplates,Kubernetes 会为每个副本单独生成一张 PVC 需求单,保证 mysql-0 永远挂载它自己的那间仓库。

技术要点是:StatefulSet 的 Pod 序号与 PVC 名称一一对应。pod-0 挂 data-mysql-0,pod-1 挂 data-mysql-1,副本重建后依然按名字重新挂载同一块 PV,数据不会丢。

如果需要把数据迁移到新集群或新可用区,正确顺序是“先备份再搬家”:用快照或 Velero 把 PV 数据和对象清单备份出来,在新环境创建 PVC 并恢复数据,最后再拉起 StatefulSet。千万不要直接删 PVC,那等于把仓库拆了重盖,里面的家当全没了。

∀i∈[0,n):PVCi⟷PVi\displaystyle \forall i \in [0, n): \text{PVC}_i \longleftrightarrow \text{PV}_i
  1. 副本索引 iStatefulSet 中每个 Pod 有一个稳定序号,如 mysql-0、mysql-1。
    i=0,1,2,…,n−1i = 0, 1, 2, \dots, n-1
  2. 一一对应每个序号的 Pod 都绑定并“独占”自己的 PVC 与 PV,互不共享。
    PVCi⟷PVi\text{PVC}_i \longleftrightarrow \text{PV}_i

性能调优与故障排查:仓库拥堵与寻仓失败

当仓库吞吐逼近上限,就像快递通道在高峰期堵得水泄不通。性能调优第一步是量化:看卷的 IOPS、吞吐、延迟和容量使用率,分清是容量瓶颈还是性能瓶颈。第二步选型:AI 训练场景若发现 checkpoint 写盘慢,优先考虑更高 IOPS 的 ESSD/GP3 卷,或在应用层将小文件合并成大文件写入。

故障排查则像仓库管理员处理“寻仓失败”的投诉:Pod 卡在 Pending、ContainerCreating 或 FailedAttachVolume,通常有四个检查点:PVC 状态是否为 Bound、PV 是否存在且未被占用、SC 是否有对应 provisioner、节点是否有权限挂载该存储。以下是监控中最常用的容量公式:

调优和排障如同仓库运营:先量化、再定位、最后动手。很多时候,所谓“存储很慢”其实是节点间网络带宽不够,或者 Pod 与卷不在同一可用区导致额外延迟。

usage_rate=used_bytescapacity_bytes×100%,usage_rate>85%⇒告警\displaystyle \text{usage\_rate} = \frac{\text{used\_bytes}}{\text{capacity\_bytes}} \times 100\%, \qquad \text{usage\_rate} > 85\% \Rightarrow \text{告警}
  1. 分子 used_bytes实际已使用的存储字节数,来自监控指标。
    used_bytes=已用空间\text{used\_bytes} = \text{已用空间}
  2. 分母 capacity_bytes卷的总容量上限,来自 PVC/PV 定义。
    capacity_bytes=总容量\text{capacity\_bytes} = \text{总容量}
  3. 触发告警使用率超过 85% 时发预警,超过 95% 通常就需要立即扩容,否则写入可能失败。
    usage_rate>85%⇒告警\text{usage\_rate} > 85\% \Rightarrow \text{告警}

备份恢复与监控告警:给仓库上保险、装监控

仓库再坚固,也怕火灾地震。存储的可用性要靠“备份 + 恢复 + 监控告警”三件套托底。备份解决“数据能不能找回来”的问题,监控解决“什么时候出问题”的问题。

技术上,备份有两层含义:一是 PV 数据快照(如云硬盘快照,或 Velero 备份整个集群资源 + PV 数据);二是演练恢复流程,确保灾难发生时能在限定时间内拉起业务。两个核心指标是 RPO(最多丢多少数据)和 RTO(多久恢复业务)。监控侧则用 Prometheus 采集 PVC 容量指标、CSI 存储性能和节点挂载状态,配合 Grafana 展示仪表盘,超过阈值就推送到钉钉/短信。

存储上云只是开始,真正让系统“睡得着觉”的是备份纪律和告警可观测性。定期做恢复演练,就像消防演习:平时多练一次,真出事时少慌十分。

---

RPO=T快照间隔,RTO=T恢复+T校验\displaystyle \text{RPO} = T_{\text{快照间隔}}, \qquad \text{RTO} = T_{\text{恢复}} + T_{\text{校验}}
  1. RPO(恢复点目标)两次快照之间的间隔决定了最多能丢多少数据,快照越频繁,RPO 越小。
    RPO=T快照间隔\text{RPO} = T_{\text{快照间隔}}
  2. RTO(恢复时间目标)从故障发生到业务重新可用的时间,包括恢复数据所需的操作时间和最终校验时间。
    RTO=T恢复+T校验\text{RTO} = T_{\text{恢复}} + T_{\text{校验}}

AI 应用场景

AI训练Checkpoint持久化

阶段:模型训练与断点续训

解决的问题:GPU 训练 Pod 常因抢占、节点故障被重启。传统做法把 checkpoint 写在 Pod 本地磁盘,Pod 一删就前功尽弃。用 PV/PVC 持久化后,Pod 重建会自动挂载原卷,训练从最近 checkpoint 继续。

具体案例:某大模型训练任务采用 StatefulSet + PVC 绑定高性能云盘,StorageClass 自动供给 1TB ESSD,每 10 分钟写入一次 checkpoint。某次 GPU 节点被抢占,新 Pod 在另一可用区启动,由于 PVC 跨可用区快照恢复,训练仅回退 10 分钟便恢复到原有进度。

多副本推理服务共享模型文件

阶段:模型部署与推理服务

解决的问题:推理服务需要多副本横向扩展,每个副本都要读取同一份最新模型文件。如果每个 Pod 自己复制一份,占用存储且更新不同步;使用支持 RWX 的 NFS/CFS 存储,多个推理 Pod 可以同时挂载同一份模型目录。

具体案例:在线推荐系统部署 12 个推理副本,模型文件存放于 20GB NFS 卷。模型热更新时只需替换卷内文件,12 个 Pod 通过 reload 机制自动加载新版本,避免了镜像重建和滚动发布的长时间等待。

训练数据集的统一供给与归档

阶段:数据处理与归档

解决的问题:数据集通常以 TB 计,放在对象存储上延迟高,训练节点直接读 S3 会很慢;传统方式是手工挂载 NFS 拷数据,管理混乱。通过 StorageClass 动态供给,把对象存储数据周期性同步到高性能云盘卷中,训练任务使用 PVC 即可快速读取。

具体案例:CV 团队将 5TB 图片数据集存放于 S3,每天凌晨通过 DataSync 同步到高性能 PVC,训练任务直接挂载 PVC 读取,整体数据加载时间从 2 小时降到 15 分钟。

Python 演示

python72 行
class PV:    """PersistentVolume: 备货仓库"""    def __init__(self, name, capacity_g, access_mode, storage_class, occupied=False):        self.name = name        self.capacity_g = capacity_g        self.access_mode = access_mode        self.storage_class = storage_class        self.occupied = occupied        self.bound_pvc = None     def bind(self, pvc):        self.occupied = True        self.bound_pvc = pvc.name        print(f"[bind] {self.name} <-> {pvc.name}")  class PVC:    """PersistentVolumeClaim: 仓库需求单"""    def __init__(self, name, request_g, access_mode, storage_class):        self.name = name        self.request_g = request_g        self.access_mode = access_mode        self.storage_class = storage_class        self.bound_pv = None  def match_pv(pool, pvc):    """静态供给: 在现成仓库中匹配一个满足三条件的 PV"""    for pv in pool:        if (not pv.occupied                and pv.capacity_g >= pvc.request_g                and pv.access_mode == pvc.access_mode                and pv.storage_class == pvc.storage_class):            pv.bind(pvc)            pvc.bound_pv = pv            return pv    return None  def provision_pv(pool, pvc, storage_class):    """动态供给: StorageClass 无现成仓库时自动新建 PV"""    new_pv = PV(        name=f"pv-dynamic-{len(pool) + 1}",        capacity_g=pvc.request_g,        access_mode=pvc.access_mode,        storage_class=storage_class,    )    pool.append(new_pv)    new_pv.bind(pvc)    pvc.bound_pv = new_pv    return new_pv  if __name__ == "__main__":    pool = [        PV("pv-manual-1", 100, "RWO", "standard"),        PV("pv-manual-2", 200, "RWO", "standard", occupied=True),        PV("pv-nfs-1", 500, "RWX", "nfs"),    ]    tasks = [        PVC("pvc-ai-1", 80, "RWO", "standard"),        PVC("pvc-ai-2", 150, "RWO", "standard"),    ]    for pvc in tasks:        pv = match_pv(pool, pvc)        if pv is None:            provision_pv(pool, pvc, pvc.storage_class)        print(f"{pvc.name} -> {pvc.bound_pv.name if pvc.bound_pv else 'Failed'}")     print("--- pool status ---")    for pv in pool:        print(f"{pv.name}: occupied={pv.occupied} bound_to={pv.bound_pvc}")
目的:用 50 行 Python 模拟 Kubernetes 中 PVC 从“静态绑定现成 PV”到“StorageClass 动态供给新 PV”的完整决策流程,帮助理解存储调度逻辑。
  1. class PV:定义仓库对象,记录容量、访问模式、存储类和占用状态。
  2. pv.capacity_g >= pvc.request_g容量匹配条件,是绑定三条件之一。
  3. pv.access_mode == pvc.access_mode访问模式必须完全一致,否则仓库不能交给该租户。
  4. pv.storage_class == pvc.storage_class存储类一致,确保使用的是同一套仓储体系。
  5. def match_pv(...)实现静态供给:在现有 PV 池中顺序查找满足条件的仓库。
  6. def provision_pv(...)实现动态供给:找不到仓库时,按需求单参数创建新 PV 并加入池中。
  7. pool.append(new_pv)新仓库建造完成后加入仓库池,未来其他 PVC 也能继续复用。
  8. pv-dynamic-2动态生产的 PV 命名包含序号,表示由 StorageClass 而非人工创建。

常见误区

误区初学者常以为创建了 PVC,应用启动后就有了一个真实的存储设备,数据写进去就是写在 PVC 里。

正解PVC 只是一张“仓库需求单”,它不是仓库本身。真正的存储设备绑定的是 PV。PVC 与 PV 绑定成功后,Pod 通过 PVC 挂载到 PV,数据最终落在 PV 背后的云盘或 NFS 上。理解这个区分,才能看懂为什么 PVC 可以删除而 PV 数据未必消失。

为何常见:因为日常开发中创建 PVC 后马上就能用,像创建文件夹一样简单,大家容易把“请求”和“实体”混为一谈。

误区很多人认为删除 PVC 后,pod 写入的数据也会被自动清空,所以大胆删 PVC 做“清理操作”。

正解删除 PVC 后,PV 的回收策略才是决定数据命运的关键。如果 PV 的回收策略是 Retain,PV 会进入 Released 状态,底层数据依然保留,只是等待管理员手动处理;只有策略为 Delete 时,底层存储才会被一并联删除。生产环境建议对重要数据使用 Retain 或多做一层快照。

为何常见:因为大多数云环境默认 StorageClass 的 reclaimPolicy 是 Delete,测试环境删了真的没了,用户便以为这是唯一行为。

误区部分新手认为 StorageClass 是公有云的专属功能,自建 K8s 集群无法使用动态供给。

正解StorageClass 是一个抽象层,只要集群里有 CSI 或 in-tree provisioner 就能工作。社区提供了 NFS、Ceph RBD、Local Path 等多种自建驱动,完全可以在私有机房实现动态供给。关键在于 provisioner 字段指向谁,而不在于是否连了云厂商。

为何常见:因为教程里最常见的例子都是 AWS EBS 或阿里云盘,容易形成“SC 等于云盘”的偏见。

误区认为 StatefulSet 缩容或删除时,每个副本的 PVC 也会一并删除,所以不敢缩容。

正解StatefulSet 管理的是 Pod,默认不会删除 PVC。缩容时 Pod 被删除,但 PVC 和 PV 仍然保留,等扩容后同名 Pod 会重新挂载旧数据。如果想彻底删数据,必须显式删除 PVC,并确认回收策略是 Delete。

为何常见:因为 Deployment 的 Pod 删除时,与其绑定的临时存储会消失,大家把这种经验错误迁移到了 StatefulSet 上。

误区存储不够用时,新手想“删掉重新申请一个更大的 PVC”,以为这样最快。

正解删除 PVC 会把原 PV 解绑,若回收策略是 Delete 则老数据直接丢失。正确做法是使用 CSI 卷的在线扩容能力,对支持扩容的存储类修改 PVC 的 spec.resources.requests.storage 字段并应用,系统会自动扩容底层卷。扩容前同样建议先做快照。

为何常见:因为日常生活中“存储不够就换个大硬盘”太常见,忽视了 K8s 中数据生命的严肃性。

面试题

Q1. 在 K8s 存储体系中,PVC 的角色相当于什么?

单选题 · easy

  • A.已经建好的仓库本体
  • B.租户提交的仓库需求单← 正确答案
  • C.自动分拣仓库的机器人
  • D.仓库墙上的储物货架

答案:B。PVC 是 PersistentVolumeClaim,即“持久卷声明”,它只是描述存储诉求(容量、模式)的需求单,而不是存储实体本身。真正的存储实体是 PV,自动建仓逻辑属于 StorageClass。

Q2. PV 访问模式中,RWO 表示什么?

单选题 · easy

  • A.多个节点同时读写
  • B.单个节点读写← 正确答案
  • C.所有节点只读访问
  • D.随机读写单节点

答案:B。RWO(ReadWriteOnce)指同一时间只能被一个节点读写,如同“独享仓库”。多节点同时读写是 RWX,多节点只读是 ROX。

Q3. PV 与 PVC 成功绑定,需要满足哪些条件?

多选题 · mid

  • A.PV 容量大于等于 PVC 请求容量← 正确答案
  • B.访问模式与需求单一致← 正确答案
  • C.storageClassName 与需求单一一致← 正确答案
  • D.PV 必须与 PVC 在同一个节点上

答案:ABC。绑定三条件是容量、访问模式、存储类,三者全部匹配才“锁仓”。PV 是集群级资源,不限定节点,所以 D 错误。这正像仓库管理员核对面积、开放模式和仓储体系后才确定候选仓库。

Q4. StorageClass 的核心作用是什么?

单选题 · mid

  • A.保存卷访问秘密和密钥
  • B.按模板自动创建新的 PV← 正确答案
  • C.只服务于静态 NFS 存储
  • D.替代 PVC 直接挂载进 Pod

答案:B。StorageClass 封装了 provisioner 与参数,当 PVC 找不到现成 PV 时,它会调用后端起建仓服务动态创建新 PV。它不保存密钥,也不替代 PVC 挂载。

Q5. PV 的回收策略为 Retain 时,PVC 被删除后会发生什么?

单选题 · mid

  • A.数据立即被系统重建
  • B.PV 保留等待人工清理← 正确答案
  • C.PV 自动绑定新的 PVC
  • D.PV 连同存储一起删除

答案:B。Retain 意为“保留”,PVC 删除后 PV 进入 Released 状态,底层数据不动,等待管理员人工决定如何处理。自动清理或删除是 Delete 策略的行为。

Q6. StatefulSet 中的 volumeClaimTemplates 有哪些特点?

多选题 · hard

  • A.每个副本 Pod 配独立 PVC← 正确答案
  • B.PVC 名称由模板生成且稳定← 正确答案
  • C.副本删除后 PVC 立刻删除
  • D.重启后副本重新挂载原 PVC← 正确答案

答案:ABD。volumeClaimTemplates 会按“卷名-副本名”的规则为每个副本生成独立 PVC,Pod 重建后依然按名称重新挂载同一块存储。PVC 默认不随副本删除,所以 C 错误。这保证了每个固定住户都有自己固定的仓库门牌号。

Q7. 有状态应用需要迁移到新可用区,最稳妥的第一步是?

单选题 · mid

  • A.删除 PVC 让系统自动重建
  • B.先做快照备份再规划迁移恢复← 正确答案
  • C.直接修改 PV 亲和性字段
  • D.强制调度新的 Pod 去新节点

答案:B。迁移前必须先备份数据,否则任何调度操作都有数据丢失风险。快照或 Velero 备份后,在新可用区创建 PVC 并恢复数据,再拉起应用。删 PVC 等于拆仓库,是最危险的操作。

Q8. PVC 容量使用率超过 85% 告警阈值后,合理的处理方式有哪些?

多选题 · hard

  • A.配置 Prometheus 告警规则← 正确答案
  • B.扩容 PVC 卷并清理历史数据← 正确答案
  • C.重启 Pod 让系统自愈
  • D.定期清理过期检查点← 正确答案

答案:ABD。存储使用率过高时,应先通过监控告警感知问题,然后扩容卷或清理历史数据。重启 Pod 不能释放 PV 上的空间,反而可能造成新的调度波动。定期清理过期 checkpoint 是 AI 训练场景最常见的降本手段。

Q9. Pod 内写入的数据在 Pod 重启后丢失,最可能的原因是什么?

单选题 · easy

  • A.挂载的是临时卷而非持久卷← 正确答案
  • B.集群节点数量不够
  • C.镜像版本太旧
  • D.namespace 配额不足

答案:A。如果 Pod 没有挂载 PV/PVC,而只使用 emptyDir 或容器可写层,Pod 删除后数据就没了。持久化必须显式声明 PVC 并绑定 PV,像仓库需求单一样先下订单才有仓库可用。

Q10. StorageClass 中 volumeBindingMode 设为 WaitForFirstConsumer 的作用是什么?

单选题 · hard

  • A.绑定前等待 Pod 完成调度← 正确答案
  • B.强制 Pod 必须分配固定节点
  • C.禁用动态供给只做人工建 PV
  • D.将 PV 永久保存在本机内存里

答案:A。WaitForFirstConsumer 延迟 PVC 到 PV 的绑定,直到第一个 Pod 被调度,再让处理器在 Pod 所在可用区创建卷,减少跨区访问延迟。这像“租户确定入住哪个小区后,再在小区楼下建仓”。