第18关 持久化存储
K8s数据安全无忧——持久化存储详解
本期课程将深入解析Kubernetes的持久化存储机制,包括PV、PVC、StorageClass等的工作原理、使用场景、最佳实践等,帮您构建稳定可靠的状态存储,确保应用和数据 100% 安全。
Volume
我们这里先来聊聊K8s的存储模型Volume,来实践下如何将各种持久化的存储映射到Pod中的容器。
在我们上面的实战中,大家如果细心的话,会发现把nginx服务pod内的默认页面改了,但当重启pod后,这个页面又恢复成nginx容器初始的状态了,所以这里要和大家说的是,在没有配置持久化存储前,任何新增的数据在pod发生重启时都是无法保留的,而在K8s上,Pod的生命周期可能是很短,它们会被频繁地销毁和创建,自然在容器销毁时,里面运行时新增的数据,如修改的配置及日志文件等也会被清除。
那么怎么解决这一现象呢,我们可以用K8s volume来持久化保存容器的数据,Volume的生命周期独立于容器,Pod中的容器可能被销毁重建,但Volume会被保留。
本质上,K8s volume是一个目录,这点和Docker volume差不多,当Volume被mount到Pod上,这个Pod中的所有容器都可以访问这个volume,在生产场景中,我们常用的类型有这几种:
- emptyDir
- hostPath
- PersistentVolume(PV) & PersistentVolumeClaim(PVC)
- StorageClass
PV不受命名空间限制
PVC受命名空间限制
emptyDir
我们先开始讲讲emptyDir,它是最基础的Volume类型,pod内的容器发生重启不会造成emptyDir里面数据的丢失,但是当pod被重启后,emptyDir数据会丢失,也就是说emptyDir与pod的生命周期是一致的,那么大家可能有个疑问,这个之前讲的没有配置它也没什么区别呀,实际上在某些时候,它的作用还是挺大的,在生产中它的最实际实用是提供Pod内多容器的volume数据共享,下面我会用一个实际的生产者,消费者的例子来演示下emptyDir的作用,相信大家动动手就会理解得更快了
# cat web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: web
name: web
namespace: test-emptydir
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- image: nginx:1.21.6
name: nginx
resources:
limits:
cpu: "50m"
memory: 20Mi
requests:
cpu: "50m"
memory: 20Mi
volumeMounts: # 准备将pod的目录进行卷挂载
- name: html-files # 自定个名称,容器内可以类似这样挂载多个卷
mountPath: "/usr/share/nginx/html"
- name: busybox # 在pod内再跑一个容器,每秒把当时时间写到nginx默认页面上
image: registry.cn-shanghai.aliyuncs.com/acs/busybox:v1.29.2
args:
- /bin/sh
- -c
- >
while :; do
if [ -f /html/index.html ];then
echo "[$(date +%F\ %T)] hello" > /html/index.html
sleep 1
else
touch /html/index.html
fi
done
volumeMounts:
- name: html-files # 注意这里的名称和上面nginx容器保持一样,这样才能相互进行访问
mountPath: "/html" # 将数据挂载到当前这个容器的这个目录下
volumes:
- name: html-files # 最后定义这个卷的名称也保持和上面一样
emptyDir: # 这就是使用emptyDir卷类型了
medium: Memory # 这里将文件写入内存中保存,这样速度会很快,配置为medium: "" 就是代表默认的使用本地磁盘空间来进行存储
sizeLimit: 10Mi # 因为内存比较珍贵,注意限制使用大小更新这个web的配置
# kubectl create ns test-emptydir
# kubectl -n test-emptydir apply -f test-emptydir-web.yaml
deployment.apps/web configured
# 可以看到READY下面容器数量变为2了
# kubectl -n test-emptydir get pod
NAME READY STATUS RESTARTS AGE
web-68b49c5b9-vmxp7 2/2 Running 0 24s
# 接着创建一个service来请求测试下
kubectl -n test-emptydir expose deployment web --port 80 --target-port 80
# kubectl -n test-emptydir get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.68.211.225 <none> 80/TCP 15s
# 可以看到每次访问都是被写入当前最新时间的页面内容
# curl 10.68.211.225
[2024-05-30 09:15:16] hello
# curl 10.68.211.225
[2024-05-30 09:15:18] hello我们来探究下原理
# 下面这个是docker容器运行时的记录
# 看下这个web的pod的描述信息
# kubectl -n test-emptydir describe pod web-68b49c5b9-vmxp7
......
Node: 10.0.1.204/10.0.1.204 # 找到这个pod运行在哪个node上
......
Containers:
nginx:
Container ID: containerd://b92f4a14a16cfcf516b96c05f188e66d1fc6637ae98226b1a9329017f40e2510 # 找到pod内nginx容器的ID
......
busybox:
Container ID: containerd://6df4f0375ce05523e1f253e89f6030d2010c3188d5c3fd242cb1a9d6f0a1c974 # 找到pod内busybox容器的ID
# 我们登陆10.0.1.204 这台node,查看pod内这两个容器的volume挂载信息,我们发现两个容器都 mount 了同一个目录
# Containerd运行时日志目录:
# ll /var/log/containers/|grep web
lrwxrwxrwx 1 root root 98 May 30 17:13 web-68b49c5b9-vmxp7_test-emptydir_busybox-6df4f0375ce05523e1f253e89f6030d2010c3188d5c3fd242cb1a9d6f0a1c974.log -> /var/log/pods/test-emptydir_web-68b49c5b9-vmxp7_6ac4e9c5-5a77-4d0f-86df-693b4a7fed3f/busybox/0.log
lrwxrwxrwx 1 root root 96 May 30 17:13 web-68b49c5b9-vmxp7_test-emptydir_nginx-b92f4a14a16cfcf516b96c05f188e66d1fc6637ae98226b1a9329017f40e2510.log -> /var/log/pods/test-emptydir_web-68b49c5b9-vmxp7_6ac4e9c5-5a77-4d0f-86df-693b4a7fed3f/nginx/0.loghostPath
hostPath Volume 的作用是将容器运行的node上已经存在文件系统目录给mount到pod的容器。在生产中大部分应用是是不会直接使用hostPath的,因为我们并不关心Pod在哪台node上运行,而hostPath又恰好增加了pod与node的耦合,限制了pod的使用,这里我们只作一下了解,知道有这个东西存在即可,一般只是一些安装服务会用到,比如下面我截取了网络插件calico的部分volume配置:
volumeMounts:
- mountPath: /host/driver
name: flexvol-driver-host
......
volumes:
......
- hostPath:
path: /usr/libexec/kubernetes/kubelet-plugins/volume/exec/nodeagent~uds
type: DirectoryOrCreate
name: flexvol-driver-hostPV&PVC
PersistentVolume(PV) & PersistentVolumeClaim(PVC)
现在讲Volume里面在生产中用的最多的PersistentVolume(持久卷,简称PV)和 PersistentVolumeClaim(持久卷消费,简称PVC),通常在企业中,Volume是由存储系统的管理员来维护,他们来提供pv,pv具有持久性,生命周期独立于Pod;Pod则是由应用的开发人员来维护,如果要进行一卷挂载,那么就写一个pvc来消费pv就可以了,K8s会查找并提供满足条件的pv。
有了pvc,我们在K8s进行卷挂载就只需要考虑要多少容量了,而不用关心真正的空间是用什么存储系统做的等一些底层细节信息,pv这些只有存储管理员才应用去关心它。
K8s支持多种类型的pv,我们这里就以生产中常用的NFS来作演示(在云上的话就用NAS),生产中如果对存储要求不是太高的话,建议就用NFS,这样出问题也比较容易解决,如果有性能需求,可以看看rook的ceph,以及Rancher的Longhorn,这些我都在生产中用过。
部署NFS-SERVER
开始部署NFS-SERVER
# 我们这里在10.0.1.201上安装(在生产中,大家要提供作好NFS-SERVER环境的规划)
# yum -y install nfs-utils
# ubuntu安装NFS服务端
# apt-get install nfs-kernel-server -y
# 创建NFS挂载目录
# mkdir /nfs_dir
# 修改为nfs的用户组,防止权限不足
# chown nobody.nogroup /nfs_dir
# 修改NFS-SERVER配置
# echo '/nfs_dir *(rw,sync,no_root_squash)' > /etc/exports
# 重启服务
# systemctl restart rpcbind.service
# systemctl restart nfs-kernel-server.service
# systemctl restart nfs-utils.service
# systemctl restart nfs-server.service
# 增加NFS-SERVER开机自启动
# systemctl enable nfs-server.service
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.
# 显示指定 NFS 服务器上导出的文件系统的信息
# showmount -e 192.168.10.201
Export list for 192.168.10.201:
/nfs_dir *
# 验证NFS-SERVER是否能正常访问
## 注:如果查看不到目录,可以到另一机器上挂载试试
root@k8s-192-168-10-205:~# mount.nfs 192.168.10.201:/nfs_dir /mnt/
root@k8s-192-168-10-205:~# df -Th |grep nfs
192.168.10.201:/nfs_dir nfs4 192G 15G 168G 9% /mnt
root@k8s-192-168-10-205:~# showmount -e 192.168.10.201
Export list for 192.168.10.201:
/nfs_dir *
# 卸载
# umount /mnt/创建基于NFS的PV
首先在NFS-SERVER的挂载目录里面创建一个目录
# mkdir /nfs_dir/pv1接着准备好pv的yaml配置,保存为pv1.yaml
# cat pv1.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
labels:
type: test-claim # 这里建议打上一个独有的标签,方便在多个pv的时候方便提供pvc选择挂载
spec:
capacity:
storage: 1Gi # <---------- 1
accessModes:
- ReadWriteOnce # <---------- 2
persistentVolumeReclaimPolicy: Recycle # <---------- 3
storageClassName: nfs # <---------- 4
nfs:
path: /nfs_dir/pv1 # <---------- 5
server: 10.0.1.201- capacity 指定 PV 的容量为 1G。
- accessModes 指定访问模式为 ReadWriteOnce,支持的访问模式有: ReadWriteOnce – PV 能以 read-write 模式 mount 到单个节点。 ReadOnlyMany – PV 能以 read-only 模式 mount 到多个节点。 ReadWriteMany – PV 能以 read-write 模式 mount 到多个节点。
- persistentVolumeReclaimPolicy 指定当 PV 的回收策略为 Recycle,支持的策略有: Retain – 需要管理员手工回收。(这个稳妥) Recycle – 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*。 Delete – (把PV也删除了)删除 Storage Provider 上的对应存储资源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。
- storageClassName 指定 PV 的 class 为 nfs。相当于为 PV 设置了一个分类,PVC 可以指定 class 申请相应 class 的 PV。
- 指定 PV 在 NFS 服务器上对应的目录,这里注意,我测试的时候,需要手动先创建好这个目录并授权好,不然后面挂载会提示目录不存在 mkdir /nfsdata/pv1 && chown -R nobody.nogroup /nfsdata 。
创建这个pv
kubectl create ns test-pv
# kubectl -n test-pv apply -f pv1.yaml
persistentvolume/pv1 created
# STATUS 为 Available,表示 pv1 就绪,可以被 PVC 申请
# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWO Recycle Available nfs 11s创建pvc
接着准备PVC的yaml,保存为pvc1.yaml
# cat pvc1.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc1
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: nfs
selector:
matchLabels:
type: test-claim# kubectl -n test-pv apply -f pvc1.yaml
persistentvolumeclaim/pvc1 created
# 看下pvc的STATUS为Bound代表成功挂载到pv了
# kubectl -n test-pv get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc1 Bound pv1 1Gi RWO nfs 10s
# 这个时候再看下pv,STATUS也是Bound了,同时CLAIM提示被default/pvc1消费
# kubectl -n test-pv get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWO Recycle Bound test-pv/pvc1 nfs 2m44spod挂载pvc
下面我们准备pod服务来挂载这个pvc,这里就以上面最开始演示用的nginx的deployment的yaml配置来作修改
# cat nginx.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx
name: nginx
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
---
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: # 卷类型使用pvc,同时下面名称处填先创建好的pvc1
claimName: pvc1更新配置
# kubectl -n test-pv apply -f nginx.yaml
service/nginx unchanged
deployment.apps/nginx configured
# 我们看到新pod已经在创建了
# kubectl -n test-pv get pod
NAME READY STATUS RESTARTS AGE
nginx-58df8b859b-prr6f 1/1 Running 0 7s
# kubectl -n test-pv exec -it nginx-58df8b859b-prr6f -- bash
root@nginx-58df8b859b-prr6f:/# df -Th
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 29G 8.6G 19G 32% /
tmpfs tmpfs 64M 0 64M 0% /dev
/dev/mapper/ubuntu--vg-ubuntu--lv ext4 29G 8.6G 19G 32% /etc/hosts
shm tmpfs 64M 0 64M 0% /dev/shm
10.0.1.201:/nfs_dir/pv1 nfs4 29G 15G 13G 56% /usr/share/nginx/html
tmpfs tmpfs 1.6G 12K 1.6G 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs tmpfs 965M 0 965M 0% /proc/acpi
tmpfs tmpfs 965M 0 965M 0% /proc/scsi
tmpfs tmpfs 965M 0 965M 0% /sys/firmware
# 我们这里直接用svc地址测试一下
# kubectl -n test-pv get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP 10.68.226.56 <none> 80/TCP 107s
# 咦,这里为什么是显示403了呢,注意,卷挂载后会把当前已经存在这个目录的文件给覆盖掉,这个和传统机器上的磁盘目录挂载道理是一样的
[root@node-1 ~]# curl 10.68.226.56
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.19.5</center>
</body>
</html>
# 我们来自己创建一个index.html页面
# echo 'hello, world!' > /nfs_dir/pv1/index.html
# 再请求下看看,已经正常了(没有马上改)
# curl 10.68.238.54
hello, world!
# 我们来手动删除这个nginx的pod,看下容器内的修改是否是持久的呢?
# kubectl -n test-pv delete pod nginx-58df8b859b-prr6f
pod "nginx-58df8b859b-prr6f" deleted
# 等待一会,等新的pod被创建好
# kubectl -n test-pv get pod
NAME READY STATUS RESTARTS AGE
nginx-58df8b859b-2xpt6 1/1 Running 0 12s
# 再测试一下,可以看到,容器内的修改现在已经被持久化了
# curl 10.68.226.56
hello, world!
# 后面我们再想修改有两种方式,一个是exec进到pod内进行修改,还有一个是直接修改挂载在NFS目录下的文件
# echo 111 > /nfs_dir/pv1/index.html
# curl 10.68.238.54
111回收PVC以及PV
# 这里删除时会一直卡着,我们按ctrl+c看看怎么回事
# kubectl -n test-pv delete pvc pvc1
persistentvolumeclaim "pvc1" deleted
^C
# 看下pvc发现STATUS是Terminating删除中的状态,我分析是因为服务pod还在占用这个pvc使用中
# kubectl -n test-pv get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc1 Terminating pv1 1Gi RWO nfs 10m
# 先删除这个pod
# kubectl -n test-pv delete pod nginx-58df8b859b-2xpt6
pod "nginx-58df8b859b-2xpt6" deleted
# 此时pod会重启
# kubectl -n test-pv describe pod nginx-58df8b859b-5ht82
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 30m default-scheduler 0/4 nodes are available: persistentvolumeclaim "pvc1" is being deleted. preemption: 0/4 nodes are available: 4 No preemption victims found for incoming pod..
Warning FailedScheduling 30m default-scheduler 0/4 nodes are available: persistentvolumeclaim "pvc1" not found. preemption: 0/4 nodes are available: 4 No preemption victims found for incoming pod..
Warning FailedScheduling 15m (x3 over 25m) default-scheduler 0/4 nodes are available: persistentvolumeclaim "pvc1" not found. preemption: 0/4 nodes are available: 4 No preemption victims found for incoming pod..
# 再看先删除的pvc已经没有了
# kubectl -n test-pv get pvc
No resources found in default namespace.
# 根据先前创建pv时的数据回收策略为Recycle – 清除 PV 中的数据,这时果然先创建的index.html已经被删除了,在生产中要尤其注意这里的模式,注意及时备份数据,注意及时备份数据,注意及时备份数据
# ll /nfs_dir/pv1/
total 0
(操作到这里pv并没有清除数据)
# kubectl -n test-pv get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWO Recycle Released test-pv/pvc1 nfs 50m
Available(可用):表示可用状态,还未被任何 PVC 绑定
Bound(已绑定):表示 PVC 已经被 PVC 绑定
Released(已释放):PVC 被删除,但是资源还未被集群重新声明
Failed(失败): 表示该 PV 的自动回收失败
# 查看pvc已经是没有了
# kubectl -n test-pv get pvc
No resources found in test-pv namespace.
# 虽然此时pv是可以再次被pvc来消费的,但根据生产的经验,建议在删除pvc时,也同时把它消费的pv一并删除,然后再重启创建都是可以的我们先提到了K8s的设计是,pv交给存储管理员来管理,我们是管用pvc来消费就好,但这里我们实际还是得一起管理pv和pvc,在实际工作中,我们(存储管理员)可以提前配置好pv的动态供给StorageClass,来根据pvc的消费动态生成pv
StorageClass
我这是直接拿生产中用的实例来作演示,利用nfs-client-provisioner来生成一个基于nfs的StorageClass,部署配置yaml配置如下,保持为nfs-sc.yaml:
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# namespace: kube-system
namespace: nfs-sc
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- 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: ["list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "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: kube-system
namespace: nfs-sc
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-provisioner-01
# namespace: kube-system
namespace: nfs-sc
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-provisioner-01
template:
metadata:
labels:
app: nfs-provisioner-01
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: bogeit/nfs-subdir-external-provisioner:v4.0.2
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: nfs-provisioner-01 # 此处供应者名字供storageclass调用
- name: NFS_SERVER
value: 192.168.10.201 # 填入NFS的地址
- name: NFS_PATH
value: /nfs_dir # 填入NFS挂载的目录
volumes:
- name: nfs-client-root
nfs:
server: 192.168.10.201 # 填入NFS的地址
path: /nfs_dir # 填入NFS挂载的目录
---
# use aliyun's nas need configure: https://help.aliyun.com/document_detail/130727.html?spm=a2c4g.11174283.6.715.1aad2ceeUrijYZ
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-icurvestar
provisioner: nfs-provisioner-01
# Supported policies: Delete、 Retain , default is Delete
reclaimPolicy: Retain开始创建这个StorageClass:
# kubectl create ns nfs-sc
# kubectl -n nfs-sc apply -f nfs-sc.yaml
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner unchanged
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner configured
deployment.apps/nfs-provisioner-01 created
storageclass.storage.k8s.io/nfs-boge unchanged
# 注意这个是在放kube-system的namespace下面,这里面放置一些偏系统类的服务
# kubectl -n nfs-sc get pod
NAME READY STATUS RESTARTS AGE
nfs-provisioner-01-5cb46dcb99-92s4s 1/1 Running 0 97s
镜像拉取用了一分钟
kubectl -n nfs-sc describe pod
# StorageClass已经创建好了
# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-icurvestar nfs-provisioner-01 Retain Immediate false 19m我们来基于StorageClass创建一个pvc,看看动态生成的pv是什么效果:
# vim pvc-sc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc-sc
spec:
storageClassName: nfs-boge
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi# kubectl create ns test-pvc-sc
# kubectl -n test-pvc-sc apply -f pvc-sc.yaml
persistentvolumeclaim/pvc-sc created
# kubectl -n test-pvc-sc get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-sc Bound pvc-61f43534-ad3f-4346-9cba-fe66a77817ac 1Mi RWX nfs-boge 19s
# pv不分命名空间
# kubectl -n test-pvc-sc get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWO Recycle Released test-pv/pvc1 nfs 70m
pvc-61f43534-ad3f-4346-9cba-fe66a77817ac 1Mi RWX Retain Bound test-pvc-sc/pvc-sc nfs-boge 36s我们修改下nginx的yaml配置,将pvc的名称换成上面的pvc-sc:
# vim nginx.yaml
kind: Deployment
metadata:
name: nginx
spec:
spec:
volumes:
- name: html-files
persistentVolumeClaim:
claimName: pvc-sc
# kubectl -n test-pvc-sc apply -f nginx.yaml
service/nginx unchanged
deployment.apps/nginx configured
# kubectl -n test-pvc-sc get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP 10.68.122.178 <none> 80/TCP 20s
# curl 10.68.122.178
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.21.6</center>
</body>
</html>
# 这里注意下,因为是动态生成的pv,所以它的目录基于是一串随机字符串生成的,这时我们直接进到pod内来创建访问页面
# kubectl -n test-pvc-sc exec -it nginx-856cd44cd6-44wfd -- bash
root@nginx-57cdc6d9b4-n497g:/# echo 'storageClass used' > /usr/share/nginx/html/index.html
root@nginx-57cdc6d9b4-n497g:/# exit
# curl 10.68.122.178
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现在是使用单点的nfs,推荐用云上的集束nas,速度、可用性