李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
工具
正文
Kubernetes之CSI介绍
Leefs
2025-05-17 PM
2232℃
0条
[TOC] ### 前言 Kubernetes从1.9版本开始引入容器存储接口 Container Storage Interface (CSI)机制,用于在Kubernetes和外部存储系统之间建立一套标准的存储管理接口,通过该接口为容器提供存储服务。 K8s 持久化存储经历了从 in-tree Volume 到 CSI Plugin(out-of-tree) 的迁移,一方面是为了将 K8s 核心主干代码与 Volume 相关代码解耦,便于更好的维护;另一方面则是为了方便各大云厂商实现统一的接口,提供个性化的云存储能力,以期达到云存储生态圈的开放共赢。 ### 一、CSI介绍 #### 1.1 概述 CSI是`Container Storage Interface`(容器存储接口)的简写。 CSI的目的是定义行业标准"容器存储接口",使存储供应商(SP,Storage Provider)能够开发一个符合CSI标准的插件并使其可以在多个容器编排(CO)系统中工作。CO包括Cloud Foundry, Kubernetes, Mesos,Swarm等。 kubernetes将通过CSI接口来跟第三方存储厂商进行通信,来操作存储,从而提供容器存储服务。 #### 1.2 解决了什么痛点 CSI 将三方存储代码与 K8s 代码解耦,不同的存储插件只要实现这些统一的接口,就能对接 K8s,用户无需接触核心的 K8s 代码。 最重要的是,**CSI 规范是现在业界容器编排统一的存储接入标准**。  ### 二、CSI组件介绍 *CSI 驱动器由三个主要组件组成,每个组件都扮演着特定的角色:* + **Node Service**: 运行在每个 Kubernetes 节点上,负责在节点上挂载和卸载存储卷,并处理节点级别的存储操作。 + **Controller Service**: 运行在 Kubernetes 控制平面中,负责管理存储卷的生命周期,包括创建、删除和扩容等操作。 + **Identity Service**: 是 CSI 驱动器的第三个组件,在 CSI 驱动器注册时提供标识信息,并向 Kubernetes 集群公开驱动器的支持能力。负责告知 Kubernetes 驱动器的存在,提供驱动器的基本信息和功能支持。 为实现具有高扩展性、out-of-tree 的持久卷管理能力,在 K8s CSI 实现中,相关协同的组件有:  #### 2.1 组件说明 - **kube-controller-manager**:K8s 资源控制器,主要通过 PVController, AttachDetach 实现持久卷的绑定(Bound)/解绑(Unbound)、附着(Attach)/分离(Detach); - **CSI-plugin**:K8s 独立拆分出来,实现 CSI 标准规范接口的逻辑控制与调用,是整个 CSI 控制逻辑的核心枢纽; - **node-driver-registrar**:是一个由**官方 K8s sig 小组维护的辅助容器(sidecar)**,它使用 kubelet 插件注册机制向 kubelet 注册插件,需要请求 CSI 插件的 Identity 服务来获取插件信息; - **external-provisioner**:是一个由**官方 K8s sig 小组维护的辅助容器(sidecar)**,主要功能是实现持久卷的创建(Create)、删除(Delete); - **external-attacher**:是一个由**官方 K8s sig 小组维护的辅助容器(sidecar)**,主要功能是实现持久卷的附着(Attach)、分离(Detach); - **external-snapshotter**:是一个由**官方 K8s sig 小组维护的辅助容器(sidecar)**,主要功能是实现持久卷的快照(VolumeSnapshot)、备份恢复等能力; - **external-resizer**:是一个由**官方 K8s sig 小组维护的辅助容器(sidecar)**,主要功能是实现持久卷的弹性扩缩容,需要云厂商插件提供相应的能力; - **kubelet**:K8s 中运行在每个 Node 上的控制枢纽,主要功能是调谐节点上 Pod 与 Volume 的附着、挂载、监控探测上报等; - **cloud-storage-provider**:由各大云存储厂商基于 CSI 标准接口实现的插件,包括 Identity 身份服务、Controller 控制器服务、Node 节点服务; #### 2.2 组件关系组合表 为了方便记忆,整理出一份组件关系组合表,它对部署 CSI 驱动程序和排查问题非常的有用。  #### 2.3 CSI 如何与 k8s 组件相互通信  CSI Controller Server 和 External CSI SideCar 之间以及 CSI Node Server 和 Kubelet 之间其实都是通过创建 Unix Domain Socket 文件使用 gRPC 协议进行互相通信。 ### 三、CSI 存储流程 K8s 中的 Pod 在挂载存储卷时需经历三个的阶段: 1. Provision/Delete(创盘/删盘) 2. Attach/Detach(挂接/摘除) 3. Mount/Unmount(挂载/卸载) #### 3.1 Provisioning Volumes 用户创建PVC,K8s根据PVC和StorageClass,让External Provisioner调用CSI插件创盘,之后创建PV并绑定PVC。删盘时,External Provisioner根据PVC回收策略,调用CSI插件删盘并删除PV。  **执行步骤** 1. 集群管理员创建 StorageClass 资源,该 StorageClass 中包含 CSI 插件名称; 2. 用户创建 PVC 资源,PVC 指定存储大小及 StorageClass; 3. 卷控制器(PV Controller)观察到集群中新创建的 PVC 没有与之匹配的 PV,且其使用的存储类型为 out-of-tree,于是为 PVC 打 annotation:`volume.beta.kubernetes.io/storage-provisioner=[out-of-tree CSI 插件名称]` 4. External Provisioner 组件观察到 PVC 的 annotation 中包含 `volume.beta.kubernetes.io/storage-provisioner`且其 value 是自己,于是开始创盘流程: 1. 获取相关 StorageClass 资源并从中获取参数,用于后面 CSI 函数调用 2. 通过 unix domain socket 调用外部 CSI 插件的CreateVolume 函数 5. 外部 CSI 插件返回成功后表示盘创建完成,此时External Provisioner 组件会在集群创建一个 PersistentVolume 资源。 6. 卷控制器会将 PV 与 PVC 进行绑定 #### 3.2 Attaching Volumes Pod调度到节点后,AD控制器创建VolumeAttachment对象,External Attacher调用CSI插件挂接存储卷,完成后更新状态。Pod删除时,AD控制器删除VolumeAttachment对象,External Attacher调用插件摘除存储卷。  **执行步骤** 1. **AD 控制器(AttachDetachController)**观察到使用 CSI 类型 PV 的 Pod 被调度到某一节点,此时 **AD 控制器**会调用**内部 in-tree CSI 插件(csiAttacher)**的 Attach 函数。 2. **内部 in-tree CSI 插件(csiAttacher)**会创建一个 VolumeAttachment 对象到集群中。 3. **External Attacher** 观察到该 VolumeAttachment 对象,并调用**外部 CSI** **插件**的 **ControllerPublish 函数**以将卷挂接到对应节点上。**外部 CSI 插件**挂载成功后,**External Attacher** 会更新相关 VolumeAttachment 对象的 .Status.Attached 为 true。 4. **AD 控制器内部 in-tree CSI 插件(csiAttacher)**观察到 VolumeAttachment 对象的 .Status.Attached 设置为 true,于是更新 **AD 控制器**内部状态(ActualStateOfWorld),该状态会显示在 Node 资源的 .Status.VolumesAttached 上。 #### 3.3 Mounting Volumes Kubelet发现使用CSI类型PV的Pod调度到本节点,等待VolumeAttachment状态就绪,调用CSI插件挂载。Pod删除时,Kubelet调用插件卸载。  **执行步骤** 1. **Volume Manager(Kubelet 组件)**观察到有新的使用 CSI 类型 PV 的 Pod 调度到本节点上,于是调用**内部 in-tree CSI 插件(csiAttacher)**的 WaitForAttach 函数。 2. **内部 in-tree CSI 插件(csiAttacher)**等待集群中 VolumeAttachment 对象状态 .Status.Attached 变为 true。 3. **in-tree CSI 插件(csiAttacher)**调用 MountDevice 函数,该函数内部通过 unix domain socket 调用**外部 CSI 插件**的 **NodeStageVolume 函数**;之后**插件(csiAttacher)**调用**内部 in-tree CSI 插件(csiMountMgr)**的 SetUp 函数,该函数内部会通过 unix domain socket 调用**外部 CSI 插件**的 **NodePublishVolume 函数**。 #### 3.4 Unmounting Volumes  **执行步骤** 1. **用户**删除相关 Pod。 2. **Volume Manager(Kubelet 组件)**观察到包含 CSI 存储卷的 Pod 被删除,于是调用**内部 in-tree CSI 插件(csiMountMgr)**的 TearDown 函数,该函数内部会通过 unix domain socket 调用**外部 CSI 插件**的 **NodeUnpublishVolume 函数**。 3. **Volume Manager(Kubelet 组件)**调用**内部 in-tree CSI 插件(csiAttacher)**的 UnmountDevice 函数,该函数内部会通过 unix domain socket 调用**外部 CSI 插件**的 **NodeUnpublishVolume 函数**。 #### 3.5 Detaching Volumes  **执行步骤** 1. **AD 控制器**观察到包含 CSI 存储卷的 Pod 被删除,此时该控制器会调用**内部 in-tree CSI 插件(csiAttacher)**的 Detach 函数。 2. **csiAttacher** 会删除集群中相关 VolumeAttachment 对象(但由于存在 finalizer,va 对象不会立即删除)。 3. **External Attacher** 观察到集群中 VolumeAttachment 对象的 DeletionTimestamp 非空,于是调用**外部 CSI 插件**的 **ControllerUnpublish 函数**以将卷从对应节点上摘除。**外部 CSI 插件**摘除成功后,**External Attacher** 会移除相关 VolumeAttachment 对象的 finalizer 字段,此时 VolumeAttachment 对象被彻底删除。 4. **AD 控制器**中**内部 in-tree CSI 插件(csiAttacher)**观察到 VolumeAttachment 对象已删除,于是更新 **AD 控制器**中的内部状态;同时 **AD 控制器**更新 Node 资源,此时 Node 资源的 .Status.VolumesAttached 上已没有相关挂接信息。 #### 3.6 Deleting Volumes  **执行步骤** 1. **用户**删除相关 PVC。 2. **External Provisioner 组件**观察到 PVC 删除事件,根据 PVC 的回收策略(Reclaim)执行不同操作: - Delete:调用**外部 CSI 插件**的 **DeleteVolume 函数**以删除卷;一旦卷成功删除,**Provisioner** 会删除集群中对应 PV 对象。 - Retain:**Provisioner** 不执行卷删除操作。 ### 四、应用示例 以下是一个简单的示例,展示如何部署一个 CSI 插件到 Kubernetes 集群中。假设要部署一个名为 `example-csi-plugin` 的插件。 #### 4.1 创建一个 `StorageClass` ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: example-csi-storageclass provisioner: example.com/csi-plugin parameters: type: pd-standard ``` #### 4.2 创建一个 `PersistentVolumeClaim` ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: example-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi storageClassName: example-csi-storageclass ``` #### 4.3 部署一个使用该 PVC 的 Pod ```yaml apiVersion: v1 kind: Pod metadata: name: example-pod spec: containers: - name: example-container image: busybox command: ["sleep", "3600"] volumeMounts: - mountPath: "/data" name: example-volume volumes: - name: example-volume persistentVolumeClaim: claimName: example-pvc ``` #### 4.4 验证部署 使用 `kubectl` 命令验证 CSI 插件是否成功部署: ```bash kubectl get pods kubectl get pvc kubectl get pv ``` *附参考链接地址* *https://www.cnblogs.com/misakivv/p/18391753*
标签:
Kubernetes
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://www.lilinchao.com/archives/2981.html
上一篇
【转载】golang map底层原理
下一篇
cAdvisor工作原理
取消回复
评论啦~
提交评论
栏目分类
随笔
2
Java
326
大数据
229
工具
35
其它
25
GO
48
NLP
8
标签云
哈希表
Eclipse
高并发
并发编程
Golang
Prometheus
算法
Golang基础
SQL练习题
JavaScript
Stream流
Kibana
SpringCloudAlibaba
持有对象
Java
FileBeat
Kubernetes
SpringBoot
DataX
Spark Core
LeetCode刷题
设计模式
Linux
Git
Netty
数据结构
Ubuntu
Jquery
机器学习
Elasticsearch
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞