关于云原生:一文读懂容器存储接口-CSI

5次阅读

共计 14961 个字符,预计需要花费 38 分钟才能阅读完成。

作者 | 惠志
起源 | 阿里巴巴云原生公众号

导读:在《一文读懂 K8s 长久化存储流程》一文咱们重点介绍了 K8s 外部的存储流程,以及 PV、PVC、StorageClass、Kubelet 等之间的调用关系。接下来本文将将重点放在 CSI(Container Storage Interface)容器存储接口上,探索什么是 CSI 及其外部工作原理。

背景

K8s 原生反对一些存储类型的 PV,如 iSCSI、NFS、CephFS 等等(详见链接),这些 in-tree 类型的存储代码放在 Kubernetes 代码仓库中。这里带来的问题是 K8s 代码与三方存储厂商的代码 强耦合

  • 更改 in-tree 类型的存储代码,用户必须更新 K8s 组件,老本较高
  • in-tree 存储代码中的 bug 会引发 K8s 组件不稳固
  • K8s 社区须要负责保护及测试 in-tree 类型的存储性能
  • in-tree 存储插件享有与 K8s 外围组件等同的特权,存在安全隐患
  • 三方存储开发者必须遵循 K8s 社区的规定开发 in-tree 类型存储代码

CSI 容器存储接口标准的呈现解决了上述问题,将三方存储代码与 K8s 代码解耦,使得三方存储厂商研发人员只需实现 CSI 接口(无需关注容器平台是 K8s 还是 Swarm 等)。

CSI 外围流程介绍

在具体介绍 CSI 组件及其接口之前,咱们先对 K8s 中 CSI 存储流程进行一个介绍。《一文读懂 K8s 长久化存储流程》一文介绍了 K8s 中的 Pod 在挂载存储卷时需经验三个的阶段:Provision/Delete(创盘 / 删盘)、Attach/Detach(挂接 / 摘除)和 Mount/Unmount(挂载 / 卸载),上面以图文的形式解说 K8s 在这三个阶段应用 CSI 的流程。

1. Provisioning Volumes

1.集群管理员 创立 StorageClass 资源,该 StorageClass 中蕴含 CSI 插件名称(provisioner:pangu.csi.alibabacloud.com)以及存储类必须的参数(parameters: type=cloud_ssd)。sc.yaml 文件如下:

2.用户 创立 PersistentVolumeClaim 资源,PVC 指定存储大小及 StorageClass(如上)。pvc.yaml 文件如下:

3.卷控制器(PersistentVolumeController)察看到集群中新创建的 PVC 没有与之匹配的 PV,且其应用的存储类型为 out-of-tree,于是为 PVC 打 annotation:volume.beta.kubernetes.io/storage-provisioner=[out-of-tree CSI 插件名称](本例中即为 provisioner:pangu.csi.alibabacloud.com)。

4.External Provisioner 组件 察看到 PVC 的 annotation 中蕴含 “volume.beta.kubernetes.io/storage-provisioner” 且其 value 是本人,于是开始创盘流程。

  • 获取相干 StorageClass 资源并从中获取参数(本例中 parameters 为  type=cloud_ssd),用于前面 CSI 函数调用。
  • 通过 unix domain socket 调用 内部 CSI 插件 CreateVolume 函数

5.内部 CSI 插件 返回胜利后示意盘创立实现,此时 External Provisioner 组件 会在集群创立一个 PersistentVolume 资源。

6.卷控制器 会将 PV 与 PVC 进行绑定。

2. Attaching Volumes

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. Mounting Volumes

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 函数

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 函数

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 上已没有相干挂接信息。

6. Deleting Volumes

1.用户 删除相干 PVC。

2.External Provisioner 组件 察看到 PVC 删除事件,依据 PVC 的回收策略(Reclaim)执行不同操作:

  • Delete:调用 内部 CSI 插件 DeleteVolume 函数 以删除卷;一旦卷胜利删除,Provisioner会删除集群中对应 PV 对象。
  • Retain:Provisioner不执行卷删除操作。

CSI Sidecar 组件介绍

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

1. Node Driver Registrar

1)性能

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

2)原理

Node-Driver-Registrar 组件 通过 Kubelet 内部插件注册机制实现注册,注册胜利后:

  • Kubelet为本节点 Node 资源打 annotation:Kubelet调用 内部 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

1)性能

创立 / 删除理论的存储卷,以及代表存储卷的 PV 资源。

2)原理

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

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

对于集群中的 PVC 资源:

  • 判断 PVC 是否须要动态创建存储卷,规范如下:

    • PVC 的 annotation 中是否蕴含 “volume.beta.kubernetes.io/storage-provisioner” 键(由卷控制器创立),并且其值是否与 Provisioner 名称相等。
    • PVC 对应 StorageClass 的 VolumeBindingMode 字段若为 WaitForFirstConsumer,则 PVC 的 annotation 中必须蕴含 “volume.kubernetes.io/selected-node” 键(详见调度器如何解决 WaitForFirstConsumer),且其值不为空;若为 Immediate 则示意须要 Provisioner 立刻提供动静存储卷。
  • 通过特定的 Unix Domain Socket 调用 内部 CSI 插件 CreateVolume 函数
  • 创立 PV 资源,PV 名称为 [Provisioner 指定的 PV 前缀] – [PVC uuid]。

对于集群中的 PV 资源:

  • 判断 PV 是否须要删除,规范如下:

    • 判断其 .Status.Phase 是否为 Release。
    • 判断其 .Spec.PersistentVolumeReclaimPolicy 是否为 Delete。
    • 判断其是否蕴含 annotation(pv.kubernetes.io/provisioned-by),且其值是否为本人。
  • 通过特定的 Unix Domain Socket 调用 内部 CSI 插件 DeleteVolume 接口
  • 删除集群中的 PV 资源。

    3. External Attacher

    1)性能

    挂接 / 摘除存储卷。

2)原理

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

对于 VolumeAttachment 资源:

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

对于 PersistentVolume 资源:

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

4. External Resizer

1)性能

扩容存储卷。

2)原理

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

对于 PersistentVolumeClaim 资源:

  • 判断 PersistentVolumeClaim 资源是否须要扩容:PVC 状态须要是 Bound 且 .Status.Capacity 与 .Spec.Resources.Requests 不等。
  • 更新 PVC 的 .Status.Conditions,表明此时处于 Resizing 状态。
  • 通过特定的 Unix Domain Socket 调用 内部 CSI 插件 ControllerExpandVolume 接口
  • 更新 PV 的 .Spec.Capacity。
  • 若 CSI 反对文件系统在线扩容,ControllerExpandVolume 接口返回值中 NodeExpansionRequired 字段为 true,External-Resizer更新 PVC 的 .Status.Conditions 为 FileSystemResizePending 状态;若不反对则扩容胜利,External-Resizer更新 PVC 的 .Status.Conditions 为空,且更新 PVC 的 .Status.Capacity。

Volume Manager(Kubelet 组件)察看到存储卷需在线扩容,于是通过特定的 Unix Domain Socket 调用 内部 CSI 插件 NodeExpandVolume 接口 实现文件系统扩容。

5. livenessprobe

1)性能

查看 CSI 插件是否失常。

2)原理

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

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)
}

2. 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)
}

3. 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)
}

K8s CSI API 对象

K8s 为反对 CSI 规范,蕴含如下 API 对象:

  • CSINode
  • CSIDriver
  • VolumeAttachment

1. CSINode

apiVersion: storage.k8s.io/v1beta1
kind: CSINode
metadata:
  name: node-10.212.101.210
spec:
  drivers:
  - name: yodaplugin.csi.alibabacloud.com
    nodeID: node-10.212.101.210
    topologyKeys:
    - kubernetes.io/hostname
  - name: pangu.csi.alibabacloud.com
    nodeID: a5441fd9013042ee8104a674e4a9666a
    topologyKeys:
    - topology.pangu.csi.alibabacloud.com/zone

作用:

  1. 判断 内部 CSI 插件 是否注册胜利。在 Node Driver Registrar 组件向 Kubelet 注册结束后,Kubelet 会创立该资源,故不须要显式创立 CSINode 资源。
  2. 将 Kubernetes 中 Node 资源名称与三方存储系统中节点名称(nodeID)一一对应。此处 Kubelet 会调用 内部 CSI 插件 NodeServer 的 GetNodeInfo 函数 获取 nodeID。
  3. 显示卷拓扑信息。CSINode 中 topologyKeys 用来示意存储节点的拓扑信息,卷拓扑信息会使得 Scheduler 在 Pod 调度时抉择适合的存储节点。

2. CSIDriver

apiVersion: storage.k8s.io/v1beta1
kind: CSIDriver
metadata:
  name: pangu.csi.alibabacloud.com
spec:
    # 插件是否反对卷挂接(VolumeAttach)attachRequired: true
  # Mount 阶段是否 CSI 插件须要 Pod 信息
  podInfoOnMount: true
  # 指定 CSI 反对的卷模式
  volumeLifecycleModes:
  - Persistent

作用:

  1. 简化 内部 CSI 插件 的发现。由集群管理员创立,通过 kubectl get csidriver 即可得悉环境上有哪些 CSI 插件。
  2. 自定 义 Kubernetes 行为,如一些内部 CSI 插件不须要执行卷挂接(VolumeAttach)操作,则能够设置 .spec.attachRequired 为 false。

3. VolumeAttachment

apiVersion: storage.k8s.io/v1
kind: VolumeAttachment
metadata:
  annotations:
    csi.alpha.kubernetes.io/node-id: 21481ae252a2457f9abcb86a3d02ba05
  finalizers:
  - external-attacher/pangu-csi-alibabacloud-com
  name: csi-0996e5e9459e1ccc1b3a7aba07df4ef7301c8e283d99eabc1b69626b119ce750
spec:
  attacher: pangu.csi.alibabacloud.com
  nodeName: node-10.212.101.241
  source:
    persistentVolumeName: pangu-39aa24e7-8877-11eb-b02f-021234350de1
status:
  attached: true

作用:VolumeAttachment 记录了存储卷的挂接 / 摘除信息以及节点信息。

反对个性

1. 拓扑反对

在 StorageClass 中有 AllowedTopologies 字段:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: csi-pangu
provisioner: pangu.csi.alibabacloud.com
parameters:
  type: cloud_ssd
volumeBindingMode: Immediate
allowedTopologies:
- matchLabelExpressions:
  - key: topology.pangu.csi.alibabacloud.com/zone
    values:
    - zone-1
    - zone-2

内部 CSI 插件 部署后会为每个节点打标,打标内容 NodeGetInfo 函数 返回的 [AccessibleTopology] 值(详见 Node Driver Registrar 局部)。

External Provisioner在调用 CSI 插件的 CreateVolume 接口之前,会在申请参数设置 AccessibilityRequirements:

  • 对于 WaitForFirstConsumer

    • 当 PVC 的 anno 中蕴含 “volume.kubernetes.io/selected-node” 且不为空,则先获取对应节点 CSINode 的 TopologyKeys,而后依据该 TopologyKeys 键从 Node 资源的 Label 获取 Values 值,最初拿该 Values 值与 StorageClass 的 AllowedTopologies 比对,判断其是否蕴含于其中;若不蕴含则报错。
  • 对于 Immediately

    • 将 StorageClass 的 AllowedTopologies 的值填进来,若 StorageClass 没有设置 AllowedTopologies 则将所有蕴含 TopologyKeys 键的节点 Value 添进来。

Scheduler 如何解决应用存储卷调度

基于社区 1.18 版本调度器

调度器的调度过程次要有如下三步:

  • 预选(Filter):筛选满足 Pod 调度要求的节点列表。
  • 优选(Score):通过外部的优选算法为节点打分,取得最高分数的节点即为选中的节点。
  • 绑定(Bind):调度器将调度后果告诉给 kube-apiserver,更新 Pod 的 .spec.nodeName 字段。

调度器预选阶段:解决 Pod 的 PVC/PV 绑定关系以及动静供给 PV(Dynamic Provisioning),同时使调度器调度时思考 Pod 所应用 PV 的节点亲和性。具体调度过程如下:

  1. Pod 不蕴含 PVC 间接跳过。
  2. FindPodVolumes

    • 获取 Pod 的 boundClaims、claimsToBind 以及 unboundClaimsImmediate。

      • boundClaims:已 Bound 的 PVC
      • claimsToBind:PVC 对应 StorageClass 的 VolumeBindingMode 为 VolumeBindingWaitForFirstConsumer
      • unboundClaimsImmediate:PVC 对应 StorageClass 的 VolumeBindingMode 为 VolumeBindingImmediate
    • 若 len(unboundClaimsImmediate) 不为空,示意这种 PVC 须要立刻绑定 PV(即存 PVC 创立后,立即动态创建 PV 并将其绑定到 PVC,该过程不走调度),若 PVC 处于 unbound 阶段则报错。
    • 若 len(boundClaims) 不为空,则查看 PVC 对应 PV 的节点亲和性与以后节点的 Label 是否抵触,若抵触则报错(可查看 Immediate 类型的 PV 拓扑)。
    • 若 len(claimsToBind) 不为空

      • 先查看环境中已有的 PV 是否与该 PVC 匹配(findMatchingVolumes),将可能匹配 PVC 的 PV 记录在调度器的 cache 中。
      • 未匹配到 PV 的 PVC 走动静调度流程,动静调度次要通过 StorageClass 的 AllowedTopologies 字段判断以后调度节点是否满足拓扑要求(针对 WaitForFirstConsumer 类型的 PVC)。

调度器优选阶段不探讨。

调度器 Assume 阶段

调度器会先 Assume PV/PVC,再 Assume Pod。

  1. 将以后待调度的 Pod 进行深拷贝。
  2. AssumePodVolumes(针对 WaitForFirstConsumer 类型的 PVC)

    • 更改调度器 cache 中曾经 Match 的 PV 信息:设置 annotation:pv.kubernetes.io/bound-by-controller=”yes”。
    • 更改调度器 cache 中未匹配到 PV 的 PVC,设置 annotation:volume.kubernetes.io/selected-node=【所选节点】。
  3. Assume Pod 结束

    • 更改调度器 cache 中 Pod 的 .Spec.NodeName 为【所选节点】。

调度器 Bind 阶段

BindPodVolumes:

  • 调用 Kubernetes 的 API 更新集群中 PV/PVC 资源,使其与调度器 Cache 中的 PV/PVC 统一。
  • 查看 PV/PVC 状态:

    • 查看所有 PVC 是否已处于 Bound 状态。
    • 查看所有 PV 的 NodeAffinity 是否与节点 Label 抵触。
  • 调度器执行 Bind 操作:调用 Kubernetes 的 API 更新 Pod 的 .Spec.NodeName 字段。

2. 存储卷扩容

存储卷扩容局部在 External Resizer 局部已提到,故不再赘述。用户只须要编辑 PVC 的 .Spec.Resources.Requests.Storage 字段即可,留神只可扩容不可缩容。

若 PV 扩容失败,此时 PVC 无奈从新编辑 spec 字段的 storage 为原来的值(只可扩容不可缩容)。参考 K8s 官网提供的 PVC 还原办法:
https://kubernetes.io/docs/co…

3. 单节点卷数量限度

卷数量限度在 Node Driver Registrar 局部已提到,故不再赘述。

4. 存储卷监控

存储商需实现 CSI 插件的 NodeGetVolumeStats 接口,Kubelet 会调用该函数,并反映在其 metrics 上:

  • kubelet_volume_stats_capacity_bytes:存储卷容量
  • kubelet_volume_stats_used_bytes:存储卷已应用容量
  • kubelet_volume_stats_available_bytes:存储卷可应用容量
  • kubelet_volume_stats_inodes:存储卷 inode 总量
  • kubelet_volume_stats_inodes_used:存储卷 inode 使用量
  • kubelet_volume_stats_inodes_free:存储卷 inode 残余量

5. Secret

CSI 存储卷反对传入 Secret 来解决不同流程中所须要的私密数据,目前 StorageClass 反对如下 Parameter:

  • csi.storage.k8s.io/provisioner-secret-name
  • csi.storage.k8s.io/provisioner-secret-namespace
  • csi.storage.k8s.io/controller-publish-secret-name
  • csi.storage.k8s.io/controller-publish-secret-namespace
  • csi.storage.k8s.io/node-stage-secret-name
  • csi.storage.k8s.io/node-stage-secret-namespace
  • csi.storage.k8s.io/node-publish-secret-name
  • csi.storage.k8s.io/node-publish-secret-namespace
  • csi.storage.k8s.io/controller-expand-secret-name
  • csi.storage.k8s.io/controller-expand-secret-namespace

Secret 会蕴含在对应 CSI 接口的参数中,如对于 CreateVolume 接口而言则蕴含在 CreateVolumeRequest.Secrets 中。

6. 块设施

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx-example
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  volumeClaimTemplates:
  - metadata:
      name: html
    spec:
      accessModes:
        - ReadWriteOnce
      volumeMode: Block
      storageClassName: csi-pangu
      resources:
        requests:
          storage: 40Gi
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        volumeDevices:
        - devicePath: "/dev/vdb"
          name: html

三方存储厂商需实现 NodePublishVolume 接口。Kubernetes 提供了针对块设施的工具包(”k8s.io/kubernetes/pkg/util/mount”),在 NodePublishVolume 阶段可调用该工具的 EnsureBlock 和 MountBlock 函数。

7. 卷快照 / 卷克隆能力

鉴于本文篇幅,此处不做过多原理性介绍。读者感兴趣见官网介绍:卷快照、卷克隆。

总结

本文首先对 CSI 外围流程进行了大体介绍,并联合 CSI Sidecar 组件、CSI 接口、API 对象对 CSI 规范进行了深度解析。在 K8s 上,应用任何一种 CSI 存储卷都离不开下面的流程,环境上的容器存储问题也肯定是其中某个环节呈现了问题。本文对其流程进行梳理,以便于宽广程序猿(媛)排查环境问题。

容器存储的坑比拟多,专有云环境下尤其如此。不过挑战越多,时机也越多!目前国内专有云市场群雄逐鹿,阿里云云原生部门欢送大侠的退出,一起共创将来!若有动向可投简历至邮箱:huizhi.szh@alibaba-inc.com。

KubeMeet 杭州站凋谢报名!

4 月 17 日,云原生基金会 CNCF 和阿里巴巴联结主办的 「KubeMeet 开发者沙龙 · 云原生利用治理专场」 来到杭州啦!这里有 Kubernetes 生态开发者都在关注的开源我的项目,以及阿里巴巴、携程、第四范式的一线云原生落地实际。点击此处即可报名!

正文完
 0