介绍
在《CSI插件注册》中提到容器存储接口(CSI)为外部存储接入云原生Kubernetes提供了标准化的注册方案,而作为CSI中的重要成员csi-provisioner,在其中发挥着重要的作用。在CSI Plugin注册完成之后,csi-driver作为服务端对外提供标准的存储服务,其中包括卷创建、删除,容量获取,插件能力获取等。csi-provisioner作为客户端,通过GRPC方式与CSI插件进行交互,最终与远端存储服务器完成对接。
本文主要讲解csi-provisioner的基本原理,以及在整个CSI环节中发挥的作用。不管是刚接触Kubernetes存储的初学者,还是想要着手进行CSI插件开发的同学,通过本文对csi-provisioner的介绍都能有所收获。
原理
全篇基于csi-provisioner的v3.0版本,主要由三个控制器(Controller)组成,分别是ProvisionController、CapacityController和CloningProtectionController,它们监听PersistentVolumeClaim(PVC)、PersistentVolume(PV)、StorgaeClass、CSINode等资源,及时处理资源变更。
- ProvisionController是核心控制器,主要职责是为PVC创建或删除PV并与CSI插件交互创建并删除存储资源。
- CapacityController的职责是对存储的容量进行管理,并创建CSIStorageCapacity资源供Kubernetes Pod调度使用。
- CloningProtectionController负责克隆存储卷场景下对PVC进行保护,及时添加或删除CloneFinalizer标记。
作用
ProvisionController控制器
动态供应
Kubernetes中PV的供应有2种方式,动态供应和静态供应。
- 静态供应指用户需要手动在存储服务器上创建存储卷,然后需要创建一个PV资源显示存储卷相关的信息,最后再创建一个PVC与PV进行绑定后,才能供pod进行使用。
- 动态供应,即用户无需关心PV资源创建以及存储服务器上的存储卷创建,ProvisionController根据StorageClass资源中parameters信息以及PVC的请求容量,自动创建PV资源。 通过动态供应的方式,能大大降低用户创建PVC的效率。实质上ProvisionController在进行自动供应时,必须完成CreateVolume和DeleteVolume的2个接口调用,一方面ProvisionController需要维护PVC和PV的资源状态,另一方面还需要与csi-plugin进行存储资源的新建与删除。
存储拓扑管理
ProvisionController存储拓扑管理,本质是对CSINode资源进行整合,用于创建存储卷的节点调度。该功能需要Kubernetes 1.17版本以上支持,并且需要在csi-provisioner上开启特性--feature-gates=Topology=True,同时需要插件支持PluginCapability_Service_VOLUME_ACCESSIBILITY_CONSTRAINTS能力。在《CSI插件注册》中有讲到CSI插件最终会生成CSINode资源,并在node资源上添加annotation——csi.volume.kubernetes.io/nodeid。
此时,ProvisionController将在调用CreateVolume接口时,将根据CSINode和Node资源进行整合,生成可调度的节点拓扑,设置在AccessibilityRequirements参数中。如果PVC上存在annotation——volume.kubernetes.io/selected-node,并且该值在节点拓扑中存在,则节点拓扑中只存在一个可被调度的节点。如果selected-node值不存在,则pod在进行调度时,将调度失败。最终,插件会将调度的节点信息nodeAffinity生成在PV中,如下所示:
apiVersion: v1
kind: PersistentVolume
metadata:
finalizers:
- kubernetes.io/pv-protection
name: pv-test
spec:
accessModes:
- ReadWriteOnce
、、、、、、
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- <nodeName>
In-tree插件与Out-tree插件转义
目前,In-tree插件中driverName为以下名称,才允许被转义。In-tree插件被转义前必须满足2个条件,一是需要Kubernetes 1.17版本以上,二是在创建PVC时添加annotation——pv.kubernetes.io/migrated-to: <driverName>。
// In-tree driverName
// gce_pd
pd.csi.storage.gke.io
// aws_ebs
ebs.csi.aws.com
// openstack_cinder
cinder.csi.openstack.org
// azure_disk
disk.csi.azure.com
// azure_file
file.csi.azure.com
// vsphere_volume
csi.vsphere.vmware.com
ProvisionController转义In-tree插件的实质,是将早期的内置插件逻辑转移至外部插件。主要表现在,一是将StorageClass资源中的parameters参数转义成能被外部插件认可的参数,能完成外部插件中的CreateVolume和DeleteVolume接口调用;其二,将PV资源中csi参数转义成内部插件的参数,防止被内部插件修改;最后,在删除存储卷时,将PV中内部插件的参数转义成csi参数,以便外部插件完成DeleteVolume操作。
以vSphere_volume存储为例:
StorageClass: parameters
| In-tree | Out-tree |
|---|---|
| fstype | csi.storage.k8s.io/fstype |
| storagepolicyname | storagepolicyname |
| datastore | datastore-migrationparam |
| diskformat | datastore-migrationparam |
| hostfailurestotolerate | hostfailurestotolerate-migrationparam |
| forceprovisioning | forceprovisioning-migrationparam |
| cachereservation | cachereservation-migrationparam |
| diskstripes | diskstripes-migrationparam |
| objectspacereservation | objectspacereservation-migrationparam |
| iopslimit | iopslimit-migrationparam |
| csimigration |
PersistentVolume: csi => vsphereVolume
| csi | vsphereVolume |
|---|---|
| fstype | fstype |
| initialvolumefilepath | volumePath |
PersistentVolume: vsphereVolume => csi
| vsphereVolume | csi |
|---|---|
| driver | |
| fstype | fstype |
| volumePath | volumeHandle |
| storagePolicyName | volumeAttributes.storagePolicyName |
CapacityController控制器
容量管理
CapacityController是在Kubernetes 1.19版本引入的控制器,主要通过CSIStorageCapacity管理存储总容量,
- 控制器完成PV创建操作后,CapacityController更新CSIStorageCapacity消耗容量;
- 控制器完成PV删除操作后,CapacityController更新CSIStorageCapacity释放空闲容量。
开启容量管理,需要在csi-provisioner启动时,添加命令参数--enable-capacity=true,注入环境变量NAMESPACE和POD_NAME(csi-provisioner的pod名称和分区)。容量不足时,用户可以先通过CSIStorageCapacity资源查看剩余容量,避免创建PVC后出现Pending的情况。
CloningProtectionController控制器
克隆finalizer管理
ProvisionController在克隆存储卷时,会对dataSource指向的PVC设置克隆保护。
finalizers:
- provisioner.storage.kubernetes.io/cloning-protection
之后,CloningProtectionController会监听克隆保护的PVC,发现目标PVC已完成克隆操作,则会删除克隆finalizer成员。这样可以避免PVC在进行克隆卷创建时,出现源PVC被删除的情况。克隆卷PVC定义如下,前提是csi-plugin需要支持克隆能力(ControllerServiceCapability_RPC_CLONE_VOLUME)。
spec:
dataSource:
kind: PersistentVolumeClaim
name: <source PVC>
总结
csi-provisioner实质是多个控制器的集成,通过与csi-plugin交互,管理Kubernetes中存储相关的资源。所以,csi-provisioner离不开csi-plugin,特别是在进行csi-plugin探究过程中,了解清楚csi-provisioner的作用,有利于csi-plugin顺利开发。