目录
背景
容器部署过程中一般有以下三种数据:
(1)启动时需要的初始数据,例如配置文件;
(2)启动过程中产生的临时数据,该临时数据需要多个容器间共享,例如类似于Redis的数据库;
(3)启动容器过程中产生的持久化数据,例如MySQL的数据目录(datadir);
所以,我们可以基于数据卷的方式实现数据的共享。
volume
介绍
在容器中的磁盘文件是短暂的,当容器崩溃时,Kubelet会重新启动容器,但容器运行时产生的数据文件都将会丢失,之后容器会以最干净的状态启动。另外,当一个Pod运行多个容器时,各个容器可能需要共享一些文件,诸如此类的需求都可以使用Volume解决。Pod只需要通过.spec.volumes字段指定为Pod提供的卷,然后在容器中配置块,使用.spec.containers.volumeMounts字段指定卷挂载的目录即可
在Kubernetes中,Volume也支持配置许多常用的存储,用于挂载到Pod中实现数据的持久化
Kubernetes Volume支持的卷的类型有很多,以下为常用的卷:
本地数据卷:
hostPath,emptyDir等。
网络数据卷:
NFS,Ceph,GlusterFS等。
公有云:
AWS,EBS等;
K8S资源:
configmap,secret等。
以上列举的是一些比较常用的类型,其他支持的类型可以查看Volume的官方文档
https://kubernetes.io/docs/concepts/storage/volumes/
emptyDir
emptyDir数据卷:
- 是一个临时存储卷,与Pod生命周期绑定在一起,如果Pod删除了,这意味着数据卷也会被删除。
emptyDir的作用:
- 可以实现持久化的功能;
- 多个Pod之间不能通信数据,但是同一个Pod的多个容器是可以实现数据共享的;
- 随着Pod的生命周期而存在,当我们删除Pod时,其数据也会被随之删除。
emptyDir的应用场景(同一个Pod中各个容器之间数据的共享):
- 如Filebeat收集容器内程序产生的日志;
- 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行;
温馨提示:
使用emptyDir持久化数据时,删除容器并不会删除数据,因为容器删除并不能说明Pod被删除哟。
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: registry.k8s.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir:
sizeLimit: 500Mi
HostPath
hotsPath数据卷:
- 挂载Node文件系统(Pod所在节点)上文件或者目录到Pod中的容器。
- 如果Pod删除了,宿主机的数据并不会被删除,这一点是否感觉和咱们的数据卷有异曲同工之妙呢?
应用场景:
- 常用的示例有挂载宿主机的时区至Pod,或者将Pod的日志文件挂载到宿主机等
在配置HostPath时,有一个type的参数,用于表达不同的挂载类型,HostPath卷常用的type(类型)如下:
- type为空字符串:默认选项,意味着挂载hostPath卷之前不会执行任何检查
- DirectoryOrCreate:如果给定的path不存在任何东西,那么将根据需要创建一个权限为0755的空目录,和Kubelet具有相同的组和权限
- Directory:目录必须存在于给定的路径下
- FileOrCreate:如果给定的路径不存储任何内容,则会根据需要创建一个空文件,权限设置为0644,和Kubelet具有相同的组和所有权
- File:文件,必须存在于给定路径中
- Socket:UNIX套接字,必须存在于给定路径中
- CharDevice:字符设备,必须存在于给定路径中
- BlockDevice:块设备,必须存在于给定路径中。
apiVersion: v1
kind: Pod
metadata:
name: hostpath-example-linux
spec:
os: { name: linux }
nodeSelector:
kubernetes.io/os: linux
containers:
- name: example-container
image: registry.k8s.io/test-webserver
volumeMounts:
- mountPath: /foo
name: example-volume
readOnly: true
volumes:
- name: example-volume
# mount /data/foo, but only if that directory already exists
hostPath:
path: /data/foo # directory location on host
type: Directory # this field is optional
NFS
和emptyDir、HostPath的配置方法类似,NFS的Volume配置也是在Volumes字段中配置的,和emptyDir不同的是,NFS属于持久化存储的一种,在Pod删除或者重启后,数据依旧会存储在NFS节点上。要使用nfs,k8s的node节点上需要安装好nfs客户端软件。
1.#全部安装nfs-utils
yum install -y nfs-utils
2.#创建共享目录
mkdir -p /data/kubernetes
3.#配置挂载点
[root@k8s151 pods]# cat /etc/exports
#k8s数据卷共享
/data/kubernetes *(rw,no_root_squash)
4.#测试看看能不能用
showmount -e 10.0.0.151
mount -t nfs 10.0.0.151:/data/kubernetes /mnt
df -h
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: registry.k8s.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /my-nfs-data
name: test-volume
volumes:
- name: test-volume
nfs:
server: my-nfs-server.example.com
path: /my-nfs-volume
readOnly: true
configMap
ConfigMap是用来存储配置文件的kubernetes资源对象,所有的配置内容都存储在etcd中。
创建ConfigMap的方式有4种:
通过直接在命令行中指定configmap参数创建,即--from-literal
通过指定文件创建,即将一个配置文件创建为一个ConfigMap--from-file=<文件>
通过指定目录创建,即将一个目录下的所有配置文件创建为一个ConfigMap,--from-file=<目录>
事先写好标准的configmap的yaml文件,然后kubectl create -f 创建
使用 YAML 文件定义 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
data:
key1: value1
key2: value2
POD 引用 ConfigMap
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: nginx
env:
- name: CONFIG_KEY
valueFrom:
configMapKeyRef:
name: my-config
key: key1
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: my-config
Secret
与ConfigMap类似,区别在于Secret主要存储铭感数据,所有的数据要经过base64编码。
但对于程序员而言,这依旧是明文数据,只不过想要拿到明文数据需要只需使用base64进行解码即可。
如下所示,基于base64进行编码和解码只需一行命令就搞定了。
[root@k8s-m1 ~]# echo -n "wfwkbgdqs$2e2f4" | base64
d2Z3a2JnZHFzZTJmNA==
[root@k8s-m1 ~]# echo -n "d2Z3a2JnZHFzZTJmNA==" | base64 -d
wfwkbgdqs$2e2f4
应用场景:
多用于存储凭据信息。
"kubectl create secret"支持常用的三种数据类型如下:(较ConfigMap支持的类型要多,我们可以理解ConfigMap支持的只是存储文本信息,对于存储仓库认证信息,TLS证书等ConfigMap均不支持,此时我们应该采用Secret哟~)
(1)docker-registry(kubernetes.io/dockerconfigjson):
存储镜像仓库认证信息。
(2)generic(Opaque):
存储密码,秘钥等。
(3)tls(kubernetes.io/tls):
存储TLS证书。
除了上面提到的几种类型,还有其它几种内置的数据类型(我从官网摘抄如下表所示),推荐阅读:
https://kubernetes.io/zh/docs/concepts/configuration/secret/
apiVersion: v1
kind: Secret
metadata:
name: db-user-pass
type: Opaque # 此处我们指定Secret资源的类型为"Opaque",通常用于存储密码,密钥等.
data:
# 将编码后的值放到Secret资源的配置文件中
username: YWRtaW4=
password: eWluemhlbmdqaWU=
PersistentVolume
介绍
虽然volume实现了持久化存储,但是诸多高级特性还是无法实现。且没有生命周期的管理。在实际使用中volume面临的问题如下:
- 当某个数据卷不再被挂载使用时,里面的数据如何处理?
- 如果想要实现只读挂载如何处理?
- 如果想要只能有一个Pod挂载如何处理?
为此k8s引入了两个新的API资源:PersistentVolume和PersistentVolumeClaim。
-
PersistentVolume:属于k8s集群的全局资源,因此并不支持名称空间(namespace)。该资源对象本身并不负责数据的真实存储,而是负责和后端存储进行关联,每个pv都有提前定义好的存储能力。持久卷是对存储资源数据卷(volume)创建和使用的抽象,使得存储作为集群中的资源管理。这样我们就可以对数据卷的使用空间,读写权限做一个基本的权限管控,而不是放任Pod使用所有的数据卷资源。
-
PersistentVolumeClaim: 对PV的请求,表示需要什么类型的PV。和单独配置Volume类似,PV也可以使用NFS、GFS、CEPH等常用的存储后端,并且可以提供更加高级的配置,比如访问模式、空间大小以及回收策略等
目前PV的提供方式有两种:静态或动态。
- 静态PV由管理员提前创建
- 动态PV无须提前创建,由storageclass创建
PV回收策略
当用户使用完volume(pv本质上也是volume)时,可以删除PVC对象,从而通过回收策略回收pv资源。目前回收策略有以下三种。
- Retain:保留,该策略允许手动回收资源,当删除PVC时,PV仍然存在,Volume被视为已释放,管理员可以手动回收卷。不指定回收策略默认为retain。
- Recycle(k8s1.14版本开始已废弃):回收,如果Volume插件支持,Recycle策略会对卷执行rm -rf清理该PV,并使其可用于下一个新的PVC,目前只有NFS和HostPath支持该策略。
- Delete:删除,如果Volume插件支持,删除PVC时会同时删除PV,动态卷默认为Delete,目前支持Delete的存储后端包括AWS EBS、GCE PD、Azure Disk、OpenStack Cinder等。
对于 Kubernetes 1.29,仅nfs和hostPath卷类型支持回收。
PV访问策略
在实际使用PV时,可能针对不同的应用会有不同的访问策略,比如某类Pod可以读写,某类Pod只能读,或者需要配置是否可以被多个不同的Pod同时读写等,此时可以使用PV的访问策略进行简单控制,目前支持的访问策略如下:
- ReadWriteOnce:可以被单节点以读写模式挂载,命令行中可以被缩写为RWO。
- ReadOnlyMany:可以被多个节点以只读模式挂载,命令行中可以被缩写为ROX。
- ReadWriteMany:可以被多个节点以读写模式挂载,命令行中可以被缩写为RWX。
- ReadWriteOncePod:只能被一个Pod以读写的模式挂载,命令中可以被缩写为RWOP(1.22以上版本)。
虽然PV在创建时可以指定不同的访问策略,但是也要后端的存储支持才行。比如一般情况下大部分块存储是不支持ReadWriteMany的,具体后端存储支持的访问模式可以参考
https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes
基于nfs或nas创建pv
[root@k8s-m1 ~]# cat pv-nfs.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs ##pv名
spec:
capacity:
storage: 5Gi ##pv的容量
volumeMode: Filesystem ##卷的模式,目前支持Filesystem(文件系统) 和 Block(块),其中Block类型需要后端存储支持,默认为文件系统
accessModes:
- ReadWriteOnce ##pv的访问模式
persistentVolumeReclaimPolicy: Recycle ##pv的回收策略
storageClassName: nfs-slow ##存储类型的名称,pvc通过该名字访问到pv
nfs: ##pv的类型
path: /data/nfs ##nfs服务器共享的目录
server: 10.0.0.151 ##nfs服务器ip地址
[root@k8s-m1 ~]# kubectl create -f pv-nfs.yaml
persistentvolume/pv-nfs created
[root@k8s-m1 ~]# kubectl get persistentvolume
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-nfs 5Gi RWO Recycle Available nfs-slow 14s
创建hostpath类型的pv
一般不推荐使用该类型的pv,因为这种情况下使用的是宿主机的一个目录,pod和宿主机强绑定,不再具备高可用性。这种情况下需要利用污点,让pod和宿主机强绑定。
kind: PersistentVolume
apiVersion: v1
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: hostpath
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
PV的状态
- Available:可用,没有被PVC绑定的空闲资源。
- Bound:已绑定,已经被PVC绑定。
- Released:已释放,PVC被删除,但是资源还未被重新使用。
- Failed:失败,自动回收失败。
PersistentVolumeClaim
pv创建好了,如何使用呢,这时就需要用到pvc,pvc可以去申请pv资源。
pvc中定义了要使用的存储类型,存储空间大小以及存储的访问模式,例如申请一个大小为5Gi且只能被一个Pod只读访问的nfs存储。
下图是一个典型的pod使用pv作为volume的流程。
管理员创建pv,用户创建pvc和pv进行绑定,pod使用pvc申请到的pv资源作为volume来持久化存储数据。
那么pvc和pv是如何进行绑定的呢,主要依赖以下参数
参数 | 描述 |
---|---|
Storageclass | PV 与 PVC 的 storageclass 类名必须相同(或同时为空)。 |
AccessMode | 主要定义 volume 的访问模式,PV 与 PVC 的 AccessMode 必须相同。 |
Size | 主要定义 volume 的存储容量,PVC 中声明的容量必须小于等于 PV,如果存在多个满足条件的 PV,则选择最小的 PV 与 PVC 绑定。 |
pvc和pv绑定后,我们就可以通过在pod中使用pvc来申请pv资源了。只需要在pod的yaml文件中配置一个persistentVolumeClaim类型的volumes,claimName配置为PVC的名称即可
下面详细介绍下如何创建和使用pvc
创建pvc与pv进行绑定
[root@k8s-m1 ~]# cat pv-nfs.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
capacity:
storage: 5Gi #pv的容量
volumeMode: Filesystem
accessModes:
- ReadWriteOnce ##pv的访问模式
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs-slow ##存储类型的名称,pvc通过该名字访问到pv
nfs:
path: /data/nfs
server: 10.0.0.151
创建pvc
[root@k8s-m1 ~]# cat nfs-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nfs-pvc-claim ##pvc名
spec:
storageClassName: nfs-slow ##pv的类名
accessModes:
- ReadWriteOnce ##访问模式,和pv要一致
resources:
requests:
storage: 3Gi ##请求的容量大小,不能超过pv的大小
检查是否成功绑定
[root@k8s-m1 ~]# kubectl create -f pv-nfs.yaml
[root@k8s-m1 ~]# kubectl create -f nfs-pvc.yaml
persistentvolumeclaim/nfs-pvc-claim created
[root@k8s-m1 ~]#
[root@k8s-m1 ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nfs-pvc-claim Bound pv-nfs 5Gi RWO nfs-slow 6s
使用pvc
pod绑定pvc,创建pod时pvc会自动去pv处申请资源作为pod的volume
[root@k8s-m1 ~]# cat pod-nfs-pvc.yaml
kind: Pod
apiVersion: v1
metadata:
name: nfs-pv-pod
spec:
volumes:
- name: nfs-pv-storage
persistentVolumeClaim:
claimName: nfs-pvc-claim ##和pvc的名称一致
containers:
- name: nfs-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: nfs-pv-storage
检查pod是否成功挂载volume资源
[root@k8s-m1 ~]# kubectl create -f pod-nfs-pvc.yaml
pod/nfs-pv-pod created
[root@k8s-m1 ~]# kubectl exec -it nfs-pv-pod -- bash
root@nfs-pv-pod:/# ls /usr/share/nginx/html/
root@nfs-pv-pod:/# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 39G 22G 18G 57% /
tmpfs 64M 0 64M 0% /dev
tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup
shm 64M 0 64M 0% /dev/shm
/dev/vda2 39G 22G 18G 57% /etc/hosts
172.18.102.233:/data/nfs 39G 22G 18G 57% /usr/share/nginx/html
tmpfs 3.8G 12K 3.8G 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 2.0G 0 2.0G 0% /proc/acpi
tmpfs 2.0G 0 2.0G 0% /proc/scsi
tmpfs 2.0G 0 2.0G 0% /sys/firmware
动态存储storageclass
PV静态供给明显的缺点就是维护成本太高了,需要K8S运维人员手动创建一堆PV。
因此K8S开始支持PV动态供给,使用StorageClass对象实现,可以借助StorageClass提供的接口来自动创建PV。
每个storageclass都包含下面几个参数
- provisioner:提供pv卷的存储类型
- parameters:与后端存储对接时使用的参数,取决于provisioner中指定的存储。如ceph存储可以指定cluster id和pool id等。
- reclaimPolicy:指定通过storageclass创建出来的pv的回收策略。可以是 Delete 或者 Retain。如果 StorageClass 对象被创建时没有指定 reclaimPolicy,它将默认为 Delete。
- mountOptions:指定挂载选项,当 PV 不支持指定的选项时会直接失败。比如 NFS 支持 hard 和 nfsvers=4.1 等选项。
本次以NFS为例,要想使用NFS,我们需要一个nfs-client的自动装载程序,称之为provisioner,这个程序会使用我们已经配置好的NFS服务器自动创建持久卷,也就是自动帮我们创建PV
创建目录
mkdir /data && chmod -R 777 /data
nfs添加授权目录
cat /etc/exports
/data *(rw,sync,no_root_squash)
创建yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass #存储类的资源名称
metadata:
name: nfs-storage #存储类的名称,自定义
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner #存储分配器的名字,自定义
parameters:
archiveOnDelete: "true" ## 删除pv的时候,pv的内容是否要备份
reclaimPolicy: Retain
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
namespace: default
spec:
replicas: 1 #只运行一个副本应用
strategy: #描述了如何用新的POD替换现有的POD
type: Recreate #Recreate表示重新创建Pod
selector: #选择后端Pod
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/lfy_k8s_images/nfs-subdir-external-provisioner:v4.0.2 #使用NFS存储分配器的镜像,4.0.0以下不支持slelink
imagePullPolicy: IfNotPresent
# resources:
# limits:
# cpu: 10m
# requests:
# cpu: 10m
volumeMounts:
- name: nfs-client-root #定义个存储卷,
mountPath: /persistentvolumes #表示挂载容器内部的路径
env:
- name: PROVISIONER_NAME #定义存储分配器的名称
value: k8s-sigs.io/nfs-subdir-external-provisioner #需要和上面定义的保持名称一致
- name: NFS_SERVER #指定NFS服务器的地址,你需要改成你的NFS服务器的IP地址
value: 10.0.0.151
- name: NFS_PATH
value: /data ## nfs服务器共享的目录 #指定NFS服务器共享的目录
volumes:
- name: nfs-client-root #存储卷的名称,和前面定义的保持一致
nfs:
server: 10.0.0.151 #NFS服务器的地址,和上面保持一致,这里需要改为你的IP地址
path: /data #NFS共享的存储目录,和上面保持一致
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: default
---
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
namespace: default
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
namespace: default
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
namespace: default
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
[root@k8s-m1 ~]# kubectl apply -f nfs-sc.yaml
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
deployment.apps/nfs-provisioner-01 created
orageclass.storage.k8s.io/nfs-storage created
我们来基于StorageClass创建一个pvc,看看动态生成的pv是什么效果
[root@k8s-m1 ~]# vim pvc-sc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc-sc
spec:
storageClassName: nfs-storage
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
# kubectl apply -f pvc-sc.yaml
persistentvolumeclaim/pvc-sc created
[root@k8s-m1 ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-sc Bound pvc-63eee4c7-90fd-4c7e-abf9-d803c3204623 1Mi RWX nfs-storage 3s
pvc1 Bound pv1 1Gi RWO nfs 24m
[root@k8s-m1 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWO Recycle Bound default/pvc1 nfs 49m
pvc-63eee4c7-90fd-4c7e-abf9-d803c3204623 1Mi RWX Retain Bound default/pvc-sc nfs-storage 7s
我们修改下nginx的yaml配置,将pvc的名称换成上面的pvc-sc:
[root@k8s-m1 ~]# vim nginx.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: nginx:1.21.6
name: nginx
volumeMounts: # 我们这里将nginx容器默认的页面目录挂载
- name: html-files
mountPath: "/usr/share/nginx/html"
volumes:
- name: html-files
persistentVolumeClaim:
claimName: pvc-sc
[root@k8s-m1 ~]# kubectl apply -f nginx.yaml
service/nginx unchanged
deployment.apps/nginx configured
# 这里注意下,因为是动态生成的pv,所以它的目录基于是一串随机字符串生成的,这时我们直接进到pod内来创建访问页面
[root@k8s-m1 ~]# kubectl exec -it nginx-57cdc6d9b4-n497g -- bash
root@nginx-57cdc6d9b4-n497g:/# echo 'storageClass used' > /usr/share/nginx/html/index.html
root@nginx-57cdc6d9b4-n497g:/# exit
# curl 10.68.238.54
storageClass used
# 我们看下NFS挂载的目录
# ll /nfs_dir/
total 0
drwxrwxrwx 2 root root 24 Nov 27 17:52 default-pvc-sc-pvc-63eee4c7-90fd-4c7e-abf9-d803c3204623
drwxr-xr-x 2 root root 6 Nov 27 17:25 pv1
标签:存储,pv,name,kubernetes,pvc,详解,nfs,k8s,root
From: https://www.cnblogs.com/Unstoppable9527/p/18344030