2. CSI驱动部署与注册:NodePlugin与ControllerPlugin部署模式、CSIDriver对象、节点注册机制与健康检查

2.1 双组件架构:ControllerPlugin 与 NodePlugin

CSI 驱动采用 ControllerPlugin 和 NodePlugin 双组件架构,分别承载控制面与数据面操作。这种分离设计使得卷的创建、删除、快照等控制操作与挂载、卸载、设备发现等节点操作解耦,提升了安全性与可维护性。

维度 ControllerPlugin NodePlugin
部署位置 任意节点(通常 Master/Control Plane) 每个 Worker 节点
运行模式 Deployment / StatefulSet(多副本 HA) DaemonSet(每节点一个 Pod)
gRPC 身份 Identity + Controller 服务 Identity + Node 服务
核心职责 CreateVolume、DeleteVolume、ControllerPublish、CreateSnapshot NodeStageVolume、NodePublishVolume、NodeGetVolumeStats、NodeExpandVolume
卷操作范围 集群级(不依赖具体节点) 节点级(依赖本地设备与挂载点)
kubelet 交互 间接(通过外部控制器) 直接(通过 Unix Socket)
关键原则:ControllerPlugin 不直接操作节点上的设备,NodePlugin 不执行卷创建/删除。任何跨越职责边界的调用都应视为架构违规,可能导致权限扩散或挂载状态不一致。

2.2 CSIDriver 对象:驱动注册的声明式契约

CSIDriver 是 Kubernetes 内置的 API 资源,用于声明 CSI 驱动的元数据与行为偏好。kubelet 在决定如何与 CSI 驱动交互前,会先读取该对象。缺少 CSIDriver 对象将导致 kubelet 回退到树内卷插件逻辑,无法正确调用 CSI gRPC 接口。

apiVersion: storage.k8s.io/v1
kind: CSIDriver
metadata:
  name: csi.example.com
spec:
  # 是否需要在 NodePublish 前执行 NodeStage
  attachRequired: true
  # 驱动是否支持 Pod Info on Mount
  podInfoOnMount: true
  # 驱动是否支持卷扩展(在线扩容)
  volumeLifecycleModes:
    - Persistent
    - Ephemeral
  # 驱动期望的挂载参数存储模式
  storageCapacity: false
  # 驱动是否需要 kubelet 执行 fsGroup 变更
  fsGroupPolicy: File
  # 驱动支持的拓扑约束
  seLinuxMount: true
  tokenRequests:
    - audience: csi-driver
  requiresRepublish: false
字段 类型 说明 调优影响
attachRequired bool 是否需要 ControllerPublish 阶段 false 可跳过附加步骤,减少延迟
podInfoOnMount bool 挂载时是否注入 Pod 元数据 true 增加挂载参数体积,但支持精细化权限
fsGroupPolicy string fsGroup 变更策略(None/File/ReadWriteOnceWithFSType) File 模式可能触发递归 chown,影响启动速度
volumeLifecycleModes []string 支持 Persistent 和/或 Ephemeral 模式 Ephemeral 模式无需 PV/PVC 创建流程
requiresRepublish bool 驱动是否需要 kubelet 定期重新发布卷 true 增加 kubelet 周期性调用开销
最佳实践:CSIDriver 对象应在驱动安装时同步创建,且 attachRequired 和 podInfoOnMount 的取值应与后端存储能力严格匹配。误设 attachRequired: false 但后端实际需要附加,会导致卷无法挂载到 Pod。

2.3 节点注册机制:从 kubelet 到 CSI NodePlugin 的握手流程

节点注册是 CSI 驱动在 Worker 节点上被 kubelet 发现并建立通信通道的过程。整个过程通过 Unix Domain Socket 完成,不依赖网络 DNS 或 Service。

  1. Socket 创建:NodePlugin Pod 启动后,在宿主机指定路径(通常为 /var/lib/kubelet/plugins/csi.example.com/csi.sock)创建 Unix Socket。
  2. kubelet 扫描:kubelet 启动时扫描 /var/lib/kubelet/plugins_registry/ 目录下的 Socket 文件,发现新的 CSI 驱动 Socket。
  3. gRPC 握手:kubelet 通过 Socket 调用 Identity.Probe() 和 Identity.GetPluginInfo(),获取驱动名称、版本、能力集。
  4. CSIDriver 校验:kubelet 将驱动名称与集群中的 CSIDriver 对象进行匹配,确认驱动已注册且配置一致。
  5. NodeGetInfo 上报:kubelet 调用 Node.NodeGetInfo() 获取节点拓扑信息(如可用区、区域),用于后续卷调度决策。
  6. 注册完成:kubelet 将驱动信息写入内部状态,开始接受该驱动的卷挂载请求。
常见故障:Socket 路径权限不足(kubelet 以 root 运行,但 Socket 由非 root 容器创建)会导致握手失败。检查 /var/lib/kubelet/plugins/ 目录的权限,确保 kubelet 有读/写权限。
节点注册握手流程 kubelet plugins_registry/ 扫描 csi.sock NodePlugin Pod ① 创建 Unix Socket ② 扫描发现 Socket ③ Probe() / GetPluginInfo() ④ CSIDriver 对象校验 ⑤ NodeGetInfo() 上报拓扑 ⑥ 注册完成,接受挂载请求 Socket 路径: /var/lib/kubelet/plugins/<driver>/csi.sock

2.4 健康检查机制:LivenessProbe 与 NodeService 可用性

CSI 驱动的健康检查分为两个层面:容器级健康检查(由 kubelet 执行)和 驱动级健康检查(由 CSI LivenessProbe Sidecar 执行)。两者协同工作,确保驱动在节点上持续可用。

2.4.1 LivenessProbe Sidecar 模式

官方推荐在 NodePlugin Pod 中注入 csi-liveness-probe Sidecar 容器。该容器定期调用 CSI 驱动的 Identity.Probe() gRPC 方法,检查驱动是否存活且能响应请求。

containers:
- name: csi-driver
  image: registry.example.com/csi-driver:v1.2.3
  volumeMounts:
    - mountPath: /csi
      name: socket-dir
- name: liveness-probe
  image: registry.k8s.io/sig-storage/csi-liveness-probe:v2.10.0
  args:
    - --csi-address=/csi/csi.sock
    - --health-port=9808
    - --probe-timeout=3s
    - --probe-interval=10s
  volumeMounts:
    - mountPath: /csi
      name: socket-dir
参数 默认值 调优建议
--probe-interval 10s 存储后端压力大时可增至 30s,减少 Probe 调用频率
--probe-timeout 3s 后端响应慢时可增至 10s,避免误判驱动不可用
--health-port 9808 确保不与 NodePlugin 其他端口冲突

2.4.2 kubelet 原生健康检查

kubelet 在每次卷挂载操作前,会通过 Unix Socket 调用 Identity.Probe() 确认驱动可用。如果连续多次 Probe 失败,kubelet 会将该驱动标记为 NotReady,并拒绝后续挂载请求,直到驱动恢复。

故障排查要点:
  • 检查 NodePlugin Pod 日志:kubectl logs -n <namespace> <pod-name> -c csi-driver
  • 检查 LivenessProbe 日志:kubectl logs -n <namespace> <pod-name> -c liveness-probe
  • 手动调用 Probe:csc identity probe --endpoint unix:///var/lib/kubelet/plugins/csi.example.com/csi.sock
  • 查看 kubelet 日志:journalctl -u kubelet -f | grep csi

2.5 部署模式对比与选型建议

场景 推荐部署模式 理由
生产环境,高可用要求 ControllerPlugin: Deployment (3副本) + NodePlugin: DaemonSet Controller 多副本保证控制面 HA,DaemonSet 确保每个节点都有驱动
开发/测试环境 ControllerPlugin: Deployment (1副本) + NodePlugin: DaemonSet 减少资源占用,快速迭代
边缘节点,资源受限 ControllerPlugin: Deployment (1副本) + NodePlugin: DaemonSet (资源限制) NodePlugin 设置 CPU/Memory 限制,避免影响边缘应用
仅需 Ephemeral 卷 仅部署 NodePlugin(DaemonSet),无需 ControllerPlugin Ephemeral 卷不涉及创建/删除操作,ControllerPlugin 非必需
部署验证命令:部署完成后,执行 kubectl get csidrivers 确认 CSIDriver 对象存在;执行 kubectl get pods -n <namespace> -o wide 确认 NodePlugin Pod 在每个节点上 Running;执行 kubectl describe csidriver csi.example.com 查看驱动配置详情。