csi插件必须实现controllerserver和nodeserver两个grpc服务,严格遵循kubernetes csi规范,通过标准接口响应请求,确保volume id全局唯一、plugin name为dns子域名格式、挂载路径语义合规,并正确配置socket路径与权限以完成注册。

CSI插件核心是实现ControllerServer和NodeServer两个gRPC服务
Kubernetes CSI规范不接受直接调用宿主机命令或轮询设备,必须通过标准gRPC接口响应kubelet和external-provisioner的请求。你写的Go服务得同时监听ControllerGetCapabilities、CreateVolume、NodePublishVolume等方法,缺一个就会被kubelet拒绝注册。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
github.com/container-storage-interface/spec/lib/go/csi生成的stub代码起步,别手写gRPC服务定义 -
NodeServer必须运行在每个Node上(DaemonSet),且需挂载/var/lib/kubelet/plugins/和/dev等宿主机路径 - 所有volume ID必须全局唯一、不可变——哪怕删除重建,ID也不能复用,否则K8s会认为是同一个卷并跳过挂载
- 不要在
CreateVolume里同步执行耗时操作(比如调用云厂商API创建块设备),要返回ALREADY_EXISTS或用异步轮询+CheckVolume兜底
CSI Identity服务必须返回稳定plugin name且支持GetPluginInfo
这个看似简单,却是插件注册失败最常见原因:Kubernetes要求Identity.GetPluginInfo返回的name字段必须是DNS子域名格式(如example.com/csi),且在整个集群中绝对唯一。一旦改名,旧PV/PVC全失效。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 硬编码
name到代码里,别从配置文件读——配置变更会导致plugin registration失败 -
GetPluginCapabilities里至少返回CONTROLLER_SERVICE和NODE_SERVICE,否则external-attacher或node-driver-registrar会跳过你的插件 - 用
NodeGetInfo返回node_id时,确保它和spec.nodeName一致,否则PV绑定会卡在WaitingForFirstConsumer
Node阶段挂载必须严格遵循NodePublishVolume的路径语义
不是“把设备挂到某个目录就行”。CSI要求:target_path是kubelet传来的空目录(由K8s创建),staging_target_path是预挂载点(由K8s管理),你的插件只能做mount --bind或mount -o bind,不能直接mount /dev/sdb /target——那会破坏K8s的volume生命周期管理。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 检查
target_path是否为绝对路径、是否为空目录,否则返回FAILED_PRECONDITION - 如果后端是本地块设备(如LVM卷),先
mount /dev/xxx /staging,再mount --bind /staging /target;不能跳过staging步骤 - 务必设置
mount propagation为shared(即mount --make-shared /target),否则容器内/proc/mounts看不到挂载点 - 避免在
NodePublishVolume里做格式化(mkfs)——那是ControllerPublishVolume或Provisioner该干的事
调试时优先看csi-node-driver-registrar日志和/var/lib/kubelet/plugins/下的socket文件
插件起不来?90%的问题出在注册环节。Registrar容器负责把你的gRPC服务暴露给kubelet,但它只认/var/lib/kubelet/plugins/<plugin-name>/csi.sock</plugin-name>这个路径。socket文件不存在、权限不对、路径拼错,Registrar就一直报failed to dial socket。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 进Node节点,确认
ls -l /var/lib/kubelet/plugins/*/csi.sock存在且属主是root:root,权限srw-rw---- - 查Registrar日志:
kubectl logs -n kube-system <csi-node-pod> -c csi-node-driver-registrar</csi-node-pod>,重点看Detected new plugin有没有出现 - 用
grpcurl -plaintext -proto csi.proto localhost:10000 csi.Identity/GetPluginInfo手动测试gRPC接口(需提前把socket转成TCP端口) - 别依赖
kubectl get csinodes判断插件是否就绪——它只反映Node对象状态,不验证gRPC连通性
真正难的不是写完五个gRPC方法,而是让K8s相信你返回的每个volume_id、node_id、publish_context都符合它内部的状态机预期。任何一步ID不一致或路径越界,都会静默失败,只在describe pv里显示FailedMount这种无意义错误。










