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
已被弃用,建议使用 Delete
或 Retain
。
2.5 状态(Status)
PV 的生命周期状态包括:
Available
:未绑定Bound
:已绑定 PVCReleased
: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 支持),但我们可以通过以下方式实现扩容:
- 修改 PV 容量
- 设置 PV 回收策略为
Retain
- 删除旧 PVC
- 创建新 PVC 并绑定原 PV
✅ 这种方法可以保留原有数据,避免数据迁移带来的风险。
如需自动化扩容,建议使用支持扩容的 StorageClass,并启用 PVC 扩容功能。