一、前言:

​ 官网:https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/

​ 容器内部存储的生命周期是短暂的,会随着容器环境的销毁而销毁,具有不稳定性。如果多个容器希望共享同一份存储,则仅仅依赖容器本身是很难实现的。在Kubernetes系统中,将对容器应用所需的存储资源抽象为存储卷(Volume)概念来解决这些问题。 Volume是与Pod绑定的(独立于容器)与Pod具有相同生命周期的资源对象。可以将Volume的内容理解为目录或文件,容器如需使用某个Volume,则仅需设置volumeMounts将一个或多个Volume挂载为容器中的目录或文件,即可访问Volume中的数据。Volume具体是什么类型,以及由哪个系统提供,对容器应用来说是透明的。

二、Kubernetes存储分类

2.1 emptyDir

​ 简介:该类型的Volume将在Pod被调度到Node时进行创建,在初始状态下目录中是空的,所以命名为“空目录”(Empty Directory),它与Pod具有相同的生命周期,当Pod被销毁时,Node上相应的目录也会被删除。 同一个Pod中的多个容器都可以挂载这种VolumePod 启动时为空,存储空间来自本地的 kubelet 根目录(通常是根磁盘)或内存中。

#>>> 创建资源清单
$ vim nginx-deploy.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
 labels:
   app: nginx
 name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
     labels:
       app: nginx
    spec:
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/hujiaming/nginx:1.24.0
        imagePullPolicy: IfNotPresent
        name: nginx
        volumeMounts:
        - mountPath: /tmp
          name: test-volume
      - image: registry.cn-hangzhou.aliyuncs.com/hujiaming/tomcat:9.0.89-jdk8
        imagePullPolicy: IfNotPresent
        name: tomcat
        volumeMounts:
        - mountPath: /tmp
          name: test-volume
      restartPolicy: Always
      volumes:
      - name: test-volume
        emptyDir: {}
 #>>> 创建资源
$ kubectl create  -f nginx-deploy.yaml 
deployment.apps/nginx created

#>>> 查看资源
$ kubectl get po  -w
NAME                     READY   STATUS    RESTARTS   AGE
nginx-66744bbb6b-8mg2r   2/2     Running   0          5s

#>>> 进入其中一个容器中
$ kubectl  exec -it nginx-66744bbb6b-8mg2r  -c nginx -- bash
root@nginx-66744bbb6b-8mg2r:/# df -Th

image-20240628224527461

#>>> 容器挂载目录创建测试文件
root@nginx-66744bbb6b-8mg2r:/# echo "hello world" > /tmp/test.txt  
root@nginx-66744bbb6b-8mg2r:/# ls /tmp/test.txt 
/tmp/test.txt
root@nginx-66744bbb6b-8mg2r:/# exit
exit

#>>> 进入Pod中另外一个容器中,查看挂载目录内容
$ kubectl  exec -it nginx-66744bbb6b-8mg2r  -c tomcat  -- bash
root@nginx-66744bbb6b-8mg2r:/usr/local/tomcat# ls /tmp/
hsperfdata_root  test.txt
root@nginx-66744bbb6b-8mg2r:/usr/local/tomcat# cat /tmp/test.txt 
hello world

image-20240922220047550

---
apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
spec:
  containers:
  - name: tomcat
    image: registry.cn-hangzhou.aliyuncs.com/hujiaming/tomcat:9.0.89-jdk8
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: app-logs
      mountPath: /usr/local/tomcat/logs
  - name: busybox
    image: registry.cn-hangzhou.aliyuncs.com/hujiaming/busybox:1.36.1
    command: ["sh", "-c", "tail -f /logs/catalina*.log"]
    volumeMounts:
    - name: app-logs
      mountPath: /logs
  volumes:
  - name: app-logs
    emptyDir: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
 labels:
   app: nginx
 name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
     labels:
       app: nginx
    spec:
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/hujiaming/nginx:1.24.0
        imagePullPolicy: IfNotPresent
        name: nginx
        volumeMounts:
        - mountPath: /tmp
          name: test-volume
      - image: registry.cn-hangzhou.aliyuncs.com/hujiaming/tomcat:9.0.89-jdk8
        imagePullPolicy: IfNotPresent
        name: tomcat
        volumeMounts:
        - mountPath: /tmp
          name: test-volume
      restartPolicy: Always
      volumes:
      - name: test-volume
        emptyDir:
          medium: Memory  # 使用内存作为存储
          sizeLimit: 500Mi  # 限制为500MB

2.2 hostPath

​ 介绍:HostPath类型的存储卷用于将Node文件系统的目录或文件挂载到容器内部使用。对于大多数容器应用来说,都不需要使用宿主机的文件系统。容器应用的关键数据需要被持久化到宿主机上。 只要节点存在且路径未被删除,数据就会保留。

​ 示例: 修改容器时区。默认容器时间为UTC时间。

#>>> 创建资源清单
$ vim nginx-deploy.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
 labels:
   app: nginx
 name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
     labels:
       app: nginx
    spec:
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/hujiaming/nginx:1.24.0
        imagePullPolicy: IfNotPresent
        name: nginx
        volumeMounts:
        - mountPath: /etc/timezone
          name: timezone
        - mountPath: /etc/localtime
          name: date
      restartPolicy: Always
      volumes:
      - name: timezone
        hostPath:
          path: /etc/timezone
          type: File
      - name: date
        hostPath:
          path: /etc/localtime
          type: File

hostPath工作类型:

  • type 为空字符串:默认选项,意味着挂载 hostPath卷之前不会执行任何检查。

  • DirectoryOrCreate:如果给定的path不存在任何东西,那么将根据需要创建一个权限为0755 的空目录,和 Kubelet 具有相同的组和权限。

  • Directory:目录必须存在于给定的路径下。

  • FileOrCreate:如果给定的路径不存储任何内容,则会根据需要创建一个空文件,权限设置为 0644,和 Kubelet 具有相同的组和所有权。

  • File:文件,必须存在于给定路径中。

  • SocketUNIX 套接字,必须存在于给定路径中。

  • CharDevice:字符设备,必须存在于给定路径中。

  • BlockDevice:块设备,必须存在于给定路径中

---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  labels:
    name: node-exporter
spec:
  selector:
    matchLabels:
      name: node-exporter
  template:
    metadata:
      labels:
        name: node-exporter
    spec:
      hostPID: true
      hostIPC: true
      # 使用宿主机网络
      hostNetwork: true
      containers:
      - name: node-exporter
        image: registry.cn-hangzhou.aliyuncs.com/hujiaming/node-exporter:v1.8.2
        livenessProbe:
          httpGet:
            path: /metrics
            port: 9100
          initialDelaySeconds: 30
          timeoutSeconds: 10
          periodSeconds: 1
        readinessProbe:
          failureThreshold: 5
          httpGet:
            path: /metrics
            port: 9100
          initialDelaySeconds: 10
          timeoutSeconds: 10
          periodSeconds: 2
        ports:
        - containerPort: 9100
        resources:
          requests:
            cpu: 0.15
        securityContext:
          privileged: true
        args:
        - --path.procfs
        - /host/proc
        - --path.sysfs
        - /host/sys
        - --collector.filesystem.ignored-mount-points
        - '"^/(sys|proc|dev|host|etc)($|/)"'
        volumeMounts:
        - name: dev
          mountPath: /host/dev
        - name: proc
          mountPath: /host/proc
        - name: sys
          mountPath: /host/sys
        - name: rootfs
          mountPath: /rootfs
      tolerations:
      - key: "node-role.kubernetes.io/master"
        operator: "Exists"
        effect: "NoSchedule"
      volumes:
        - name: proc
          hostPath:
            path: /proc
        - name: dev
          hostPath:
            path: /dev
        - name: sys
          hostPath:
            path: /sys
        - name: rootfs
          hostPath:
            path: /

2.3 NFS(网络共享存储)

​ 简介:NFS 卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。

#>>> 所有节点安装nfs服务
$ yum -y install nfs-utils rpcbind

#>>> master01作为服务端
$ mkdir -p /data/nfs

$ chmod 666 /data/nfs

$ cat /etc/exports 
/data/nfs  192.168.174.0/24(rw,sync,no_root_squash,no_subtree_check)

$ exportfs -r

$ systemctl enable --now rpcbind nfs

#>>> 客户端测试
$ showmount  -e 192.168.174.30
#>>> 编写挂载测试资源清单
$ cat nginx-deploy.yaml 
---
apiVersion: apps/v1
kind: Deployment
metadata:
 labels:
   app: nginx
 name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
     labels:
       app: nginx
    spec:
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/hujiaming/nginx:1.24.0
        imagePullPolicy: IfNotPresent
        name: nginx
        volumeMounts:
        - mountPath: /etc/timezone
          name: timezone
        - mountPath: /etc/localtime
          name: date
        - mountPath: /tmp
          name: nfs-volume
      restartPolicy: Always
      volumes:
      - name: timezone
        hostPath:
          path: /etc/timezone
          type: File
      - name: date
        hostPath:
          path: /etc/localtime
          type: File
      - name: nfs-volume
        nfs:
          server: 192.168.174.30
          path: /data/nfs/test


#>>> 创建资源
$ kubectl apply -f nginx-deploy.yaml 

#>>> 测试
$ kubectl exec  nginx-54879c9c9c-zp4zl  -- touch  /tmp/222

#>>> 查看服务端挂载路径
$ ll /data/nfs/test/
-rw-r--r-- 1 root root 0 629 22:00 222

2.4 持久卷

Kubernetes对于有状态的容器应用或者对数据需要持久化的应用,不仅需要将容器内的目录挂载到宿主机的目录或者emptyDir临时存储卷,而且需要更加可靠的存储来保存应用产生的重要数据,以便容器应用在重建之后仍然可以使用之前的数据。不过,存储资源和计算资源(CPU/内存)的管理方式完全不同。为了能够屏蔽底层存储实现的细节,让用户方便使用,同时让管理员方便管理,Kubernetes引入PersistentVolume(PV)PersistentVolumeClaim(PVC)两个资源对象来实现对存储的管理子系统。

持久卷(PersistentVolumePV 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的Volume一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV Pod 的生命周期。 此API对象中记述了存储的实现细节,无论其背后是NFS还是特定于云平台的存储系统。
持久卷申领(PersistentVolumeClaimPVC 表达的是用户对存储的请求。概念上与 Pod 类似。Pod 会耗用节点资源,而 PVC 申领会耗用PV资源。Pod 可以请求特定数量的资源(CPU 和内存)。同样 PVC 申领也可以请求特定的大小和访问模式。

​ 虽然PersistentVolumeClaim 允许用户消耗抽象的存储资源, 常见的情况是针对不同的问题用户需要的是具有不同属性(如,性能)的 PersistentVolume 卷。 集群管理员需要能够提供不同性质的PersistentVolume, 并且这些 PV 卷之间的差别不仅限于卷大小和访问模式,同时又不能将卷是如何实现的这些细节暴露给用户。 为了满足这类需求,就有了存储类(StorageClass)资源。

2.4.1 PersistentVolume详细介绍

​ 简称PV,是由Kubernetes管理员设置的存储,可以配置CephNFSGlusterFS等常用存储配置,相对于Volume配置,提供了更多的功能,比如生命周期的管理、大小的限制。PV分为静态和动态。PV可以被理解成Kubernetes集群中的某个网络存储对应的一块存储,PV作为存储资源,主要包括存储能力、访问模式、存储类型、回收策略、后端存储类型等关键信息的设置。

1. PersistentVolume的关键配置参数
  • 存储能力(Capacity):描述存储设备具备的能力,目前仅支持对存储空间的设置。目前,存储大小是可以设置和请求的唯一资源。

  • 存储卷模式(Volume Mode):针对 PV 持久卷,Kubernetes 支持两种卷模式(volumeModes):Filesystem(文件系统)Block(块)volumeMode 是一个可选的 API 参数。 如果该参数被省略,默认的卷模式是 FilesystemvolumeMode 属性设置为 Filesystem 的卷会被 Pod挂载(Mount)到某个目录。 如果卷的存储来自某块设备而该设备目前为空,Kuberneretes 会在第一次挂载卷之前在设备上创建文件系统。你可以将 volumeMode 设置为 Block,以便将卷作为原始块设备来使用。 这类卷以块设备的方式交给 Pod 使用,其上没有任何文件系统。 这种模式对于为Pod提供一种使用最快的方式来访问卷, Pod 和卷之间不存在文件系统层。另外,Pod中运行的应用必须知道如何处理原始块设备。如何在 Pod 中使用 volumeMode: Block 的卷, 可参阅原始块卷支持

2. PersistentVolume回收策略(Reclaim Policy
  • Retain:保留,该策略允许手动回收资源,当删除PVC时,PV仍然存在,PV被视为已释放,管理员可以手动回收卷。
  • Recycle:回收,如果Volume插件支持,Recycle策略会对卷执行 rm -rf清理该PV,并使其可用于下一个新的PVC,目前回收策略 Recycle 已被废弃。
  • Delete:删除,如果Volume插件支持,删除PVC时会同时删除PV,动态卷默认为Delete
3. PersistentVolume访问策略(Access Modes
  • ReadWriteOnce:可以被单节点以读写模式挂载,命令行中可以被缩写为RWO。(块存储)适用于单Pod独占存储的场景(如数据库)。PV 只能被调度到同一节点上的 Pod 挂载;同一节点上只能有一个 Pod 挂载该 PV 并写入数据。

  • ReadOnlyMany:可以被多个节点以只读模式挂载,命令行中可以被缩写为ROX。适用于需要多节点共享静态数据的场景(如配置文件)。

  • ReadWriteMany:可以被多个节点以读写模式挂载,命令行中可以被缩写为RWX。(文件共享存储)

  • ReadWriteOncePod:可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个Pod可以读取或写入该 PVC, 请使用 ReadWriteOnce访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本。命令行中可以缩写为RWOPKubernetesv1.28进入到了Beta版本。

4. PersistentVolume周期的各个阶段状态
  • Available:可用,没有被PVC绑定的空闲资源。

  • Bound:已绑定,已经被PVC绑定。

  • Released:已释放,PVC被删除,但是资源还未被重新使用。

  • Failed:失败,自动回收失败

PVPVC成功创建并不代表挂载成功,需要Pod成功启动过后才可以发现挂载是否成功。

5. 存储分类
  • 文件存储:一些数据可能需要被多个节点使用,比如用户的头像、用户上传的文件等,实现方式:NFSNASFTPCephFS等。
  • 块存储:一些数据只能被一个节点使用,或者是需要将一块裸盘整个挂载使用,比如数据库、Redis等,实现方式:CephGlusterFS、公有云。
  • 对象存储:由程序代码直接实现的一种存储方式,云原生应用无状态化常用的实现方式,实现方式:一般是符合S3协议的云存储,比如AWSS3存储、Minio、七牛云等

2.4.2 PersistentVolume使用案例

PV没有命名空间隔离性

1. NFS网络共享存储配置
#>>> 服务端安装NFS
$ yum install nfs-utils rpcbind -y

#>>> 服务端创建共享目录
$ mkdir -p /data/k8s

$ vim /etc/exports 
/data/ 192.168.174.0/24(rw,sync,no_subtree_check,no_root_squash)

$ exportfs -r
	
#>>> 服务端启动NFS
$ systemctl restart nfs rpcbind

#>>> 所有客户端安装nfs-utils
$ yum install -y nfs-utils

#>>> 挂载测试
$ mount -t nfs 192.168.174.30:/data/ /mnt/
	
#>>> 取消挂载
$ umount /mnt/

#>>> 创建资源清单
$ vim nfs-pv.yaml
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs  # PV名称
spec:
  capacity:
    storage: 5Gi  # 容量配置,nfs不支持容量配置,配置时需要后端存储设备支持。
  volumeMode: Filesystem # 卷的模式,目前支持Filesystem(文件系统)和Block(块),其中Block类型需要后端存储支持,默认为文件系统。NFS不支持块存储
  accessModes:
  - ReadWriteOnce     # PV的访问模式
  persistentVolumeReclaimPolicy: Delete   # PV的回收策略
  storageClassName: nfs-slow   # PV的类,一个特定类型的PV只能绑定到特定类别的PVC;
  nfs:
    path: /data/k8s    # NFS上的共享目录
    server: 192.168.174.30   # NFS的IP地址

#>>> 创建PV
$ kubectl create -f nfs-pv.yaml
	
#>>> 查看PV
$ kubectl get pv 

image-20240630002010458

2. hostPath配置
$ vim hostPath-pv.yaml
---
kind: PersistentVolume
apiVersion: v1
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  storageClassName: hostpath
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  hostPath:  # hostPath服务配置
    path: "/mnt/data"  # 宿主机路径

#>>> 创建PV
$ kubectl create -f hostPath-pv.yaml
	
#>>> 查看PV
$ kubectl  get -f hostPath-pv.yaml 

image-20240630002447200

2.4.3 PersistentVolumeClaim详细介绍

​ 简称PVC,是对存储PV的请求,表示需要什么类型的PV,需要存储的技术人员只需要配置PVC即可使用存储,或者Volume配置PVC的名称即可。PVC作为用户对存储资源的需求申请,主要包括存储空间请求、访问模式、PV选择条件和存储类别等信息的设置。

1. PersistentVolumeClaim关键配置参数
  • 资源请求(Resources):描述对存储资源的请求。申请的资源只能小于或等于PV的容量。但如果申请的容量小于PV的实际容量,那么剩余未申请容量会浪费。

  • 访问模式(Access Modes):PVC也可以设置访问模式,用于描述用户应用对存储资源的访问权限。访问模式的设置与PV的设置相同。

  • 存储卷模式(Volume Modes):PVC也可以设置存储卷模式,用于描述希望使用的PV存储卷模式,包括文件系统和块设备。

2.4.4 PersistentVolumeClaim使用案例

PersistentVolumeClaim受命名空间隔离性

$ vim pvc-nfs.yaml
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: task-pvc-claim   # pvc的名称
spec:
  storageClassName: nfs-slow     # pv SC名称
  accessModes:
  - ReadWriteOnce         # 和PV的访问策略一致
  resources:
    requests:
      storage: 3Gi      # 只能小于等于PV的容量

#>>> 创建PVC
$ kubectl create -f pvc-nfs.yaml 
persistentvolumeclaim/task-pvc-claim created

#>>> 查看PVC 
$ kubectl get -f pvc-nfs.yaml
NAME             STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
task-pvc-claim   Bound    pv-nfs   5Gi        RWO            nfs-slow       17s
	
#>>> 查看绑定的PV
$ kubectl get -f  nfs-pv.yaml

image-20240630002901244

结果显示PersistentVolume的状态变为Bound,已被挂载。

创建deploy实例,测试挂载PVC

$ vim nfs-pvc-deploy.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
    labels:
      app: nginx
    name: nginx
    namespace: default
spec:
    replicas: 1
    selector:
      matchLabels:
        app: nginx
    template:
      metadata:
        labels:
          app: nginx
      spec:
        containers:
        - image: registry.cn-hangzhou.aliyuncs.com/hujiaming/nginx:1.24.0
          imagePullPolicy: IfNotPresent
          name: nginx
          volumeMounts:
          - mountPath: "/usr/share/nginx/html"  # 挂载路径
            name: task-pv-storage
        restartPolicy: Always
        volumes:
        - name: task-pv-storage     # volume名称
          persistentVolumeClaim:
            claimName: task-pvc-claim     # pvc名称

#>>> 创建资源
$ kubectl create -f nfs-pvc-deploy.yaml

#>>> 查看pod
$ kubectl get po 
NAME                    READY   STATUS    RESTARTS   AGE
nginx-8d486646b-tcrp4   1/1     Running   0          5s
nginx-8d486646b-v46hc   1/1     Running   0          5s

#>>> 测试
$ kubectl exec  nginx-8d486646b-tcrp4  -- df -Th

2.5 PVC创建和挂载失败的原因

  • PVC一直Pending的原因:

    • PVC的空间申请大小大于PV的大小;

    • PVCStorageClassName没有和PV的一致;

    • PVCaccessModesPV的不一致。

  • 挂载PVCPod一直处于Pending

    • PVC没有创建成功PVC不存在;

    • PVCPod不在同一个Namespace

2.6 PVPVC的生命周期

​ 可以将PersistentVolume看作可用的存储资源,PersistentVolumeClaim则是对存储资源的需求,PersistentVolumePersistentVolumeClaim的相互关系的生命周期。

image-20250224220213182

2.7 持久卷资源供应模式

Kubernetes支持两种资源的供应模式:静态模式(Static)和动态模式(Dynamic)。 本质上讲资源供应的结果就是创建好的PV

2.7.1 静态模式

​ 集群管理员手工创建许多PV,在定义PV时需要将后端存储的特性进行设置。这些卷对象带有真实存储的细节信息, 并且对集群用户可用(可见)。PV 卷对象存在于 Kubernetes API 中,可供用户消费(使用)。

​ 在静态资源供应模式下,通过PVPVC完成绑定,并供Pod使用的存储管理机制。

image-20250224220913317

2.7.2 动态模式

​ 集群管理员无须手工创建PV,而是通过StorageClass的设置对后端存储进行描述,标记为某种类型。此时要求PVC对存储的类型进行声明,系统将自动完成PV的创建及与PVC的绑定。

​ 在动态资源供应模式下,通过StorageClassPVC完成资源动态绑定(系统自动生成PV),并供Pod使用的存储管理机制。

image-20250224221022949

2.8 持久卷基于NFS实现动态存储实战

Provisioner(存储置备程序/存储供应者) 是一个用于动态创建和管理持久卷(PersisentVolume, PV)的组件。它的核心作用是将存储资源的分配自动化,替代传统手动创建 PV PVCPersistentVolumeClaim)的流程。

  1. 动态存储卷分配:当用户创建 PVC 时,Provisioner 会根据 PVC 的请求(如存储大小、访问模式等)自动在NFS服务器上创建对应的子目录,并生成一个 PV 与之绑定。
  2. 与存储后端交互Provisioner 通过对接 NFS 服务器,将存储资源抽象为 Kubernetes 可识别的存储类(StorageClass),实现存储资源的按需分配。
  3. 命名规则:动态创建的 PV 对应的目录通常遵循 ${namespace}-${pvcName}-${pvName} 的命名规则,确保隔离性和可追溯性。

2.8.1 创建NFS RBAC

# Step 1:创建namespace
$ kubectl create ns  nfs-storage

# Step 2:创建RBAC
$ vim nfs-rbac.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: nfs-storage
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: nfs-storage
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: nfs-storage
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: nfs-storage
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: nfs-storage
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

$ kubectl create  -f nfs-rbac.yaml 

2.8.2 创建NFS provisioner存储供应者

$ vim nfs-client-provisioner-deploy.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: nfs-storage
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: registry.cn-hangzhou.aliyuncs.com/hujiaming/nfs-subdir-external-provisioner:v4.0.2
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              value: 192.168.174.111
            - name: NFS_PATH
              value: /data/nfs
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.174.111
            path: /data/nfs
  
$ kubectl create -f nfs-client-provisioner-deploy.yaml 

2.8.3 创建NFS StorageClass存储类

$ vim nfs-class.yaml    
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"  # 设为默认存储类
  name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # 必须与 Deployment 中的 PROVISIONER_NAME 一致
parameters:
  archiveOnDelete: "false" # 删除 PVC 时是否保留数据(true=保留,false=删除)
  
$ kubectl create  -f nfs-class.yaml 

$ kubectl get sc

image-20250225210336232

2.8.4 Deployment测试使用动态存储

$ vim test-pvc-deploy.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pvc
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/hujiaming/nginx:1.24.0-alpine
        name: web
        volumeMounts:
        - name: nfs-volume
          mountPath: /usr/share/nginx/html
      volumes:
      - name: nfs-volume
        persistentVolumeClaim:
          claimName: test-pvc     
$ kubectl create  -f test-pvc-deploy.yaml 

$ kubectl get pv,pvc

image-20250225212657194

# Step 1:查看NFS共享目录
$ tree /data/k8s/
/data/k8s/
└── default-test-pvc-pvc-766bc3b6-6412-4540-b2b9-506082a84ca1

1 directory, 0 files

# Step 2:删除deploy和pvc资源
$  kubectl delete -f test-pvc-deploy.yaml 
persistentvolumeclaim "test-pvc" deleted
deployment.apps "web" deleted

# Step 1:再次查看NFS共享目录
$ tree /data/k8s/
/data/k8s/

0 directories, 0 files

2.8.5 StatefulSet 测试挂载动态存储

$ test-sc-sts.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # has to match .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # by default is 1
  minReadySeconds: 10 # by default is 0
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: registry.cn-hangzhou.aliyuncs.com/hujiaming/nginx:1.24.0-alpine
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "nfs-client"
      resources:
        requests:
          storage: 1Gi
$ kubectl create -f test-sc-sts.yaml 

$ kubectl get pv,pvc

image-20250225211408273

# Step 1: NFS服务器共享目录查看
$ tree /data/k8s/
/data/k8s/
├── default-www-web-0-pvc-4379f00e-d697-4edd-add7-6b32f8922290
├── default-www-web-1-pvc-3df26c83-512f-4809-8223-3bbb630b3807
└── default-www-web-2-pvc-10f8c352-b0ea-4450-b0ad-c31600f41ccb

# Step 2:删除资源
$ kubectl delete -f test-sc-sts.yaml 

# Step 3:再次查看pv和pvc
$ kubectl get pv,pvc

image-20250225211612708

# Step 4:删除PVC
$ kubectl delete pvc www-web-0 www-web-1 www-web-2
persistentvolumeclaim "www-web-0" deleted
persistentvolumeclaim "www-web-1" deleted
persistentvolumeclaim "www-web-2" deleted

# Step 5:NFS服务器共享目录查看
$ tree /data/k8s/
/data/k8s/

0 directories, 0 files

注意:根据StorageClass资源清单中设置的回收策略。当删除PVC时。PV会被自动删除
accessModes: [ “ReadWriteOnce” ]
storageClassName: “nfs-client”
resources:
requests:
storage: 1Gi


```bash
$ kubectl create -f test-sc-sts.yaml 

$ kubectl get pv,pvc

[外链图片转存中…(img-dyfijj1S-1749641481746)]

# Step 1: NFS服务器共享目录查看
$ tree /data/k8s/
/data/k8s/
├── default-www-web-0-pvc-4379f00e-d697-4edd-add7-6b32f8922290
├── default-www-web-1-pvc-3df26c83-512f-4809-8223-3bbb630b3807
└── default-www-web-2-pvc-10f8c352-b0ea-4450-b0ad-c31600f41ccb

# Step 2:删除资源
$ kubectl delete -f test-sc-sts.yaml 

# Step 3:再次查看pv和pvc
$ kubectl get pv,pvc

[外链图片转存中…(img-6pLvXzWQ-1749641481746)]

# Step 4:删除PVC
$ kubectl delete pvc www-web-0 www-web-1 www-web-2
persistentvolumeclaim "www-web-0" deleted
persistentvolumeclaim "www-web-1" deleted
persistentvolumeclaim "www-web-2" deleted

# Step 5:NFS服务器共享目录查看
$ tree /data/k8s/
/data/k8s/

0 directories, 0 files

注意:根据StorageClass资源清单中设置的回收策略。当删除PVC时。PV会被自动删除

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐