bigywater.tech
Kubernetes · 58

StorageClass 动态供给:从 PVC 到 PV

PVC 是怎么变出一个 PV 的?Immediate 和 WaitForFirstConsumer 差在哪?不写 storageClassName 会怎样?这篇用 kind 自带的 local-path provisioner 把一次动态供给的每个环节(事件、注解、PV 的字段)都拆开看。

2.2 秒
创建使用 WaitForFirstConsumer 类 PVC 的 Pod 之后,PVC 变成 Bound 的时间
no node was specified
local-path 在 Immediate 模式下供给失败的原因
2 个默认类
同时标了两个默认 StorageClass 之后,不写类的 PVC 拿到的是最新创建的那个

什么时候造卷

实测 · 本机执行输出
  PVC p-imm(没有 Pod 使用):phase=Pending;volumeName=(空)
  PVC p-wait(没有 Pod 使用):phase=Pending;volumeName=(空)
    p-imm 的事件:['Provisioning: External provisioner is provisioning volume for claim "k52/p-imm"', 'ProvisioningFailed: failed to provision volume with StorageClass "k52-imm": configuration error, no node was specified']
  创建使用 p-wait 的 Pod 后 2.2 秒 PVC Bound:PV pvc-4133762b-fc71-40…;PV 的 nodeAffinity=['kx-sched-worker3'];Pod 调度到 kx-sched-worker3(PV 在 Pod 被调度的那个节点上造出来)
  PVC 上的注解:{'volume.beta.kubernetes.io/storage-provisioner': 'rancher.io/local-path', 'volume.kubernetes.io/selected-node': 'kx-sched-worker3', 'volume.kubernetes.io/storage-provisioner': 'rancher.io/local-path'}

两个 PVC,一个用 Immediate 类、一个用 WaitForFirstConsumer 类,没有 Pod 使用时都 Pending。区别是 Immediate 的 PVC 一创建就让 provisioner 去造,而 local-path 造本地卷必须知道节点,于是失败:ProvisioningFailed … configuration error, no node was specified。WaitForFirstConsumer 的 PVC 等 Pod 被调度后:调度器在 PVC 上写下 volume.kubernetes.io/selected-node 注解,provisioner 在那个节点上造卷,2.2 秒后 Bound,PV 的 nodeAffinity 就是那个节点。

一次供给的事件

实测 · 本机执行输出
    事件 PersistentVolumeClaim/p2 WaitForFirstConsumer: waiting for first consumer to be created before binding
    事件 PersistentVolumeClaim/p2 ExternalProvisioning: Waiting for a volume to be created either by the external provisioner 'rancher.io/local-path' or manually by the system administra
    事件 PersistentVolumeClaim/p2 Provisioning: External provisioner is provisioning volume for claim "k52/p2"
    事件 PersistentVolumeClaim/p2 ProvisioningSucceeded: Successfully provisioned volume pvc-28888008-d3bd-4762-a1ca-9e1ce7050f12
  PV 的 spec:hostPath/local 路径=/var/local-path-provisioner/pvc-28888008-d3bd-4762-a1ca-9e1ce7050f12_k52_p2;storageClassName=k52-wait;reclaim=Delete;annotations={'local.path.provisioner/selected-node': 'kx-sched-worker2', 'pv.kubernetes.io/provisioned-by': 'rancher.io/local-path'}
  从创建 Pod 到 PVC Bound 用了 3.2 秒

PVC 的事件依次是 WaitForFirstConsumer → ExternalProvisioning(等外部 provisioner rancher.io/local-path 来造)→ Provisioning → ProvisioningSucceeded: Successfully provisioned volume pvc-…,共 3.2 秒。PV 是 provisioner 造出来并回填字段的:路径 /var/local-path-provisioner/pvc-…_k52_p2,storageClassName、reclaim 来自存储类,注解 pv.kubernetes.io/provisioned-by 记着谁造的。供给失败时第一件事是看 PVC 的事件。

默认存储类

实测 · 本机执行输出
  现在的默认存储类:['standard']
  不写 storageClassName 的 PVC nocls:spec.storageClassName=standard(准入补成了默认类)
  storageClassName: "" 的 PVC emptycls:spec.storageClassName='';phase=Pending(空字符串表示“不要动态供给,只绑静态 PV”,没有静态 PV 就一直 Pending)
  再标一个默认类 k52-default 之后,集群里有 2 个默认存储类:['k52-default', 'standard']
  不写 storageClassName 的 PVC two-defaults:spec.storageClassName=k52-default(多个默认类时选“最新创建的”)

不写 storageClassName 的 PVC 在准入时被补成默认类(standard);写成空字符串 "" 则明确表示“不要动态供给,只绑静态 PV”,没有静态 PV 就一直 Pending。再给 k52-default 也标上默认注解,集群里就有两个默认类,不写类的 PVC 拿到最新创建的那个——含糊,生产里应该只留一个。

回收策略继承

实测 · 本机执行输出
  存储类 k52-keep(reclaimPolicy: Retain)供给的 PV pvc-d05f5525-9851-44…:persistentVolumeReclaimPolicy=Retain
  删掉 PVC 之后:PV 仍在,phase=Released;节点 worker4 上目录里的文件:hello(数据还在)

reclaimPolicy 写在存储类上,供给出来的 PV 直接继承:k52-keep(Retain)供给的 PV 也是 Retain。删掉 PVC 之后 PV 变成 Released,节点上目录里的文件还是 hello,数据还在,要人工处理。standard 默认是 Delete,所以删 PVC 数据就没了。

分步演示

动态供给
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。独立的 5 节点 kind 集群 kx-sched(Kubernetes v1.37.0),存储是 kind 自带的 local-path-provisioner;自建的 StorageClass k52-imm / k52-wait / k52-keep / k52-default 与 PV、目录在实验后都已删除(Retain 的 PV 与节点上的目录手动清理);“Bound 用时”是 0.2~0.3 秒间隔轮询;PV 的 nodeAffinity 与 Pod 的 nodeName 取自对象;每种情形各一次
    • 没有验证:allowVolumeExpansion 与在线扩容(下一篇);mountOptions、parameters 的传递;volumeBindingMode 与 allowedTopologies;CSI 驱动与外部 provisioner 的 leader 选举;存储类的不可变字段(provisioner、parameters 修改会被拒绝);快照类(VolumeSnapshotClass);存储容量跟踪(CSIStorageCapacity)
    京ICP备2026067502号-1京公网安备11010502063487号