K8S-数据持久化PVPVCStorageClass的关系
Posted 清风明月li
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了K8S-数据持久化PVPVCStorageClass的关系相关的知识,希望对你有一定的参考价值。
一、引言
在使用K8S时总绕不开的话题就是K8S持久化存储,要实现持久化数据,就要把数据存储到硬盘上。在K8S持久化的过程中会有许多的概念PV、PVC、StorageClass、Provisioner等等。我只想存储一个文件有么有简单的方式呢?答案是有的。
K8S环境下,Container 中的文件在磁盘上是临时存放的,当容器崩溃时文件丢失。kubelet 会重新启动容器, 但容器会以干净的状态重启。所以我们要使用 Volume 来持久化数据。
Docker 也有 卷(Volume) 的概念,但对它只有少量且松散的管理。 Docker 卷是磁盘上或者另外一个容器内的一个目录 Docker 提供卷驱动程序,但是其功能非常有限。
Kubernetes 支持很多类型的卷。 Pod 可以同时使用任意数目的卷类型。
临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁 持久卷。对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。
卷的核心是一个目录,其中可能存有数据,Pod 中的容器可以访问该目录中的数据。 所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放 的内容。 |
使用卷时,在 .spec.volumes 字段中设置为 Pod 提供的卷,并在 .spec.containers[*].volumeMounts 字段中声明卷在容器中的挂载位置。 各个卷则挂载在镜像内的指定路径上。 卷不能挂载到其他卷之上,也不能与其他卷有硬链接。Pod 配置中的每个容器必须独立指定各个卷的挂载位置。 |
二、K8S存储的分类
2.1、Emptydir---临时存储
当pod的存储方案设定为emptydir的时候,pod启动时,就会在pod所在节点的磁盘空间开辟出一块空卷,最开始里面是什么都没有的,pod启动后容器产生的数据会存放到那个空卷中。空卷变成了一个临时卷供pod内的容器读取和写入数据,一旦pod容器消失,节点上开辟出的这个临时卷就会随着pod的销毁而销毁。
Emptydir的用途
- 缓存空间,例如基于磁盘的归并排序。
- 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
- 在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。
emptyDir配置示例
Go [root@k8s-master emptydir]# cat emptydir.yaml apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: nginx name: test-container volumeMounts: - mountPath: /usr/share/nginx/html name: cache-volume volumes: - name: cache-volume emptyDir: |
创建和验证
Go [root@k8s-master emptydir]# kubectl apply -f emptydir.yaml pod/test-pd created [root@k8s-master emptydir]# kubectl get pod -owide |grep test-pd test-pd 1/1 Running 0 29s 10.244.2.172 k8s-node2 <none> <none>
#1、在K8S-node2上进行操作 [root@k8s-node2 cache-volume]# docker ps -a|grep test-pd ec7a0faec6bb nginx "/docker-entrypoint.…" 3 minutes ago Up 3 minutes k8s_test-container_test-pd_default_0e27bcf4-7ebe-4dc3-b48a-3230cf7641ae_0
#2、查看容器的详细信息 [root@k8s-node2 ~]# docker inspect f87f032bbaf8 .... "Mounts": [ "Type": "bind", "Source": "/home/k8s/kubelet/pods/0e27bcf4-7ebe-4dc3-b48a-3230cf7641ae/volumes/kubernetes.io~empty-dir/cache-volume", "Destination": "/usr/share/nginx/html", "Mode": "", "RW": true, "Propagation": "rprivate" , ...... ###Source是宿主机的地址 ###Destination是挂载到容器的地址
#3、进入宿主机的目录并创建文件 [root@k8s-node2 cache-volume]# ls /home/k8s/kubelet/pods/0e27bcf4-7ebe-4dc3-b48a-3230cf7641ae/volumes/kubernetes.io~empty-dir/cache-volume [root@k8s-node2 cache-volume]# cd /home/k8s/kubelet/pods/0e27bcf4-7ebe-4dc3-b48a-3230cf7641ae/volumes/kubernetes.io~empty-dir/cache-volume [root@k8s-node2 cache-volume]# echo date > index.html [root@k8s-node2 cache-volume]# cat index.html date ##访问该pod的IP地址 [root@k8s-node2 cache-volume]# curl 10.244.2.173 date [root@k8s-node2 cache-volume]# date > index.html [root@k8s-node2 cache-volume]# curl 10.244.2.173 Sun Jun 5 17:02:30 CST 2022 ###创建其他的访问文件 [root@k8s-node2 cache-volume]# echo 2022-06-05 > test.html [root@k8s-node2 cache-volume]# curl 10.244.2.173/test.html 2022-06-05
#4、删除对应的pod清除实验。 [root@k8s-master emptydir]# kubectl delete -f emptydir.yaml pod "test-pd" deleted ###再次在node2计算节点上查看 [root@k8s-node2 cache-volume]# ls /home/k8s/kubelet/pods/0e27bcf4-7ebe-4dc3-b48a-3230cf7641ae/volumes/kubernetes.io~empty-dir/cache-volume ls: cannot access /home/k8s/kubelet/pods/0e27bcf4-7ebe-4dc3-b48a-3230cf7641ae/volumes/kubernetes.io~empty-dir/cache-volume: No such file or directory #emptydir是随pod创建而创建,然后再随pod删除而删除 |
2.2、hostPath
hostPath 卷能将主机节点文件系统上的文件或目录挂载到你的 Pod 中, 虽然这不是大多数 Pod 需要的,但是它为一些应用程序提供了强大的逃生舱。
hostPath的用途
- 运行一个需要访问 Docker 内部机制的容器;可使用 hostPath 挂载/var/lib/docker 路径。
- 在容器中运行 cAdvisor 时,以 hostPath 方式挂载/sys。
- 允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在。
Type的值
除了必需的path 属性之外,用户可以选择性地为hostPath 卷指定type。
支持的type 值如下:
取值 | 行为 |
空 | 空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。 |
DirectoryOrCreate | 如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 kubelet 相同的组和属主信息。 |
Directory | 在给定路径上必须存在的目录。 |
FileOrCreate | 如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 kubelet 相同的组和所有权。 |
File | 在给定路径上必须存在的文件。 |
Socket | 在给定路径上必须存在的 UNIX 套接字。 |
CharDevice | 在给定路径上必须存在的字符设备。 |
BlockDevice | 在给定路径上必须存在的块设备。 |
hostPath配置示例
Go [root@k8s-master hostpath]# cat hostpath.yaml apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: nginx name: test-container volumeMounts: - mountPath: /usr/share/nginx/html name: test-hostpath volumes: - name: test-hostpath hostPath: path: /data type: DirectoryOrCreate |
创建和配置示例
Go #1、创建pod [root@k8s-master hostpath]# kubectl apply -f hostpath.yaml pod/test-pd created [root@k8s-master hostpath]# kubectl get pod -owide|grep test-pd test-pd 1/1 Running 0 6m54s 10.244.2.174 k8s-node2 <none> <none> #2、在K8S-node2节点上查看内部 [root@k8s-node2 data]# docker ps -a|grep test-container b4ff1de7e371 nginx "/docker-entrypoint.…" 8 minutes ago Up 8 minutes k8s_test-container_test-pd_default_e48b2791-54d6-40d6-bc77-56328ca4c038_0 [root@k8s-node2 data]# docker inspect b4ff1de7e371 [ .... "Mounts": [ "Type": "bind", "Source": "/data", "Destination": "/usr/share/nginx/html", "Mode": "", "RW": true, "Propagation": "rprivate" ,
.... ] ##"Source": "/data", # 宿主机的目录 ## "Destination": "/usr/share/nginx/html", # 容器中的目录
#3、在master节点上进入pod [root@k8s-master hostpath]# kubectl exec -it pod/test-pd /bin/bash kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead. root@test-pd:/# cd /usr/share/nginx/html root@test-pd:/usr/share/nginx/html# ls index.html
root@test-pd:/usr/share/nginx/html# echo date >> index.html root@test-pd:/usr/share/nginx/html# cat index.html Sun Jun 5 17:23:17 CST 2022 test hostpath date [root@k8s-master hostpath]# curl 10.244.2.174 Sun Jun 5 17:23:17 CST 2022 test hostpath date
#4、删除pod [root@k8s-master hostpath]# kubectl delete -f hostpath.yaml pod "test-pd" deleted [root@k8s-master hostpath]# kubectl delete -f hostpath.yaml Error from server (NotFound): error when deleting "hostpath.yaml": pods "test-pd" not found
#5、重启创建pod [root@k8s-master hostpath]# kubectl apply -f hostpath.yaml pod/test-pd created [root@k8s-master hostpath]# kubectl get pod -owide|grep test-pd test-pd 1/1 Running 0 63s 10.244.2.175 k8s-node2 <none> <none> [root@k8s-master hostpath]# curl 10.244.2.175 Sun Jun 5 17:23:17 CST 2022 test hostpath date
###这种方式如果宿主机一直在的话,或者该pod一直被调度到该节点的话,可以实现pod的存储,如果该node停机维护或其他 问题,该数据就无法保持了。 |
2.3、pv、pvc
既然Host类型的持久化存储无法解决节点宕机或者pod在另外的机器上拉起导致数据无法恢复的Bug,那我们就应该思考一个问题:既然我无法在宿主机持久化,那我在集群之外的服务器上存储数据,让我的Pod关联到这个数据存储服务器上,同时我对这个数据存储服务器做高可用,岂不美哉? 想法很好,那我们来介绍一下K8S给我们的解决方案: PersistentVolumes 简称PV
- PV(PersistentVolume): 持久化卷。PV 是对底层共享存储的一种抽象,由管理员进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如 Ceph、GlusterFS、NFS、hostPath 等,都是通过插件机制完成与共享存储的对接。
- PVC(PersistentVolumeClaim): 持久化卷声明。PVC 是用户存储的一种声明,PVC 和 Pod 比较类似,Pod 消耗的是节点,PVC 消耗的是 PV 资源,Pod 可以请求 CPU 和内存,而 PVC 可以请求特定的存储空间和访问模式。对于真正使用存储的用户不需要关心底层的存储实现细节,只需要直接使用 PVC 即可。
hostpath创建pv、pvc
Go #1、创建pv [root@k8s-master volumes]# cat pv.yaml apiVersion: v1 kind: PersistentVolume metadata: name: nginx-pv-volume labels: type: local spec: storageClassName: manual-nginx capacity: storage: 5Gi accessModes: - ReadWriteOnce hostPath: path: "/home/nfs"
[root@k8s-master volumes]# kubectl apply -f pv.yaml persistentvolume/nginx-pv-volume created [root@k8s-master ~]# kubectl get pv -owide NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE VOLUMEMODE nginx-pv-volume 5Gi RWO Retain Available manual-nginx 10s Filesystem
##可以看到这个PV为10G,访问模式为RWO,卸载后保留,目前的STATUS为可用状态。
#2、创建pvc [root@k8s-master volumes]# kubectl apply -f pvc.yaml persistentvolumeclaim/nginx-pv-claim created [root@k8s-master volumes]# cat pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nginx-pv-claim spec: storageClassName: manual-nginx accessModes: - ReadWriteOnce resources: requests: storage: 3Gi [root@k8s-master volumes]# kubectl get pv,pvc -owide|grep pv-claim persistentvolume/nginx-pv-volume 5Gi RWO Retain Bound default/nginx-pv-claim manual-nginx 5m54s Filesystem persistentvolumeclaim/nginx-pv-claim Bound nginx-pv-volume 5Gi RWO manual-nginx 97s Filesystem
3、创建pod [root@k8s-master volumes]# cat pvc-pod.yaml apiVersion: v1 kind: Pod metadata: name: task-pv-pod spec: volumes: - name: nginx-pv-volume persistentVolumeClaim: claimName: nginx-pv-claim containers: - name: task-pv-container image: nginx:1.7.9 ports: - containerPort: 80 name: "http-server" volumeMounts: - mountPath: "/usr/share/nginx/html" name: nginx-pv-volume nodeSelector: kubernetes.io/hostname: k8s-node6 [root@k8s-master volumes]# kubectl apply -f pvc-pod.yaml pod/task-pv-pod created [root@k8s-master volumes]# kubectl get pod -owide|grep task-pv-pod task-pv-pod 1/1 Running 0 20s 10.244.4.117 k8s-node6 <none> <none>
#3、在k8s-node3中执行 [root@k8s-node6 ~]# hostname > /home/nfs/index.html [root@k8s-node6 ~]# curl 10.244.4.117 k8s-node6
|
NFS创建pv pvc
大多数情况下,持久化 Volume 的实现,往往依赖于一个远程存储服务,比如:远程文件存储(比如,NFS、GlusterFS)、远程块存储(比如,公有云提供的远程磁盘)等等。
Go 1、搭建nfs服务器 yum -y install nfs-utils rpcbind [root@k8s-master ~]# vim /etc/exports /data/k8s *(rw,sync,no_root_squash) [root@k8s-master ~]# systemctl enable rpcbind [root@k8s-master ~]# systemctl start rpcbind [root@k8s-master ~]# systemctl status rpcbind ####在启动nfs [root@k8s-master ~]# systemctl enable nfs Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service. [root@k8s-master ~]# systemctl start nfs [root@k8s-master ~]# systemctl status nfs
客户端挂在 [root@k8s-node1 ~]# mkdir /data/k8s/ -p [root@k8s-node1 ~]# mount -t nfs 172.16.4.169:/data/k8s /data/k8s
2、创建pv和pvc [root@k8s-master volumes]# kubectl apply -f nfs-pv.yaml persistentvolume/nginx-nfs-pv created [root@k8s-master volumes]# cat nfs-pv.yaml apiVersion: v1 kind: PersistentVolume # 类型为PV metadata: name: nginx-nfs-pv # pv的名称 spec: accessModes: # 访问模式 - ReadWriteMany # PV以read-write挂载到多个节点 capacity: # 容量 storage: 2Gi # pv可用的大小 nfs: path: /home/k8s/nfs # NFS的路径 server: 172.16.4.169 # NFS服务器地址
##创建pvc [root@k8s-master volumes]# cat nfs-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim # 类型 metadata: name: nginx-nfs-pvc # PVC的名称 spec: accessModes: # 访问模式 - ReadWriteMany # PVC以read-write挂载到多个节点 resources: requests: storage: 2Gi # PVC允许申请的大小 [root@k8s-master volumes]# kubectl apply -f nfs-pvc.yaml persistentvolumeclaim/nginx-nfs-pvc created [root@k8s-master ~]# kubectl get pv,pvc -owide|grep nfs persistentvolume/nginx-nfs-pv 2Gi RWX Retain Bound default/nginx-nfs-pvc 4m37s Filesystem persistentvolumeclaim/nginx-nfs-pvc Bound nginx-nfs-pv 2Gi RWX 53s Filesystem
##c创建pod [root@k8s-master volumes]# cat nfs-pod.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-nfs-pvc spec: selector: matchLabels: app: nginx-pvc template: metadata: labels: app: nginx-pvc spec: containers: - name: nginx-test-pvc image: nginx imagePullPolicy: IfNotPresent ports: - name: web-port containerPort: 80 protocol: TCP volumeMounts: - name: nginx-persistent-storage # 取个名字,与下面的volumes的名字要一致 mountPath: /usr/share/nginx/html # 容器中的路径 volumes: - name: nginx-persistent-storage persistentVolumeClaim: claimName: nginx-nfs-pvc # 引用前面声明的PVC
[root@k8s-master volumes]# kubectl apply -f nfs-pod.yaml deployment.apps/nginx-nfs-pvc created [root@k8s-master volumes]# kubectl get pod -owide|grep nfs nginx-nfs-pvc-577dbd6d5-jsjqf 1/1 Running 0 2m32s 10.244.2.183 k8s-node2 <none> <none> [root@k8s-master volumes]# curl 10.244.2.183 Thu May 12 19:26:48 CST 2022 this is mater
4、回收的顺序为:先删除pod,在删除pvc,查看pv的时候pv的状态为:Released ,如果变为avaiable, 则需要删除claimRef:字段下的所有配置。
|
2.4、StorageClass
创建PV有两种方式:一种是人工管理 PV 的方式就叫作 Static Provisioning;另一种是Dynamic Provisioning动态创建存储卷,动态供给的关键就是StorageClass,它的作用就是创建PV模板,Provision意为”提供者。 |
创建StorageClass里面需要定义PV属性比如存储类型、大小等;另外创建这种PV需要用到存储插件。最终效果是,用户提交PVC,里面指定存储类型,如果符合我们定义的StorageClass,则会为其自动创建PV并进行绑定。 |
什么是StorageClass
StorageClass 为管理员提供了描述存储 "class(类)" 的方法。 不同的 class 可能会映射到不同的服务质量等级或备份策略,或由群集管理员确定的任意策略。
Kubernetes提供了一套可以自动创建PV的机制,即:Dynamic Provisioning。而这个机制的核心在于StorageClass这个API对象。
StorageClass对象会定义下面两部分内容:
- PV的属性。比如,存储类型,Volume的大小等。
- 创建这种PV需要用到的存储插件,即存储制备器,比如,Ceph 等等。
有了这两个信息之后,Kubernetes就能够根据用户提交的PVC,找到一个对应的StorageClass,之后Kubernetes就会调用该StorageClass声明的存储插件,进而创建出需要的PV。
StorageClass 为管理员提供了描述存储 “类” 的方法。 不同的类型可能会映射到不同的服务质量等级或备份策略,或是由集群管理员制定的任意策略。 Kubernetes 本身并不清楚各种类代表的什么。这个类的概念在其他存储系统中有时被称为 “配置文件”。
StorageClass的功能
StorageClass 的作用,则是充当 PV 的模板。并且,只有同属于一个 StorageClass 的 PV 和 PVC,才可以绑定在一起。StorageClass 的另一个重要作用,是指定 PV 的 Provisioner(存储插件)。这时候,如果你的存储插件支持 Dynamic Provisioning 的话,Kubernetes 就可以自动为你创建 PV 了。
通过StorageClass创建pv和pvc例子
Go 1、创建pvc和storageclass的yaml文件 [root@k8s-master storageclass]# cat storageclass-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: storageclass-claim1 spec: accessModes: - ReadWriteOnce # 指定所使用的存储类,此存储类将会自动创建符合要求的 PV storageClassName: fast resources: requests: storage: 30Gi
--- apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: fast provisioner: kubernetes.io/gce-pd parameters: type: pd-ssd
[root@k8s-master storageclass]# kubectl apply -f storageclass-pvc.yaml persistentvolumeclaim/storageclass-claim1 created storageclass.storage.k8s.io/fast created
2、查看该pvc处于pending原因,由于没有没有gce插件 [root@k8s-master storageclass]# kubectl describe persistentvolumeclaim/storageclass-claim1 Name: storageclass-claim1 Namespace: default StorageClass: fast Status: Pending Volume: Labels: <none> Annotations: volume.beta.kubernetes.io/storage-provisioner: kubernetes.io/gce-pd Finalizers: [kubernetes.io/pvc-protection] Capacity: Access Modes: VolumeMode: Filesystem Used By: <none> Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning ProvisioningFailed 14m persistentvolume-controller storageclass.storage.k8s.io "fast" not found Warning ProvisioningFailed 68s (x10 over 13m) persistentvolume-controller Failed to provision volume with StorageClass "fast": failed to get GCE GCECloudProvider with error <nil>
|
StorageClass 的作用,则是充当 PV 的模板。并且,只有同属于一个 StorageClass 的 PV 和 PVC,才可以绑定在一起。StorageClass 的另一个重要作用,是指定 PV 的 Provisioner(存储插件)。这时候,如果你的存储插件支持 Dynamic Provisioning 的话,Kubernetes 就可以自动为你创建 PV 了。
2.5、Local PV(local-volume-provisioner)
Kubernetes 依靠 PV、PVC 实现了一个新的特性,这个特性的名字叫作:Local Persistent Volume,也就是 Local PV。
Local PV 实现的功能就非常类似于 hostPath 加上 nodeAffinity,比如,一个 Pod 可以声明使用类型为 Local 的 PV,而这个 PV 其实就是一个 hostPath 类型的 Volume。如果这个 hostPath 对应的目录,已经在节点 A 上被事先创建好了,那么,我只需要再给这个 Pod 加上一个 nodeAffinity=nodeA,不就可以使用这个 Volume 了吗?理论上确实是可行的,但是事实上,我们绝不应该把一个宿主机上的目录当作 PV 来使用,因为本地目录的存储行为是完全不可控,它所在的磁盘随时都可能被应用写满,甚至造成整个宿主机宕机。所以,一般来说 Local PV 对应的存储介质是一块额外在挂载在宿主机的磁盘或者块设备,我们可以认为就是“一个 PV 一块盘”。
local-volume-provisioner创建local PV
以上是关于K8S-数据持久化PVPVCStorageClass的关系的主要内容,如果未能解决你的问题,请参考以下文章
k8s数据持久化
K8s实现数据持久化
k8s中的Mysql数据库持久化存储
k8s etcd 与持久化存储
k8s之volumes持久化存储
k8s 持久化存储-常见的存储卷介绍