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