Skip to content

NFS存储

NFS存储

目录

[TOC]

1、NFS 共享存储

前面我们学习了 hostPath 与 Local PV 两种本地存储方式,但是平时我们的应用更多的是无状态服务,可能会同时发布在不同的节点上,这个时候本地存储就不适用了,往往就需要使用到共享存储了,比如最简单常用的网络共享存储 NFS,本节课我们就来介绍下如何在 Kubernetes 下面使用 NFS 共享存储。

NFS卷:提供对NFS挂载支持,可以自动将NFS共享路径挂载到Pod中

NFS:是一个主流的文件共享服务器。

2、原理

我们只是在 volumes 中指定了我们上面创建的 PVC 对象,当这个 Pod 被创建之后, kubelet 就会把这个 PVC 对应的这个 NFS 类型的 Volume(PV)挂载到这个 Pod 容器中的目录中去。前面我们也提到了这样的话对于普通用户来说完全就不用关心后面的具体存储在 NFS 还是 Ceph 或者其他了,只需要直接使用 PVC 就可以了,因为真正的存储是需要很多相关的专业知识的,这样就完全职责分离解耦了。

普通用户直接使用 PVC 没有问题,但是也会出现一个问题**,那就是当普通用户创建一个 PVC 对象的时候,这个时候系统里面并没有合适的 PV 来(或者没有默认的stora)和它进行绑定,因为 PV 大多数情况下是管理员给我们创建的,这个时候启动 Pod 肯定就会失败了**,如果现在管理员如果去创建一个对应的 PV 的话,PVC 和 PV 当然就可以绑定了,然后 Pod 也会自动的启动成功,这是因为在 Kubernetes 中有一个专门处理持久化存储的控制器 Volume Controller,这个控制器下面有很多个控制循环,其中一个就是用于 PV 和 PVC 绑定的 PersistentVolumeController

PersistentVolumeController会不断地循环去查看每一个 PVC,是不是已经处于 Bound(已绑定)状态。如果不是,那它就会遍历所有的、可用的 PV,并尝试将其与未绑定的 PVC 进行绑定,这样,Kubernetes 就可以保证用户提交的每一个 PVC,只要有合适的 PV 出现,它就能够很快进入绑定状态。而所谓将一个 PV 与 PVC 进行绑定,其实就是将这个 PV 对象的名字,填在了 PVC 对象的 spec.volumeName字段上。

PV 和 PVC 绑定上了,那么又是如何将容器里面的数据进行持久化的呢,我们知道 Docker 的 Volume 挂载其实就是将一个宿主机上的目录和一个容器里的目录绑定挂载在了一起,具有持久化功能当然就是指的宿主机上面的这个目录了,当容器被删除或者在其他节点上重建出来以后,这个目录里面的内容依然存在,所以一般情况下实现持久化是需要一个远程存储的,比如 NFS、Ceph 或者云厂商提供的磁盘等等。所以接下来需要做的就是持久化宿主机目录这个过程

当 Pod 被调度到一个节点上后,节点上的 kubelet 组件就会为这个 Pod 创建它的 Volume 目录,默认情况下 kubelet 为 Volume 创建的目录在 kubelet 工作目录下面:

/var/lib/kubelet/pods/<Pod的ID>/volumes/kubernetes.io~<Volume类型>/<Volume名字>

比如上面我们创建的 Pod 对应的 Volume 目录完整路径为:

/var/lib/kubelet/pods/effdc8a1-1981-4a26-924a-c62a246a5865/volumes/kubernetes.io~nfs/pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92/

提示

bash
要获取Pod的唯一标识uid:方法1:可通过命令`kubectl get pod pod名 -o jsonpath={.metadata.uid}`获取。[root@master1 pods]#kubectl get po nfs-sc-pod -o jsonpath={.metadata.uid}effdc8a1-1981-4a26-924a-c62a246a5865方法2:可直接导出pod的yaml并用grep筛选。[root@master1 pods]#kubectl get po nfs-sc-pod -oyaml|grepuiduid:effdc8a1-1981-4a26-924a-c62a246a5865

然后就需要根据我们的 Volume 类型来决定需要做什么操作了,假如后端存储使用的 Ceph RBD,那么 kubelet 就需要先将 Ceph 提供的 RBD 挂载到 Pod 所在的宿主机上面,这个阶段在 Kubernetes 中被称为 Attach 阶段。Attach 阶段完成后,为了能够使用这个块设备,kubelet 还要进行第二个操作,即:格式化这个块设备,然后将它挂载到宿主机指定的挂载点上。这个挂载点,也就是上面我们提到的 Volume 的宿主机的目录。将块设备格式化并挂载到 Volume 宿主机目录的操作,在 Kubernetes 中被称为 Mount 阶段。但是对于我们这里使用的 NFS 就更加简单了, 因为 NFS 存储并没有一个设备需要挂载到宿主机上面,所以这个时候 kubelet 就会直接进入第二个 Mount阶段,相当于直接在宿主机上面执行如下的命令:

bash
mount-tnfs172.29.9.51:/var/lib/k8s/data//var/lib/kubelet/pods/effdc8a1-1981-4a26-924a-c62a246a5865/volumes/kubernetes.io~nfs/pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92/

同样可以在测试的 Pod 所在节点查看 Volume 的挂载信息:

bash
[root@node1 ~]#findmnt /var/lib/kubelet/pods/effdc8a1-1981-4a26-924a-c62a246a5865/volumes/kubernetes.io~nfs/pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92/TARGETSOURCEFSTYPEOPTIONS/var/lib/kubelet/pods/effdc8a1-1981-4a26-924a-c62a246a5865/volumes/kubernetes.io~nfs/pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92172.29.9.51:/var/lib/k8s/data/default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92

我们可以看到这个 Volume 被挂载到了 NFS(172.29.9.51:/var/lib/k8s/data/)下面,以后我们在这个目录里写入的所有文件,都会被保存在远程 NFS 服务器上。

这样在经过了上面的阶段过后,我们就得到了一个持久化的宿主机上面的 Volume 目录了,接下来 kubelet 只需要把这个 Volume 目录挂载到容器中对应的目录即可,这样就可以为 Pod 里的容器挂载这个持久化的 Volume 了,这一步其实也就相当于执行了如下所示的命令:

bash
# docker 或者 nerdctldockerrun-v/var/lib/kubelet/pods/<Pod的ID>/volumes/kubernetes.io~<Volume类>/<Volume名>:/<容器内的目标目>我的镜像...

整个存储的架构可以用下图来说明:

  • PV Controller:负责 PV/PVC 的绑定,并根据需求进行数据卷的 Provision/Delete 操作
  • AD Controller:负责存储设备的 Attach/Detach 操作,将设备挂载到目标节点
  • Volume Manager:管理卷的 Mount/Unmount 操作、卷设备的格式化等操作
  • Volume Plugin:扩展各种存储类型的卷管理能力,实现第三方存储的各种操作能力和 Kubernetes 存储系统结合

我们上面使用的 NFS 就属于 In-Tree这种方式,In-Tree就是在 Kubernetes 源码内部实现的,和 Kubernetes 一起发布、管理的,但是更新迭代慢、灵活性比较差,另外一种方式 Out-Of-Tree是独立于 Kubernetes 的,目前主要有 CSIFlexVolume两种机制,开发者可以根据自己的存储类型实现不同的存储插件接入到 Kubernetes 中去,其中 CSI是现在也是以后主流的方式,接下来我们会主要介绍 CSI这种存储插件的使用。

3、安装

4、使用

💘 实战:nfs共享存储测试,未使用到pv/pvc(测试成功)-2022.8.13

🍀 创建deployment的yaml并修改

注意:这里在deployment.yaml中修改下nfs字段,工作中一般不这么使用,而是直接使用pv,pvc的,本次实验让你知道下pod之间共享数据的网络存储卷nfs解决方法。

yaml
[root@k8s-master ~]#vim nfs.yamlapiVersion:apps/v1kind:Deploymentmetadata:name:web2spec:selector:matchLabels:app:nginxreplicas:3template:metadata:labels:app:nginxspec:containers:- name:nginximage:nginxvolumeMounts:- name:wwwrootmountPath:/usr/share/nginx/htmlports:- containerPort:80volumes:#数据卷来源- name:wwwrootnfs:server:172.29.9.33path:/ifs/kubernetes

🍀 apply下nfs.yaml并查看

bash
[root@k8s-master ~]#kubectl apply -f nfs.yamldeployment.apps/web2created#暴露deployment web2[root@k8s-master ~]#kubectl expose deployment web2 --port=80--target-port=80--type=NodePortservice/web2exposed#查看[root@k8s-master ~]#kubectl get pod,svcNAMEREADYSTATUSRESTARTSAGEpod/web2-7d7fc87d85-9lszr1/1Running07m51spod/web2-7d7fc87d85-b4pbr1/1Running07m51spod/web2-7d7fc87d85-w4xll1/1Running07m51sNAMETYPECLUSTER-IPEXTERNAL-IPPORT(S) AGEservice/kubernetesClusterIP10.96.0.1<none>443/TCP26dservice/web2NodePort10.103.200.91<none>80:32359/TCP13m[root@k8s-master ~]#

🍀 验证效果

注意:此时通过浏览器访问报错了

403一般是权限类的错误:

bash
[root@k8s-master ~]#kubectl get pod,svcNAMEREADYSTATUSRESTARTSAGEpod/web2-7d7fc87d85-9lszr1/1Running011mpod/web2-7d7fc87d85-b4pbr1/1Running011mpod/web2-7d7fc87d85-w4xll1/1Running011mNAMETYPECLUSTER-IPEXTERNAL-IPPORT(S) AGEservice/kubernetesClusterIP10.96.0.1<none>443/TCP26dservice/web2NodePort10.103.200.91<none>80:32359/TCP16m[root@k8s-master ~]#kubectl exec -it web2-7d7fc87d85-9lszr -- bash #进入到容器查看root@web2-7d7fc87d85-9lszr:/#cd/usr/share/nginx/html/root@web2-7d7fc87d85-9lszr:/usr/share/nginx/html#ls1.txt2.txtroot@web2-7d7fc87d85-9lszr:/usr/share/nginx/html##因为这个nginx的主目录是从nfs直接挂载的,且nfs目录下没有相应的index.html文件,所以这里当然报错了;root@web2-7d7fc87d85-9lszr:/usr/share/nginx/html#df-hTFilesystemTypeSizeUsedAvailUse%Mountedonoverlayoverlay17G6.5G11G38%/tmpfstmpfs64M064M0%/devtmpfstmpfs910M0910M0%/sys/fs/cgroup/dev/mapper/centos-rootxfs17G6.5G11G38%/etc/hostsshmtmpfs64M064M0%/dev/shm172.29.9.33:/ifs/kubernetesnfs417G6.8G11G40%/usr/share/nginx/html#挂载目录tmpfstmpfs910M12K910M1%/run/secrets/kubernetes.io/serviceaccounttmpfstmpfs910M0910M0%/proc/acpitmpfstmpfs910M0910M0%/proc/scsitmpfstmpfs910M0910M0%/sys/firmwareroot@web2-7d7fc87d85-9lszr:/usr/share/nginx/html#root@web2-7d7fc87d85-9lszr:/usr/share/nginx/html#pwd/usr/share/nginx/htmlroot@web2-7d7fc87d85-9lszr:/usr/share/nginx/html#ls#且nfs目录下没有相应的index.html文件1.txt2.txtroot@web2-7d7fc87d85-9lszr:/usr/share/nginx/html##因此这里手动创建一个index.html文件进行测试:root@web2-7d7fc87d85-9lszr:/usr/share/nginx/html#echo"<h1>i miss you</h1>">index.htmlroot@web2-7d7fc87d85-9lszr:/usr/share/nginx/html#ls1.txt2.txtindex.htmlroot@web2-7d7fc87d85-9lszr:/usr/share/nginx/html##nfs服务端挂载目录下也会出现这个文件:[root@k8s-node2 ~]#ls /ifs/kubernetes/1.txt2.txtindex.html[root@k8s-node2 ~]##其他2个容器下也是有的,因为用一个pod下各个容器之间是共享一切的;[root@k8s-master ~]#kubectl get podNAMEREADYSTATUSRESTARTSAGEweb2-7d7fc87d85-9lszr1/1Running021mweb2-7d7fc87d85-b4pbr1/1Running021mweb2-7d7fc87d85-w4xll1/1Running021m[root@k8s-master ~]#kubectl exec -it web2-7d7fc87d85-b4pbr -- bashroot@web2-7d7fc87d85-b4pbr:/#ls/usr/share/nginx/html/1.txt2.txtindex.html#该index.html文件存在root@web2-7d7fc87d85-b4pbr:/#

index.html文件创建好后,再次到浏览器上测试:=>此时已经符合预期实验效果了。

即,可以通过3个节点的nodeip:port均可以访问这个nginx服务;

实验结束,完美。😘

💘 实战:nfs共享存储测试,有使用到pv/pvc(测试成功)-2022.8.13

上面已经安装好了nfs服务,也测试过k8s节点是可以正常访问nfs服务的;

前面我们已经了解到了 PV、PVC、StorgeClass 的使用,那么我们的 NFS 又应该如何在 Kubernetes 中使用呢?

🍀 创建pv.yaml,p

同样创建一个如下所示 nfs 类型的 PV 资源对象:

yaml
# 01-nfs-volume.yamlapiVersion:v1kind:PersistentVolumemetadata:name:nfs-pvspec:storageClassName:manualcapacity:storage:1GiaccessModes:- ReadWriteOncepersistentVolumeReclaimPolicy:Retainnfs:path:/var/lib/k8s/data/# 指定nfs的挂载点server:172.29.9.51# 指定nfs服务地址---apiVersion:v1kind:PersistentVolumeClaimmetadata:name:nfs-pvcspec:storageClassName:manualaccessModes:- ReadWriteOnceresources:requests:storage:1Gi

🍀 我们知道用户真正使用的是 PVC,而要使用 PVC 的前提就是必须要先和某个符合条件的 PV 进行一一绑定,比如存储容器、访问模式,以及 PV 和 PVC 的 storageClassName 字段必须一样,这样才能够进行绑定,当 PVC 和 PV 绑定成功后就可以直接使用这个 PVC 对象了:

yaml
# 02-nfs-pod.yamlapiVersion:v1kind:Podmetadata:name:test-volumesspec:volumes:- name:nfspersistentVolumeClaim:claimName:nfs-pvccontainers:- name:webimage:nginxports:- name:webcontainerPort:80volumeMounts:- name:nfssubPath:test-volumes#注意:这个就是在nfs目录会创建有一个子目录`test-volumes`mountPath:"/usr/share/nginx/html"

🍀 直接创建上面的资源对象即可:

bash
$kubectl apply -f 01-nfs-volume.yaml persistentvolume/nfs-pvcreatedpersistentvolumeclaim/nfs-pvccreated$kubectl apply -f 02-nfs-pod.yaml pod/test-volumescreated$kubectl get pvNAMECAPACITYACCESSMODESRECLAIMPOLICYSTATUSCLAIMSTORAGECLASSREASONAGEnfs-pv1GiRWORetainBounddefault/nfs-pvcmanual54s$kubectl get pvcNAMESTATUSVOLUMECAPACITYACCESSMODESSTORAGECLASSAGEnfs-pvcBoundnfs-pv1GiRWOmanual62s$kubectl get po test-volumes -owideNAMEREADYSTATUSRESTARTSAGEIPNODENOMINATEDNODEREADINESSGATEStest-volumes1/1Running066s10.244.2.60node2<none><none>

🍀 由于我们这里 PV 中的数据为空,所以挂载后会将 nginx 容器中的 /usr/share/nginx/html目录覆盖,那么访问应用的时候就没有内容了(就会报403错误):

bash
[root@master1 ~]#curl 10.244.2.60:80 <html><head><title>403 Forbidden</title></head><body><center><h1>403 Forbidden</h1></center><hr><center>nginx/1.21.5</center></body></html>

我们可以在 PV 目录中添加一些内容:

bash
# 在 nfs 服务器上面执行[root@master1 ~]#echo "nfs pv content">/var/lib/k8s/data/test-volumes/index.html[root@master1 ~]#curl 10.244.2.60:80nfspvcontent

然后重新访问就有数据了,而且当我们的 Pod 应用挂掉或者被删掉重新启动后数据还是存在的,因为数据已经持久化了:

bash
$kubectldelete-f02-nfs-pod.yamlpod"test-volumes"deleted$kubectlapply-f02-nfs-pod.yamlpod/test-volumescreated$kubectlgetpotest-volumes-owideNAMEREADYSTATUSRESTARTSAGEIPNODENOMINATEDNODEREADINESSGATEStest-volumes1/1Running017s10.244.2.88node2<none><none># curl 10.244.2.88:80nfspvcontent

实验结束,完美。😘

💘 实战:k8s持久化存储pv和pvc实验完整模拟(测试成功-simmon教练)

0.搭建一个nfs存储

注意:本次复用k8s-master节点作为nfs server端来提供nfs服务。

(1)在nfs server端安装nfs服务

bash
# 在nfs server服务端节点(k8s-master)安装[root@k8s-master ~]#yum install nfs-utils -y[root@k8s-master ~]#mkdir /nfs[root@k8s-master ~]#echo "/nfs *(rw,insecure,sync,no_root_squash)">/etc/exports[root@k8s-master ~]#systemctl enable rpcbind nfs-utils nfs-server --now# 检测一下[root@k8s-master ~]#showmount -e 172.29.9.31Exportlistfor172.29.9.31:/nfs*[root@k8s-master ~]#

(2)nfs server端配置完成后,可以在其他的机器上挂在检测一下

bash
#本次在k8s-node1上验证下nfs server提供的nfs服务是否正常用下面这个命令验证下:mount-tnfs-overs=3,nolock,proto=tcp,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport172.29.9.31:/nfs/opt#如果是报下面这个错误的话,是因为nfs client也是需要安装nfs-utils包的,要不这个测试命令用不了的。#最后,特别注意:无论nfs客户端还是服务端均要安装nfs-utils软件包的,否则后期pod创建会报错的!!![root@k8s-node1 ~]#mount -t nfs -o vers=3,nolock,proto=tcp,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport172.29.9.31:/nfs/optmount:wrongfstype,badoption,badsuperblockon172.29.9.31:/nfs,missingcodepageorhelperprogram,orothererror(forseveral filesystems (e.g.nfs,cifs) you mightneeda/sbin/mount.<type>helperprogram)Insomecasesusefulinfoisfoundinsyslog-trydmesg|tailorso.[root@k8s-master ~]##安装nfs-utils[root@k8s-node1 ~]#yum install -y nfs-utils#再次用命令测试nfs 服务是否正常[root@k8s-node1 ~]#mount -t nfs -o vers=3,nolock,proto=tcp,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport172.29.9.31:/nfs/opt#可以看到nfs服务一切正常[root@k8s-node1 ~]#df|grepnfs172.29.9.31:/nfs1781145662794241153203236%/opt[root@k8s-node1 ~]#mount|grepnfssunrpcon/var/lib/nfs/rpc_pipefstyperpc_pipefs(rw,relatime)172.29.9.31:/nfson/opttypenfs(rw,relatime,vers=3,rsize=262144,wsize=262144,namlen=255,hard,nolock,noresvport,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=172.29.9.31,mountvers=3,mountport=20048,mountproto=tcp,local_lock=all,addr=172.29.9.31)[root@k8s-node1 ~]##我们可以顺便写几个文件测测下效果:[root@k8s-node1 ~]#df -hT|grepnfs172.29.9.31:/nfsnfs17G6.0G11G36%/opt[root@k8s-node1 ~]#cd /opt/[root@k8s-node1 opt]#ls[root@k8s-node1 opt]#echo node2 >node2.txt #在node2 nfs client的nfs目录下写一个文件[root@k8s-node1 opt]#[root@k8s-node1 opt]#exitlogoutConnectiontok8s-node1closed.[root@k8s-master ~]#cd /nfs/ #然后再nfs serrver端的nfs目录下就可以看见这个文件了,说明我们的nfs服务时没有问题的[root@k8s-master nfs]#lsnode2.txt[root@k8s-master nfs]#cat node2.txtnode2[root@k8s-master nfs]##测试nfs正常后,我们这里把刚挂载的nfs给卸载掉[root@k8s-node1 ~]#df -hT|grepnfs172.29.9.31:/nfsnfs17G6.0G11G36%/opt[root@k8s-node1 ~]#umount /opt/[root@k8s-node1 ~]#df -hT|grepnfs[root@k8s-node1 ~]#

nfs服务搭建成功后,我们就可以来测试pv、pvc的实验了。

问题:测试pv,pvc实验时,需要把刚才测试nfs服务可用性时挂载的目录给卸载吗?

答案:要去掉,一般情况是需要去掉的,当时我们只是测试下nfs服务的可用性而已。并且我们只要保证k8s节点均能使用nfs服务即可。记得,节点上要安装nfs-utils软件包哦。

1.创建基于NFS的PV

我们先来查看下当前k8s集群环境,保证本次实验环境纯净:

bash
[root@k8s-master ~]#kubectl get po,deploy,svc,pv,pvc #可以看到,当前k8s集群环境是ok的NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S) AGEservice/kubernetesClusterIP10.96.0.1<none>443/TCP166d[root@k8s-master ~]#

1、创建pv的yaml文件

运维工程师准备好PV的配置文件,如下所示:

vim pv.yaml

yaml
apiVersion:v1kind:PersistentVolumemetadata:name:pv1labels:type:k8s-claim# <---------- 0 spec:capacity:storage:1Gi# <---------- 1accessModes:["ReadWriteOnce"] # <---------- 2persistentVolumeReclaimPolicy:Recycle# <---------- 3storageClassName:nfs# <---------- 4nfs:path:/nfs/pv1# <---------- 5server:172.29.9.31

对上面的yaml文件我们做个说明:

bash
0、labels:type:k8s-claim# 这里建议打上一个独有的标签,在多个pv的时候方便提供pvc选择挂载,工作中一般上打标签格式是:项目组-团队名-pv名1、capacity是指定pv的容量是1G2、accessModes是指定的访问模式,支持的访问模式有:ReadWriteOnce表示PV能以read-write模式mount到单个节点ReadOnlyMany表示pv有read-only只读模式mount到多个节点ReadWriteMany表示pv以read-write模式mount到多个节点3、persistentVolumeReclaimPolicy指定当PV的回收策略,有三种方式:#研发使用完后,就会删除pvc,删除pvc后就会有3个动作Retain:删除容器之后,但是pv由管理员手工回收,安全级别比较高#工作环境中一般常用的是`retain`Recycle:删除容器之后,清除PV中的数据,效果相当于执行了rm-rf/nfs/pv1/*Delete:删除容器之后,删除整个PV对应的存储资源,不推荐!!!4、storageClassName是指定PV的class为我们这里定义的nfs,相当于为PV设置了一个分类,`pvc可以指定class申请相应class的pv`。举个例子:开发去申请2G大小的pvc,那么符合pvc条件的pv有两种。一种是高速存储Gclass,一种是低速存储Lclass,开发申请的2G是用于数据库,那么需要高速存储,此时定义storageClassName为Gclass,所以pvc会自动去找Gclass。这里有个坑,`就是PVC里面的storageClassName必须和PV里面的storageClassName保持一致`#我们可以看到,pvC的限制非常多,都要符合pv的条件才行;

另外:这里需要先指定PV在NFS服务器上对应的目录,这里注意,需要手动提前创建好这个目录并授权,否则后面挂载会提示目录不存在。授权的用户及用户组是nobody #这里需要注意下。

bash
[root@k8s-master ~]#cat /etc/passwd|grepnobodynobody:x:99:99:Nobody:/:/sbin/nologinnfsnobody:x:65534:65534:AnonymousNFSUser:/var/lib/nfs:/sbin/nologin[root@k8s-master ~]#mkdir /nfs/pv1[root@k8s-master ~]#chown -R nobody.nobody /nfs/pv1/[root@k8s-master ~]#chown -R nobody.nobody /nfs/[root@k8s-master ~]#ll -d /nfs/ /nfs/pv1/drwxr-xr-x3nobodynobody34Nov1614:29/nfs/drwxr-xr-x2nobodynobody6Nov1614:29/nfs/pv1/[root@k8s-master ~]#

2、创建PV

bash
# 部署pv[root@k8s-master ~]#kubectl apply -f pv.yamlpersistentvolume/pv1created# 查看pv的状态[root@k8s-master ~]#kubectl get pvNAMECAPACITYACCESSMODESRECLAIMPOLICYSTATUSCLAIMSTORAGECLASSREASONAGEpv11GiRWORecycleAvailablenfs5s[root@k8s-master ~]## 注意:查看STATUS字段,只有为Available字段才可以正常使用,被pvc申请

2.创建基于NFS的PVC

1、接下来创建pvc

vim pvc.yaml

yaml
apiVersion:v1kind:PersistentVolumeClaimmetadata:name:pvc1# <----------4spec:accessModes:["ReadWriteOnce"] # <----------0resources:requests:storage:1Gi# <----------1storageClassName:nfs# <----------2selector:matchLabels:type:k8s-claim# <----------3

接下来我们对上面的参数做一个说明

bash
0、accessModes是指定的访问模式,跟我们上面解释的一个道理,需要注意,`pv里面是什么模式,这里也要写对应的模式`1、storage存储大小,与pv里面保持一致2、storageClassName名字,至少和某个pv的storageClassName保持一致,否则找不到对应的分类3、matchLabels:这是匹配的pv的标签,标签在k8s里面非常重要的一个概念4、metadata:pvc的标签,后面挂载成功后会用到

2、创建pvc

bash
# 应用pvc.yaml配置文件[root@k8s-master ~]#kubectl apply -f pvc.yamlpersistentvolumeclaim/pvc1created# 获取pvc状态[root@k8s-master ~]#kubectl get pvcNAMESTATUSVOLUMECAPACITYACCESSMODESSTORAGECLASSAGEpvc1Boundpv11GiRWOnfs53s[root@k8s-master ~]## 主要是看STATUS字段,当表示Bound代表成功找到pv并成功绑定# 此时再去看pv的状态[root@k8s-master ~]#kubectl get pvNAMECAPACITYACCESSMODESRECLAIMPOLICYSTATUSCLAIMSTORAGECLASSREASONAGEpv11GiRWORecycleBounddefault/pvc1nfs12m[root@k8s-master ~]## 可以看到pv的STATUS字段显示Bound,而且CLAIM字段提示被default/pvc所消费

3.创建Pod应用挂载pvc

pvc与pv绑定成功后就可以使用pvc了,使用pvc其实就是挂载pvc,当目录挂载了pvc之后,应用就可以正常工作了,比如将pvc挂载到nginx的web根目录/usr/share/www/html上,如下例子所示:

vim nginx.yaml

yaml
apiVersion:v1kind:Servicemetadata:name:nginxlabels:app:nginxspec:ports:- port:80protocol:TCPtargetPort:80selector:app:nginx---apiVersion:apps/v1kind:Deploymentmetadata:name:nginxlabels:app:nginxspec:replicas:1selector:matchLabels:app:nginxtemplate:metadata:labels:app:nginxspec:containers:- name:nginximage:nginxvolumeMounts:# 我们这里将nginx容器默认的页面目录挂载- name:html-files# 与下面的volumes的name保持一致mountPath:"usr/share/nginx/html"volumes:- name:html-filespersistentVolumeClaim:# 卷类型使用pvc,同时下面名称处填先创建好的pvc1claimName:pvc1

4.创建对应存储上面的资源

在实际的工作环境当中,创建一个相对应的web服务之前已经准备好了访问的资源,等容器创建完毕,就可以立马访问了。

bash
# 先在对应的存储上创建访问的资源[root@k8s-master ~]#cd /nfs/pv1/[root@k8s-master pv1]#echo 'HelloWorld'>index.html

5.创建nginx的pod和service

bash
[root@k8s-master ~]#kubectl apply -f nginx.yamlservice/nginxcreateddeployment.apps/nginxcreated[root@k8s-master ~]#

等待这个pod资源创建完毕

bash
[root@k8s-master ~]#kubectl get po,deploy,svcNAMEREADYSTATUSRESTARTSAGEpod/nginx-ffc5548d-48zwj1/1Running030sNAMEREADYUP-TO-DATEAVAILABLEAGEdeployment.apps/nginx1/11130sNAMETYPECLUSTER-IPEXTERNAL-IPPORT(S) AGEservice/kubernetesClusterIP10.96.0.1<none>443/TCP167dservice/nginxClusterIP10.97.70.55<none>80/TCP30s[root@k8s-master ~]#

访问一下

bash
~]# curl http:[root@k8s-master ~]#curl 10.97.70.55HelloWorld[root@k8s-master ~]#

进入容器内部修改index.html文件(不建议),再次访问查看效果:

bash
[root@k8s-master ~]#kubectl get poNAMEREADYSTATUSRESTARTSAGEnginx-ffc5548d-48zwj1/1Running03m13s[root@k8s-master ~]#kubectl exec -it nginx-ffc5548d-48zwj -- bashroot@nginx-ffc5548d-48zwj:/#echo222222>>/usr/share/nginx/html/index.htmlroot@nginx-ffc5548d-48zwj:/#exitexit[root@k8s-master ~]#cat /nfs/pv1/index.htmlHelloWorld222222[root@k8s-master ~]##以上符合预期效果。

看的出来容器里面的修改都会保存到nfs存储上。

有的同学访问403的原因和解决方法

没有在nfs存储创建相应的资源导致的。主要是容器里面的/usr/share/nginx/html目录下的内容挂载后被覆盖了,所以没有index.html文件

在nginx的pod所在的节点执行下面的命令可以查看相关的挂载信息

bash
#查看次pod被调度在了k8s-node1节点上了[root@k8s-master ~]#kubectl get po -owideNAMEREADYSTATUSRESTARTSAGEIPNODENOMINATEDNODEREADINESSGATESnginx-ffc5548d-48zwj1/1Running04m56s10.244.36.122k8s-node1<none><none>[root@k8s-master ~]##到k8s-node1节点上可以看到次pod的挂载信息的[root@k8s-node1 ~]#mount -l|grepnfssunrpcon/var/lib/nfs/rpc_pipefstyperpc_pipefs(rw,relatime)172.29.9.31:/nfs/pv1on/var/lib/kubelet/pods/91246cf2-5a69-4e93-999b-5166be2a12b6/volumes/kubernetes.io~nfs/pv1typenfs4(rw,relatime,vers=4.1,rsize=262144,wsize=262144,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=172.29.9.32,local_lock=none,addr=172.29.9.31)[root@k8s-node1 ~]##是可以看到index.html文件的内容的[root@k8s-node1 ~]#ls /var/lib/kubelet/pods/91246cf2-5a69-4e93-999b-5166be2a12b6/volumes/kubernetes.io~nfs/pv1/index.html[root@k8s-node1 ~]#cat /var/lib/kubelet/pods/91246cf2-5a69-4e93-999b-5166be2a12b6/volumes/kubernetes.io~nfs/pv1/index.htmlHelloWorld222222[root@k8s-node1 ~]## 在nfs-server端执行,也是可以查看存储目录相关信息的fuser-mv/nfs/pv1#注意:这个fuser命令需要安装psmisc-22.20-17.el7.x86_64软件包[root@k8s-master ~]#yum install -y psmisc-22.20-17.el7.x86_64[root@k8s-master ~]#fuser -mv /nfs/pv1|grepnfsUSERPIDACCESSCOMMAND/nfs/pv1:rootmount/rootknfsd/nfsroot.rce.systemdroot.rc..kthreaddroot.rc..ksoftirqd/0root.rc..kworker/0:0Hroot.rc..migration/0root.rc..rcu_bhroot.rc..rcu_schedroot.rc..lru-add-drainroot.rc..watchdog/0root.rc..watchdog/1root.rc..migration/1root.rc..ksoftirqd/1root.rc..kworker/1:0Hroot.rc..netnsroot.rc..khungtaskdroot.rc..writebackroot.rc..kintegritydroot.rc..biosetroot.rc..biosetroot.rc..biosetroot.rc..kblockdroot.rc..mdroot.rc..edac-pollerroot.rc..watchdogdroot.rc..kswapd0root.rc..ksmdroot.rc..khugepagedroot.rc..cryptoroot.rc..kthrotldroot.rc..kmpath_rdacdroot.rc..kaluadroot.rc..kpsmousedroot.rc..ipv6_addrconfroot.rc..deferwqroot.rc..kauditdroot.rc..nfitroot.rc..ata_sffroot.rc..mpt_poll_0root.rc..mpt/0root.rc..scsi_eh_0root.rc..scsi_tmf_0root.rc..scsi_eh_1root.rc..scsi_tmf_1root.rc..scsi_eh_2root.rc..scsi_tmf_2root.rc..ttm_swaproot.rc..irq/16-vmwgfxroot.rc..kworker/1:0root.rc..kdmflushroot.rc..biosetroot.rc..kdmflushroot.rc..biosetroot.rc..biosetroot.rc..xfsallocroot.rc..xfs_mru_cacheroot.rc..xfs-buf/dm-0root.rc..xfs-data/dm-0root.rc..xfs-conv/dm-0root.rc..xfs-cil/dm-0root.rc..xfs-reclaim/dm-root.rc..xfs-log/dm-0root.rc..xfs-eofblocks/droot.rc..xfsaild/dm-0root.rc..kworker/1:1Hroot.rce.systemd-journalroot.rce.lvmetadrootfrce.systemd-udevdroot.rc..kworker/0:1Hroot.rc..xfs-buf/sda1root.rc..xfs-data/sda1root.rc..xfs-conv/sda1root.rc..xfs-cil/sda1root.rc..xfs-reclaim/sdaroot.rc..xfs-log/sda1root.rc..xfs-eofblocks/sroot.rc..xfsaild/sda1root.rc..rpciodroot.rc..xprtiodrootFrce.auditdroot.rce.systemd-logindroot.rce.irqbalancerootFrce.VGAuthServicerootFrce.vmtoolsddbus.rce.dbus-daemonrootFrce.gssproxypolkitd.rce.polkitdroot.rce.crondroot.rce.loginroot.rc..kworker/1:2root.rce.sshdrootFrce.tunedrootFrce.rsyslogdrootFrce.kubeletrootFrce.containerdrootFrce.dockerdrootFrce.masterpostfix.rce.qmgrroot.rce.sshroot.rc..kworker/1:1root.r.e.containerd-shimroot.r.e.containerd-shimroot.r.e.containerd-shimroot....mpauseroot....mpauseroot....mpauseroot.rce.greproot.r.e.containerd-shimroot....mpauseroot.r.e.containerd-shimrootF...metcdroot.r.e.containerd-shimroot....mpauseroot.r.e.containerd-shimroot....mpauseroot.r.e.containerd-shimroot....mkube-proxyroot.r.e.containerd-shimroot....mpauseroot.r.e.containerd-shimpolkitd....mkube-controllerroot.r.e.containerd-shimroot.r.e.containerd-shimroot....mpauseroot....mpauseroot.r.e.containerd-shimroot.r.e.containerd-shimroot....mpauseroot....mpauseroot.rc..kworker/0:1rpc.rce.rpcbindrpcuser.rce.rpc.statdroot.rce.rpc.idmapdrootfrce.rpc.mountdroot.rc..nfsd4_callbacksroot.rc..lockdroot.rc..nfsdroot.rc..nfsdroot.rc..nfsdroot.rc..nfsdroot.rc..nfsdroot.rc..nfsdroot.rc..nfsdroot.rc..nfsdroot.rc..kworker/u256:0root.r.e.containerd-shimroot....mkube-controllerroot.r.e.containerd-shimroot.r.e.containerd-shim(unknown) F...mmetrics-sidecarroot....mkube-schedulerroot.r.e.containerd-shimroot.r.e.containerd-shimroot....mcorednsroot....mcorednsroot.r.e.containerd-shimroot....mkube-apiserverroot.r.e.containerd-shim(unknown) F...mdashboardroot.r.e.containerd-shimroot....mrunsvdirroot....mrunsvroot....mrunsvroot....mrunsvroot....mrunsvroot....mrunsvroot....mrunsvroot....mcalico-noderoot....mcalico-noderoot....mcalico-noderoot....mcalico-noderoot....mbird6root....mbirdroot.rc..kworker/u256:1root.rc..kworker/0:0root.rc..kworker/u257:0root.rc..hci0root.rc..hci0root.rc..kworker/u257:2root.rce.sshdroot.rce.sshdroot.rce.sftp-serverroot.rce.bashpostfix.rce.pickuproot.rce.sshdroot.rce.sshdroot.rce.sftp-serverroot.rce.bashroot.rc..nfsiodroot.rce.bash[root@k8s-master ~]#

6.模拟容器意外退出

手动删除pod,查看容器里面的修改是否持久化了

bash
kubectldeletepodxxxxxcurlhttp:#测试过程[root@k8s-master ~]#kubectl get po,svcNAMEREADYSTATUSRESTARTSAGEpod/nginx-ffc5548d-dj9t71/1Running02m8sNAMETYPECLUSTER-IPEXTERNAL-IPPORT(S) AGEservice/kubernetesClusterIP10.96.0.1<none>443/TCP167dservice/nginxClusterIP10.97.70.55<none>80/TCP16m[root@k8s-master ~]#kubectl delete pod nginx-ffc5548d-dj9t7pod"nginx-ffc5548d-dj9t7"deleted[root@k8s-master ~]#kubectl get po,svcNAMEREADYSTATUSRESTARTSAGEpod/nginx-ffc5548d-f78lf1/1Running031sNAMETYPECLUSTER-IPEXTERNAL-IPPORT(S) AGEservice/kubernetesClusterIP10.96.0.1<none>443/TCP167dservice/nginxClusterIP10.97.70.55<none>80/TCP17m[root@k8s-master ~]#curl 10.97.70.55HelloWorld222222[root@k8s-master ~]#

此时会发现我们之前修改的内容依然保存着,这就是容器由于某些原因意外退出后,数据依然持久化的目的。

7.回收PVC和PV

bash
[root@k8s-master ~]#kubectl get pvcNAMESTATUSVOLUMECAPACITYACCESSMODESSTORAGECLASSAGEpvc1Boundpv11GiRWOnfs56m[root@k8s-master ~]#kubectl delete pvc pvc1persistentvolumeclaim"pvc1"deleted# 一直卡在这里# 卡着的原因就是因为我们的pod应用正在使用pvc,所以删除不了# 看下pvc发现STATUS是Terminating删除中的状态,这是因为服务pod还在占用这个pvc使用中[root@k8s-master ~]#kubectl get pvcNAMESTATUSVOLUMECAPACITYACCESSMODESSTORAGECLASSAGEpvc1Terminatingpv11GiRWOnfs57m[root@k8s-master ~]#

接下来我们先删除pod

bash
# 先访问一把[root@k8s-master ~]#curl 10.97.70.55HelloWorld222222# 删除nginx资源[root@k8s-master ~]#kubectl delete -f nginx.yamlservice"nginx"deleteddeployment.apps"nginx"deleted#再次查看pvc[root@k8s-master ~]#kubectl get pvcNoresourcesfoundindefaultnamespace.[root@k8s-master ~]## 会发现,删除了nginx资源,pvc这个资源立马就消失了,# 根据先前创建pv时的数据回收策略为Recycle – 清除 PV 中的数据,这时果然先创建的index.html已经被删除了,在生产中要尤其注意这里的模式,注意及时备份数据[root@k8s-master ~]#ls /nfs/pv1/[root@k8s-master ~]## 注意:虽然此时pv是可以再次被pvc来消费的,但根据生产的经验,建议在删除pvc时,也同时把它消费的pv一并删除,然后再重启创建都是可以的

至此,以上实验结束,感谢阅读。😘

FAQ

📍 特别注意:nfs网络共享存储只需提供nfs服务就好

实际nfs作为k8s集群共享存储使用时(不管是nfs作为共享存储或者是使用storageClass),只要保证nfs server可用,k8s node节点都可以正常访问到nfs服务就可以(不需要在节点上挂载nfs配置,因为在pv里面已经有指定了nfs路径新信息;或者storageclass里面已经配置了nfs server相关信息了;)

另外:

k8s集群all的节点都是需要安装nfs服务的,一定要记得都安装的。

📍 注意:更改nfs插件"是否归档"参数

注意:nfs-external-provisioner插件默认开启了归档功能,因此这里虽然声明的是Delete,但是从现象来看,Nfs后端存储那边的数据不是还在的吗,这是因为现在这个参数默认是true,因此才会出现归档,如果设置为false的话,那么原来的数据就会被彻底删除了。

测试过程如下:

bash
01.查看当前环境[root@master1 ~]#kubectl get pvNAMECAPACITYACCESSMODESRECLAIMPOLICYSTATUSCLAIMSTORAGECLASSREASONAGEnfs-pv1GiRWORetainBounddefault/nfs-pvcmanual5hpvc-3f576ba3-deeb-4161-af24-5588e96f20c71GiRWODeleteBounddefault/nfs-sc-pvcnfs-client97s[root@master1 ~]#kubectl get pvcNAMESTATUSVOLUMECAPACITYACCESSMODESSTORAGECLASSAGEnfs-pvcBoundnfs-pv1GiRWOmanual5hnfs-sc-pvcBoundpvc-3f576ba3-deeb-4161-af24-5588e96f20c71GiRWOnfs-client99s[root@master1 ~]#kubectl get po -owideNAMEREADYSTATUSRESTARTSAGEIPNODENOMINATEDNODEREADINESSGATESnfs-sc-pod1/1Running02m32s10.244.1.123node1<none><none>test-volumes1/1Running03h8m10.244.2.88node2<none><none>[root@master1 ~]#kubectl get pv pvc-3f576ba3-deeb-4161-af24-5588e96f20c7 -oyaml|greppersistentVolumeReclaimPolicypersistentVolumeReclaimPolicy:Delete#可以看到这个nfs storage class默认的回收策略是delete#查看pod所在宿主机volume存放情况[root@master1 ~]#kubectl get po nfs-sc-pod -owideNAMEREADYSTATUSRESTARTSAGEIPNODENOMINATEDNODEREADINESSGATESnfs-sc-pod1/1Running03m40s10.244.1.123node1<none><none>[root@master1 ~]#kubectl get po nfs-sc-pod -owide -oyaml|grepuiduid:17b601d3-afcf-4b89-9cb5-0bc654cacfc0[root@master1 ~]#ssh node1 [root@node1 ~]#ls /var/lib/kubelet/pods/17b601d3-afcf-4b89-9cb5-0bc654cacfc0/volumes/kubernetes.io~nfs/pvc-3f576ba3-deeb-4161-af24-5588e96f20c702.我们删除pvc(注意这里要按正常流程删除pod),并再次观察$kubectldelete-f04-nfs-sc-pod.yamlpod"nfs-sc-pod"deleted[root@master1 ~]#kubectl delete pvc nfs-sc-pvc persistentvolumeclaim"nfs-sc-pvc"deleted[root@master1 ~]#ls /var/lib/k8s/data/ #可以看到nfs server端,pv数据还是会被archived的;archived-default-nfs-sc-pvc-pvc-3f576ba3-deeb-4161-af24-5588e96f20c7test-volumesarchived-default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92test.txt

📍 面试题:k8s挂载pv、pvc的流程

bash
1.调度器把根据各种调度算法把该POD分配到某个节点,比如node012.Node01上的kubelet等待VolumeManager准备存储设备3.PV控制器调用存储插件创建PV并与PVC进行绑定4、Kubelet被告知卷已经准备好,开始启动POD,通过映射方式挂载到容器中

📍 nfs的高可用问题

nfs公司,小规模应用还是比较可以的,性能够用,易于维护;

但存在的问题是:

单点故障,做高可用,又不好做;很多公司做高可用,都是基于分布式存储的块存储来做nfs的高可用。比如说:drdb同步数据块。  或者使用ceph的块存储。 在这些基础之上,做nfs的共享。

注意:nfs服务器重启后可能会出现卡主现象。

一般存储:采用分布式存储会好些;例如ceph,不过这个可能学起来稍微会复杂些;

上云存储;-->有钱就上啊。

image-20230911055121576

关于我

我的博客主旨:我希望每一个人拿着我的博客都可以做出实验现象,先把实验做出来,然后再结合理论知识更深层次去理解技术点,这样学习起来才有乐趣和动力。并且,我的博客内容步骤是很完整的,也分享源码和实验用到的软件,希望能和大家一起共同进步!

各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人免费帮您解决问题:

  1. 个人微信二维码:x2675263825 (舍得), qq:2675263825。

  1. 个人博客地址:www.onlyonexl.cn

  1. 个人微信公众号:云原生架构师实战

  1. 个人csdn

https:

版权:此文章版权归 One 所有,如有转载,请注明出处!

链接:可点击右上角分享此页面复制文章链接

上次更新时间:

最近更新