先来个一句话总结:PV、话说PVC是明白K8S用来做存储管理的资源对象,它们让存储资源的大白的使用变得可控,从而保障系统的话说稳定性、可靠性。明白StorageClass则是大白的为了减少人工的工作量而去自动化创建PV的组件。所有Pod使用存储只有一个原则:先规划 → 后申请 → 再使用。话说
PV是话说对K8S存储资源的抽象,PV一般由运维人员创建和配置,明白供容器申请使用。
没有PV之前,服务器的磁盘没有分区的概念,有了PV之后,相当于通过PV对服务器的磁盘进行分区。
PVC 是Pod对存储资源的一个申请,主要包括存储空间申请、访问模式等。创建PV后,Pod就可以通过PVC向PV申请磁盘空间了。类似于某个应用程序向操作系统的D盘申请1G的使用空间。
PVC 创建成功之后,Pod 就可以以存储卷(Volume)的方式使用 PVC 的存储资源了。Pod 在使用 PVC 时必须与PVC在同一个Namespace下。
PV相当于对磁盘的分区,PVC相当于APP(应用程序)向某个分区申请多少空间。比如说安装WPS程序时,一般会告知我们安装它需要多少存储空间,让你选择在某个磁盘下安装。如果将来某个分区磁盘满了,也不会影响别的分区磁盘的使用。
一旦 PV 与PVC绑定,Pod就可以使用这个 PVC 了。如果在系统中没有满足 PVC 要求的 PV,PVC则一直处于 Pending 状态,直到系统里产生了一个合适的 PV。
K8S有两种存储资源的供应模式:静态模式和动态模式,资源供应的最终目的就是将适合的PV与PVC绑定:
StorageClass就是动态模式,根据PVC的需求动态创建合适的PV资源,从而实现存储卷的按需创建。
一般某个商业性的应用程序,会用到大量的Pod,如果每个Pod都需要使用存储资源,那么就需要人工时不时的去创建PV,这也是个麻烦事儿。解决方法就是使用动态模式:当Pod通过PVC申请存储资源时,直接通过StorageClass去动态的创建对应大小的PV,然后与PVC绑定,所以基本上PV → PVC是一对一的关系。
在创建 PVC 时需要指定 StorageClass,PVC 选择到对应的StorageClass后,与其关联的 Provisioner 组件来动态创建 PV 资源。
那Provisioner是个啥呢?其实就一个存储驱动,类似操作系统里的磁盘驱动。
StorageClass 资源对象的定义主要包括:名称、Provisioner、存储的相关参数配置、回收策略。StorageClass一旦被创建,则无法修改,只能删除重新创建。
PV和PVC的生命周期,包括4个阶段:资源供应(Provisioning)、资源绑定(Binding)、资源使用(Using)、资源回收(Reclaiming)。首先旧的有资源供应,说白了就是得有存储驱动,然后才能创建、绑定和使用、回收。
在没有使用PV、PVC之前,各个Pod都可以任意的向存储资源里(比如NFS)写数据,随便一个Pod都可以往磁盘上插一杠子,长期下去磁盘的管理会越来越混乱,然后导致数据使用超限,磁盘爆掉,最后导致磁盘上的所有应用全部挂掉。
为了解决这个问题,引入了PV、PVC的概念,达到限制Pod写入存储数据大小的目的,从而更好地保障了系统的可用性、稳定性。
有了PVC、PV之后,所有Pod使用存储资源,保持一个原则:先规划 → 后申请 → 再使用。
那你肯定有一个疑问,“StorageClass是自动化创建PV,跟原本的无序不可控是一样的效果啊,都可以随便占用存储资源啊”。
其实不然,使用StorageClass只是自动化了创建PV的流程,但依旧执行的是一个存储可控的流程。每个Pod使用多少存储空间是固定的,Pod没有办法超额使用存储空间,更不会影响到别的应用,要出故障也只是某个Pod自己出故障。
没有使用PV、PVC之前的情况,如下面2张图:
有了PV、PVC之后的情况,如下图:
在实践PV、PVC、StorageClass之前,需要读者朋友自行安装NFS服务器。文中演示的内容是通过yaml编排自动到NFS服务器起上创建PV。
文中演示的是:Pod的某个目录挂载到NFS的某个目录下。使用了nginx镜像,将html文件写在PV所在的NFS服务器上,最终可以看到利用PV / PVC 成功挂载上去了。
yaml文件如下:
# PV编排apiVersion: v1kind: PersistentVolumemetadata: name: nfs-pv1 namespace: dev1 labels: pv: nfs-pv1spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce # Recycle 删除PVC会同步删除PV | Retain 删除PVC不会同步删除PV persistentVolumeReclaimPolicy: Recycle nfs: path: /data/nfstest/share/pv1 server: 10.20.1.20 readOnly: false---# PVC 编排,通过selector查找PV,K8S里的资源查找都是通过selector查找label标签apiVersion: v1kind: PersistentVolumeClaimmetadata: name: nfs-pvc1 namespace: dev1 labels: pv: nfs-pvc1spec: resources: requests: storage: 100Mi accessModes: - ReadWriteOnce selector: matchLabels: pv: nfs-pv1---# Pod挂载PVC,这里为了测试,直接通过node节点的hostPort暴露服务apiVersion: v1kind: Podmetadata: name: webapp namespace: dev1 labels: app: webappspec: containers: - name: webapp image: nginx imagePullPolicy: IfNotPresent ports: - containerPort: 80 hostPort: 8081 volumeMounts: - name: workdir mountPath: /usr/share/nginx/html volumes: - name: workdir persistentVolumeClaim: claimName: nfs-pvc1
执行kubectl命令,查看实践效果如下:
然后查看pod的情况,发现pod一直处于创建中,如下:
于是查看pod的情况kubectl describe pod webapp -n dev1,发现如下异常信息:
是因为没有在NFS上创建此文件夹。到NFS创建此文件夹之后,重启Pod,一切正常了,然后找到Pod所在Node节点。通过http://nodeip:port访问,可以看到成功的界面:
[root@k8s-master pv-pvc-storageclass]# kubectl get pods -n dev1 -owide | grep webappwebapp 1/1 Running 0 4m17s 10.21.69.214 k8s-worker-3 <none> <none>
此时因为nginx下还没有html页面,所以看不到内容。此时到NFS服务器对应的目录/data/nfstest/share/pv1下增加index.html页面,然后刷新页面即可,界面如下:
也可以通过进入到Pod内部,查看验证是够挂载成功。
执行进入Pod的命令kubectl exec -it webapp -n dev1 -- /bin/sh,可以看到如下页面:
文中选择通过helm的方式安装nfs-subdir-external-provisioner,这种方式相对简单。安装文档、安装过程见下文:
https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#nfs
https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
通过以下3个步骤完成nfs-subdir-external-provisioner的安装。
brew install heml
$ helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/$ helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner -n kube-system \ --set image.repository=dyrnq/nfs-subdir-external-provisioner \ --set nfs.server=10.20.1.20 \ --set nfs.path=/data/nfstest/nfs-storage
这里注意几个参数:
image.repository:修改了镜像的地址,默认用的国外镜像很有可能拉不下来
nfs.server:你的NFS服务器地址
nfs.path:存储目录
执行命令:helm list -A,查看helm安装结果:
查看是否创建了对应的pod,如果没有修改镜像地址会一直拉取失败,如下图:
修改镜像地址后成功启动Pod,如下图:
文中演示的是:Pod利用StorageClass自动创建PV,同时在对应的存储目录上创建了文件,写入了数据。
yaml文件如下:
apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: nfs-storage-1provisioner: cluster.local/nfs-subdir-external-provisionerparameters: # 设置为"false"时删除PVC不会保留数据,"true"则保留数据 archiveOnDelete: "false"mountOptions: # 指定NFS版本,这个需要根据NFS Server版本号设置 - nfsvers=4---# 创建PVCkind: PersistentVolumeClaimapiVersion: v1metadata: name: nfs-storage-pvc-1 namespace: dev1spec: storageClassName: nfs-storage-1 #需要与上面创建的storageclass的名称一致 accessModes: - ReadWriteOnce resources: requests: storage: 10Mi---kind: PodapiVersion: v1metadata: name: nfs-storage-pod-1 namespace: dev1spec: containers: - name: nfs-storage-pod-1 image: busybox command: - "/bin/sh" args: - "-c" - "touch /mnt/teststorage && echo 111 > /mnt/teststorage && exit 0 || exit 1" ## 创建一个名称为"SUCCESS"的文件 volumeMounts: - name: nfs-pvc mountPath: "/mnt" restartPolicy: "Never" volumes: - name: nfs-pvc persistentVolumeClaim: claimName: nfs-storage-pvc-1
执行kubectl命令后,可以看到如下效果:
可以看到如我们预料的那样,通过storageClass自动创建了PV,同时在NFS对应的存储目录上创建了文件,写入了数据。
至此,我们实践过程全部结束。
本文主要讲解了PV、PVC、StorageClass的理论和实战。
一句话总结:PV、PVC是K8S用来做存储管理的资源对象,它们让存储资源的使用变得可控,从而保障系统的稳定性、可靠性。StorageClass则是为了减少人工的工作量而去自动化创建PV的组件。所有Pod使用存储只有一个原则:先规划 → 后申请 → 再使用。
责任编辑:华轩 来源: 不焦躁的程序员 Kubernetes云原生(责任编辑:百科)
农历小年夜火车票昨天开始预售 多趟班次在短时间内被“一抢而空”
海外客商抢抓中国新春机遇 境外消费回流对进口消费产生一定带动作用