Kubernetes基础(二十二)-K8S的PV/PVC/StorageClass详解

1 概述

先来个一句话总结 :PV、PVC是K8S用来做存储管理的资源对象,它们让存储资源的使用变得可控 ,从而保障系统的稳定性、可靠性。StorageClass则是为了减少人工的工作量而去自动化创建 PV的组件。所有Pod使用存储只有一个原则:先规划后申请再使用

1.1 PV概念

PV是对K8S存储资源的抽象,PV一般由运维人员创建和配置,供容器申请使用。

没有PV之前,服务器的磁盘没有分区的概念,有了PV之后,相当于通过PV对服务器的磁盘进行分区。

1.2 PVC概念

PVC 是Pod对存储资源的一个申请,主要包括存储空间申请、访问模式等。创建PV后,Pod就可以通过PVC向PV申请磁盘空间了。类似于某个应用程序向操作系统的D盘申请1G的使用空间。

PVC 创建成功之后,Pod 就可以以存储卷(Volume)的方式使用 PVC 的存储资源了。Pod 在使用 PVC 时必须与PVC在同一个Namespace下。

1.3 PV / PVC的关系

PV相当于对磁盘的分区,PVC相当于APP(应用程序)向某个分区申请多少空间。比如说安装WPS程序时,一般会告知我们安装它需要多少存储空间,让你选择在某个磁盘下安装。如果将来某个分区磁盘满了,也不会影响别的分区磁盘的使用。

一旦 PV 与PVC绑定,Pod就可以使用这个 PVC 了。如果在系统中没有满足 PVC 要求的 PV,PVC则一直处于 Pending 状态,直到系统里产生了一个合适的 PV。

1.4 StorageClass概念

K8S有两种存储资源的供应模式:静态模式和动态模式,资源供应的最终目的就是将适合的PV与PVC绑定:

  • 静态模式:管理员预先创建许多各种各样的PV,等待PVC申请使用。
  • 动态模式:管理员无须预先创建PV,而是通过StorageClass自动完成PV的创建以及与PVC的绑定。

StorageClass就是动态模式,根据PVC的需求动态创建合适的PV资源,从而实现存储卷的按需创建。

一般某个商业性的应用程序,会用到大量的Pod,如果每个Pod都需要使用存储资源,那么就需要人工时不时的去创建PV,这也是个麻烦事儿。解决方法就是使用动态模式:当Pod通过PVC申请存储资源时,直接通过StorageClass去动态的创建对应大小的PV,然后与PVC绑定,所以基本上PV → PVC是一对一的关系。

1.5 Provisioner概念

在创建 PVC 时需要指定 StorageClass,PVC 选择到对应的StorageClass后,与其关联的 Provisioner 组件来动态创建 PV 资源。

那Provisioner是个啥呢?其实就一个存储驱动,类似操作系统里的磁盘驱动。

StorageClass 资源对象的定义主要包括:名称、Provisioner、存储的相关参数配置、回收策略。StorageClass一旦被创建,则无法修改,只能删除重新创建。

PV和PVC的生命周期,包括4个阶段:资源供应(Provisioning)、资源绑定(Binding)、资源使用(Using)、资源回收(Reclaiming)。首先旧的有资源供应,说白了就是得有存储驱动,然后才能创建、绑定和使用、回收。

1.6 使用PV / PVC前后对比

1.6.1 通过描述对比

在没有使用PV、PVC之前,各个Pod都可以任意的向存储资源里(比如NFS)写数据,随便一个Pod都可以往磁盘上插一杠子,长期下去磁盘的管理会越来越混乱,然后导致数据使用超限,磁盘爆掉,最后导致磁盘上的所有应用全部挂掉。

为了解决这个问题,引入了PV、PVC的概念,达到限制Pod写入存储数据大小的目的,从而更好地保障了系统的可用性、稳定性。

有了PVC、PV之后,所有Pod使用存储资源,保持一个原则:先规划 → 后申请 → 再使用。

那你肯定有一个疑问,"StorageClass是自动化创建PV,跟原本的无序不可控是一样的效果啊,都可以随便占用存储资源啊"。

其实不然,使用StorageClass只是自动化了创建PV的流程,但依旧执行的是一个存储可控的流程。每个Pod使用多少存储空间是固定的,Pod没有办法超额使用存储空间,更不会影响到别的应用,要出故障也只是某个Pod自己出故障。

1.6.2 通过图片对比

没有使用PV、PVC之前的情况,如下面2张图:

有了PV、PVC之后的情况,如下图:

2 存储实战

在实践PV、PVC、StorageClass之前,需要读者朋友自行安装NFS服务器。文中演示的内容是通过yaml编排自动到NFS服务器起上创建PV。

2.1 Pod使用PV、PVC挂载存储卷

2.1.1 编排PV、PVC、Pod挂载PVC

文中演示的是:Pod的某个目录挂载到NFS的某个目录下。使用了nginx镜像,将html文件写在PV所在的NFS服务器上,最终可以看到利用PV / PVC 成功挂载上去了。yaml文件如下:

复制代码
# PV编排
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv1
  namespace: dev1
  labels:
    pv: nfs-pv1
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  # Recycle 删除PVC会同步删除PV | Retain 删除PVC不会同步删除PV
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /data/nfstest/share/pv1
    server: 10.20.1.20
    readOnly: false
---
# PVC 编排,通过selector查找PV,K8S里的资源查找都是通过selector查找label标签
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc1
  namespace: dev1
  labels:
    pv: nfs-pvc1
spec:
  resources:
    requests:
      storage: 100Mi
  accessModes:
    - ReadWriteOnce
  selector:
    matchLabels:
      pv: nfs-pv1
---
# Pod挂载PVC,这里为了测试,直接通过node节点的hostPort暴露服务
apiVersion: v1
kind: Pod
metadata:
  name: webapp
  namespace: dev1
  labels:
    app: webapp
spec:
  containers:
    - name: webapp
      image: nginx
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
          hostPort: 8081
      volumeMounts:
        - name: workdir
          mountPath: /usr/share/nginx/html
  volumes:
    - name: workdir
      persistentVolumeClaim:
        claimName: nfs-pvc1

执行kubectl命令。

然后查看pod的情况,发现pod一直处于创建中,如下:

于是查看pod的情况kubectl describe pod webapp -n dev1,发现如下异常信息:

是因为没有在NFS上创建此文件夹。到NFS创建此文件夹之后,重启Pod,一切正常了,然后找到Pod所在Node节点。通过http://nodeip:port访问,可以看到成功的界面:

复制代码
[root@k8s-master pv-pvc-storageclass]# kubectl get pods -n dev1 -owide  | grep webapp
webapp                                                 1/1     Running            0          4m17s   10.21.69.214   k8s-worker-3   <none>           <none>

此时因为nginx下还没有html页面,所以看不到内容。此时到NFS服务器对应的目录/data/nfstest/share/pv1下增加index.html页面,然后刷新页面即可,界面如下:

也可以通过进入到Pod内部,查看验证是够挂载成功。

执行进入Pod的命令kubectl exec -it webapp -n dev1 -- /bin/sh,可以看到如下页面:

2.2 Pod使用StorageClass自动挂载存储卷

2.2.1 安装 Provisioner

文中选择通过helm的方式安装nfs-subdir-external-provisioner,这种方式相对简单。安装文档、安装过程见下文:

安装文档

https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#nfs

https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

安装过程

通过以下3个步骤完成nfs-subdir-external-provisioner的安装。

1)安装helm,本文以mac为例

复制代码
brew install heml

2)安装nfs-subdir-external-provisioner,执行以下2个命令:

复制代码
$ helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
$ helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner -n kube-system \
    --set image.repository=dyrnq/nfs-subdir-external-provisioner \
    --set nfs.server=10.20.1.20 \
    --set nfs.path=/data/nfstest/nfs-storage

这里注意几个参数:

  • image.repository:修改了镜像的地址,默认用的国外镜像很有可能拉不下来
  • nfs.server:你的NFS服务器地址
  • nfs.path:存储目录

3)查看helm安装的结果:

执行命令:helm list -A,查看helm安装结果:

查看是否创建了对应的pod,如果没有修改镜像地址会一直拉取失败,如下图:

修改镜像地址后成功启动Pod,如下图:

2.2.2 使用StorageClass

文中演示的是:Pod利用StorageClass自动创建PV,同时在对应的存储目录上创建了文件,写入了数据。yaml文件如下:

复制代码
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-storage-1
provisioner: cluster.local/nfs-subdir-external-provisioner
parameters:
  # 设置为"false"时删除PVC不会保留数据,"true"则保留数据
  archiveOnDelete: "false"
mountOptions:
  # 指定NFS版本,这个需要根据NFS Server版本号设置
  - nfsvers=4
---
# 创建PVC
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nfs-storage-pvc-1
  namespace: dev1
spec:
  storageClassName: nfs-storage-1    #需要与上面创建的storageclass的名称一致
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Mi
---
kind: Pod
apiVersion: v1
metadata:
  name: nfs-storage-pod-1
  namespace: dev1
spec:
  containers:
    - name: nfs-storage-pod-1
      image: busybox
      command:
        - "/bin/sh"
      args:
        - "-c"
        - "touch /mnt/teststorage && echo 111 > /mnt/teststorage && exit 0 || exit 1"  ## 创建一个名称为"SUCCESS"的文件
      volumeMounts:
        - name: nfs-pvc
          mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim:
        claimName: nfs-storage-pvc-1

执行kubectl命令后,可以看到如下效果:

可以看到如我们预料的那样,通过storageClass自动创建了PV,同时在NFS对应的存储目录上创建了文件,写入了数据。

相关推荐
明月_清风1 分钟前
K8s 从入门到上手:核心概念+常用工具全解析
后端·kubernetes
阿里云云原生3 小时前
阿里云可观测 2026 年 4 月产品动态
云原生
qq_364371723 小时前
基于 Docker 容器化环境配置
运维·docker·容器
塔克拉玛攻城狮3 小时前
详解cni插件cilium篇一:它为什么这么快?它还有哪些高级功能?
kubernetes·cilium
阿里云云原生4 小时前
Skills Registry 公测开启:为企业打造私有的 Skill 管理中心
云原生
吃胖点儿4 小时前
DevOps与自动化原理
云原生
GentleDevin4 小时前
Docker 运维常用命令大全
docker·容器·运维命令
运维全栈笔记5 小时前
基于Docker的MinIO单机部署与功能测试指南
运维·docker·容器
阿里云云原生5 小时前
HiClaw 发布 v1.1.0,提供 Kubernetes 集群部署实现,支持 Hermes Worker 运行时
kubernetes
心机之蛙qee6 小时前
docker的安装(RHEL9)
运维·docker·容器