引言

在Pod对象中,如果一个容器需要使用到存储的话,可以通过以下这种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
name: nginx-test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: vol
mountPath: /app
volumes:
- name: vol
nfs:
path: /app/data/nfsdata/DATA_00
server: 10.1.1.15

在 pod.spec.volumes 仍然定义了其他很多种类型的存储可以使用: awsElasticBlockStore cephfs gcePersistentDisk glusterfs 等等。

得益于K8S的这种设计,在Pod中可以直接定义并使用多种存储,但这样使用存储带来了另外的问题:

  1. 使用k8s的开发者需要对存储要有所了解,才能够正确使用,这增加了使用的复杂度
  2. 对于使用到的存储,其地址、授权信息都暴露给用户,增加了信息暴露风险

通常来讲,对于开发者,他们并不需要关注底层的存储连接信息、如何配置、甚至不必关注存储地址,只需要告诉系统要使用多大存储以及读写权限即可。

基于这种情况,K8S 引入了另外一种存储对象:PV(persistent volume) PVC(persistent volume claim),用于降低用户使用存储的门槛。

PV / PVC

PV: Persistent Volume,持久化存储卷。详细的定义了存储卷的类型、大小、连接挂载方式等。

PVC: Persistent Volume Claim,持久化存储卷声明。定义了存储卷大小、读写权限等,没有存储卷的细节信息。

开发者需要使用到存储时,只需要: 1. 定义pvc; 2. 在podvolume配置中引用pvc。不需要关注存储本身。存储则由运维人员添加。

在这种设计中,将存储的定义与实现解耦,开发者关注定义pvc,运维人员关注实现(pv),实现了职责分离,也避免向外部暴露更多存储信息。

静态提供存储(static provisioning)

由运维人员创建出pv。开发者定义pvc,然后由controller-manager来找到最合适的pv然后绑定它,然后给用户使用(也可以在pvc的定义中,通过pvc.spec.volumeName来指定绑定某一个特定的pv。)。

动态提供存储(dynamic provisioning)

在静态提供存储中,需要人工创建大量的pv给用户使用,但在实际应用场景中,是个比较麻烦的工作。因此,k8s中设计了动态提供存储(dynamic provisioning)的功能(CSI)。

CSI设计

csi架构设计

如图,是csi架构设计。

csi的架构协作者主要包括四部分:

  1. k8s:使用k8s的主体,参与csi协作的组件主要有:
    1. apiserver
    2. scheduler(主要针对立即绑定的storageClass类型,在pod调度前要先创建出PV,scheduler作条件判断)
    3. kubelet(pod启动过程中,需要挂载存储,调用csiNode
  2. external components:由k8s社区维护,打通k8s与用户开发组件的中间层,主要包括以下组件:
    1. driverRegistar: 负责注册用户csi组件
    2. externalProvisionor: 负责监听apiserverpvc,然后调用用户组件创建存储以及pv等
    3. externalAttacher: 负责attach/unattach操作,对应csi中的publishing/unpublishing(attach区别于主机attach存储的操作,个人理解这里相当于把存储attach到云上,并非attach到具体node上)
  3. custom components:用户自定义开发的组件,用于管理存储,主要3个组件:
    1. csiIdentify: csi标识
    2. csiCcontroller: 存储控制器(publish/unpublishcreate/delete volsnapshot等操作)
    3. csiNode: 节点插件,daemonset方式运行,每个节点都要有,由kubelet调用(node stage/unstage:挂载存储到主机、node publish/unpublish:mount存储到pod目录 等操作)
  4. external persistent storage:外部存储供应商,提供存储服务。主要由用户自开发组件中的controller中的调用。

storageClass

CSI的设计中,storageClass是核心的API 资源,它定义了管理存储者(provisioner)的名字以及创建存储时额外的参数、挂载opt、回收策略、pv绑定策略等。

立即绑定

情形1: 未设置storageClass

此时,controllerManager会自动寻找最合适的pv,并将其与pvc绑定


情形2:设置storageClass,但sotrageClassVOLUMEBINDINGMODEImmediate
此时,controllerManager会自动寻找最合适的pv,并将其与pvc绑定。不同于情形1的是,此处的pv可能是由管理员添加,或者由provisioner添加(provisioner会立即调用相关方法生成pv)。(此处管理员添加的pvstorageClass要同pvcstorageClass一致才可以绑定)

延迟绑定

未设置storageClass且未配置默认的storageClass时,不存在延迟绑定


情形:设置storageClass,且其VOLUMEBINDINGMODE值为WaitForFirstConsumer
此时,在控制器控制循环内不会绑定pv,在该pvc被使用时,才会绑定。过程:

  1. waitForConsumer
  2. 创建使用pvcpod
  3. pod调度器在pvc上打上annotation(volume.kubernetes.io/selected-node)
  4. controller-managerpvc打上annotation(volume.beta.kubernetes.io/storage-provisioner)
  5. external-provisioner调用csi方法创建pv(创建出来的pv已经带上pvc了)
  6. controller-manager执行绑定操作

storageClass