TT Lab
开始
学习 学习路径 课程

KCNA — Kubernetes 与云原生入门

明明有匹配的磁盘,PVC 却绑定不上

在 TT Lab 中继续学习

目标

用 no-provisioner StorageClass 和手工创建的 PV,亲自观察 PVC 何时绑定(WaitForFirstConsumer)、 什么样的要求最终无法绑定(accessMode 不匹配),以及删除 PVC 之后 PV 会怎样留下来(Retain → Released)。

为什么重要

Pod 会消失,数据必须保留。Kubernetes 为此把“磁盘本身(PV)”和“索要磁盘的请求(PVC)”分开, 并把两者配对的工作交给控制器。所以,PVC 处于 Pending 这一个症状的背后, 可能隐藏着“还没有使用它的 Pod,所以故意在等”和“根本没有符合条件的卷”这两种完全不同的原因。

reclaim 策略也是生产环境中经常出事的地方。策略为 Delete 时,只删掉一个 PVC 数据就会消失; 策略为 Retain 时数据得以保住,但 PV 会以 Released 状态留下,不会自动被重用。 如果不了解两者的区别就删除 PVC,就会发生无法恢复的事情。

步骤

  1. 创建命名空间 kcna-storage。
  2. 创建 StorageClass local-fast(no-provisioner、WaitForFirstConsumer、Retain)。
  3. 创建 PV pv-fast-1(1Gi、ReadWriteOnce、Retain、hostPath)。
  4. 创建 PVC data,并确认它因为没有使用者而停留在 Pending。
  5. 确认 Pod writer 挂载 data 后,PVC 绑定到 pv-fast-1。
  6. 确认要求 ReadWriteMany 的 PVC wide 和 Pod wider 最终无法绑定。
  7. 删除 writer 和 data,确认 pv-fast-1 以 Released 状态留下。
  8. 把观察结果以账簿形式记录到 /root/kcna-storage/report.txt。

参考

打开分发磁盘的工作区

创建命名空间 kcna-storage。

PVC 和 Pod 属于命名空间,而 PV 和 StorageClass 属于集群范围。请先准备好 PVC 所在的命名空间。用 --dry-run=client -o yaml 生成 create 的结果再 apply,重复执行也是安全的。

等到使用者出现才动手的存储等级

创建 StorageClass local-fast。provisioner 为 kubernetes.io/no-provisioner,volumeBindingMode 为 WaitForFirstConsumer,reclaimPolicy 为 Retain。

no-provisioner 表示不会自动创建卷,所以 PV 由管理员手工准备。volumeBindingMode 的默认值 Immediate 会在 PVC 一创建就为其配对 PV,而 WaitForFirstConsumer 会把配对推迟到使用该 PVC 的 Pod 被调度之后。StorageClass 没有 kubectl create 子命令,所以请写成 YAML(apiVersion storage.k8s.io/v1)再 apply。

管理员手工铺好的 1Gi 磁盘

创建 PersistentVolume pv-fast-1。容量 1Gi,accessModes [ReadWriteOnce],persistentVolumeReclaimPolicy Retain,storageClassName local-fast,hostPath 路径 /tmp/pv-fast-1。

PV 是集群范围的对象,所以 metadata 中没有 namespace。要与 PVC 配对,storageClassName、accessModes 和容量必须满足请求。StorageClass 的 reclaimPolicy 只适用于动态供应的 PV,所以手工创建的 PV 需要在 spec 中直接写上 reclaim 策略。创建后请立即查看 STATUS。

明明有合适的磁盘,PVC 却是 Pending

在命名空间 kcna-storage 中创建 PVC data。accessModes [ReadWriteOnce],storageClassName local-fast,请求容量 1Gi。此时还没有使用这个 PVC 的 Pod,所以它停留在 Pending。请把那一刻记录到 /root/kcna-storage/pending.json——包含 PVC 的 uid、phase、volumeName 三个键。即使在后面的步骤中 PVC 被绑定又被删除,这一步也以这份记录和集群的事件来判定。

即使有满足条件的 PV,在 WaitForFirstConsumer 等级下,使用者(Pod)出现之前也不会发生绑定。这不是故障,而是设计——是为了在确定 Pod 会去哪个节点之后,再选出该节点上能使用的卷。请在 kubectl describe pvc data 的 Events 中读出原因,再从 kubectl get pvc data -o json 中用 jq 选出所需的三个值并保存为文件。如果手工编造值,会因与事件中的 UID 不一致而失败。

Pod 出现后 PVC 被绑定

在命名空间 kcna-storage 中创建 Pod writer(镜像 nginx:1.27-alpine),把 PVC data 挂载到 /data。当 PVC data 变为 Bound 且配对的卷是 pv-fast-1 时,把那一刻的 PVC uid、phase、volumeName 记录到 /root/kcna-storage/bound.json。

在 Pod spec 的 volumes 中声明 persistentVolumeClaim(claimName)卷,并在容器的 volumeMounts 中把该卷名称连接到 mountPath。调度器把 Pod 放到节点上的那一刻,被推迟的绑定才会进行。不要用固定的 sleep,而是每隔几秒重新读取 PVC 的 status.phase,等待它变为 Bound。

要求多方共用的请求没人接受

在命名空间 kcna-storage 中创建 PVC wide。accessModes [ReadWriteMany],storageClassName local-fast,请求容量 1Gi。再让 Pod wider(镜像 nginx:1.27-alpine)挂载这个 PVC。没有提供 ReadWriteMany 的 PV,所以 PVC wide 必须不会变为 Bound。

PVC 的 accessModes 是“请给我支持这种方式的卷”的要求。集群里的 PV 只有一个 ReadWriteOnce,而且它已经绑定给了另一个 PVC。即使有使用者 Pod,只要没有满足条件的 PV,PVC 就是 Pending,Pod 也无法被调度。请按第 5 步的样子创建 PVC 和 Pod,只是 accessModes 不同。

删了 PVC,磁盘却留了下来

先删除 Pod writer,再删除 PVC data。reclaim 策略为 Retain 的 PV pv-fast-1 不会被删除,必须以 Released 状态留下。

正在使用的 PVC 因为有保护 finalizer,在 Pod 消失之前删不掉,所以请先删除 Pod。PVC 消失后,PV 遵循 reclaim 策略——Delete 会连 PV 一起删除,Retain 则为了保护数据以 Released 状态留下,不会再绑定到其他 PVC。请轮询,直到 PV 的 STATUS 发生变化。

把磁盘的去向记成账簿

把观察到的结果恰好写成四行,记录到 /root/kcna-storage/report.txt——sc-binding-mode=WaitForFirstConsumer、pv-reclaim=Retain、pv-phase-after-release=Released、wide-pvc=Pending。值必须与集群的实际状态一致。

评分器会把每一行与实际 StorageClass 的 volumeBindingMode、PV 的 reclaim 策略和 phase、PVC wide 的 phase 逐一比对。请原样抄写用 kubectl get sc,pv 和 kubectl get pvc -n kcna-storage 确认过的值。