K8S的PV、PVC和storageClass的相关概念及实验

pv是容器使用的永久存储卷,pvc则是对pv的声明。一般pv有静态和动态两种。

静态的pv需要你先手工新建pv。然后你再创建一个pvc,如果静态pv符合pvc的要求则此pv会跟pvc绑定。不管是静态还是动态PV,pv和pvc都是一对一的关系。

动态的pv则是你不需要先手工建立pv,你只需要创建一个pvc,在pvc里面指定storageClass,则由storageClass关联的驱动会帮你自动创建pv并跟这个pvc绑定。当然你也可以不指定storageClass,这时的pvc会先找有没有合适的静态pv可以绑定,如果有则直接绑定,如果没有则查看默认storageClass,由默认storageClass创建pv之后绑定。

如果是批量使用PV的话,一个个创建PVC太麻烦了,可以使用volumeClaimTemplates批量创建pvc

下面用具体的例子来进行说明。

一、静态PV

1、先手工创建pv

复制代码
more pv_static.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: static-pv
  labels:
    type: static
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /mnt/data
kubectl apply -f pv_static.yaml

2、再创建一个PVC来绑定它

复制代码
more pvc_test1.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-static
  labels:
    type: static
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

kubectl apply -f pvc_test1.yaml

3、查看是否已经绑定

复制代码
kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS   REASON   AGE
static-pv                                  10Gi       RWO            Retain           Bound    default/pvc-static                              6d1h

可以看到static-pv 已经跟default/pvc-static进行了绑定。后面就可以在POD里面使用它了。

4、创建POD来使用这个pvc

复制代码
more web2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: web2
  labels:
    app: nginx
spec:
  volumes:
  - name: html
    hostPath:
      path: /root/k8s/html
  - name: data
    persistentVolumeClaim:
      claimName: pvc-static
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
    - name: data
      mountPath: /etc/config
    ports:
    - name: http
      protocol: TCP
      containerPort: 80

kubectl apply -f web2.yaml

这时查看这个pod创建在worker01上,此时worker01上会自动创建一个目录/mnt/data和/root/k8s/html,分别挂载到pod的/etc/config和/usr/share/nginx/html目录下。其中/etc/config是通过pvc创建的。

静态PVC实验

创建一个静态PV,挂载文件系统/mnt/data

创建一个PVC,并在两个不同的POD引用它,发现这两个POD运行在不同工作节点,并且都绑定了PV。都能正常运行

再创建一个pod,发现还是能正常启动,跟在同一工作节点的WEB容器共用了同一个PV

一个 PVC 只能绑定到一个 PV(一对一关系)

一个 PV 可以被多个 Pod 同时使用(取决于访问模式)

多个 Pod 引用同一个 PVC → 这些 Pod 共享同一个 PV

静态PV和动态PV都是这样

从这个流程可以看出,使用静态PV比较麻烦,每次都要管理员先创建PV,而且PV和PVC容量不一样,有可能会出现浪费的情况。

二、动态PV实验

动态 PV 依赖于 StorageClass 资源,它定义了如何动态创建存储的"类"。一个 StorageClass 包含以下关键信息:

  • provisioner:指定存储后端驱动的名称,例如:

    • kubernetes.io/aws-ebs -- AWS EBS

    • kubernetes.io/gce-pd -- GCE Persistent Disk

    • kubernetes.io/azure-disk -- Azure Disk

    • rook-ceph.rbd.csi.ceph.com -- Ceph RBD(CSI)

    • k8s.io/minikube-hostpath -- Minikube 测试用

    • 也可以使用第三方 provisioner,如 NFS Client Provisioner。

本次实验使用第三方 provisioner:NFS Client Provisioner。所以需要先安装这个存储驱动。kubernetes.io/开头的是K8S默认带的存储驱动,不需要安装。provisioner和StorageClass是一对多的关系,一个 provisioner可以创建多个StorageClass

1、先要找台服务器创建nfs服务器,这里面使用的IP是:192.168.23.42

复制代码
apt install -y nfs-kernel-server
sudo mkdir -p /data/k8s
sudo chown nobody:nogroup /data/k8s
sudo chmod 777 /data/k8s

/etc/exports
/data/k8s 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)

exportfs -ra
sudo systemctl restart nfs-kernel-server
sudo systemctl enable nfs-kernel-server

2、在所有工作节点安装nfs客户端

apt install -y nfs-common

3、安装helm

复制代码
wget https://get.helm.sh/helm-v3.15.0-linux-amd64.tar.gz
tar -zxvf helm-v3.15.0-linux-amd64.tar.gz
mv linux-amd64/helm /usr/local/bin/helm
helm version

4、添加仓库

复制代码
helm repo add azure http://mirror.azure.cn/kubernetes/charts/
helm repo update
helm repo list

helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/

5、安装NFS的Provisioner,成功之后会生成nfc的storageClass

复制代码
helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
    --set nfs.server=192.168.23.42 \
    --set nfs.path=/data/k8s \
    --set storageClass.name=nfs-sc \
    --set storageClass.defaultClass=true

这时发现nfs的pod没起来,拉取镜像失败:

复制代码
kubectl get pods -n default | grep nfs-subdir-external-provisioner
nfs-subdir-external-provisioner-dd7dfcb85-j2v5w   0/1     ImagePullBackOff   0          14m

在所有节点,包括主控节点和工作节点上执行以下命令:

先下载镜像

crictl pull registry.cn-hangzhou.aliyuncs.com/weiyigeek/nfs-subdir-external-provisioner:v4.0.2

再重命名

ctr -n k8s.io image tag registry.cn-hangzhou.aliyuncs.com/weiyigeek/nfs-subdir-external-provisioner:v4.0.2 registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2

这里面crictl 上K8S的容器管理工具,ctr是containerd的管理工具。crictl看到的容器和镜像是在 k8s.io这个命名空间里面的。

这时再查看pod,发现pod已经起来了

复制代码
kubectl get sc
NAME               PROVISIONER                                     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs-sc (default)   cluster.local/nfs-subdir-external-provisioner   Delete          Immediate           true                   11s

有了一个默认的storageClass:nfs-sc

6、最后测试动态pv

复制代码
more pvc_dyn.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-dynamic
  labels:
    type: dynamic
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi
这个PVC实际上跟静态的完全一样,K8S会根据规格先找静态PV,如果没有则使用默认的storageclass创建动态PV
kubectl apply -f pvc_dyn.yaml
这时会自动创建一个PV
kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS   REASON   AGE
pvc-dae92664-44e1-4657-8bd8-9f803af7e645   2Gi        RWO            Delete           Bound    default/pvc-dynamic     nfs-sc                  4d22h
static-pv                                  10Gi       RWO            Retain           Bound    default/pvc-static                              6d15h

在pod中使用

复制代码
more web6.yaml
apiVersion: v1
kind: Pod
metadata:
  name: web6
  labels:
    app: nginx
spec:
  volumes:
  - name: html
    persistentVolumeClaim:
      claimName: pvc-dynamic
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
引用这个PVC就可以了,多个POD可以引用同一个PVC,共享一个PV

7、批量自动创建pvc

从前面的例子可以看到,PV是自动创建了,但PVC还得我们一个个手工建,还是比较麻烦,可不可以自动创建pvc呢。在有状态的工作负载,即StatefulSet是可以做到的。这里面用到了volumeClaimTemplates。

复制代码
more statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx2
  labels:
    app: nginx2
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web2
  volumeClaimTemplates:
  - metadata:
      name: html
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 10Gi
  template:
    metadata:
      name: new-web
      labels:
        app: web2
    spec:
      containers:
      - image: nginx
        name: newweb
        volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html

volumeClaimTemplates里面没有定义storageClass,那就使用了默认的storageClass。我这里就是刚刚新建立的nfs-sc

复制代码
kubectl get pvc
NAME            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
html-nginx2-0   Bound    pvc-47b88f95-4b5e-4e35-960a-bd589d28ff11   10Gi       RWO            nfs-sc         2d19h
html-nginx2-1   Bound    pvc-52ceb697-af80-44e8-a400-ed1b696ba6e4   10Gi       RWO            nfs-sc         2d19h

kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS   REASON   AGE
pvc-47b88f95-4b5e-4e35-960a-bd589d28ff11   10Gi       RWO            Delete           Bound    default/html-nginx2-0   nfs-sc                  2d19h
pvc-52ceb697-af80-44e8-a400-ed1b696ba6e4   10Gi       RWO            Delete           Bound    default/html-nginx2-1   nfs-sc                  2d19h

登录上nfs服务器上查看,确实生成了相关目录:

复制代码
ls /data/k8s
default-html-nginx2-0-pvc-47b88f95-4b5e-4e35-960a-bd589d28ff11  default-pvc-dynamic2-pvc-5f8485d3-659c-4c04-9004-ca6e3624e85a
default-html-nginx2-1-pvc-52ceb697-af80-44e8-a400-ed1b696ba6e4  default-pvc-dynamic-pvc-dae92664-44e1-4657-8bd8-9f803af7e645
相关推荐
中国IT2 小时前
第3章:Docker与传统虚拟化比较
运维·docker·容器
luom01023 小时前
使用 Docker 部署 RabbitMQ 的详细指南
docker·容器·rabbitmq
阿望要努力上研究生5 小时前
Docker入门常用指令和Docker概念
运维·docker·容器
战南诚5 小时前
docker的使用技巧
运维·docker·容器
無限神樂5 小时前
docker,docker compose,k8s之间的区别
docker·容器·kubernetes
小峰编程6 小时前
二进制安装Nginx——详细
linux·运维·服务器·nginx·云原生
AC赳赳老秦6 小时前
2026多智能体协同趋势:DeepSeek搭建多智能体工作流,实现复杂任务自动化
人工智能·python·microsoft·云原生·virtualenv·量子计算·deepseek
AI攻城狮7 小时前
OpenClaw 的 reserveTokensFloor 到底怎么影响 auto-compaction?
人工智能·云原生·aigc
Elastic 中国社区官方博客8 小时前
Elasticsearch Serverless 的无状态架构
大数据·数据库·elasticsearch·搜索引擎·云原生·架构·serverless