K8s 运维系列 | 第 7 天:存储管理——PV、PVC 与持久化存储

容器天生是「无状态」的,一旦 Pod 被销毁或重建,容器内部写入的文件都会随之消失。但在真实的业务场景中,数据库、消息队列、日志系统、文件上传服务等应用,都必须把数据持久地保存下来,即使 Pod 重建、节点宕机,数据也不能丢。Kubernetes 为此提供了一套完整的持久化存储体系,核心就是 PV(PersistentVolume)、PVC(PersistentVolumeClaim)和 StorageClass。今天我们就从「为什么需要持久化存储」出发,一步步把这套机制讲清楚、用起来。

K8s 运维 第7天

为什么容器需要持久化存储

先看一个最直观的例子:我们在 Pod 里写入一个文件,然后重建 Pod,看看数据还在不在。

# 进入一个临时 Pod 写入文件
kubectl run busybox --image=busybox --restart=Never -- sh -c "echo hello > /data/test.txt && sleep 3600"
kubectl exec busybox -- cat /data/test.txt   # 输出 hello

# 删除 Pod 后重建
kubectl delete pod busybox
kubectl run busybox --image=busybox --restart=Never -- sh -c "sleep 3600"
kubectl exec busybox -- ls /data              # 文件不见了

结果很清楚:容器自身的文件系统生命周期与 Pod 绑定,Pod 没了,数据也就没了。要解决这个问题,就需要把「数据的生命周期」和「容器的生命周期」解耦,这正是 PV / PVC 机制存在的意义。

核心概念:PV、PVC 与 StorageClass

Kubernetes 把持久化存储抽象成了三个层次的角色,各司其职:

  • PV(PersistentVolume):集群级的存储资源,相当于「管理员预先准备好的存储空间」。它由管理员创建,或由 StorageClass 动态生成,独立于任何 Pod 存在。
  • PVC(PersistentVolumeClaim):用户(应用)发起的存储申请,相当于「我要一个多大、什么类型的存储」。Pod 通过引用 PVC 来使用存储,而不直接接触 PV。
  • StorageClass:存储的「配方」。它定义了用什么后端(如 NFS、云盘、Ceph)、什么参数去动态创建 PV,实现按需供给。

这套设计实现了申请与供给的分离:开发者只需要声明 PVC(要多少、什么模式),无需关心底层是本地磁盘还是云硬盘;而管理员负责定义 StorageClass 和 PV 池。这样职责清晰,也便于在多租户环境下统一管理。

访问模式与回收策略

PV / PVC 有两个关键属性需要理解。

访问模式(accessModes)主要有三种:

accessModes:
  - ReadWriteOnce     # RWO:单个节点读写
  - ReadOnlyMany      # ROX:多节点只读
  - ReadWriteMany     # RWX:多节点读写

回收策略(persistentVolumeReclaimPolicy)决定了 PVC 删除后 PV 的去向:

persistentVolumeReclaimPolicy: Retain   # 保留数据,需手动清理
# 或 Delete:删除 PVC 时同时删除底层存储
# 或 Recycle:已废弃,不推荐使用

实战步骤:创建 PV、PVC 并挂载到 Pod

下面我们用一个 NFS 类型的静态 PV 完整演示一遍流程。假设已经有一台 NFS 服务器 192.168.1.100,共享目录为 /data/k8s

第一步:创建 PV

# pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-5g
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /data/k8s
    server: 192.168.1.100
kubectl apply -f pv.yaml
kubectl get pv
# NAME        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM
# pv-nfs-5g   5Gi        RWX            Retain           Available

可以看到 PV 处于 Available 状态,等待被绑定。

第二步:创建 PVC 并绑定

# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-nginx-data
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
kubectl apply -f pvc.yaml
kubectl get pvc
# NAME            STATUS   VOLUME      CAPACITY   ACCESS MODES
# pvc-nginx-data  Bound    pv-nfs-5g   5Gi        RWX

当 PVC 的请求(5Gi、RWX)与某个 PV 的规格匹配时,二者自动绑定,PVC 状态变为 Bound

第三步:Pod 挂载 PVC

# pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-with-pv
spec:
  containers:
    - name: nginx
      image: nginx
      volumeMounts:
        - name: data
          mountPath: /usr/share/nginx/html
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: pvc-nginx-data
kubectl apply -f pod.yaml
kubectl exec nginx-with-pv -- sh -c "echo persisted > /usr/share/nginx/html/index.html"

此时无论 Pod 如何重启,/usr/share/nginx/html 下的数据都会持久化到 NFS 上,不再随 Pod 消失。

动态供给:StorageClass 实战

手动创建 PV 在存储规模大时会非常繁琐,实际生产环境更常用 StorageClass 动态供给——用户创建 PVC 时,由系统自动创建对应的 PV。下面是一个使用 NFS 的动态供给示例(需要先部署 nfs-subdir-external-provisioner):

# storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-client
provisioner: nfs.storage.k8s.io/nfs
parameters:
  archiveOnDelete: "false"
# dynamic-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dynamic-data
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
kubectl apply -f storageclass.yaml
kubectl apply -f dynamic-pvc.yaml
kubectl get pv   # 会自动生成一个 10Gi 的新 PV

动态供给大幅降低了运维成本:应用只需声明需求,存储资源按需自动创建、绑定。

常见问题

1. PVC 一直处于 Pending 状态怎么办?

通常是三个原因:一是没有匹配的 PV,二是没有对应的 StorageClass 或 provisioner 未就绪,三是访问模式不匹配。用 kubectl describe pvc <name> 查看 Events 定位原因。

2. PV 删除不了,一直卡在 Terminating?

这说明 PV 还绑定着 PVC,或底层存储存在保护机制。先删除引用它的 PVC,若仍卡住,可尝试给 PV 打补丁强制删除终结器:

kubectl patch pv <pv-name> -p '{"metadata":{"finalizers":null}}'

3. 多副本应用如何共享存储?

需要存储支持 ReadWriteMany(RWX)访问模式,例如 NFS、CephFS、GlusterFS。本地磁盘(如 hostPath、local)只支持单节点读写,无法用于多副本共享。

4. Pod 挂载报 mount 失败?

检查 NFS 服务器网络与权限、导出目录是否可访问,以及节点上是否安装了对应存储插件。查看 kubectl describe pod 的 Events 是最直接的排查手段。

总结

持久化存储是 Kubernetes 上运行有状态应用的基础。今天我们从容器数据易失的问题出发,理解了 PV、PVC、StorageClass 三个核心概念的分工,并通过「静态 PV + PVC + Pod 挂载」和「StorageClass 动态供给」两个实战场景,完整走通了数据持久化的流程。掌握这套机制后,你就能在集群上稳定地运行数据库、消息队列等有状态服务。存储还只是开始,后续我们会继续深入 CSI 驱动、备份恢复等进阶话题,敬请期待。

下期预告:第 8 天我们将进入「调度机制」,讲解节点选择(nodeSelector / nodeAffinity)、污点(taint)与容忍度(toleration),让 Pod 精确调度到合适的节点上。

微信二维码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容