想象一下,你家的电脑桌上堆着 U 盘、移动硬盘、打印机、摄像头,形态各异、品牌繁多。但只要它们有 USB 插口,插上电脑就能用,你完全不用关心设备内部是哪种芯片、哪家方案。USB 标准最了不起的地方,是把“连接”这个动作彻底标准化了。 云原生世界里的容器,就像一群永远记不住事的员工——一重启就忘光所有数据。为了让数据库、消息队列这类“有记忆”的应用能在 Kubernetes 上跑起来,我们必须把数据放在容器外部的存储系统里:本地盘、云盘、NAS、分布式存储……可每一种存储的接入方式都不同,就像没有 USB 标准的年代,每买一台打印机都要拆开主机焊线。今天要讲的 CSI(Container Storage Interface,容器存储接口),正是云原生存储界的“USB 标准”。它让任何存储厂商按同一套协议接入 Kubernetes,让使用者用声明式方法申请存储,而不必关心底层实现。我们从存储需求讲起,逐步拆解 CSI 的结构、卷生命周期、挂载流程、快照扩容和工程实践,最后用代码和习题帮你巩固理解。

岗位关联

理解 CSI,等于掌握了 Kubernetes 存储领域的通用语言。无论你是做平台工程、云原生运维还是数据库容器化,都会直接受益于这项核心抽象。

目录
  1. 可视化演示
  2. 持久化存储需求
  3. CSI标准诞生
  4. 核心架构组件
  5. 卷生命周期管理
  6. 挂载工作流
  7. 快照与扩容
  8. 工程实践与排障
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

动态展示CSI三层服务架构、块/文件/对象存储分类、PVC动态供给流程与卷生命周期管理机制。

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

持久化存储需求

电脑主机可以随时重启,但如果 U 盘里的文件没保存,重启后照样消失。容器也一样,它天生是“没有记忆的临时工”,Pod 一删,里面写的文件全部随风而逝。可现实世界中的数据库、消息队列、推荐系统,恰恰都是“必须记住一切”的应用。要让它们跑在 Kubernetes 上,就必须把数据寄存在容器外部的存储设备中。

这些外部存储五花八门:有的来自本地机架的磁盘,有的来自云厂商的云盘,有的来自共享文件系统。每一种系统都有自己的接入协议和界面,就像不同时代的打印机各有各的接口。如果没有统一标准,Kubernetes 每接入一种存储,都要为它单独写一套适配逻辑,成本高得惊人。

因此,在规划存储容量时,我们需要一个简单的账本:集群里到底能容纳多少数据?这取决于卷数量、单卷大小、副本系数以及存储系统自身的开销。

有了这个账本,我们才明白:不是随便申请多少个卷都能放下,必须精打细算。而这种“算账”的复杂性,正是催生 CSI 统一接口的原始动力——我们不想为每一块牌子不同的 U 盘,单独学习一套使用方法。

Cusable=Nvol×Svol×Rrep×(1−Oovh)C_{usable} = N_{vol} \times S_{vol} \times R_{rep} \times (1 - O_{ovh})
这就像在电脑桌上数一数有几块 U 盘、每块多大、是否备份了双份、以及格式化损耗了多少。
  1. $N_{vol}$(卷数量)卷是 Kubernetes 中最小的存储单元,类似于一块“移动硬盘”的逻辑表示。
    NvolN_{vol}
  2. $S_{vol}$(单卷大小)每个卷声明的容量,比如 100GB,就像 U 盘上标着的存储空间。
    SvolS_{vol}
  3. $R_{rep}$(副本系数)数据为了可靠性而复制几份,副本数越多,实际消耗空间越大。
    RrepR_{rep}
  4. $O_{ovh}$(开销率)文件系统格式化、快照元数据、存储系统自身冗余带来的固定损耗。
    OovhO_{ovh}

CSI标准诞生

USB 标准诞生之前,打印机厂家各自为战,电脑厂商被迫在主板上焊满各种专用接口。后来 USB-IF 组织统一了协议,外设厂商只要按标准制造,电脑就能自动识别。CSI 的故事几乎一模一样:在它出现之前,Kubernetes 的存储插件都是“in-tree”方式——各家存储厂商把代码提交进 Kubernetes 核心仓库,像是往别人的主板里焊自己的线。结果是版本耦合、编译风险、发布节奏互相拖累。

后来出现的 FlexVolume 尝试把插件拆出去,但它只是“半标准化”:需要各节点预装脚本,能力也有限。到了 2018 年前后,SIG-Storage 社区推出了 CSI 规范,用一组标准 gRPC 接口把 Kubernetes 与存储系统解耦。存储厂商只需实现一个 CSI 驱动,部署进集群,Kubernetes 就能通过统一协议与它对话。这就像外设厂商只要按 USB 规格做接头,操作系统就能自动安装驱动。

从数学上看,CSI 的接口模型可以抽象为一个三元组:身份服务、控制面服务、节点面服务。

CSI 的出现,让“存储插件化”从理想变成现实。Kubernetes 不再关心后端是云盘还是本地盘,就像操作系统不再关心 USB 设备内部是闪存还是机械硬盘。这套标准,至今仍是云原生存储世界的基石。

Icsi=(Iidentity,Icontroller,Inode)I_{csi} = \left( I_{identity}, I_{controller}, I_{node} \right)
这三个服务分别回答了三个问题:你是谁、你能管理什么卷、你能在节点上做什么。
  1. $I_{identity}$(身份服务)驱动向 Kubernetes 自报家门,包括驱动名称、版本、能力清单,这是双方对话的“握手信号”。
    IidentityI_{identity}
  2. $I_{controller}$(控制面服务)负责卷的创建、删除、快照、扩容,相当于电脑对设备发出“格式化”“弹出”指令。
    IcontrollerI_{controller}
  3. $I_{node}$(节点面服务)负责在具体节点上把存储挂载到目录、供容器使用,相当于 USB 设备在操作系统中获得一个盘符。
    InodeI_{node}

核心架构组件

一支 U 盘能工作,靠的是接头、控制芯片和存储颗粒三层分工。CSI 驱动也类似,它是一个包含三个 gRPC 服务的二进制程序:Identity Service、Controller Service、Node Service。这三者共同组成了 Kubernetes 与存储系统之间的“USB 协议栈”。

先看 Controller Service。它监听来自 Kubernetes controller-manager 侧的请求,负责创建卷、删除卷、创建快照、扩容。它操作的是“存储系统”这个远程对象,相当于你在电脑上点击“安全弹出硬件”时,系统对设备控制芯片发出的命令。Node Service 则运行在每一个工作节点上,由 kubelet 调用,负责把卷实际挂载到宿主机目录、再绑定进 Pod 的目录,相当于数据线把数据从设备传输到系统。Identity Service 则负责回答“我是谁、我支持什么”,驱动只有在自报身份后才能被上层组件信任。

除此之外,Kubernetes 还提供了几个“外挂小助手”——sidecar 容器,例如 external-provisioner、external-attacher、external-resizer。它们监听 API 对象变化,把用户的声明翻译成对 CSI 驱动 RPC 的调用。整个架构可以用一个集合来表达:

这三个服务合起来,就是一台“存储翻译机”。用户不必直接操作云厂商控制台,Kubernetes 中的 PVC 对象就是你的指令,CSI 驱动会自动把指令翻译成存储系统能听懂的语言。就像你按一下 USB 接口的“弹出”按钮,操作系统会替你完成背后所有复杂的握手流程。

Dcsi={Sidentity,Scontroller,Snode}D_{csi} = \left\{ S_{identity}, S_{controller}, S_{node} \right\}
一个 CSI 驱动 = 一套身份服务 + 一套控制面服务 + 一套节点面服务。
  1. $S_{identity}$(身份服务)实现 GetPluginInfo 等方法,让 Kubernetes 认识驱动,并查询它支持哪些 RPC。
    SidentityS_{identity}
  2. $S_{controller}$(控制面服务)实现 CreateVolume、DeleteVolume、CreateSnapshot 等方法,管理存储设备的“生老病死”。
    ScontrollerS_{controller}
  3. $S_{node}$(节点面服务)实现 NodePublishVolume、NodeUnpublishVolume 等方法,在节点上完成挂载与卸载动作。
    SnodeS_{node}

卷生命周期管理

一块 U 盘的一生,从商店货架上被买走开始,到插进电脑被识别,再到使用完毕后安全弹出,最后被淘汰。CSI 卷的生命周期也有类似的节奏。用户声明需要存储,就像下单买盘;系统分配卷,就像盘从货架到了你手里;然后卷被附加到节点、挂载进容器,数据开始流动;不再需要时,卷被卸载、分离和删除。

在 Kubernetes 中,用户通过 PVC(PersistentVolumeClaim)表达存储需求,系统根据 StorageClass 动态创建 PV(PersistentVolume)。external-provisioner 监听 PVC 事件并调用 CSI 驱动的 CreateVolume,卷被创建出来。之后 external-attacher 调用 ControllerPublishVolume,把卷附加到目标节点,相当于把 U 盘插入 USB 口。最后 kubelet 调用 Node 服务完成挂载,Pod 才真正读到数据。

生命周期可以用一个简洁的状态迁移来描述:

理解生命周期,就像掌握了一张 U 盘的使用手册。每一环节都有 Kubernetes 对象和 CSI RPC 一一对应,出了问题也能迅速定位是卡在“插入”“分配盘符”还是“弹出”阶段。

Vlife:P→createC→publishA→mountM→deleteDV_{life}: \quad P \xrightarrow{\text{create}} C \xrightarrow{\text{publish}} A \xrightarrow{\text{mount}} M \xrightarrow{\text{delete}} D
卷的旅途:声明(P)→ 创建(C)→ 附加(A)→ 挂载(M)→ 删除(D)。
  1. $P \xrightarrow{\text{create}} C$用户提交 PVC,触发 CreateVolume,卷被创建,相当于你下单买到一块新盘。
    P→createCP \xrightarrow{\text{create}} C
  2. $C \xrightarrow{\text{publish}} A$卷附加到指定节点,对应 ControllerPublishVolume,相当于把 U 盘插进电脑的 USB 口。
    C→publishAC \xrightarrow{\text{publish}} A
  3. $A \xrightarrow{\text{mount}} M$节点级挂载完成,对应 NodePublishVolume,相当于系统为 U 盘分配盘符,应用可以读写。
    A→mountMA \xrightarrow{\text{mount}} M
  4. $M \xrightarrow{\text{delete}} D$Pod 删掉后卷被卸载、分离、最终删除,整个生命周期闭环。
    M→deleteDM \xrightarrow{\text{delete}} D

挂载工作流

你有没有注意过,插入 U 盘时电脑并不是瞬间就显示文件?系统要先识别设备、再装上驱动、然后分配盘符。CSI 的挂载工作流也是层层递进的三步走,每一步都在前一步的基础上更靠近“数据通路”。

第一步是 ControllerPublishVolume,发生在控制面:Kubernetes 让 CSI 驱动把卷“发布”到某个节点,也就是在存储系统与节点之间建立一条逻辑连接。第二步是 NodeStageVolume,发生在节点层:驱动把卷暂存到节点上的一个全局目录,相当于给数据提供了一个“中转站”。第三步是 NodePublishVolume,驱动把卷绑定到 Pod 的指定目录,容器应用这才真正看到文件系统。

这条链路中每一步都包含超时与重试机制,因为底层网络可能抖动、存储系统可能繁忙。理解耗时构成,可以帮助我们定位性能瓶颈:

挂载工作流就像一条自动化的 USB 连接流水线:识别设备、安装驱动、分配盘符,每一步都有明确责任方。排查挂载问题时,我们只需要沿着这三步走,看哪一步卡住,就能快速锁定故障点。

Ttotal=Tpublish+Tstage+TnodeT_{total} = T_{publish} + T_{stage} + T_{node}
从发起挂载到数据可见,总耗时为三个阶段耗时的总和。
  1. $T_{publish}$(控制面发布耗时)ControllerPublishVolume 执行时间,包括存储分配、节点绑定等操作。
    TpublishT_{publish}
  2. $T_{stage}$(节点暂存耗时)NodeStageVolume 执行时间,包括设备格式化、挂载到节点共享目录。
    TstageT_{stage}
  3. $T_{node}$(节点发布耗时)NodePublishVolume 执行时间,包括绑定到 Pod 目录、设置权限等。
    TnodeT_{node}

快照与扩容

U 盘里的重要文件,你总想复制一份备份;U 盘容量不够了,你可能会换一块更大的盘,把数据整体迁过去。CSI 中的快照(Snapshot)与扩容(Resize)就是这两件事的标准解法。

快照功能需要三类资源协作:VolumeSnapshotClass(快照类,类似 StorageClass)、VolumeSnapshot(快照对象的声明)、以及 CSI 驱动自身必须实现快照相关 RPC。用户创建 VolumeSnapshot 后,external-snapshotter 侧车容器会调用 CreateSnapshot,为指定卷生成一份一致性镜像。之后可以用这份快照创建出全新卷,实现“时光倒流”式的数据恢复。

扩容则简单直接:修改 PVC 的容量字段,external-resizer 会调用驱动的 ControllerExpandVolume,把后端卷撑大。如果存储支持在线扩容,Pod 无需重启就能看到新容量。扩容的本质是对旧容量的扩展:

快照和扩容,一个负责“防丢”,一个负责“能装”。它们让云原生存储真正具备了企业级数据的韧性,也让人敢于把核心业务数据库放进容器里。

Snew=Sold×r(r>1)S_{new} = S_{old} \times r \quad (r > 1)
新容量等于旧容量乘以一个大于 1 的倍率,如从 100GB 扩到 200GB。
  1. $S_{old}$(原容量)扩容前的存储大小,比如数据库当前卷为 100GB。
    SoldS_{old}
  2. $r$(扩容倍率)目标容量与原容量的比值,通常大于 1,如 2.0 表示翻倍扩容。
    rr
  3. $S_{new}$(新容量)扩容完成后的卷容量,必须大于等于 PVC 声明的目标值。
    SnewS_{new}

工程实践与排障

即使 USB 标准再成熟,我们仍会遇到“设备无法识别”“供电不足”等状况。根因往往不在标准,而在线缆、驱动或接口接触。CSI 的工程实践也是如此:配置不当、版本不匹配、资源不足,都会让存储链路出问题。

部署 CSI 驱动的第一件事是权限。驱动通常需要独立的 ServiceAccount,并绑定 RBAC 规则,才能访问 Kubernetes API。第二件事是查看日志,常见的排障命令包括 kubectl describe pvc、kubectl logs 查看 CSI 驱动容器日志、journalctl 查看 kubelet 日志。第三件事是提前预留容量,避免磁盘写满导致节点异常。

预留容量的核算可以看作一个减法账本:

掌握工程实践,就像从“会用 USB”升级到“会修 USB 问题”。理解权限、日志、容量三件套,你就具备了独立排查绝大多数 CSI 故障的能力。

Afree=Atotal−Aused−AreservedA_{free} = A_{total} - A_{used} - A_{reserved}
可用容量 = 总容量 − 已用容量 − 预留容量。
  1. $A_{total}$(总容量)存储池声明总容量,比如一块云盘 1TB。
    AtotalA_{total}
  2. $A_{used}$(已用容量)已被卷占用的空间,包括数据、快照和其他开销。
    AusedA_{used}
  3. $A_{reserved}$(预留容量)为文件系统元数据、紧急恢复、系统稳定性预留的缓冲空间。
    AreservedA_{reserved}

AI 应用场景

云数据库容器化迁移

阶段:应用容器化与数据持久化

解决的问题:传统 MySQL 跑在虚拟机本地磁盘,节点一旦故障数据就有丢失风险。通过 CSI 驱动接入云硬盘后,数据库 Pod 可随时调度到新节点,数据跟着卷走,彻底摆脱“绑定在一台机器上”的宿命。

具体案例:某金融团队将自建 MySQL 5.7 迁移到 Kubernetes,使用云厂商 CSI 驱动,PVC 自动创建云盘。扩容只改请求大小,备份用快照完成,三个月内将数据库运维成本降低了 70%。

AI训练任务断点恢复

阶段:模型训练与 checkpoint 管理

解决的问题:GPU 训练跑三天后因节点内存不足被杀,未保存的模型权重全部丢失,损失 GPU 费用以万元计。用 CSI 快照定期保存 checkpoint 卷,训练失败后五分钟内即可从快照恢复出新卷继续训练。

具体案例:某自动驾驶公司的训练任务在 Kubernetes 上运行,MLOps 团队配置 VolumeSnapshotClass,每 30 分钟打一次快照。在一次节点崩溃中,训练任务通过快照卷恢复,累计避免超过 100 小时 GPU 算力浪费。

日志存储自动扩容

阶段:数据采集与归档

解决的问题:日志数据峰值难以预测,人工扩容总是慢半拍。通过监控 PVC 使用率,超过阈值自动修改 PVC 容量声明,external-resizer 便会触发 CSI 扩容,整个过程无需人工介入。

具体案例:某互联网公司每天产生 20TB 日志,平台团队开发了一个调度器,定时读取 PVC 指标,当使用率超过 80% 自动扩容 20%。上线后日志丢失事件下降为零。

Python 演示

python64 行
class Volume:    def __init__(self, name, size_gb):        self.name = name        self.size_gb = size_gb        self.attached = False        self.mounted = False        self.snapshot = None  class CSIDriver:    """模拟一个极简 CSI 驱动,演示卷生命周期。"""     def __init__(self):        self.volumes = {}     def create_volume(self, name, size_gb):        vol = Volume(name, size_gb)        self.volumes[name] = vol        print('创建卷', name, '容量', size_gb, 'GB')        return vol     def delete_volume(self, name):        vol = self.volumes.pop(name)        if vol.attached:            raise RuntimeError('不能删除已附加的卷')        print('删除卷', name)     def controller_publish(self, name):        vol = self.volumes[name]        if not vol.attached:            vol.attached = True            print('附加卷', name, '到节点')     def node_publish(self, name, mount_path):        vol = self.volumes[name]        if not vol.mounted:            vol.mounted = True            print('挂载卷', name, '到', mount_path)     def create_snapshot(self, name):        vol = self.volumes[name]        snap = 'snap-' + name        vol.snapshot = snap        print('创建快照', snap)     def resize_volume(self, name, new_size):        vol = self.volumes[name]        old = vol.size_gb        vol.size_gb = new_size        print('扩容卷', name, '从', old, 'GB 到', new_size, 'GB')  def main():    driver = CSIDriver()    driver.create_volume('data-vol', 10)    driver.controller_publish('data-vol')    driver.node_publish('data-vol', '/mnt/data')    driver.create_snapshot('data-vol')    driver.resize_volume('data-vol', 20)    driver.delete_volume('data-vol')  if __name__ == '__main__':    main()
目的:用不到 60 行 Python 模拟 CSI 驱动的核心方法,完整演示卷从创建、附加、挂载、快照、扩容到删除的整个生命周期。
  1. class CSIDriver:定义 CSI 驱动主体,相当于 USB 标准中的“设备驱动程序框架”。
  2. self.volumes = {}用字典记录集群中已创建的卷,就像操作系统维护一张“已连接设备清单”。
  3. def create_volume(self, name, size_gb):对应 CSI 的 CreateVolume RPC,新建立一块“移动硬盘”。
  4. def controller_publish(self, name):对应 ControllerPublishVolume,设备被“插到”指定节点。
  5. def node_publish(self, name, mount_path):对应 NodePublishVolume,让 Pod 目录真正看到这块卷。
  6. def create_snapshot(self, name):对应快照 RPC,给卷拍下一份数据备份镜像。
  7. def resize_volume(self, name, new_size):对应扩容 RPC,把卷容量从原先大小扩展到新大小。
  8. if __name__ == '__main__':程序入口,按顺序跑完整个卷的生命周期流程。

常见误区

误区很多初学者以为部署完 CSI 驱动就等于拥有了一套存储,马上就去申请大容量 PV,却发现根本没有真正可用的存储后端。

正解CSI 驱动只是一座“翻译桥”,它本身不存数据。真正的存储能力来自后端存储系统,比如云盘、本地盘、分布式文件系统。驱动负责把 Kubernetes 的请求翻译成存储系统的动作,就像 USB 驱动只是让电脑认识 U 盘,U 盘里的数据仍然在闪存芯片里。

为何常见:因为 CSI 驱动确实像一个“存储程序”,而且它经常和存储系统一起安装,容易让人混淆边界。

误区很多人拿到一个 CSI 驱动后,直接创建 VolumeSnapshot,结果事件报错“not supported”,以为是自己用错了 API。

正解CSI 规范定义了可选能力:快照、扩容、拓扑感知等都需要驱动显式声明。驱动必须在 Identity 服务中通过 ControllerGetCapabilities 上报能力,Kubernetes 才能调用对应 RPC。就像不是每个 U 盘都支持硬件加密一样,功能支持清单由设备决定。

为何常见:因为 Kubernetes 的 API 看起来总是统一的,容易误以为底层存储功能也会自动统一。

误区有人以为 CSI 卷共享给 Kubernetes 集群后,任意 Pod 在任何节点都能直接并发读写,结果遇到数据损坏。

正解多数块存储卷默认是“单节点读写”的,Kubernetes 通过 VolumeAttachment 确保卷同一时刻只附加到一个节点。共享读写需要后端存储专门支持(如 RWX 的 NAS 类存储),不是 CSI 协议本身提供的默认能力。

为何常见:因为“网络存储”这几个字容易让人误以为是公共网盘,忽略了块设备与文件系统的本质差异。

误区一些使用本地机房的公司认为 CSI 与自己无关,只把它当作云厂商的专利。

正解CSI 是开放的插件规范,本地盘可以有 Local CSI 驱动,分布式文件系统如 Ceph、GlusterFS 也有对应的 CSI 驱动。任何存储厂商只要实现规范,就能接入 Kubernetes。

为何常见:因为各大云厂商的 CSI 驱动曝光度最高,导致大众形成“云盘才用 CSI”的刻板印象。

误区有人修改 PVC 容量后以为立刻生效,结果发现 Pod 里看到的容量没有变化,或者出现了短暂读写中断。

正解扩容能否在线完成取决于存储后端。部分存储需要在节点上先做文件系统扩容,可能需要重启 Pod;部分后端只支持离线扩容,必须在 PVC 未挂载时进行。扩容前应阅读驱动文档,确认是“在线扩容”还是“离线扩容”。

为何常见:因为 Kubernetes API 让扩容看起来只是一个字段修改,掩盖了底层存储操作的复杂性。

面试题

Q1. CSI 的英文全称是什么?

单选题 · easy

  • A.容器存储接口← 正确答案
  • B.云存储接口
  • C.容器服务整合
  • D.计算存储基础设施

答案:A。CSI 全称 Container Storage Interface,即容器存储接口。它定义的是 Kubernetes 与存储系统之间的标准协议,而不是某种具体的存储实现。CD两个选项明显偏离了“存储”和“接口”的核心含义。

Q2. CSI 驱动中的哪个服务负责在节点上挂载卷?

单选题 · easy

  • A.Node Service← 正确答案
  • B.Controller Service
  • C.Identity Service
  • D.Scheduler Service

答案:A。Node Service 运行在节点上,被 kubelet 调用,负责把卷挂载到 Pod 目录。Controller Service 管理卷的创建删除,Identity Service 负责驱动身份,调度不是 CSI 的职责。

Q3. 用户在 Kubernetes 中申请存储时创建的对象是什么?

单选题 · easy

  • A.持久卷声明 PVC← 正确答案
  • B.无状态部署 Deployment
  • C.有状态应用 StatefulSet
  • D.动态供给 StorageClass

答案:A。PVC(PersistentVolumeClaim)是用户申请存储的声明,相当于“我要一块盘”。Deployment 管理 Pod,StatefulSet 管理有状态应用,StorageClass 定义存储类型,它们都不是直接申请存储的对象。

Q4. CSI 中 Controller Service 的主要职责是什么?

单选题 · mid

  • A.负责创建删除卷和快照← 正确答案
  • B.在节点上挂载存储设备
  • C.为容器提供运行镜像
  • D.调度 Pod 到合适的节点

答案:A。Controller Service 运行在控制面,负责 CreateVolume、DeleteVolume、CreateSnapshot 等操作。选项 B 是 Node Service 的职责;C 是容器运行时的职责;D 是调度器的职责。

Q5. 谁负责把 Kubernetes 的存储请求转发给 CSI 驱动?

单选题 · mid

  • A.外部 Sidecar 容器← 正确答案
  • B.直接由 Kubelet 底层调用
  • C.Scheduler 统一转发请求
  • D.Etcd 直接执行存储命令

答案:A。external-provisioner、external-attacher 等 sidecar 容器监听 API 对象,调用 CSI 驱动对应 RPC。kubelet 只调用 Node 侧接口,调度器不参与存储转发,etcd 只保存状态数据。

Q6. 卷的挂载流程包含哪三步?

单选题 · single

  • A.ControllerPublish、NodeStage、NodePublish← 正确答案
  • B.CreateVolume、DeleteVolume、ExpandVolume
  • C.Attach、Detach、Reattach
  • D.Format、Mount、Unmount

答案:A。挂载工作流依次为 ControllerPublishVolume(控制面发布)、NodeStageVolume(节点暂存)、NodePublishVolume(节点发布)。B 是卷管理操作,C 是错误名称组合,D 是文件系统层面的动作。

Q7. CSI 驱动中哪些方法属于 Node Service?

多选题 · mid

  • A.NodePublishVolume← 正确答案
  • B.NodeUnpublishVolume← 正确答案
  • C.NodeGetCapabilities← 正确答案
  • D.CreateSnapshot

答案:ABC。Node Service 负责节点上的挂载、卸载与能力查询,前三个都属于它。CreateSnapshot 属于 Controller Service,因为快照是存储控制面行为。

Q8. 删除 CSI 卷之前必须满足什么条件?

单选题 · hard

  • A.节点已完成卸载且卷无引用← 正确答案
  • B.卷必须扩容到最大容量
  • C.所有快照必须永久保留
  • D.Pod 必须重新启动一次

答案:A。只有卷未被任何 Pod 引用且节点已卸载,Kubernetes 才允许调用 DeleteVolume。扩容和快照与删除没有因果关系,Pod 重启也不是删除前置条件。

Q9. 使用 CSI 快照功能需要哪些组件配合?

多选题 · hard

  • A.VolumeSnapshotClass 资源← 正确答案
  • B.VolumeSnapshot 对象声明← 正确答案
  • C.CSI 驱动实现快照 RPC← 正确答案
  • D.专属快照硬件设备

答案:ABC。快照需要快照类、快照对象、驱动能力三要素。快照依赖的是控制面逻辑,不一定需要专属硬件,D 项过于绝对。

Q10. 扩容 PVC 时,下列哪个流程是正确的?

单选题 · extreme

  • A.修改 PVC 请求大小,触发 Resize 流程← 正确答案
  • B.直接删除 PVC 后重新创建
  • C.先清理所有快照记录
  • D.重启节点使其生效

答案:A。正确做法是修改 PVC 的 storage 字段,external-resizer 观察变化后触发 ControllerExpandVolume。删除 PVC 会丢数据,清快照与扩容无关,重启节点不触发任何扩容逻辑。