1. 概述

容器本质上是临时性的,Kubernetes 通过隔离的命名空间和临时卷来管理存储。但有时我们需要持久化的存储方案,以便在 Pod、节点甚至集群之间持久保存数据。尽管持久卷(Persistent Volume, PV)提供了这样的能力,但它们也有容量限制。因此,我们有时需要调整其大小。虽然扩容本身不难,但将新容量应用到使用它的 Pod 上却可能遇到挑战。

本文将深入探讨 Kubernetes 中的 PV 和 PVC,包括它们的核心概念、如何创建和查看 PV/PVC,以及如何调整它们的大小。

我们使用的测试环境为 Debian 12 (Bookworm),Shell 为 GNU Bash 5.2.15。除非特别说明,大多数命令在大多数 POSIX 兼容环境中均可运行。


2. 持久卷(PV)

2.1 概念

在 Kubernetes 中,PV 是集群中的一块存储资源,可以由管理员手动创建,也可以根据 PVC 的请求动态创建。与临时卷不同,PV 的内容在 Pod 重启后依然保留。

2.2 类型

目前主流的非废弃 PV 类型包括:

  • csi:容器存储接口(Container Storage Interface)
  • fc:光纤通道(Fibre Channel)
  • hostPath:映射宿主机路径
  • iscsi:基于 IP 的 SCSI 存储
  • local:本地节点存储
  • nfs:网络文件系统(Network File System)

这些类型的具体实现细节由底层存储系统决定,Kubernetes 通过 StorageClass 抽象了这些细节,使用户无需关心底层实现。

2.3 供应方式

PV 的供应方式分为两种:

静态供应:先创建 PV,再由 PVC 绑定
动态供应:PVC 请求时自动创建 PV

无论哪种方式,PVC 最终都会绑定到一个 PV 上。

2.4 回收策略(Reclaim Policy)

当 PVC 被删除后,PV 的处理方式由回收策略决定:

策略 说明
Retain 保留 PV,需手动处理
Delete 自动删除 PV 及其数据
Recycle 清空数据后重新可用(已废弃)

⚠️ Recycle 已被弃用,建议使用 DeleteRetain

2.5 状态(Status)

PV 的生命周期状态包括:

  • Available:未绑定
  • Bound:已绑定 PVC
  • Released:PVC 已删除,等待手动回收
  • Failed:自动回收失败

可通过 kubectl get pv 查看 PV 的状态。

2.6 访问模式(Access Modes)

PV 支持多种访问模式,决定其如何被挂载:

模式 说明
ReadWriteOnce (RWO) 单节点读写
ReadOnlyMany (ROX) 多节点只读
ReadWriteMany (RWX) 多节点读写
ReadWriteOncePod (RWOP) 单 Pod 读写(K8s v1.27+)

⚠️ 一个 PVC 只能使用一种访问模式。


3. 创建持久卷(PV)

以下是一个使用 hostPath 类型的 PV 示例:

kubectl apply --filename=<(echo '
apiVersion: v1
kind: PersistentVolume
metadata:
  name: hostpath-vol0
  namespace: default
spec:
  storageClassName: xclass
  capacity:
    storage: 6Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/hostpath-vol0-source-path"
    type: DirectoryOrCreate
')

该 PV 使用 hostPath 类型,路径为 /mnt/hostpath-vol0-source-path,容量为 6Gi,访问模式为 RWO。


4. 创建持久卷声明(PVC)

PVC 是 Pod 对存储的请求。我们先创建一个与上述 PV 匹配的 PVC:

kubectl apply --filename=<(echo '
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: hostpath-vol0-claim
  namespace: default
spec:
  storageClassName: xclass
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
')

再创建一个未指定 StorageClass 的 PVC:

kubectl apply --filename=<(echo '
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pod0-claim
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi
')

⚠️ PVC 不直接指定 PV,而是通过匹配条件(如 StorageClass、容量、访问模式)自动绑定。


5. 查看 PV 与 PVC 状态

执行以下命令查看 PVC 状态:

kubectl get pvc

输出示例:

NAME                  STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
hostpath-vol0-claim   Bound    hostpath-vol0                              6Gi        RWO            xclass         27m
pod0-claim            Bound    pvc-04ae85d9-6c3b-4f56-8ed0-7e666f965991   2Gi        RWO            standard       2m10s

查看 PV 状态:

kubectl get pv

输出示例:

NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                         STORAGECLASS   AGE
hostpath-vol0                              6Gi        RWO            Retain           Bound    default/hostpath-vol0-claim   xclass         29m
pvc-04ae85d9-6c3b-4f56-8ed0-7e666f965991   2Gi        RWO            Delete           Bound    default/pod0-claim            standard       4m15s

可以看到,pod0-claim 动态创建了一个 PV。


6. 扩容持久卷(PV)

修改 PV 容量非常简单,例如将 hostpath-vol0 从 6Gi 扩容到 7Gi:

kubectl edit pv/hostpath-vol0

找到 capacity.storage 字段并修改:

capacity:
  storage: 7Gi

同样适用于动态生成的 PV:

kubectl edit pv/pvc-04ae85d9-6c3b-4f56-8ed0-7e666f965991

⚠️ 扩容 PV 后,PVC 的容量不会自动更新。


7. 扩容持久卷声明(PVC)

7.1 直接修改 PVC(不推荐)

尝试直接修改 PVC 容量:

kubectl edit pvc/hostpath-vol0-claim

修改 resources.requests.storage

resources:
  requests:
    storage: 2Gi

⚠️ 会报错,提示“只有动态供应的 PVC 才能扩容,且 StorageClass 必须支持扩容”。

7.2 修改回收策略

为防止数据丢失,先将 PV 的回收策略设为 Retain

kubectl edit pv/hostpath-vol0

修改字段:

persistentVolumeReclaimPolicy: Retain

7.3 删除原 PVC

删除 PVC 后 PV 会变为 Released 状态:

kubectl delete pvc/hostpath-vol0-claim

验证 PV 状态:

kubectl get pv/hostpath-vol0

输出示例:

NAME            CAPACITY   STATUS     RECLAIM POLICY
hostpath-vol0   7Gi        Released   Retain

7.4 创建新 PVC

创建新的 PVC,容量为 3Gi:

kubectl apply --filename=<(echo '
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: hostpath-vol0-claim-resized
  namespace: default
spec:
  storageClassName: xclass
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
')

查看状态:

kubectl get pvc/hostpath-vol0-claim-resized

输出示例:

NAME                          STATUS    VOLUME   CAPACITY   ACCESS MODES
hostpath-vol0-claim-resized   Pending

7.5 释放 PV

编辑 PV,清空 claimRef

kubectl patch pv/hostpath-vol0 --patch='{"spec":{"claimRef": null}}'

再次查看 PVC 状态,应为 Bound

kubectl get pvc/hostpath-vol0-claim-resized

输出示例:

NAME                          STATUS   VOLUME
hostpath-vol0-claim-resized   Bound    hostpath-vol0

✅ PVC 已成功扩容并绑定到原 PV,数据未丢失。


8. 总结

本文介绍了 Kubernetes 中的 PV 和 PVC 的基本概念、创建方式、状态查看方法,以及扩容技巧。尽管 PVC 本身不能直接扩容(除非 StorageClass 支持),但我们可以通过以下方式实现扩容:

  1. 修改 PV 容量
  2. 设置 PV 回收策略为 Retain
  3. 删除旧 PVC
  4. 创建新 PVC 并绑定原 PV

✅ 这种方法可以保留原有数据,避免数据迁移带来的风险。

如需自动化扩容,建议使用支持扩容的 StorageClass,并启用 PVC 扩容功能。


原始标题:Kubernetes Persistent Volume (PV) and Persistent Volume Claim (PVC)