CSI 即容器存储接口,借助 CSI 就可以很容易的给工作负载提供存储使用。K8s 的存储插件又分为 in-tree 和 out-of-tree 两种类型,前者是与 K8s 主库共同迭代维护,后者则是独立维护的存储插件。
out-of-tree 类型的插件则是通过 gRPC 与 K8s 的组件进行交互,为了简化 CSI 的开发与部署,K8s 也提供了多个 sidecar 组件:
- node-driver-registrar: 监听来自 kubelet 的 gRPC 请求,从 CSI driver 获取驱动程序信息(通过
NodeGetInfo
方法),并使用 kubelet 插件注册机制在该节点上的 kubelet 中对其进行注册 - provisioner: 监听来自 kube-apiserver 的 gRPC 请求,监听 PVC 的创建和删除,调用 CSI driver 创建和删除 PV(通过
CreateVolume
和Delete Volume
)方法 - attacher: 监听来自 kube-apiserver 的 gRPC 请求,监听 volumeAttachment 对象并触发 CSI 执行
ControllerPublishVolume
和ControllerUnpublishVolume
的操作 - resizer: 监听来自 kube-apiserver 的 gRPC 请求,监听 PVC 的修改,调用 CSI Controller 执行
ExpandVolume
方法,来调整 Volume 的大小 - livenessProbe: 检查 CSI 程序的健康状态,如不健康则会进行重启
CSI 工作流程
CSI 的工作流分为三个阶段:
- Provision/Delete
- Attach/Detach
- Mount/Unmount
这三个阶段会用到 Sidecar 组件,也会用到 K8s 的 PV Controller 和 AD Controller 组件
当然并不是所有的 CSI 都会经历这三个阶段,如 NFS CSI 的工作流就没有涉及 volumeAttachment
Privision 阶段
在此阶段,Sidecar provisioner 和 PV Controller 都会监听 PVC 资源
- 当 PV Controller 观察到新的 PVC 被创建时,就会去判断是否有与之匹配的 in-tree 插件,如果没有则判定为 out-of-tree,并为该 PVC 添加 annotation
- provisioner 观察到 PVC 的 annotation 与自己的 CSI 是相匹配的时候,就会去调用
CreateVolume
方法 - 当
CreateVolume
调用返回成功时,provisioner 就会创建 PV - PV Controller 监听到该 PV 时,就会将其与 PVC 做绑定
Attach 阶段
在此阶段,会将数据卷附在一个节点上
- AD Controller 监听到 Pod 被调度到某个节点后,会调用 in-tree 内部接口创建 volumeAttachment 资源
- attacher 监听到 volumeAttachment 就会调用
ControllerPublishVolume
接口 - 当接口返回成功时就会将 volumeAttachment 资源的 status.attached 设置为 true
Mount 阶段
在此阶段,会将数据卷挂载到 Pod 上
- kubelet 观察到 volumeAttachment 资源的 status.attached 设置为 true 时,就会调用 in-tree 内部接口进行实际的卷挂载操作
Unmount 阶段
在此阶段,会将数据卷从 Pod 上取消挂载
- kubelet 监听到节点上的 Pod 被删除,就会调用 in-tree 内部接口进行实际的卷卸载操作
Detach 阶段
在此阶段,会将对应的 volumeAttachment 资源删除
- attacher 会讲被删除 Pod 对应的 volumeAttachment 进行删除
- AD Controller 监听到 volumeAttachment 被删除后,会去更新节点的 status.volumesInUse,将对应的卷信息摘除
Delete 阶段
在此阶段,会判断 PV 的回收策略进行不同的操作
- provisioner 观察 PV 的 persistentVolumeReclaimPolicy,如果为 Retain 则保留,Delete 则删除