06-Kubernetes-存储篇
官网:https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/容器内部存储的生命周期是短暂的,会随着容器环境的销毁而销毁,具有不稳定性。如果多个容器希望共享同一份存储,则仅仅依赖容器本身是很难实现的。在Kubernetes系统中,将对容器应用所需的存储资源抽象为存储卷(Volume)概念来解决这些问题。Volume是与Pod绑定的(独
文章目录
一、前言:
官网: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中的多个容器都可以挂载这种Volume。 Pod 启动时为空,存储空间来自本地的 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

#>>> 容器挂载目录创建测试文件
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

---
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:文件,必须存在于给定路径中。 -
Socket:UNIX套接字,必须存在于给定路径中。 -
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 6月 29 22:00 222
2.4 持久卷
Kubernetes对于有状态的容器应用或者对数据需要持久化的应用,不仅需要将容器内的目录挂载到宿主机的目录或者emptyDir临时存储卷,而且需要更加可靠的存储来保存应用产生的重要数据,以便容器应用在重建之后仍然可以使用之前的数据。不过,存储资源和计算资源(CPU/内存)的管理方式完全不同。为了能够屏蔽底层存储实现的细节,让用户方便使用,同时让管理员方便管理,Kubernetes引入PersistentVolume(PV)和PersistentVolumeClaim(PVC)两个资源对象来实现对存储的管理子系统。
持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的Volume一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此API对象中记述了存储的实现细节,无论其背后是NFS还是特定于云平台的存储系统。
持久卷申领(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求。概念上与 Pod 类似。Pod 会耗用节点资源,而 PVC 申领会耗用PV资源。Pod 可以请求特定数量的资源(CPU 和内存)。同样 PVC 申领也可以请求特定的大小和访问模式。
虽然PersistentVolumeClaim 允许用户消耗抽象的存储资源, 常见的情况是针对不同的问题用户需要的是具有不同属性(如,性能)的 PersistentVolume 卷。 集群管理员需要能够提供不同性质的PersistentVolume, 并且这些 PV 卷之间的差别不仅限于卷大小和访问模式,同时又不能将卷是如何实现的这些细节暴露给用户。 为了满足这类需求,就有了存储类(StorageClass)资源。
2.4.1 PersistentVolume详细介绍
简称PV,是由Kubernetes管理员设置的存储,可以配置Ceph、NFS、GlusterFS等常用存储配置,相对于Volume配置,提供了更多的功能,比如生命周期的管理、大小的限制。PV分为静态和动态。PV可以被理解成Kubernetes集群中的某个网络存储对应的一块存储,PV作为存储资源,主要包括存储能力、访问模式、存储类型、回收策略、后端存储类型等关键信息的设置。
1. PersistentVolume的关键配置参数
-
存储能力(
Capacity):描述存储设备具备的能力,目前仅支持对存储空间的设置。目前,存储大小是可以设置和请求的唯一资源。 -
存储卷模式(
Volume Mode):针对PV持久卷,Kubernetes支持两种卷模式(volumeModes):Filesystem(文件系统)和Block(块)。volumeMode是一个可选的API参数。 如果该参数被省略,默认的卷模式是Filesystem。volumeMode属性设置为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以上版本。命令行中可以缩写为RWOP。Kubernetesv1.28进入到了Beta版本。
4. PersistentVolume周期的各个阶段状态
-
Available:可用,没有被PVC绑定的空闲资源。 -
Bound:已绑定,已经被PVC绑定。 -
Released:已释放,PVC被删除,但是资源还未被重新使用。 -
Failed:失败,自动回收失败
PV和PVC成功创建并不代表挂载成功,需要Pod成功启动过后才可以发现挂载是否成功。
5. 存储分类
- 文件存储:一些数据可能需要被多个节点使用,比如用户的头像、用户上传的文件等,实现方式:
NFS、NAS、FTP、CephFS等。 - 块存储:一些数据只能被一个节点使用,或者是需要将一块裸盘整个挂载使用,比如数据库、
Redis等,实现方式:Ceph、GlusterFS、公有云。 - 对象存储:由程序代码直接实现的一种存储方式,云原生应用无状态化常用的实现方式,实现方式:一般是符合
S3协议的云存储,比如AWS的S3存储、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

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

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

结果显示
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的大小; -
PVC的StorageClassName没有和PV的一致; -
PVC的accessModes和PV的不一致。
-
-
挂载
PVC的Pod一直处于Pending:-
PVC没有创建成功PVC不存在; -
PVC和Pod不在同一个Namespace。
-
2.6 PV和PVC的生命周期
可以将PersistentVolume看作可用的存储资源,PersistentVolumeClaim则是对存储资源的需求,PersistentVolume和PersistentVolumeClaim的相互关系的生命周期。

2.7 持久卷资源供应模式
Kubernetes支持两种资源的供应模式:静态模式(Static)和动态模式(Dynamic)。 本质上讲资源供应的结果就是创建好的PV。
2.7.1 静态模式
集群管理员手工创建许多PV,在定义PV时需要将后端存储的特性进行设置。这些卷对象带有真实存储的细节信息, 并且对集群用户可用(可见)。PV 卷对象存在于 Kubernetes API 中,可供用户消费(使用)。
在静态资源供应模式下,通过PV和PVC完成绑定,并供Pod使用的存储管理机制。

2.7.2 动态模式
集群管理员无须手工创建PV,而是通过StorageClass的设置对后端存储进行描述,标记为某种类型。此时要求PVC对存储的类型进行声明,系统将自动完成PV的创建及与PVC的绑定。
在动态资源供应模式下,通过StorageClass和PVC完成资源动态绑定(系统自动生成PV),并供Pod使用的存储管理机制。

2.8 持久卷基于NFS实现动态存储实战
Provisioner(存储置备程序/存储供应者) 是一个用于动态创建和管理持久卷(PersisentVolume, PV)的组件。它的核心作用是将存储资源的分配自动化,替代传统手动创建 PV 和 PVC(PersistentVolumeClaim)的流程。
- 动态存储卷分配:当用户创建
PVC时,Provisioner会根据PVC的请求(如存储大小、访问模式等)自动在NFS服务器上创建对应的子目录,并生成一个PV与之绑定。 - 与存储后端交互:
Provisioner通过对接NFS服务器,将存储资源抽象为Kubernetes可识别的存储类(StorageClass),实现存储资源的按需分配。 - 命名规则:动态创建的
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

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

# 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

# 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

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