目录
Please enable Javascript to view the contents

K8s存储-Volume生命周期全解析

 ·  ☕ 5 分钟

系列导航

本系列从 K8s 存储模型入手,逐步展开到 CSI 插件实现与云原生存储全景。

① 概念 → ② Volume 生命周期 → ③ CSI 架构 → ④ FlexVolume 演进 → ⑤ 对比 → ⑥ 排障 ‖ ⑦ 开发 → ⑧ 高级特性 → ⑨ 演进展望

顺序文章定位
概念与入门基础——K8s 存储模型、PV/PVC/StorageClass、Volume 分类树
本篇 - Volume 生命周期全解析全貌——两阶段处理、动态/静态供应、Volume 流转路径
CSI 架构详解标准——gRPC 三服务、Sidecar 模式、External Components
FlexVolume 与 CSI 演进演进——FlexVolume 原理、CSI 设计思想
存储方案对比与选型选型——主流 CSI 插件横向对比 + 决策树
排障思路与常用命令运维——工具链 + 场景排查 + 性能
核心路径 ↑扩展展望 ↓
CSI 插件开发指南扩展——从零开发一个 CSI 插件
高级特性详解进阶——快照、克隆、扩容、拓扑感知
云原生存储演进与展望展望——容器原生存储、DPU 卸载、未来方向

重要

Volume 从创建到销毁,经历供应(Provision)→ 绑定(Bind)→ 挂载(Attach+Mount)→ 使用 → 卸载(Unmount+Detach)→ 回收(Reclaim)六个阶段。本文逐阶段拆解,重点说明"两阶段处理"机制和动态/静态供应的区别。


1. Volume 生命周期总览

PVC 创建
  ↓
供应(Provision)
  ├── 静态供应:运维预先创建 PV,PVC 匹配绑定
  └── 动态供应:Provisioner 根据 StorageClass 自动创建 PV + 底层存储
  ↓
绑定(Bind):PersistentVolumeController 匹配 PVC 与 PV
  ↓
调度(Schedule):Pod 被调度到某节点
  ↓
Attach:Volume Controller 将远程存储挂载到该节点(块存储特有)
  ↓
Mount:kubelet 将 Volume 挂载到 Pod 容器目录
  ↓
Pod 运行,容器读写 Volume
  ↓
Pod 删除
  ↓
Unmount:kubelet 卸载容器内 Volume
  ↓
Detach:Volume Controller 从节点卸载远程存储
  ↓
回收(Reclaim):Retain / Delete

2. 供应阶段

2.1 静态供应

运维人员手动创建 PV,开发人员创建 PVC,由 PersistentVolumeController 匹配绑定。

匹配条件:

条件说明
storageClassNamePV 和 PVC 必须一致(或都为空)
accessModesPV 的模式必须包含 PVC 请求的模式
capacity.storagePV 的容量必须 ≥ PVC 的请求量
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# PV(运维创建)
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  capacity:
    storage: 10Gi
  accessModes: ["ReadWriteMany"]
  storageClassName: manual
  nfs:
    server: 10.10.0.25
    path: /export
---
# PVC(开发创建)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  accessModes: ["ReadWriteMany"]
  storageClassName: manual
  resources:
    requests:
      storage: 5Gi

2.2 动态供应

开发人员只创建 PVC(引用 StorageClass),Provisioner 自动创建 PV 和底层存储。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# StorageClass(运维创建一次)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: csi.example.com/volume
parameters:
  type: ssd
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
---
# PVC(开发创建,自动触发供应)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-pvc
spec:
  storageClassName: fast
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 100Gi
静态供应 = 运维建仓、开发领用。动态供应 = 开发提需求、系统自动建仓。生产环境推荐动态供应,减少人工干预。

3. 两阶段处理:Attach + Mount

Volume 挂载到 Pod 分两步,这是理解 K8s 存储机制的核心:

3.1 第一阶段:Attach(挂接到节点)

Volume Attach Controller(kube-controller-manager 的循环)负责,将远程存储设备挂接到 Pod 所在节点。

存储类型是否需要 Attach说明
块存储(EBS、Ceph RBD、iSCSI)将远程块设备映射为节点的 /dev/xvdX
文件存储(NFS、CephFS)直接走网络协议,无需设备挂接
本地存储(Local PV、hostPath)设备已在节点上
1
2
3
4
5
6
7
8
9
# Attach 后节点上可见块设备
lsblk
#NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
#xvdf    202:80   0  100G  0 disk

# 相当于执行了
iscsiadm -m node -T <iqn> -p <portal> --login  # iSCSI
# 或
nvme connect -t tcp -n <subsysnqn> -a <addr>   # NVMe-oF

3.2 第二阶段:Mount(挂载到容器目录)

kubelet 负责,在节点上将 Volume 挂载到 Pod 的目录,再 bind mount 到容器内。

节点上的流程:
  /dev/xvdf(块设备)
    ↓ format + mount
  /var/lib/kubelet/pods/<podID>/volumes/<plugin>/<volumeName>/
    ↓ bind mount
  容器 rootfs 的 mountPath(如 /usr/share/nginx/html)
1
2
3
4
5
6
7
8
9
# kubelet 执行的等效操作
# 1. 格式化(首次使用)
mkfs.ext4 /dev/xvdf

# 2. 挂载到 Pod 的 Volume 目录
mount /dev/xvdf /var/lib/kubelet/pods/<podID>/volumes/kubernetes.io~csi/<volName>/mount

# 3. bind mount 到容器目录
mount --bind /var/lib/kubelet/pods/<podID>/volumes/.../mount /var/lib/docker/overlay2/<hash>/merged/usr/share/nginx/html
Attach 是"把存储设备连接到节点"(节点级别),Mount 是"把存储目录映射到容器"(容器级别)。块存储需要两步,文件存储跳过 Attach 直接 Mount。

4. 绑定模式

4.1 Immediate Binding(立即绑定)

PVC 创建后立即匹配 PV。适用于网络存储(NFS、CephFS),因为不依赖 Pod 调度位置。

4.2 WaitForFirstConsumer(延迟绑定)

PVC 创建后等待 Pod 调度完成,再根据 Pod 所在节点匹配 PV。适用于 Local PV 和拓扑感知存储。

模式触发时机适用场景
ImmediatePVC 创建即绑定NFS、CephFS 等网络存储
WaitForFirstConsumerPod 调度后绑定Local PV、云盘(需感知拓扑)

5. Local PV 的特殊性

5.1 一个 PV 一块盘

Local PV 不等于 hostPath。hostPath 使用的是宿主机根目录所在磁盘,随时可能被写满。Local PV 要求使用额外挂载的独立磁盘或块设备

对比hostPathLocal PV
存储介质宿主机任意目录独立磁盘/分区
I/O 隔离有(独立设备)
调度感知有(nodeAffinity)
持久化不保证保证(Retain 策略)

5.2 调度与 Volume 分布

常规 PV:先调度 Pod → 再处理 Volume。

Local PV:调度器必须知道 Volume 在哪个节点,将 Pod 调度到有对应磁盘的节点。由 VolumeBindingChecker 过滤条件实现。

1
2
3
4
5
6
7
# StorageClass 配置延迟绑定
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

5.3 Local PV 示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-1
spec:
  capacity:
    storage: 100Gi
  accessModes: ["ReadWriteOnce"]
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /mnt/disks/ssd1
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values: ["node-1"]

6. PV 回收策略

PVC 删除后,PV 的处理方式由 persistentVolumeReclaimPolicy 决定:

策略行为适用场景
RetainPV 保留,状态变为 Released,数据不删除生产环境、重要数据
DeletePV 和底层存储一起删除动态供应的云盘、测试环境
Recycle(已废弃)执行 rm -rf 清空数据,PV 变为 Available旧版本兼容

7. Volume Plugin 机制

K8s 内置了 20 多种 Volume Plugin,正在从 In-tree 迁移到 CSI:

对比In-tree PluginCSI Plugin
代码位置K8s 主仓库独立仓库
升级方式随 K8s 版本独立升级
接口Go 接口gRPC
部署编译进 kubelet容器化 Sidecar

详见 CSI 架构详解


参考链接

分享

Hex
作者
Hex
CloudNative Developer