Kubernetes-PV(PersistentVolume)和PVC(PersistentVolumeClaim)

在 Kubernetes 持久化存储的学习中,PV(PersistentVolume)与 PVC(PersistentVolumeClaim)是绕不开的核心概念。本文将结合我在离线单节点环境下的完整实验经历,从理论到实战,带你彻底掌握 Local PV/PVC 的配置、部署与排障,同时深入理解 StorageClass 的动态供给能力。


🎯 一、PV/PVC 核心概念与设计价值

1.1 为什么需要 PV/PVC?

Kubernetes 中 Pod 是临时、可销毁 的,但很多应用(如数据库、日志系统)需要持久化存储 来保存数据。直接在 Pod 中使用 hostPathemptyDir 存在以下问题:

  • 存储与应用强耦合:开发人员需要关心底层存储细节,无法做到 "一次编写,到处运行"。
  • 资源管理混乱:管理员无法统一管控集群存储资源,容易出现存储滥用或浪费。
  • 数据迁移困难 :Pod 重建或调度到其他节点时,hostPath 数据无法跟随。

1.2 PV/PVC 如何解决问题?

PV/PVC 采用了 **"资源池 + 申请单"的设计模式,实现了存储的供给与使用解耦 **:

  • 管理员视角(PV):负责创建和维护 PV 资源池,定义存储的容量、访问模式、回收策略等属性。
  • 开发人员视角(PVC):只需通过 PVC 向集群 "申请" 存储,无需关心底层是 Local PV、NFS 还是 Ceph。
  • 调度器视角:自动将 PVC 绑定到满足需求的 PV,实现存储的动态分配和管理。

📋 二、PV 核心属性与类型

2.1 PV 关键配置字段

字段 说明 可选值
capacity PV 的存储容量 1Gi10Gi
accessModes PV 的访问模式 ReadWriteOnce(单节点读写)、ReadOnlyMany(多节点只读)、ReadWriteMany(多节点读写)
persistentVolumeReclaimPolicy PV 回收策略 Retain(保留数据,手动清理)、Delete(删除存储和数据)、Recycle(清理数据后复用,已废弃)
storageClassName PV 所属存储类 local-storagenfs-storage
nodeAffinity PV 节点亲和性(仅 Local PV 需配置) 强制 PV 绑定到特定节点

2.2 PV 主要类型

  1. Local PV:使用节点本地磁盘,性能高但不支持跨节点调度,适合离线单节点、数据库等场景。
  2. NFS PV:使用网络文件系统,支持多节点读写,适合开发测试、共享存储场景。
  3. Ceph PV:分布式块存储,高可用、高可靠,适合生产环境核心业务。
  4. 云厂商 PV:如 AWS EBS、阿里云云盘,与云平台深度集成,适合云原生场景。

2.3 PV 访问模式详解

  • ReadWriteOnce(RWO):仅允许一个节点挂载并读写,是 Local PV 的唯一支持模式。
  • ReadOnlyMany(ROX):允许多个节点挂载但仅能读取,适合静态内容共享场景。
  • ReadWriteMany(RWX):允许多个节点挂载并读写,仅 NFS、Ceph 等网络存储支持。

💡 注意:PV 的访问模式是能力声明 ,而非强制限制。例如,一个 ReadWriteOnce 的 PV 被绑定后,Kubernetes 会确保只有一个节点能挂载它,但不会阻止该节点上的多个 Pod 访问。


📝 三、PVC 核心属性与绑定机制

3.1 PVC 关键配置字段

PVC 的配置是对 PV 的 "筛选条件",Kubernetes 会根据这些条件匹配最合适的 PV:

字段 说明
accessModes 申请的访问模式,必须与 PV 的 accessModes 兼容
resources.requests.storage 申请的存储容量,不能超过 PV 的 capacity
storageClassName 申请的存储类,必须与 PV 的 storageClassName 完全匹配
selector 标签选择器,进一步筛选 PV(可选)

3.2 PV/PVC 绑定机制

  1. 静态供给:管理员提前创建 PV,PVC 提交后直接绑定到满足条件的 PV。
  2. 动态供给 :管理员配置 StorageClass 和对应的存储插件,PVC 提交后自动创建 PV。

💡 绑定是一对一、不可逆 的:一个 PVC 只能绑定一个 PV,一个 PV 也只能被一个 PVC 绑定。绑定后,PVC 不能修改 accessModesstorageClassName

3.3 PVC 与 Pod 的关联

Pod 通过 volumesvolumeMounts 字段使用 PVC:

复制代码
spec:
  volumes:
  - name: www
    persistentVolumeClaim:
      claimName: www-web-0  # 关联 PVC 名称
  containers:
  - name: nginx
    volumeMounts:
    - name: www
      mountPath: /usr/share/nginx/html  # 挂载到容器内路径

🔄 四、PV/PVC 生命周期

4.1 PV 生命周期阶段

  1. Available:PV 已创建,未被任何 PVC 绑定。
  2. Bound:PV 已被 PVC 绑定,处于使用中。
  3. Released :PVC 被删除,但 PV 回收策略为 Retain,数据保留,需手动清理。
  4. Failed:PV 回收或清理过程中发生错误。

4.2 PVC 生命周期阶段

  1. Pending:PVC 已创建,但未找到匹配的 PV。
  2. Bound:PVC 已绑定到 PV,可被 Pod 使用。
  3. Lost:绑定的 PV 丢失(如节点故障),PVC 无法正常使用。

🛠️ 五、离线单节点环境 PV/PVC + StorageClass 实战

5.1 环境准备

  • 节点信息 :单节点集群,节点名称 172-16-2-47
  • 存储目录 :在节点上创建 /root/1.19/nfs-test 目录,放入 index.html 测试文件。
  • 镜像准备 :离线环境下已导入 nginx:1.22.0-alpine 镜像。

5.2 实验步骤

  1. 创建命名空间

    复制代码
    apiVersion: v1
    kind: Namespace
    metadata:
      name: nfs-storageclass
  2. 创建 ServiceAccount 与 RBAC 权限为 StorageClass 实验创建专用的服务账号和权限,确保组件能正常访问 Kubernetes API。

  3. 创建 StorageClass

    复制代码
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: local-storage
      namespace: nfs-storageclass
    provisioner: kubernetes.io/no-provisioner
    volumeBindingMode: WaitForFirstConsumer
    parameters:
      onDelete: delete
  4. 创建 Local PV

    复制代码
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: local-pv-1
    spec:
      capacity:
        storage: 1Mi
      accessModes:
        - ReadWriteMany
      persistentVolumeReclaimPolicy: Delete
      storageClassName: local-storage
      local:
        path: "/root/1.19/nfs-test"
      nodeAffinity:
        required:
          nodeSelectorTerms:
          - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - 172-16-2-47
  5. 创建 PVC

    复制代码
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: test-claim
      namespace: nfs-storageclass
    spec:
      accessModes:
        - ReadWriteMany
      resources:
        requests:
          storage: 1Mi
      storageClassName: local-storage
  6. 创建测试 Pod

    复制代码
    apiVersion: v1
    kind: Pod
    metadata:
      name: test-pod
      namespace: nfs-storageclass
    spec:
      containers:
        - name: test-pod
          image: global.barn.registry/public/nginx:1.22.0-alpine
          volumeMounts:
            - name: nfs-pvc
              mountPath: "/usr/local/nginx/html"
      restartPolicy: "Never"
      volumes:
        - name: nfs-pvc
          persistentVolumeClaim:
            claimName: test-claim

5.3 验证实验成功

bash 复制代码
# 1. 验证 PVC 绑定
kubectl get pvc -n nfs-storageclass
# 输出:test-claim   Bound    local-pv-1   1Mi        RWX            local-storage   68s

# 2. 验证 Pod 状态
kubectl get pod test-pod -n nfs-storageclass
# 输出:test-pod   1/1     Running   0          25s

# 3. 验证数据挂载
kubectl exec -it test-pod -n nfs-storageclass -- cat /usr/local/nginx/html/index.html
# 输出:Local StorageClass Test

🚑 六、常见问题与排障指南

6.1 PV 无法删除,卡在 Terminating

现象 :执行 kubectl delete pv --all 后,PV 一直处于 Terminating 状态。原因 :PV 与 PVC 仍处于绑定状态,且 persistentVolumeReclaimPolicy: Retain 阻止了自动删除。解决

bash 复制代码
# 1. 先删除绑定的 PVC
kubectl delete pvc www-web-0 --grace-period=0 --force

# 2. 强制删除 PV
kubectl delete pv local-pv-1 --grace-period=0 --force

# 3. 若仍未删除,移除 PV 的 finalizers
kubectl patch pv local-pv-1 -p '{"metadata":{"finalizers":null}}'

6.2 Pod 处于 Pending 状态

现象 :Pod 一直处于 Pending,无法调度。原因 :PV 的 nodeAffinity 配置的节点名称与集群节点真实名称不匹配。解决

bash 复制代码
# 1. 获取节点真实名称
kubectl get nodes -o wide

# 2. 修正 PV 的 nodeAffinity 配置
# 将 values 改为节点真实名称(如 172-16-2-47)

6.3 PV 存储源不可变错误

现象 :修改 PV 配置时提示 spec.persistentvolumesource is immutable after creation原因 :PV 的存储源(如 local.path)是不可变字段,创建后无法修改。解决

bash 复制代码
# 1. 删除旧 PV
kubectl delete pv local-pv-1 --grace-period=0 --force

# 2. 用新配置创建 PV
kubectl apply -f 05-pv.yaml

📊 七、生产环境最佳实践

7.1 PV 配置建议

  • 回收策略 :测试环境用 Retain 保留数据排障,生产环境用 Delete 自动清理(需存储插件支持)。
  • 存储类 :为不同业务场景创建不同 StorageClass(如 local-storagenfs-storage),实现存储资源的隔离和精细化管理。
  • 节点亲和性 :Local PV 必须配置 nodeAffinity,确保 Pod 调度到 PV 所在节点。

7.2 PVC 配置建议

  • 容量规划:申请的容量应略大于实际需求,避免频繁扩容;同时不要过度申请,造成存储浪费。
  • 访问模式 :根据业务场景选择最小必要的访问模式(如单节点业务用 RWO,多节点共享用 RWX)。
  • 标签选择器 :复杂场景下使用 selector 精准匹配 PV,避免绑定到不符合预期的存储。

🎓 八、总结与思考

通过本次离线单节点环境的 PV/PVC + StorageClass 实验,我们不仅掌握了 Local PV/PVC 的配置与部署,更深入理解了 Kubernetes 持久化存储的核心原理。在离线环境下,我们需要:

  1. 选择合适的存储方案:Local PV 是离线单节点的最优选择。
  2. 重视节点亲和性:Local PV 必须绑定到特定节点,节点名称的正确性至关重要。
  3. 严格验证 YAML 格式:YAML 的缩进、拼写和大小写错误是常见的排障点。
  4. 理解不可变特性:PV 的核心字段创建后无法修改,需通过删除重建来更新配置。
相关推荐
Curvatureflight2 小时前
Kubernetes完全指南:从集群搭建到生产部署
云原生·容器·kubernetes
努力也学不会java2 小时前
【Spring Cloud】环境和工程基本搭建
java·人工智能·后端·spring·spring cloud·容器
博思云为2 小时前
企业级智能PPT生成:Amazon云+AI驱动,全流程自动化提效
人工智能·语言模型·云原生·数据挖掘·云计算·语音识别·aws
laozhoy12 小时前
K8s基础命令
云原生·容器·kubernetes
不做码农好多年,该何去何从。2 小时前
云原生k8s(二)——核心组件详解与基础命令
云原生·容器·kubernetes
ChineHe3 小时前
Docker基础篇001_Docker入门指南(基于官方教程,5W字详细版)
运维·docker·微服务·容器·云计算·devops
Zsr10233 小时前
POD控制器:集群的“自动化运维管家”
docker·容器·kubernetes
汪碧康3 小时前
一文掌握k8s容器的资源限制
docker·云原生·容器·golang·kubernetes·k8s·xkube
可爱又迷人的反派角色“yang”3 小时前
k8s(七)
java·linux·运维·docker·云原生·容器·kubernetes