k8s 存储:带 PVC 的 Pod 提交后时序事件
前言
关于 k8s 存储,互联网上的资料应该说不少,但我始终没看到一个详细描述:一个带 PVC 的 Pod 提交后,按照时间顺序,各个组件都发生了什么?
所以,我将文档资料,结合从代码中看到的,整理成本文。
由于本文关注点在于存储,所以 k8s 的其他组件被视为一个整体黑箱。例如,k8s 调度 Pod 的过程被略去,而只说类似于“k8s 按照某种方式调度 Pod”的叙述。
当一个 PVC 提交后,Immediate 模式
先看一个最简单的情形,客户端只提交一个 PVC(没有 Pod 使用它),且该 PVC 使用的 StorageClass 的 volumeBindingMode 字段为 Immediate。
1.创建 PVC,kube-apiserver 将其落库,此时 PVC 的 status.phase 字段为 Pending。
2.external-provisioner 得知 PVC 创建,它立即调用 CSI driver 的 Controller.CreateVolume 函数。CSI driver 在该函数中进行真正的存储置备(provisioning)动作。
例如,云盘则创建一块盘,NAS 则啥也不干,Ceph RBD 则在池中创建 rbd-image。
3.external-provisioner 根据上一步的函数返回值创建 PV。
外头置备的存储 k8s 是不知道的,创建 PV 让 k8s 知道。
4.k8s 发现存在未绑定的 PVC 以及为它动态置备的 PV,于是绑定它们,此时 PVC 和 PV 的 status.phase 字段均为 Bound。
这里的绑定,就是把 PVC 的 spec.volumeName 设为 PV 的 name,并且把 PV 的 claimRef.name 字段和 claimRef.namespace 字段分别设为 PVC 的对应字段。
至此,全流程结束。因为没有 Pod 使用该 PVC,所以不会发生挂载。
当一个 PVC 提交后,WaitForFirstConsumer 模式
现在看稍微复杂一点的,客户端只提交一个 PVC(没有 Pod 使用它),且该 PVC 使用的 StorageClass 的 volumeBindingMode 字段为 WaitForFirstConsumer。
1.创建 PVC,kube-apiserver 将其落库,此时 PVC 的 status.phase 字段为 Pending。
2.external-provisioner 得知 PVC 创建,它等待 PVC 上的 volume.kubernetes.io/selected-node 注解。
至此,全流程结束。因为没有 Pod 使用该 PVC,所以这个注解不会出现。等以后 kube-scheduler 给把某个使用该 PVC 的 Pod 调度到某个节点上,就会给该 PVC 的 volume.kubernetes.io/selected-node 注解写入值,external-provisioner 才会有后续动作。
当一个带 PVC 的 Pod 提交后,Immediate 模式
光板 PVC 提交后发生了什么我们已经清楚,现在看更复杂的例子,即有 Pod 使用 PVC。
1.如前所述,创建 PVC,而后置备存储、创建 PV、k8s 绑定 PVC 和 PV。
1'.与此同时,kube-scheduler 为 Pod 选择一个节点,并查看所用的 PVC 的状态。在大部分情况下,PVC 已经好了。如果 PVC 状态不是 Bound 则按照某种策略等待,等 PVC 好了之后顺着 PVC 的字段找到 PV,并进行必要的检查(节点亲和性之类的)。至此,节点选择完毕。
2.k8s 创建 VolumeAttachment,并将其 spec.nodeName 设为目标节点。
注意!此步骤未必存在,有些 CSI driver 会配置 CSIDriver.spec.attachRequired=false,若如此,此步骤和所有和 attach 相关的步骤就都没有了。
3.external-attacher 得知 VolumeAttachment 创建,调用 CSI driver 的 Controller.ControllerPublishVolume 函数,CSI driver 在该函数的实现中执行具体存储的 attach。该函数成功返回后,external-attacher 把 VolumeAttachment 的 status.attached 设为 true。
例如,Azure Disk 情形,external-attacher 调用 Azure API,把盘添加到目标节点(一个 Azure VM)的 dataDisks 列表中,也就是分配一个 LUN。这涉及到 Azure 云的概念,可以查阅其他资料。反正,作用是把盘插到目标节点。
例如,Ceph RBD 没有这个东西。
4.目标节点上,kubelet 该拿到的信息已经全部拿到,包括 PVC、PV、VolumeAttachment(如果有),kubelet 调用 CSI driver 的 Node.NodeStageVolume 函数。CSI driver 在函数实现中做一些准备工作(见例子)。
例如,Azure Disk,找到盘 /dev/disk/azure/scsi1/lunX,或许会格式化(有些 Pod 要求挂载块设备,此时不格式化),将盘挂载到本机的某个挂载点(目录)。
例如,Ceph RBD,执行 rbd map 找到设备,或许格式化,将盘挂载。
5.目标节点上,kubelet 调用 CSI driver 的 Node.NodePublishVolume 函数。CSI driver 在函数实现中把上一步中准备好的路径 bind-mount 到 Pod 所需要的路径。
这一步可能被执行多次,因为多个 Pod 可以挂载同一个卷,一个 Pod 也可以多次挂载一个卷。当然,前提是具体存储支持这么做。
另外,上一步可能不存在。有些存储不需要,有些存储不支持。后一种情况,所有的工作必须由 Node.NodePublishVolume 承担。
6.kubelet 启动容器。
7.如果容器都启动成功,更新 Pod 状态为 Running。
至此,全流程结束,Pod 运行起来了。
当一个带 PVC 的 Pod 提交后,WaitForFirstConsumer 模式
1.kube-scheduler 为 Pod 选择一个节点,并把 Pod 的 spec.nodeName 字段设为所选节点的名字,而后把 Pod 所使用的 PVC 的 volume.kubernetes.io/selected-node 注解的值设为所选节点的名字。
1'.与此同时,external-provisioner 得知 PVC 创建,但它知道自己处于 WaitForFirstConsumer 模式,所以等待 PVC 的 volume.kubernetes.io/selected-node 注解。
2.external-provisioner 等到了上一步提到的那个注解。进而如前所述,置备存储、创建 PV、k8s 绑定 PVC 和 PV。
2'.与此同时,kube-scheduler 等待 Pod 使用的 PVC 的状态,等到状态为 Bound,则确定把 Pod 调度到这个节点。
3.开始执行 Immediate 模式的第 2 步,之后与 Immediate 模式相同。

浙公网安备 33010602011771号