K8s-PVC供应与绑定流程
PVC 供应与绑定流程
本文档描述 Kubernetes 中 PVC(PersistentVolumeClaim)、PV(PersistentVolume)和 StorageClass 的完整交互流程。
概述
PVC 供应与绑定涉及以下核心组件:
- PV Controller:管理 PV 和 PVC 的绑定
- StorageClass:定义动态供应参数
- Provisioner:实际创建存储卷(in-tree 或 CSI)
- Scheduler:处理 WaitForFirstConsumer 模式
flowchart TD
subgraph User["用户操作"]
A["创建 PVC"]
end
subgraph APIServer["API Server"]
B["PVC 资源"]
C["PV 资源"]
D["StorageClass"]
end
subgraph PVController["PV Controller"]
E["Watch PVC"]
F["syncClaim"]
G["FindMatchingVolume"]
H["provisionClaim"]
end
subgraph Provisioner["Provisioner"]
I["In-tree Provisioner"]
J["CSI External Provisioner"]
end
subgraph StorageBackend["存储后端"]
K["Cloud Disk"]
L["NFS"]
M["Local Path"]
end
A --> B
B -->|触发| E
E --> F
alt 静态供应
F --> G
G -->|匹配现有 PV| C
else 动态供应
F --> H
H --> I
H --> J
I -->|创建| K
I -->|创建| L
J -->|创建| K
K -->|创建 PV| C
M -->|创建 PV| C
end
C -->|绑定| B
style F fill:#c8e6c9
style H fill:#fff9c4
流程详解
1. PV Controller 架构
代码路径: pkg/controller/volume/persistentvolume/pv_controller.go
1 | type PersistentVolumeController struct { |
2. PVC 绑定状态机
stateDiagram-v2
[*] --> Pending: 创建 PVC
Pending --> Bound: 找到匹配 PV (静态供应)
Pending --> Bound: 动态供应完成
Bound --> Released: 删除 PVC
Released --> Available: ReclaimPolicy=Retain
Released --> [*]: ReclaimPolicy=Delete
Pending --> Lost: 绑定失败
3. syncClaim 核心逻辑
代码路径: pkg/controller/volume/persistentvolume/pv_controller.go:250
1 | func (ctrl *PersistentVolumeController) syncClaim(ctx context.Context, claim *v1.PersistentVolumeClaim) error { |
4. 未绑定 PVC 处理 (syncUnboundClaim)
1 | func (ctrl *PersistentVolumeController) syncUnboundClaim(ctx context.Context, claim *v1.PersistentVolumeClaim) error { |
5. 静态供应 vs 动态供应
5.1 静态供应
管理员预先创建 PV:
1 | apiVersion: v1 |
PVC 绑定到预创建的 PV:
sequenceDiagram
participant Admin as 管理员
participant API as API Server
participant PVC as PV Controller
participant PV as PV 资源
Admin->>API: 创建 PV
API->>PV: Available 状态
Note over API: 用户创建 PVC
API->>PVC: syncClaim 触发
PVC->>PV: findBestMatchForClaim
PV->>PVC: 返回匹配的 PV
PVC->>API: 更新 PVC.Spec.VolumeName
PVC->>API: 更新 PV.Spec.ClaimRef
API->>PV: Bound 状态
API->>PVC: Bound 状态
5.2 动态供应
根据 StorageClass 自动创建 PV:
1 | apiVersion: storage.k8s.io/v1 |
sequenceDiagram
participant User as 用户
participant API as API Server
participant PVC as PV Controller
participant Prov as Provisioner
participant Backend as 存储后端
User->>API: 创建 PVC (指定 StorageClass)
API->>PVC: syncClaim 触发
PVC->>PVC: findBestMatchForClaim (无匹配)
PVC->>PVC: shouldProvision (返回 true)
PVC->>Prov: provisionClaim
Prov->>Backend: 创建存储卷
Backend->>Prov: 返回卷 ID
Prov->>API: 创建 PV
API->>PVC: PV Available
Note over PVC: 下一次 syncClaim
PVC->>API: 绑定 PV 到 PVC
API->>PVC: Bound 状态
6. FindMatchingVolume 逻辑
代码路径: staging/src/k8s.io/component-helpers/storage/volume/pv_helpers.go:185
1 | func FindMatchingVolume( |
7. VolumeBindingMode
7.1 Immediate(立即绑定)
1 | volumeBindingMode: Immediate |
PVC 创建后立即绑定,不考虑 Pod 调度位置。
7.2 WaitForFirstConsumer(延迟绑定)
1 | volumeBindingMode: WaitForFirstConsumer |
等待使用该 PVC 的 Pod 被调度后再绑定:
sequenceDiagram
participant User as 用户
participant API as API Server
participant PVC as PV Controller
participant Sched as Scheduler
participant Prov as Provisioner
User->>API: 创建 PVC (WaitForFirstConsumer)
API->>PVC: syncClaim
PVC->>PVC: IsDelayBindingMode (true)
Note over PVC: 不立即绑定,等待调度
User->>API: 创建 Pod (使用 PVC)
API->>Sched: 调度 Pod
Sched->>Sched: 选择节点
Sched->>API: 添加 AnnSelectedNode 注解
API->>PVC: PVC 更新事件
PVC->>Prov: provisionClaim (with selected node)
Prov->>API: 创建 PV (with node affinity)
API->>PVC: 绑定完成
相关注解:
1 | const ( |
8. Reclaim Policy
代码路径: pkg/controller/volume/persistentvolume/pv_controller.go:1154
1 | func (ctrl *PersistentVolumeController) reclaimVolume(ctx context.Context, volume *v1.PersistentVolume) error { |
| Reclaim Policy | 行为 |
|---|---|
| Retain | 保留 PV 和数据,需手动清理 |
| Delete | 删除 PV 和后端存储 |
| Recycle | 已废弃,执行 rm -rf /volume/* |
9. syncVolume 核心逻辑
代码路径: pkg/controller/volume/persistentvolume/pv_controller.go:556
1 | func (ctrl *PersistentVolumeController) syncVolume(ctx context.Context, volume *v1.PersistentVolume) error { |
10. CSI 动态供应
现代 Kubernetes 推荐使用 CSI (Container Storage Interface):
flowchart TD
subgraph Kubernetes["Kubernetes 集群"]
A["PVC"]
B["StorageClass"]
C["CSIDriver"]
end
subgraph ExternalProvisioner["external-provisioner"]
D["Watch PVC"]
E["调用 CSI 接口"]
end
subgraph CSI["CSI Driver"]
F["CreateVolume"]
G["DeleteVolume"]
H["ControllerPublishVolume"]
end
subgraph Storage["存储系统"]
I["云磁盘 / NFS / 本地存储"]
end
A -->|指定| B
B -->|provisioner:csi.xxx| C
A -->|触发| D
D --> E
E --> F
F -->|创建| I
I -->|返回卷 ID| F
F -->|创建 PV| A
关键代码锚点
| 功能 | 文件路径 |
|---|---|
| PV Controller 结构 | pkg/controller/volume/persistentvolume/pv_controller.go |
| syncClaim 入口 | pkg/controller/volume/persistentvolume/pv_controller.go:250 |
| syncVolume 入口 | pkg/controller/volume/persistentvolume/pv_controller.go:556 |
| reclaimVolume | pkg/controller/volume/persistentvolume/pv_controller.go:1154 |
| FindMatchingVolume | staging/src/k8s.io/component-helpers/storage/volume/pv_helpers.go:185 |
| 注解常量 | staging/src/k8s.io/component-helpers/storage/volume/pv_helpers.go:33 |
完整流程示例
1 | # 1. 创建 StorageClass |
常见问题
PVC 一直 Pending
- 检查是否有匹配的 PV 或 StorageClass
- 检查 Provisioner 是否正常运行
- 检查 WaitForFirstConsumer 模式是否有 Pod 使用
绑定失败
- 检查 PV 容量是否满足 PVC 请求
- 检查 AccessModes 是否匹配
- 检查 StorageClass 是否一致
动态供应失败
- 检查 Provisioner 日志
- 检查存储后端配额和权限
- 检查 CSI Driver 状态
高频面试题
Q1: PV 和 PVC 的绑定流程是怎样的?
参考答案:
静态供应:
- 管理员预先创建 PV(状态为 Available)
- 用户创建 PVC
- PV Controller 执行
syncClaim() - 调用
findBestMatchForClaim()查找匹配的 PV - 匹配条件:容量、AccessMode、StorageClass、VolumeMode、Selector
- 执行双向绑定:更新 PV.Spec.ClaimRef 和 PVC.Spec.VolumeName
- 状态更新:PV → Bound,PVC → Bound
动态供应:
- 用户创建 PVC(指定 StorageClass)
- PV Controller 发现没有匹配的 PV
- 调用
provisionClaim()触发动态供应 - Provisioner 创建后端存储,然后创建 PV
- 下一次 syncClaim 时完成绑定
Q2: StorageClass 的 VolumeBindingMode 有哪几种?
参考答案:
1 | # 1. Immediate(立即绑定) |
Q3: PV 的 ReclaimPolicy 有哪几种?分别是什么行为?
参考答案:
| ReclaimPolicy | 行为 | 适用场景 |
|---|---|---|
| Retain | 保留 PV 和数据,需手动清理 | 生产环境、重要数据 |
| Delete | 删除 PV 和后端存储 | 动态供应的临时数据 |
| Recycle | 已废弃,执行 rm -rf /volume/* |
旧版本兼容 |
Retain 模式下的处理流程:
- PVC 删除后,PV 状态变为 Released
- ClaimRef 保留(指向已删除的 PVC)
- 管理员手动处理:
- 删除 PV(保留数据)
- 或清除 ClaimRef 后 PV 重新 Available
Q4: PVC 一直处于 Pending 状态,可能的原因有哪些?
参考答案:
1 | # 排查命令 |
Q5: CSI 动态供应的流程是怎样的?
参考答案:
PV Controller 处理:
- 发现 PVC 需要动态供应
- 在 PVC 上设置
volume.kubernetes.io/storage-provisionerannotation - 发送 ExternalProvisioning 事件
external-provisioner(CSI Sidecar):
- Watch PVC,发现 annotation 匹配自己的 driver name
- 调用 CSI Driver 的
CreateVolumegRPC 接口 - 获取后端存储返回的 Volume ID
创建 PV:
- external-provisioner 创建 PV 对象
- 设置
pv.kubernetes.io/provisioned-byannotation - 设置 CSI 卷源(包含 Volume ID)
完成绑定:
- PV Controller 监听到新 PV
- 执行正常的绑定流程
Q6: PV Controller 是如何保证绑定一致性的?
参考答案:
双向绑定:
1
2
3
4
5// bind() 函数执行顺序
// 1. 绑定 PV 到 PVC(设置 PV.Spec.ClaimRef)
// 2. 更新 PV 状态为 Bound
// 3. 绑定 PVC 到 PV(设置 PVC.Spec.VolumeName)
// 4. 更新 PVC 状态为 BoundAnnotation 标记:
pv.kubernetes.io/bind-completed:绑定已完成pv.kubernetes.io/bound-by-controller:由控制器绑定
syncBoundClaim 验证:
- 检查 PVC.Spec.VolumeName 是否为空
- 检查 PV 是否存在
- 检查 PV.ClaimRef.UID 是否与 PVC.UID 匹配
- 不匹配则设置 PVC 状态为 Lost
Finalizer 保护:
kubernetes.io/pv-controller:in-tree PV 删除保护external-provisioner.volume.kubernetes.io/finalizer:CSI PV 保护
Q7: 如何实现存储的跨 Zone 高可用?
参考答案:
1 | # 1. 使用 WaitForFirstConsumer 模式 |

