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的用途

  1. 缓存空间,例如基于磁盘的归并排序。
  2. 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
  3. 在 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的用途

  1. 运行一个需要访问 Docker 内部机制的容器;可使用 hostPath 挂载/var/lib/docker 路径。
  2. 在容器中运行 cAdvisor 时,以 hostPath 方式挂载/sys
  3. 允许 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

  1. PV(PersistentVolume): 持久化卷。PV 是对底层共享存储的一种抽象,由管理员进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如 Ceph、GlusterFS、NFS、hostPath 等,都是通过插件机制完成与共享存储的对接。
  2. 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对象会定义下面两部分内容:

  1. PV的属性。比如,存储类型,Volume的大小等。
  2. 创建这种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 持久化存储-常见的存储卷介绍