Kubernetes环境偶尔出现StatefulSet中的Pod被删除,新启动的Pod(还是调度到原有节点)挂载volume失败的问题,如下图,经过一番定位分析,也让我们对于Kubernetes系统复杂程度有了新的认知。
在分析此问题之前,作为相关背景知识,先简单介绍对于Kubernetes存储系统的理解。
| 存储系统简析
存储也是Kubernetes中比较重要而复杂的系统,功能比较庞大,涉及到不同组件中,不同控制器的协作,如下图。
从三个维度分析:
1、从卷的生命周期来讲,卷被Pod使用或者卷被回收,会依赖顺序严格的几个阶段
- 卷被Pod使用:
-
provision,卷分配成功
-
attach,卷挂载在对应worker node
-
mount,卷挂载为文件系统并且映射给对应Pod
卷要成功被Pod使用,需要遵循以上顺序。
- 卷被回收:
-
umount,卷已经和对应worker node解除映射,且已经从文件系统umount
-
detach,卷已经从worker node卸载
-
recycle,卷被回收
卷要成功回收,需要遵循以上顺序。
2、从Kubernetes存储系统来讲,卷生命周期管理的职责,又分散于不同的控制器中
-
pv controller,负责创建和回收卷
-
attach detach controller,负责挂载和卸载卷
-
volume manager,负责mount和umount卷
3、controller模式,每个controller负责特定功能,并且不断进行reconcile(对比期望状态与实际状态),然后进行调整
比如attach detach controller和volume manager中,各自都会有desiredStateOfWorld(缓存期望状态)和actualStateOfWorld(缓存实际状态)缓存,并且由各自的syncLoopFunc不断对比两个缓存的差异,并进行调整,下文中会介绍。
for {
desired := getDesiredState();
current := getCurrentState();
makeChanges(desired, current);
}
结合以上三个维度,Kubernetes需要保证卷的管理功能分布在不同控制器的前提下保证卷生命周期顺序的正确性。以Pod使用卷为例,看Kubernetes是如何做到这一点?
| Pod启动流程
假设scheduler已经完成worker node选择,确定调度的节点,此时启动Pod前,需要先完成卷映射到Pod路径中,结合前面的分析,整个过程如下:
1、卷分配,pvc绑定pv,由pv controller负责完成,结合相关代码1[1]。
此时pvc绑定pv。
2、attach detach controlle,如果pod分配到worker node,并且对应卷已经创建,则将卷挂载到对应worker node,并且给worker node资源增加volume已经挂载对应卷的状态信息,结合相关代码2[2]和代码3[3]。
此时对应node资源状态中增加volume信息。
[root@10-10-88-152 ~]# kubectl get nodes 10-10-88-113 -o yaml
apiVersion: v1
kind: Node
....
volumesAttached:
- devicePath: csi-add9fc778d9593d01818d65ccde7013e87327d9f675b47df42a34b860c581711
name: kubernetes.io/csi/csi-qcfsplugin^csi-qcfs-volume-4faa18f5bbbd11e8-1365
- devicePath: csi-5dd249387138238e8e2209eb471450a072dd6543adde7a6769c8461943c789ca
name: kubernetes.io/csi/csi-qcfsplugin^csi-qcfs-volume-4fa9b764bbbd11e8-1366
- dev