k8s之CSI详解

Kubernetes CSI 详解

一、CSI 概述

Container Storage Interface(CSI) 是一个标准化接口,用于定义容器编排系统(如 Kubernetes)与存储供应商(Storage Providers)之间的交互方式。其目标是让存储供应商开发的插件能够兼容多个容器编排系统(如 Kubernetes、Mesos、Swarm 等),从而简化存储资源的管理和使用。

在 Kubernetes 中,CSI 提供了一种标准的方式,用于与第三方存储供应商(如 NFS、AWS EFS、Ceph、GlusterFS 等)对接,实现存储卷的创建、挂载、卸载等操作。CSI 的引入取代了旧的 FlexVolume 插件机制,提供了更灵活、更强大的存储管理能力。

二、CSI 系统架构

在这里插入图片描述
图源:https://kubernetes.feisky.xyz/extension/volume/csi#yuan-li

Controller Server(控制服务器)

  • 功能:负责存储卷的生命周期管理,包括卷的创建、删除、挂载、卸载等操作。
  • 部署方式:通常以 StatefulSet 或 Deployment 的形式运行,确保高可用性和唯一性。
  • 组件
    • CSI 驱动容器:由第三方存储供应商提供,实现 CSI 接口的IdentityServer和ControllerServer。
    • Sidecar 容器(如 external-provisioner、external-attacher 等):负责与 Kubernetes 的组件(如 kube-controller-manager)通信,触发 CSI 驱动的操作。

Node Server(节点服务器)

  • 功能:负责节点级别的存储卷挂载和卸载操作。
  • 部署方式:以 DaemonSet 的形式运行,确保每个节点上都有 CSI 插件实例。
  • 组件
    • CSI 驱动容器:由第三方存储供应商提供,实现 CSI 接口的IdentityServer和NodeServer。
    • Sidecar 容器(如 node-driver-registrar):负责与 kubelet 通信,注册 CSI 驱动。

三、CSI 组件构成及功能

(一)CSI Controller

CSI Controller 是 CSI 插件的控制端,负责存储卷的生命周期管理。其主要组件包括:

Sidecar 容器
  • external-provisioner
    • 监听 PersistentVolumeClaim(PVC)的变化。
    • 触发 CSI 驱动的 CreateVolume 和 DeleteVolume 操作,实现存储卷的创建和删除。
  • external-attacher
    • 监听 VolumeAttachment 的变化。
    • 触发 CSI 驱动的 ControllerPublish 和 ControllerUnpublish 操作,实现存储卷的挂载和卸载。
  • external-resizer(可选)
    • 监听 PersistentVolumeClaim 的大小变化。
    • 触发 CSI 驱动的 ResizeVolume 操作,实现存储卷的容量调整(Beta 阶段)。
  • externalsnapshotter(可选)
    • 监听 VolumeSnapshot 的变化。
    • 触发 CSI 驱动的 CreateSnapshot 和 DeleteSnapshot 操作,实现存储卷的快照管理(Alpha 阶段)。
CSI Driver 容器
  • 由第三方存储供应商提供,实现 CSI 接口。
  • 通过 Unix Domain Socket 与 Sidecar 容器通信,完成具体的存储卷操作。
部署建议

CSI Controller 通常以 StatefulSet 或 Deployment 的形式运行,副本数设为 1,以确保唯一性。

(二)CSI Node

CSI Node 是 CSI 插件的节点端,负责节点级别的存储卷管理。其主要组件包括:

Sidecar 容器
  • node-driver-registrar
    • 将 CSI 驱动注册到 kubelet。
    • 监听 kubelet 的操作请求,触发 CSI 驱动的节点端接口(如 NodePublishVolume、NodeUnpublishVolume 等)。
CSI Driver 容器
  • 由第三方存储供应商提供,实现 CSI 接口。
  • 接收 kubelet 的调用,完成存储卷的挂载、卸载等操作。
通信方式
  • node-driver-registrar 与 kubelet 通过 Unix Domain Socket 通信。
  • CSI Driver 容器与 kubelet 通过另一个 Unix Domain Socket 通信,并挂载 kubelet 的工作目录(默认为 /var/lib/kubelet),用于管理 Pod 的 Volume。
部署建议

CSI Node 以 DaemonSet 的形式运行,确保每个节点上都有 CSI 插件实例。

四、CSI 接口介绍

三方存储厂商需实现 CSI 插件的三大接口:IdentityServer、ControllerServer、NodeServer。

  1. IdentityServer
    IdentityServer 主要用于认证 CSI 插件的身份信息。
// IdentityServer is the server API for Identity service.
type IdentityServer interface {
    // 获取CSI插件的信息,比如名称、版本号
    GetPluginInfo(context.Context, *GetPluginInfoRequest) (*GetPluginInfoResponse, error)
    // 获取CSI插件提供的能力,比如是否提供ControllerService能力
    GetPluginCapabilities(context.Context, *GetPluginCapabilitiesRequest) (*GetPluginCapabilitiesResponse, error)
    // 获取CSI插件健康状况
    Probe(context.Context, *ProbeRequest) (*ProbeResponse, error)
}
  1. ControllerServer
    ControllerServer 主要负责存储卷及快照的创建/删除以及挂接/摘除操作。
// ControllerServer is the server API for Controller service.
type ControllerServer interface {
    // 创建存储卷
    CreateVolume(context.Context, *CreateVolumeRequest) (*CreateVolumeResponse, error)
    // 删除存储卷
    DeleteVolume(context.Context, *DeleteVolumeRequest) (*DeleteVolumeResponse, error)
    // 挂接存储卷到特定节点
    ControllerPublishVolume(context.Context, *ControllerPublishVolumeRequest) (*ControllerPublishVolumeResponse, error)
    // 从特定节点摘除存储卷
    ControllerUnpublishVolume(context.Context, *ControllerUnpublishVolumeRequest) (*ControllerUnpublishVolumeResponse, error)
    // 验证存储卷能力是否满足要求,比如是否支持跨节点多读多写
    ValidateVolumeCapabilities(context.Context, *ValidateVolumeCapabilitiesRequest) (*ValidateVolumeCapabilitiesResponse, error)
    // 列举全部存储卷信息
    ListVolumes(context.Context, *ListVolumesRequest) (*ListVolumesResponse, error)
    // 获取存储资源池可用空间大小
    GetCapacity(context.Context, *GetCapacityRequest) (*GetCapacityResponse, error)
    // 获取ControllerServer支持功能点,比如是否支持快照能力
    ControllerGetCapabilities(context.Context, *ControllerGetCapabilitiesRequest) (*ControllerGetCapabilitiesResponse, error)
    // 创建快照
    CreateSnapshot(context.Context, *CreateSnapshotRequest) (*CreateSnapshotResponse, error)
    // 删除快照
    DeleteSnapshot(context.Context, *DeleteSnapshotRequest) (*DeleteSnapshotResponse, error)
    // 获取所有快照信息
    ListSnapshots(context.Context, *ListSnapshotsRequest) (*ListSnapshotsResponse, error)
    // 扩容存储卷
    ControllerExpandVolume(context.Context, *ControllerExpandVolumeRequest) (*ControllerExpandVolumeResponse, error)
}
  1. NodeServer
    NodeServer 主要负责存储卷挂载/卸载操作。
// NodeServer is the server API for Node service.
type NodeServer interface {
    // 将存储卷格式化并挂载至临时全局目录
    NodeStageVolume(context.Context, *NodeStageVolumeRequest) (*NodeStageVolumeResponse, error)
    // 将存储卷从临时全局目录卸载
    NodeUnstageVolume(context.Context, *NodeUnstageVolumeRequest) (*NodeUnstageVolumeResponse, error)
    // 将存储卷从临时目录bind-mount到目标目录
    NodePublishVolume(context.Context, *NodePublishVolumeRequest) (*NodePublishVolumeResponse, error)
    // 将存储卷从目标目录卸载
    NodeUnpublishVolume(context.Context, *NodeUnpublishVolumeRequest) (*NodeUnpublishVolumeResponse, error)
    // 获取存储卷的容量信息
    NodeGetVolumeStats(context.Context, *NodeGetVolumeStatsRequest) (*NodeGetVolumeStatsResponse, error)
    // 存储卷扩容
    NodeExpandVolume(context.Context, *NodeExpandVolumeRequest) (*NodeExpandVolumeResponse, error)
    // 获取NodeServer支持功能点,比如是否支持获取存储卷容量信息
    NodeGetCapabilities(context.Context, *NodeGetCapabilitiesRequest) (*NodeGetCapabilitiesResponse, error)
    // 获取CSI节点信息,比如最大支持卷个数
    NodeGetInfo(context.Context, *NodeGetInfoRequest) (*NodeGetInfoResponse, error)
}

五、CSI Sidecar 组件介绍

为使 K8s 适配 CSI 标准,社区将与 K8s 相关的存储流程逻辑放在了 CSI Sidecar 组件中。

1. Node Driver Registrar
功能

Node-Driver-Registrar 组件会将外部 CSI 插件注册到 Kubelet,从而使 Kubelet 通过特定的 Unix Domain Socket 来调用外部 CSI 插件函数(Kubelet 会调用外部 CSI 插件的 NodeGetInfo、NodeStageVolume、NodePublishVolume、NodeGetVolumeStats 等函数)。

原理

Node-Driver-Registrar 组件通过 Kubelet 外部插件注册机制实现注册,注册成功后:

  • Kubelet 为本节点 Node 资源打 annotation:调用外部 CSI 插件的 NodeGetInfo 函数,其返回值 [nodeID][driverName] 将作为值用于 "csi.volume.kubernetes.io/nodeid" 键。
  • Kubelet 更新 Node Label:将 NodeGetInfo 函数返回的 [AccessibleTopology] 值用于节点的 Label。
  • Kubelet 更新 Node Status:将 NodeGetInfo 函数返回的 maxAttachLimit(节点最大可挂载卷数量)更新到 Node 资源的 Status.Allocatable: attachable-volumes-csi-[driverName]=[maxAttachLimit]
  • Kubelet 更新 CSINode 资源(没有则创建):将 [driverName][nodeID][maxAttachLimit][AccessibleTopology] 更新到 Spec 中(拓扑仅保留 Key 值)。

2. External Provisioner
功能

创建/删除实际的存储卷,以及代表存储卷的 PV 资源。

原理

External-Provisioner 在启动时需指定参数 --provisioner,该参数指定 Provisioner 名称,与 StorageClass 中的 provisioner 字段对应。

External-Provisioner 启动后会 watch 集群中的 PVC 和 PV 资源。

对于集群中的 PVC 资源:

  1. 判断 PVC 是否需要动态创建存储卷,标准如下:
    • PVC 的 annotation 中是否包含 "volume.beta.kubernetes.io/storage-provisioner" 键(由卷控制器创建),并且其值是否与 Provisioner 名称相等。
    • PVC 对应 StorageClass 的 VolumeBindingMode 字段若为 WaitForFirstConsumer,则 PVC 的 annotation 中必须包含 "volume.kubernetes.io/selected-node" 键(详见调度器如何处理 WaitForFirstConsumer),且其值不为空;若为 Immediate 则表示需要 Provisioner 立即提供动态存储卷。
  2. 通过特定的 Unix Domain Socket 调用外部 CSI 插件的 CreateVolume 函数。
  3. 创建 PV 资源,PV 名称为 [Provisioner 指定的 PV 前缀]-[PVC uuid]

对于集群中的 PV 资源:

  1. 判断 PV 是否需要删除,标准如下:
    • 判断其 .Status.Phase 是否为 Release
    • 判断其 .Spec.PersistentVolumeReclaimPolicy 是否为 Delete
    • 判断其是否包含 annotation pv.kubernetes.io/provisioned-by,且其值是否为自己。
  2. 通过特定的 Unix Domain Socket 调用外部 CSI 插件的 DeleteVolume 接口。
  3. 删除集群中的 PV 资源。

3. External Attacher
功能

挂接/摘除存储卷。

原理

External-Attacher 内部会时刻 watch 集群中的 VolumeAttachment 资源和 PersistentVolume 资源。

对于 VolumeAttachment 资源:

  1. 从 VolumeAttachment 资源中获得 PV 的所有信息,如 volume ID、node ID、挂载 Secret 等。
  2. 判断 VolumeAttachment 的 DeletionTimestamp 字段是否为空来判断其为卷挂接或卷摘除:
    • 若为卷挂接,则通过特定的 Unix Domain Socket 调用外部 CSI 插件的 ControllerPublishVolume 接口。
    • 若为卷摘除,则通过特定的 Unix Domain Socket 调用外部 CSI 插件的 ControllerUnpublishVolume 接口。

对于 PersistentVolume 资源:

  1. 在挂接时为相关 PV 打上 Finalizer:external-attacher/[driver 名称]
  2. 当 PV 处于删除状态时(DeletionTimestamp 非空),删除 Finalizer:external-attacher/[driver 名称]

4. External Resizer
功能

扩容存储卷。

原理

External-Resizer 内部会 watch 集群中的 PersistentVolumeClaim 资源。

对于 PersistentVolumeClaim 资源:

  1. 判断 PVC 是否需要扩容:PVC 状态需要是 Bound.Status.Capacity.Spec.Resources.Requests 不等。
  2. 更新 PVC 的 .Status.Conditions,表明此时处于 Resizing 状态。
  3. 通过特定的 Unix Domain Socket 调用外部 CSI 插件的 ControllerExpandVolume 接口。
  4. 更新 PV 的 .Spec.Capacity
  5. 若 CSI 支持文件系统在线扩容:
    • ControllerExpandVolume 接口返回值中 NodeExpansionRequired 字段为 true,External-Resizer 更新 PVC 的 .Status.ConditionsFileSystemResizePending 状态。
    • 若不支持,则扩容成功,External-Resizer 更新 PVC 的 .Status.Conditions 为空,且更新 PVC 的 .Status.Capacity
  6. Volume Manager(Kubelet 组件)观察到存储卷需在线扩容,于是通过特定的 Unix Domain Socket 调用外部 CSI 插件的 NodeExpandVolume 接口实现文件系统扩容。

5. Livenessprobe
功能

检查 CSI 插件是否正常。

原理

通过对外暴露一个 /healthz HTTP 端口以服务 kubelet 的探针探测器,内部是通过特定的 Unix Domain Socket 调用外部 CSI 插件的 Probe 接口。

六、CSI 与 Kubernetes 组件的通信方式

CSI Controller 与 Kubernetes 的通信

  • 组件:external-provisioner、external-attacher 等 Sidecar 容器。
  • 通信方式
    • 通过 Kubernetes API 监听资源对象的变化(如 PersistentVolumeClaim、VolumeAttachment 等)。
    • 通过 Unix Domain Socket 调用 CSI 驱动的接口。

CSI Node 与 Kubelet 的通信

  • 组件:node-driver-registrar Sidecar 容器。
  • 通信方式:通过 Unix Domain Socket 与 kubelet 通信。CSI Driver 容器通过 Unix Domain Socket 接收 kubelet 的调用,完成存储卷的挂载和卸载。

CSI 驱动与 Sidecar 的通信

  • 通信方式:通过 Unix Domain Socket 使用 gRPC 协议进行通信。Sidecar 容器通过 gRPC 调用 CSI 驱动的接口,完成具体的存储操作。

七、CSI 工作原理

CSI 的工作流程围绕 Kubernetes 的存储资源(如 PersistentVolume、PersistentVolumeClaim)展开,主要包含以下步骤:

  1. 存储资源的创建:用户创建 PersistentVolumeClaim(PVC),指定存储需求(如大小、访问模式等)。external-provisioner 监听到 PVC 的变化,调用 CSI 驱动的 CreateVolume 接口,创建存储卷。
  2. 存储卷的挂载:external-attacher 监听到 VolumeAttachment 的变化,调用 CSI 驱动的 ControllerPublish 接口,将存储卷与目标节点绑定。kubelet 通过 node-driver-registrar 调用 CSI 驱动的 NodePublishVolume 接口,将存储卷挂载到容器的指定路径。
  3. 存储卷的卸载:用户删除 PersistentVolumeClaim 或 VolumeAttachment。external-attacher 调用 CSI 驱动的 ControllerUnpublish 接口,解除存储卷与节点的绑定。kubelet 调用 CSI 驱动的 NodeUnpublishVolume 接口,将存储卷从容器中卸载。
  4. 存储卷的删除:external-provisioner 监听到 PVC 的删除操作,调用 CSI 驱动的 DeleteVolume 接口,删除存储卷。

八、CSI 与 FlexVolume 的对比

CSI 是 FlexVolume 的升级版,主要区别如下:

  • 接口标准化:CSI 提供了更标准化的接口,支持更复杂的存储操作(如快照、扩容等)。
  • 组件分离:CSI 将功能分离为 Controller 和 Node 两个部分,支持更灵活的部署和扩展。
  • 性能优化:CSI 使用 gRPC 协议,通信效率更高。

九、总结

CSI 是 Kubernetes 中存储管理的重要组成部分,通过标准化的接口和灵活的组件设计,为用户提供了强大的存储管理能力。掌握 CSI 的工作原理和组件构成,能够帮助用户更好地管理和优化 Kubernetes 集群中的存储资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值