K8s CSI 是 Kubernetes 中非常重要的一个组件,它解决了存储与计算分离的复杂性,并为容器化应用提供了持久化存储的能力。

1. 基本概念#

CSI (Container Storage Interface) 译为 容器存储接口。它是由 Kubernetes 社区与存储厂商共同制定的一套标准接口规范。

在 CSI 出现之前,Kubernetes 存储插件的开发和管理存在以下痛点:

  • 紧耦合问题: Kubernetes 内部集成了大量的存储驱动(In-tree 存储插件),例如 AWS EBS、GCE PD、Azure Disk、Ceph RBD 等。这意味着每当存储厂商需要支持 Kubernetes 时,他们都必须将其存储驱动代码提交到 Kubernetes 的核心代码库中。这种方式导致了:
    • Kubernetes 代码库臃肿: 集成了大量存储逻辑,增加了核心代码的复杂性和维护难度。
    • 发布周期长: 存储驱动的更新需要跟随 Kubernetes 的发布周期,新功能和 bug 修复不能及时推送到用户。
    • 存储厂商开发受限: 每次更新都需要与 Kubernetes 社区协调,开发和测试流程繁琐。
  • 兼容性问题: 不同存储厂商的存储系统差异巨大,缺乏统一的接口规范,导致存储系统与 Kubernetes 之间的集成困难。

CSI 的目标就是解决这些问题,实现存储系统与 Kubernetes 的解耦。 它定义了一套通用的接口,允许任何存储厂商开发自己的 CSI 驱动,然后通过这些驱动来与 Kubernetes 进行交互,从而为容器提供存储服务。

1.1. 资源定义#

1.1.1. Volumes#

Volumes 是 Kubernetes 中 Pod 通过文件系统访问和共享数据的抽象。它主要提供了如下功能:

  • 通过 ConfigMap 或者 Secret 共享配置;
  • 跨容器、跨 Pod 甚至跨 Node 共享数据;
  • 数据持久化。在 Pod 销毁之后仍能继续访问数据。 对于 Pod 来说,Volumes 通过 .spec.volumes 提供给 Pod,容器通过 .spec.containers[*].volumeMounts 来将指定 Volumes 挂载到指定目录。

1.1.2. Persistent Volumes 和 Persistent Volumes Claim#

PV 和 PVC 提供了两套 API 将存储的提供和消费分离。

PV 作为存储的提供者视角,定义了存储的底层信息,如存储的供应商(storageClassName),回收策略(persistentVolumeReclaimPolicy)等。

# dynamic provisioning persistent volume instance
apiVersion: v1
kind: PersistentVolume
metadata:
  annotations:
    pv.kubernetes.io/provisioned-by: openebs.io/local
  creationTimestamp: "2025-06-17T06:14:42Z"
  finalizers:
  - kubernetes.io/pv-protection
  labels:
    openebs.io/cas-type: local-hostpath
  name: pvc-aaa15ac7-b306-45dd-8d37-190b77f152c6
  resourceVersion: "4648555"
  uid: 38769acb-881b-4f10-a7d6-950bd5414b83
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 5G
  claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: local-hostpath-pvc
    namespace: default
    resourceVersion: "4648514"
    uid: aaa15ac7-b306-45dd-8d37-190b77f152c6
  local:
    fsType: ""
    path: /data/local-hostpath/pvc-aaa15ac7-b306-45dd-8d37-190b77f152c6
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - a0cho6pgqfrifsg
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-hostpath
  volumeMode: Filesystem
status:
  lastPhaseTransitionTime: "2025-06-17T06:14:42Z"
  phase: Bound

PVC 作为存储的使用者视角,是用户对所需的存储的声明。根据 PVC 中的资源定义,消耗指定的 PV 资源。PVC 指定了存储的容量(request.Storage),访问模式(accessMode)等信息。

# persistent volume claim, using local host-path 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"PersistentVolumeClaim","metadata":{"annotations":{},"name":"local-hostpath-pvc","namespace":"default"},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"5G"}},"storageClassName":"local-hostpath"}}
    pv.kubernetes.io/bind-completed: "yes"
    pv.kubernetes.io/bound-by-controller: "yes"
    volume.beta.kubernetes.io/storage-provisioner: openebs.io/local
    volume.kubernetes.io/selected-node: a0cho6pgqfrifsg
    volume.kubernetes.io/storage-provisioner: openebs.io/local
  creationTimestamp: "2025-06-17T06:09:55Z"
  finalizers:
  - kubernetes.io/pvc-protection
  name: local-hostpath-pvc
  namespace: default
  resourceVersion: "4648557"
  uid: aaa15ac7-b306-45dd-8d37-190b77f152c6
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 5G
  storageClassName: local-hostpath
  volumeMode: Filesystem
  volumeName: pvc-aaa15ac7-b306-45dd-8d37-190b77f152c6
status:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 5G
  phase: Bound

accessModes

  • ReadWriteOnce:可以被单个 Node 以读写模式挂载。同一个 Node 上的多个 Pods 都可以绑定该 PVC 并读写
  • ReadOnlyMany:可以被多个节点以只读方式挂载
  • ReadWriteMany:可以被多个节点以读写方式挂载
  • ReadWriteOncePod:可以被单个 Node 以读写模式挂载。但是只允许一个 Pod 读写该卷

storageClassName:指定的存储引擎,指向同名的 storageClass 对象

persistentVolumeReclaimPolicy

  • Retain:允许手动回收资源。当持久卷声明被删除时,持久卷仍然存在,并且该卷被视为“已释放”。但是,它尚未可用于另一个声明,因为之前声明者的数据仍然保留在卷上。管理员可以通过以下步骤手动回收该卷。
    • Delete the PersistentVolume. The associated storage asset in external infrastructure still exists after the PV is deleted.
    • Manually clean up the data on the associated storage asset accordingly.
    • Manually delete the associated storage asset.
  • Delete:对于支持 Delete Reclaim 的卷插件,Delete 会同时移除 Kubernetes 中的PersistentVolume 对象以及外部基础设施中相关的存储资产。动态分配的卷继承其 StorageClass 的回收策略,默认为 Delete

1.1.2.1. Static Provisioning 和 Dynamic Provisioning#

根据 PV 的生成方式,CSI(Container Storage Interface)中的存储供应(Provisioning)方式主要分为两种:静态供应 (Static Provisioning)动态供应 (Dynamic Provisioning)

特性 静态供应 (Static Provisioning) 动态供应 (Dynamic Provisioning)
存储卷创建者 管理员手动在后端存储系统上创建 Kubernetes (通过 CSI 驱动) 自动创建
PV 创建者 管理员手动创建 PV 对象 csi-provisioner (Sidecar) 自动创建 PV 对象
前提 存储卷和 PV 必须预先存在 需要 StorageClass 定义存储类型和 CSI 驱动
PVC 引用 匹配可用的 PV (通常通过容量、访问模式、标签等) 引用 storageClassName
自动化程度 低,需要大量手动操作 高,完全自动化
依赖 不依赖 CSI 驱动的 ControllerService.CreateVolume() 严重依赖 CSI 驱动的 ControllerService.CreateVolume()
删除行为 通常需要手动删除后端卷 (取决于 PV 的 reclaimPolicy) 根据 StorageClassreclaimPolicy 自动删除后端卷

1.1.3. storageClass#

StorageClass 用来描述能被提供的存储类型。不同的存储类型可能映射到不同的服务质量级别、备份策略或其它存储策略。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: low-latency
  annotations:
    storageclass.kubernetes.io/is-default-class: "false"
provisioner: csi-driver.example-vendor.example
reclaimPolicy: Retain # default value is Delete
allowVolumeExpansion: true
mountOptions:
  - discard # this might enable UNMAP / TRIM at the block storage layer
volumeBindingMode: WaitForFirstConsumer
parameters:
  guaranteedReadWriteLatency: "true" # provider-specific

storageclass.kubernetes.io/is-default-class annotation 如果设置为 true,表示当 PVC 没有配置时默认使用的 storageClass。

volumeBindingMode

  • WaitForFirstConsumer:延迟绑定。如 local hostpath 不知道 PV 会在哪个 Node 上被消费,所以只能在 Consumer(Pod) 出现时再进行绑定
  • Immediate:立即绑定

1.1.4. CSIDriver#

CSI Driver CRD 是一个集群范围的 Kubernetes API 对象,用于注册 CSI Driver。每个 CSI Driver 都可以创建一个对应的 CSIDriver 对象来向 Kubernetes 声明其存在和能力。

apiVersion: storage.k8s.io/v1
kind: CSIDriver
metadata:
  name: disk.csi.aws.com # !!!这是 CSI Driver 的唯一名称,也是 StorageClass 中 `provisioner` 字段引用的名称
spec:
  attachRequired: true # 指示该驱动是否需要显式地将卷“附加”到节点(例如,块存储)
  podInfoOnMount: false # 指示该驱动是否需要在挂载时接收 Pod 的信息
  requiresRepublish: false # 指示卷是否需要在节点上重新发布(通常用于读写分离等复杂场景)
  fsGroupPolicy: File # 或者 None, ReadWriteOnceWithFSType,控制 fsGroup 的行为
  volumeLifecycleModes: # 驱动支持的卷生命周期模式
    - Persistent # 标准的持久卷模式
    # - Ephemeral # 临时卷模式,例如 CSI Ephemeral Volumes

2. CSI Architecture#

2.1. CSI 定义的目标#

CSI 解耦了存储提供商和 Kubernetes,只要编写符合规范的 CSI 插件,就能在所有实现 CSI 的编排系统中“即插即用”。CSI 定义 API 主要提供的功能如下:

  • 动态配置和取消配置卷
  • 将卷 attach 到节点或从节点中 detach 卷
  • 从节点 mount 或者 unmount 卷
  • 使用 block volumes 或者 mountable volumes
  • 本地存储供应(例如,设备映射,lvm)
  • 创建和删除快照(快照的源是一个卷)
  • 从快照中配置新卷(还原快照,其中原卷中的数据被清除并替换为快照中的数据)

2.2. 架构#

CSI 将存储供应商需要提供的接口抽象成了3个服务:

  • Identity Service:用于向 Kubernetes 提供当前存储的一些信息
  • Controller Service:提供存储卷的创建、删除、镜像等操作,由 csi-provisoner sidecar 负责交互
  • Node Service:提供卷的预处理、挂载、卸载等操作,由 kubelet 负责交互

CSI Spec 中将这三种服务划分到 Node-Plugin 和 Controller-Plugin 两种功能实体中,其中:

  • Node-Plugin 需要实现 Identity Service 和 Node Service,运行在每一个支持该存储卷的节点上,一般是一个 DaemonSet
  • Controller-Plugin 需要实现 Identity Service 和 Controller Service,可以运行在任意节点上,一般是一个 Deployment

虽然 CSI 做了这个功能划分,在实操中也不一定完全按照这种方式去部署。从 SPEC 中的 Figure 1 到 Figure 4 也可以看出 Controller-Plugin 和 Node-Plugin 既可以严格按照上面的方式部署,也可以合并成一个组件部署。本质上,只要符合 CSI 定义的交互方式,能正确地给 Pod 提供 PV 和后端存储即可。

2.3. 交互流程#

sequenceDiagram
    participant User as 用户
    participant K8s_API as Kubernetes API Server
    participant K8s_Scheduler as K8s 调度器
    participant K8s_ControllerMgr as K8s 控制器管理器
    participant Ext_Provisioner as CSI External Provisioner
    participant Ext_Attacher as CSI External Attacher
    participant Ext_Mounter as CSI External Mounter (或 Node CSI Driver)
    participant CSIDriver as CSI 驱动 (Controller Plugin)
    participant NodeCSIDriver as CSI 驱动 (Node Plugin)
    participant Underlying_Storage as 底层存储系统

    Note over User, K8s_API: 1. 用户定义并提交存储请求
    User->>K8s_API: 创建 PVC (PersistentVolumeClaim)

    Note over K8s_ControllerMgr: 2. K8s 发现未绑定的 PVC
    K8s_ControllerMgr->>K8s_API: 监听 PVC 事件

    Note over Ext_Provisioner: 3. CSI 供应器处理 PVC
    Ext_Provisioner->>K8s_API: 监听 PVC 事件 (status=Pending)
    alt PVC 指定 StorageClass
        Ext_Provisioner->>K8s_API: 获取 StorageClass 定义
        Ext_Provisioner->>CSIDriver: 调用 CSI CreateVolume RPC (依据 StorageClass Parameters)
        CSIDriver-->>Underlying_Storage: 创建实际的存储卷
        Underlying_Storage-->>CSIDriver: 返回卷信息 (e.g., volumeID)
        CSIDriver-->>Ext_Provisioner: 返回 CreateVolume 结果 (volumeID, attributes)
        Ext_Provisioner->>K8s_API: 创建 PV (PersistentVolume) 对象, 绑定 PVC
        K8s_API-->>K8s_ControllerMgr: PVC & PV 状态更新 (Bound)
    else PVC 静态绑定 (PV已存在)
        K8s_ControllerMgr->>K8s_API: 尝试匹配现有 PV
        K8s_API-->>K8s_ControllerMgr: PVC & PV 状态更新 (Bound)
    end

    Note over User, K8s_API: 4. 用户定义并部署 Pod
    User->>K8s_API: 创建 Pod (引用 PVC)

    Note over K8s_Scheduler: 5. 调度器调度 Pod
    K8s_Scheduler->>K8s_API: 发现待调度的 Pod
    K8s_Scheduler->>K8s_Scheduler: 根据 Pod 资源需求、亲和性等选择节点
    K8s_Scheduler-->>K8s_API: 更新 Pod.spec.nodeName

    Note over Ext_Attacher: 6. 外部 Attacher 挂载卷到节点
    Ext_Attacher->>K8s_API: 监听 Pod 调度到节点事件 (Pod.spec.nodeName)
    Ext_Attacher->>K8s_API: 获取 Pod 引用 PV 的信息 (volumeID, nodeName)
    Ext_Attacher->>CSIDriver: 调用 CSI ControllerPublishVolume RPC (volumeID, nodeID)
    CSIDriver-->>Underlying_Storage: 将存储卷附加/连接到选定节点
    Underlying_Storage-->>CSIDriver: 确认连接
    CSIDriver-->>Ext_Attacher: 返回 ControllerPublishVolume 结果
    Ext_Attacher->>K8s_API: 更新 VolumeAttachment 状态 (Attacher/Attached)

    Note over K8s_ControllerMgr, NodeCSIDriver: 7. Kubelet 和 CSI Node Driver 挂载卷到 Pod
    K8s_ControllerMgr->>K8s_API: 监听 Pod 状态和 VolumeAttachment 状态
    K8s_API->>K8s_Scheduler: 触发 Kubelet 行为
    Kubelet->>K8s_API: 发现 Pod 调度到自身节点,且卷已附加
    Kubelet->>NodeCSIDriver: 调用 CSI NodeStageVolume RPC (volumeID, stagedPath)
    NodeCSIDriver-->>Underlying_Storage: 准备挂载 (e.g., format if needed, mount to a staging path)
    Underlying_Storage-->>NodeCSIDriver: 完成准备
    NodeCSIDriver-->>Kubelet: 返回 NodeStageVolume 结果
    Kubelet->>NodeCSIDriver: 调用 CSI NodePublishVolume RPC (stagedPath, targetPath, Pod context)
    NodeCSIDriver-->>Underlying_Storage: 将卷从 staging path 绑定挂载到 Pod 的实际 targetPath
    Underlying_Storage-->>NodeCSIDriver: 完成挂载
    NodeCSIDriver-->>Kubelet: 返回 NodePublishVolume 结果
    Kubelet->>K8s_API: 更新 Pod 状态 (Ready)

    Note over User: 8. Pod 运行,使用持久卷
    User->>K8s_API: 查看 Pod 状态
    Pod->>Underlying_Storage: 读写数据

08dad402a2667853736930063c59ff8f_MD5

图例说明:

  • User (用户): 操作 Kubernetes 的最终用户或管理员。
  • K8s API Server: Kubernetes 集群的核心,所有操作的入口。
  • K8s 调度器 (K8s Scheduler): 负责将 Pod 分配到合适的节点。
  • K8s 控制器管理器 (K8s Controller Manager): 运行各种控制器,如 PVC/PV 绑定控制器、Attach/Detach 控制器等。
  • CSI External Provisioner (外部供应器): CSI sidecar 容器,负责监听 PVC,并调用 CSI 驱动的 CreateVolume RPC 来动态创建卷。
  • CSI External Attacher (外部连接器): CSI sidecar 容器,负责监听 Pod 调度事件,并调用 CSI 驱动的 ControllerPublishVolume RPC 将卷连接到节点。
  • CSI External Mounter (外部挂载器) / CSI 驱动 (Node Plugin): 这部分通常由 Kubelet 调用 CSI 驱动的 Node Plugin 来完成,负责在节点上进行实际的卷挂载。
  • CSI 驱动 (Controller Plugin): CSI 驱动的控制器部分,通常作为独立的 Pod 运行,负责与底层存储系统交互,执行创建、删除、连接等操作。
  • CSI 驱动 (Node Plugin): CSI 驱动的节点部分,通常作为 DaemonSet 运行在每个节点上,负责在节点上执行卷的挂载、卸载、格式化等操作。
  • Underlying Storage System (底层存储系统): 实际提供存储的服务,如 AWS EBS、Azure Disk、Ceph、NFS 等。
  • Kubelet: 运行在每个工作节点上的代理,负责管理 Pod 和容器,并与 CSI Node Plugin 交互进行卷挂载。

流程解释:

  1. 用户提交 PVC: 用户通过 kubectl apply -f pvc.yaml 创建一个 PersistentVolumeClaim
  2. K8s 发现未绑定 PVC: PersistentVolumeController 发现新的 Pending 状态的 PVC
  3. CSI 动态供应 (Provisioning):
    • CSI External Provisioner 监听到这个 PVC
    • 它检查 PVC 的 storageClassName,找到对应的 StorageClass,并根据 StorageClass 定义的 provisioner 名称调用对应的 CSI 驱动(Controller Plugin)的 CreateVolume RPC。
    • CSI 驱动与底层存储系统通信,创建实际的存储卷。
    • CSI 驱动返回卷的唯一 ID (volumeHandle) 和其他属性。
    • CSI External Provisioner 使用这些信息在 Kubernetes 中创建一个 PersistentVolume 对象,并将其状态设置为 Bound,与 PVC 绑定。
  4. 用户部署 Pod: 用户创建一个 Pod,并在其 volumes 定义中引用之前创建的 PVC
  5. K8s 调度器调度 Pod: 调度器将 Pod 调度到合适的节点上。
  6. CSI 连接卷到节点 (Attaching):
    • CSI External Attacher 监听到 Pod 被调度到某个节点。
    • 它获取 Pod 关联的 PV 信息和节点信息,然后调用 CSI 驱动(Controller Plugin)的 ControllerPublishVolume RPC。
    • CSI 驱动与底层存储系统通信,将存储卷连接/附加到目标节点(对于块存储,这意味着使卷对节点可见)。
    • CSI External Attacher 更新 VolumeAttachment 对象的 status,表示卷已附加。
  7. CSI 挂载卷到 Pod (Mounting):
    • Kubelet 在节点上发现有新的 Pod 被调度,并且它需要的卷已通过 VolumeAttachment 标记为 Attached
    • Kubelet 调用 CSI 驱动的 Node Plugin 的 NodeStageVolume RPC,将卷挂载到一个临时的 staging 路径(通常进行文件系统格式化、权限设置等准备工作)。
    • Kubelet 调用 CSI 驱动的 Node Plugin 的 NodePublishVolume RPC,将卷从 staging 路径 bind mount 到 Pod 容器内实际使用的路径。
    • Pod 启动并可以访问卷中的数据。

2.4. sidecar containers#

Kubernetes 将一些常见的 API 操作抽象成了一系列 sidecar containers,来简化存储供应商接入 Kubernetes 生态的成本。

Benefits of these sidecar containers include:

  • Reduction of “boilerplate” code.
    • CSI Driver developers do not have to worry about complicated, “Kubernetes specific” code.
  • Separation of concerns.
    • Code that interacts with the Kubernetes API is isolated from (and in a different container than) the code that implements the CSI interface.

使不使用这些 sidecar containers 完全取决于 CSI Driver 的编写者,只要符合 CSI 规范即可。

2.4.1. external-provisoner#

负责监听 PersistentVolumeClaim (PVC) 事件,并调用 CSI 驱动的 ControllerService 来动态创建 PersistentVolume (PV) 和实际的存储卷。

如果 PVC 中指定的 storageClassName 和当前 sidecar 通过 GetPluginInfo 获取的 storageClass 匹配,则由当前 ControllerService 处理。

有些 csi 并没有显式部署 csi-provisoner(如 openEBS local-pv-provisoner),是其在 provisoner 内集成了 csi-provisoner library (https://github.com/kubernetes-sigs/sig-storage-lib-external-provisioner

2.4.2. external-attacher#

负责监听 VolumeAttachment 事件,并调用 CSI 驱动的 ControllerService Controller[Publish|Unpublish]Volume 操作来将存储卷 Attach/Detach 到节点上。

2.4.3. external-resizer#

负责监听 PVC 扩容事件,并调用 CSI 驱动的 ControllerService ControllerExpandVolume 来扩容存储卷。

2.4.4. external-snapshotter#

负责监听 VolumeSnapshotVolumeSnapshotContent 事件,并调用 CSI 驱动的 ControllerService CreateSnapshotDeleteSnapshot, and ListSnapshots来创建/删除卷快照。

2.4.5. csi-node-driver-registrar#

负责向 kubelet 注册 CSI 驱动(使用 kubelet plugin registration mechanism),并提供 CSI 驱动的基本信息 NodeGetInfo(例如驱动名称、节点能力等)。

3. CSI gRPC proto#

3.1. Controller Service#

service Controller {
  rpc CreateVolume (CreateVolumeRequest)
    returns (CreateVolumeResponse) {}

  rpc DeleteVolume (DeleteVolumeRequest)
    returns (DeleteVolumeResponse) {}

  rpc ControllerPublishVolume (ControllerPublishVolumeRequest)
    returns (ControllerPublishVolumeResponse) {}

  rpc ControllerUnpublishVolume (ControllerUnpublishVolumeRequest)
    returns (ControllerUnpublishVolumeResponse) {}

  rpc ValidateVolumeCapabilities (ValidateVolumeCapabilitiesRequest)
    returns (ValidateVolumeCapabilitiesResponse) {}

  rpc ListVolumes (ListVolumesRequest)
    returns (ListVolumesResponse) {}

  rpc GetCapacity (GetCapacityRequest)
    returns (GetCapacityResponse) {}

  rpc ControllerGetCapabilities (ControllerGetCapabilitiesRequest)
    returns (ControllerGetCapabilitiesResponse) {}

  rpc CreateSnapshot (CreateSnapshotRequest)
    returns (CreateSnapshotResponse) {}

  rpc DeleteSnapshot (DeleteSnapshotRequest)
    returns (DeleteSnapshotResponse) {}

  rpc ListSnapshots (ListSnapshotsRequest)
    returns (ListSnapshotsResponse) {}

  rpc GetSnapshot (GetSnapshotRequest)
    returns (GetSnapshotResponse) {
        option (alpha_method) = true;
    }

  rpc ControllerExpandVolume (ControllerExpandVolumeRequest)
    returns (ControllerExpandVolumeResponse) {}

  rpc ControllerGetVolume (ControllerGetVolumeRequest)
    returns (ControllerGetVolumeResponse) {
        option (alpha_method) = true;
    }

  rpc ControllerModifyVolume (ControllerModifyVolumeRequest)
    returns (ControllerModifyVolumeResponse) {
        option (alpha_method) = true;
    }
}

3.2. Node Service#

service Node {
  rpc NodeStageVolume (NodeStageVolumeRequest)
    returns (NodeStageVolumeResponse) {}

  rpc NodeUnstageVolume (NodeUnstageVolumeRequest)
    returns (NodeUnstageVolumeResponse) {}

  rpc NodePublishVolume (NodePublishVolumeRequest)
    returns (NodePublishVolumeResponse) {}

  rpc NodeUnpublishVolume (NodeUnpublishVolumeRequest)
    returns (NodeUnpublishVolumeResponse) {}

  rpc NodeGetVolumeStats (NodeGetVolumeStatsRequest)
    returns (NodeGetVolumeStatsResponse) {}


  rpc NodeExpandVolume(NodeExpandVolumeRequest)
    returns (NodeExpandVolumeResponse) {}


  rpc NodeGetCapabilities (NodeGetCapabilitiesRequest)
    returns (NodeGetCapabilitiesResponse) {}

  rpc NodeGetInfo (NodeGetInfoRequest)
    returns (NodeGetInfoResponse) {}
}

3.3. Identity Service#

service Identity {
  rpc GetPluginInfo(GetPluginInfoRequest)
    returns (GetPluginInfoResponse) {}

  rpc GetPluginCapabilities(GetPluginCapabilitiesRequest)
    returns (GetPluginCapabilitiesResponse) {}

  rpc Probe (ProbeRequest)
    returns (ProbeResponse) {}
}

4. Reference#

5. kubelet#

Jun 23 22:46:03 a0cho6pgqfrifsg kubelet[4177872]: I0623 22:46:03.438203 4177872 csi_plugin.go:99] kubernetes.io/csi: Trying to validate a new CSI Driver with name: ch.ctrox.csi.s3-driver endpoint: /data01/kubelet/plugins/csi-s3/csi.sock versions: 1.0.0
Jun 23 22:46:03 a0cho6pgqfrifsg kubelet[4177872]: I0623 22:46:03.438244 4177872 csi_plugin.go:112] kubernetes.io/csi: Register new plugin with name: ch.ctrox.csi.s3-driver at endpoint: /data01/kubelet/plugins/csi-s3/csi.sock

registrar:

kubectl logs -ndlf csi-s3-22qmt
Defaulted container "driver-registrar" out of: driver-registrar, csi-s3
I0623 14:46:02.410875       1 main.go:150] "Version" version="v1.12.0"
I0623 14:46:02.410967       1 main.go:151] "Running node-driver-registrar" mode=""
I0623 14:46:02.410975       1 main.go:172] "Attempting to open a gRPC connection" csiAddress="/csi/csi.sock"
I0623 14:46:02.410993       1 connection.go:234] "Connecting" address="unix:///csi/csi.sock"
I0623 14:46:03.413314       1 main.go:180] "Calling CSI driver to discover driver name"
I0623 14:46:03.413344       1 connection.go:264] "GRPC call" method="/csi.v1.Identity/GetPluginInfo" request="{}"
I0623 14:46:03.421149       1 connection.go:270] "GRPC response" response="{\"name\":\"ch.ctrox.csi.s3-driver\",\"vendor_version\":\"v1.1.1\"}" err=null
I0623 14:46:03.421173       1 main.go:189] "CSI driver name" csiDriverName="ch.ctrox.csi.s3-driver"
I0623 14:46:03.421209       1 node_register.go:56] "Starting Registration Server" socketPath="/registration/ch.ctrox.csi.s3-driver-reg.sock"
I0623 14:46:03.421430       1 node_register.go:66] "Registration Server started" socketPath="/registration/ch.ctrox.csi.s3-driver-reg.sock"
I0623 14:46:03.421501       1 node_register.go:96] "Skipping HTTP server"
I0623 14:46:03.437833       1 main.go:96] "Received GetInfo call" request="&InfoRequest{}"
I0623 14:46:03.489487       1 main.go:108] "Received NotifyRegistrationStatus call" status="&RegistrationStatus{PluginRegistered:true,Error:,}"