抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

Kubernetes数据存储

image-20220525094307190

数据存储概述

​ 简单来说,存储卷是定义在Pod资源之上、可被其内部的所有容器挂载的共享目录,它关联至某外部的存储设备之上的存储空间,从而独立于容器自身的文件系统,而数据是否具有持久能力取决于存储卷自身是否支持持久机制,Pod、容器与存储卷的关系图如下。

img

什么是数据卷

Pod本身具有生命周期,这就带了一系列的问题,

  • 当一个容器损坏之后,kubelet会重启这个容器,但是文件会丢失-这个容器会是一个全新的状态;
  • 当很多容器在同一Pod中运行的时候,很多时候需要数据文件的共享。

Docker支持配置容器使用存储卷将数据持久存储于容器自身文件系统之外的存储空间之中,它们可以是节点文件系统或网络文件系统之上的存储空间。相应的,kubernetes也支持类似的存储卷功能,不过,其存储卷是与Pod资源绑定而非容器。

数据持久化的目的

数据销毁

通常情况下,容器运行起来后,写入到其容器内部的文件是暂时性的。

​ 当容器崩溃后,k8s将这个容器kill掉,然后生成一个新的容器,此时,新运行的容器将没有原来容器内的文件,因为容器是从镜像重新创建的。 (容器一旦重启或者被删除了,里面存放的数据是会被销毁的)

数据共享

​ 数据共享:同一个pod中运行的容器之间,经常会存在共享文件或目录的需求,比如采集容器和主容器

生命周期

​ k8s里面的volume数据卷存在明确的生命周期,并且volume的生命周期比同一容器组pod中的任意容器的生命周期都要长,不管容器本身重启了多少次,数据都被保留下来,当然,pod如果不存在了,数据卷自然就退出了。

注意:可以定义pod所使用的数据卷类型不同,数据可能随着数据卷的退出而删除,也可能被真正的持久化,即使后期重新使用容器时仍然可以使用该数据卷,下面有例子。

存储卷类型

Kubernetes支持非常丰富的存储卷类型,包括本地存储(节点)和网络存储系统中的诸多存储机制,还支持SecretConfigMap这样的特殊存储资源。

​ 例如,关联节点本地的存储目录与关联GlusterFS存储系统所需要的配置参数差异巨大,因此指定存储卷类型时也就限定了其关联到的后端存储设备。通过命令# kubectl explain pod.spec可以查看当前kubernetes版本支持的存储卷类型。常用类型如下:

非持久性存储
  • emptyDir
  • hostPath
网络连接性存储
  • SAN:iscsi

  • NFS:nfs、cfs

分布式存储
  • glusterfs、cephfs、rbd
云端存储
  • awsElasticBlockStore、azureDisk、gitRepo

临时存储卷(emptyDir)

emptyDir存储卷是Pod对象生命周期中的一个临时目录,类似于Docker上的“docker 挂载卷”,在Pod对象启动时即被创建,而在Pod对象被移除时会被一并删除(永久删除)。

​ 当pod的存储方案设定为emptydir的时候,pod启动时,就会在pod所在节点的磁盘空间开辟出一块空卷,最开始里面是什么都没有的,pod启动后容器产生的数据会存放到那个空卷中,空卷变成了一个临时卷供pod内的容器读取和写入数据,一旦pod容器消失,节点上开辟出的这个临时卷就会随着pod的销毁而销毁

注意:一个容器崩溃了不会导致数据的丢失,因为容器的崩溃并不移除Pod

使用场景

一般来说emptydir的用途都是用来充当临时存储空间,例如一些不需要数据持久化的微服务,我们都可以用emptydir来当做微服务pod的存储方案

  • 充当临时存储空间,当pod内容器产生的数据不需要做持久化存储的时候用emptydir
  • 设制检查点以从崩溃事件中恢复未执行完毕的长计算

使用示例

案例说明

​ 这里定义了一个deploy资源对象(vol-emptydir-deploy),在其内部定义了两个容器,其中一个容器是辅助容器sidecar,每隔10秒生成一行信息追加到index.html文件中;

​ 另一个是nginx容器,将存储卷挂载到站点家目录。然后访问nginxhtml页面验证两个容器之间挂载的emptyDir实现共享。

img

创建资源清单
1
vi vol-emptydir-deploy.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
apiVersion: apps/v1
kind: Deployment
metadata:
name: vol-emptydir-deploy
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes: #定义存储卷
- name: html #定义存储卷的名称
emptyDir: {} #定义存储卷的类型
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /usr/share/nginx/html
- name: sidecar
image: alpine
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /html
command: ["/bin/sh", "-c"]
args:
- while true; do
echo $(hostname) $(date) >> /html/index.html;
sleep 10;
done
应用配置

我们应用配置

1
2
kubectl apply -f vol-emptydir-deploy.yml
kubectl get pods -o wide

这样我们就创建出来了一个临时存储

image-20220601150353980

访问测试

我们通过CURL命令进行访问测试

1
curl 10.244.1.125

因为我们每隔一段时间就写入行数据,所以我们可以看到两个容器共享的数据,我们发现nginx可以共享来自sidecar的数据

image-20220601150509840

存储卷测试

共享测试

下面我们测试以下存储卷在多个容器之间的共享问题

登录sidecar

登录sidecar ,并查看目录文件

1
2
3
4
kubectl exec vol-emptydir-deploy-86cd768757-x5mtw -c sidecar -it -- /bin/sh
#登录终端后执行以下命令
ls /html
cat /html/index.html

这样我们发现终端之间是共享数据的

image-20220601150827904

修改数据

为了真实的验证数据共享,我们对sidecar容器中的数据进行修改

1
2
echo "hello world" > /html/hello
ls /html

我们通过命令将hello world写入到hello文件中

image-20220601151133383

登录nginx

再打开一个窗口登录nginx,查看刚才写入的hello文件是否存在

1
2
3
4
kubectl exec vol-emptydir-deploy-86cd768757-5mtzx -c nginx -it -- /bin/sh
# 登录终端后执行下面的命令
ls /usr/share/nginx/html
cat /usr/share/nginx/html/hello

image-20220601151406277

删除文件

在nginx容器中删除hello文件

1
2
rm -rf /usr/share/nginx/html/hello
ls /usr/share/nginx/html

我们发现hello文件已经被删除了

image-20220601151529280

查看sidecar文件

我们再次登录sidecar容器查看hello文件是否被删除

1
ls /html

我们发现文件已经被删除了

image-20220601151824058

持久性测试
登录sidecar

登录sidecar并在html中写入一段文件

1
2
3
4
kubectl exec vol-emptydir-deploy-86cd768757-x5mtw -c sidecar -it -- /bin/sh
# 登录终端后执行下面的命令
echo "This is a text to test whether the pod is persistent" > /html/index.html
cat /html/index.html

这样我们就把一段文字写入了存储卷中

image-20220601152015943

访问测试

我们访问Nginx测试写入的这一段文字是否存在

1
curl 10.244.1.125

我们发现这一段文字是存在的

image-20220601152150792

删除容器

我们删除Nginx容器后测试是否能够持久化

1
2
docker ps |grep nginx
docker rm -f ca61c727583f

我们登录对应的节点删除对应的容器,我们发现删除容器后马上就启动了一个新的容器

image-20220601152551432

我们再来验证以下持久化的文字是否存在

1
curl 10.244.1.125

我们发现持久化的文字还存在,说明容器重启不影响临时存储的村九华

image-20220601152736946

删除Pod

手动删除容器模拟容器销毁,用于是pod是被控制器管理的,删除后会被重建新的pod

1
2
kubectl delete pod vol-emptydir-deploy-86cd768757-x5mtw
kubectl get pod -o wide

我们删除POD后Deployment会帮我们重建这个POD,然后我们再来验证下存储的数据是否还存在

image-20220601153104625

我们发现销毁pod后新建的pod转移到node2节点了,并且pod名称也改变了

访问测试

我们再来访问这个POD看下我们写入的文字是否还存在

1
curl 10.244.2.140

这时候在看我们之前写入的文字不见了,说明POD销毁临时存储也会跟着销毁

image-20220601153226529

节点存储卷(hostPath)

hostPath类型的存储卷是指将工作节点上的某文件系统的目录或文件挂载于Pod中的一种存储卷,独立于Pod资源的生命周期,具有持久性,在Pod删除时,数据不会丢失。

img

​ hostPath类型则是映射node文件系统中的文件或者目录到pod里。在使用hostPath类型的存储卷时,也可以设置type字段,支持的类型有文件、目录、File、Socket、CharDevice和BlockDevice。

​ 其实这个功能就相当于docker中的-v 目录映射,只不过在k8s中的时候,pod会漂移,当pod漂移到其他node节点的时候,pod不会跨节点的去读取目录。所以说hostpath只能算一种半持久化的存储方式

使用场景

  • 当运行的容器需要访问Docker内部结构时,如使用hostPath映射/var/lib/docker到容器;
  • 当在容器中运行cAdvisor时,可以使用hostPath映射/dev/cgroups到容器中;

使用示例

案例说明

这里定义了一个deploy资源对象(vol-hostpath-deploy),在其内部定义了一个容器,将nginx的html文件映射到外部,我们外面给html写入文件,访问nginx来验证是否能够访问。

创建资源清单
1
vi vol-hostpath-deploy.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: apps/v1
kind: Deployment
metadata:
name: vol-hostpath-deploy
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes: #定义存储卷
- name: html # 定义存储名称
hostPath: # 定义存储类型
path: /tmp/k8s/data/volumn # 宿主机存储路径
type: DirectoryOrCreate # 不存在路径创建路径
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /usr/share/nginx/html
应用配置
1
2
kubectl apply -f vol-hostpath-deploy.yml
kubectl get pods -o wide

这样我们就创建出来了一个节点存储,我们发现当前的POD处于node01节点

image-20220601153957629

写入文件

我们在node01节点中写入文件,验证是否能够被容器访问

1
2
3
# 登录node01节点,进入挂载目录
cd /tmp/k8s/data/volumn/
echo "This is a text to test whether the pod is persistent" > index.html

我们发现k8s自动帮我们创建了一个挂载目录

image-20220601154239098

访问测试

我们来访问以下看看刚才写入的文件是否能够被nginx访问到

1
curl 10.244.1.126

我们发现已经能够访问到数据了,说明宿主机和POD之间是互通的

image-20220601154418778

存储卷测试

共享测试

下面我们测试以下存储卷在多个容器之间的共享问题

登录nginx

我们登录Nginx并通过容器写入一些文件

1
2
3
4
5
6
kubectl exec vol-emptydir-deploy-86cd768757-5mtzx -c nginx -it -- /bin/sh
# 登录终端后执行下面的命令
ls /usr/share/nginx/html
cat /usr/share/nginx/html/index.html
echo "hello hostpath" > /usr/share/nginx/html/hello
ls /usr/share/nginx/html

这样我们就将一个文件写入了容器的挂载目录中

image-20220601154912526

登录宿主机查看

因为使用的是hostpath,我们登录pod所在的节点的宿主机目录查看文件是否存在

1
2
3
4
#登录node01节点执行以下的命令
cd /tmp/k8s/data/volumn/
ll
cat hello

发现写入的文件是存在的

image-20220601155122341

持久性测试
删除Pod

手动删除容器模拟容器销毁,用于是pod是被控制器管理的,删除后会被重建新的pod

1
2
3
kubectl get pod -o wide
kubectl delete pod vol-hostpath-deploy-684496d8cc-tb4fg
kubectl get pod -o wide

我们发现删除POD后,新的POD调度到了node02这个节点上了

image-20220601155658962

访问测试

我们知道hostpath是在宿主机创建文件的,现在我们发现pod漂移到了node2节点,我们尝试访问,并登录容器查看

1
curl 10.244.2.141

发现文件不存在符合我们的预期,这个因为node02节点没有数据

image-20220601155841157

再次删除容器

因为上一次删除漂移到了node2,我们再次删除让其漂移到node1

1
2
3
kubectl get pod -o wide
kubectl delete pod vol-hostpath-deploy-684496d8cc-tb4fg
kubectl get pod -o wide

我们发现POD已经被调度到了node01节点上了

image-20220601160107864

访问测试

我们再次访问测试,检查是否可以访问到持久化的数据

1
curl 10.244.1.128

我们发现已经可以访问到数据了

image-20220601160219360

小结

可以发现容器被删除后,新建的pod也可以看到我们映射的目录,我们写入的文件是存在的

​ 这有个缺点就是不能够跨容器去读取数据,如果删除后的pod被调度到其他节点的话,原来的数据也就没有了,如果能不受节点的影响,并且挂载的数据不会随生命周期的结束而结束,我们应该怎么解决呢?就是我们后面讲到的持久化存储了

共享存储卷(NFS )

nfs存储卷用于将事先存在的NFS服务器上导出的存储空间挂载到Pod中供容器使用。

​ 与emptyDir不同的是,当pod资源删除时emptyDir也会被删除,而NFSPod对象删除时仅是被卸载而非删除,这就意味NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递,并且NFS可以同时被多个Pod挂载并进行读写。

NFS 简介

NFS 是Network File System的缩写,即网络文件系统。一种使用于分散式文件系统的协定,由Sun公司开发,于1984年向外公布。

功能是通过网络让不同的机器、不同的操作系统能够彼此分享个别的数据,让应用程序在客户端通过网络访问位于服务器磁盘中的数据,是在类Unix系统间实现磁盘文件共享的一种方法。

​ NFS在文件传送或信息传送过程中依赖于RPC协议,RPC远程过程调用 (Remote Procedure Call) 是能使客户端执行其他系统中程序的一种机制,NFS本身是没有提供信息传输的协议和功能的。

​ NFS应用场景,常用于高可用文件共享,多台服务器共享同样的数据,可扩展性比较差,本身高可用方案不完善,取而代之的数据量比较大的可以采用MFS、TFS、HDFS、GFS等等分布式文件系统。

img

NFS 安装

NFS分为客户端和服务端,下面我们进行NFS安装

安装计划

下面是我们安装NFS的服务器计划

安装软件 服务器IP 共享目录
NFS服务器端 192.168.245.151(master) /tmp/k8s/data/volumn/
NFS客户端 192.168.245.152(node01) /tmp/k8s/data/volumn/
NFS客户端 192.168.245.153(node02) /tmp/k8s/data/volumn/
服务端安装

我们登录到mastet节点完成以下安装几乎是

创建共享目录

我们先建立需要共享的目录

1
mkdir -p /tmp/k8s/data/volumn/
安装NFS服务端

执行下面的命令安装NFS的服务端工具

1
yum  install  nfs-utils rpcbind  -y
配置NFS

NFS安装完毕,需要创建共享目录,共享目录在vi /etc/exports文件里面配置,配置方式如下

1
vi /etc/exports

编辑exports配置文件将需要NFS同步的目录配置到配置文件中

1
/tmp/k8s/data/volumn/ *(rw,no_root_squash,no_all_squash,sync)
NFS配置参数

可配置参数如下

  • /tmp/k8s/data/volumn/: 表示需要共享的目录
  • IP:表示允许哪个客户端访问

IP后括号里的设置表示对该共享文件的权限

ro 只读访问
rw 读写访问
sync 所有数据在请求时写入共享
all_squash 共享文件的UID和GID映射匿名用户anonymous,适合公用目录。
no_all_squash 保留共享文件的UID和GID(默认)
root_squash root用户的所有请求映射成如anonymous用户一样的权限(默认)
no_root_squash root用户具有根目录的完全管理访问权限
生效配置

安装完成后还不能马上生效,我们需要将配置文件生效

1
exportfs -r
重启NFS服务

最后我们重启以下NFS的服务器

1
service rpcbind restart;service nfs restart
客户端安装

这个安装需要在node01和node02上面进行安装

创建共享目录

我们先建立需要共享的目录

1
mkdir -p /tmp/k8s/data/volumn/
安装客户端

我们安装客户端需要的工具

1
yum -y install nfs-utils
查看共享目录

安装客户端后就可以尝试连接服务器端,检查需要共享的目录来检查NFS是否安装正常

1
showmount -e 192.168.245.151
挂载目录

验证共享目录没有问题后,我们就需要将我们客户端的共享目录挂载到服务端上,进行数据的同步

​ Linux客户端,如何想使用这个NFS文件系统,需要在客户端挂载,挂载命令为(为了提高NFS的稳定性,使用TCP协议挂载,NFS默认用UDP协议):

1
mount -t nfs 192.168.245.151:/tmp/k8s/data/volumn /tmp/k8s/data/volumn -o proto=tcp -o nolock
开启自动挂载

因为有时候服务器重启,导致同步服务未开启,数据未同步

1
vi /etc/fstab

我们一般开启自动挂载,在/etc/fstab下加上如下配置

1
192.168.245.151:/tmp/k8s/data/volumn /tmp/k8s/data/volumn  nfs  defaults  0 0 
验证NFS

安装完成后,我们来验证下NFS工作是否正常

写入数据

在任意一个参与同步的服务器写入文件

1
2
cd /tmp/k8s/data/volumn
echo "This is a text to test whether the pod is persistent" > index.html

image-20220601164113517

读取数据

在任意一个参与同步的服务器检查数据是否存在

1
2
cd /tmp/k8s/data/volumn
cat index.html

image-20220601164145879

清理数据

在任意一个参与同步的服务器删除目录中的数据

1
rm -rf /tmp/k8s/data/volumn/*

使用案例

案例说明

这里定义了一个deploy资源对象(vol-nfs-deploy.yml),在其内部定义了一个容器,将nginx的html文件映射到外部,我们外面给html写入文件,访问nginx来验证是否能够访问。

创建资源清单
1
vi vol-nfs-deploy.yml

我们发现和hostpath的配置清单一样,这是因为我们使用的就是hostpath的存储卷,只是挂载目录通过NFS实现了数据同步,完成了数据共享

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: apps/v1
kind: Deployment
metadata:
name: vol-nfs-deploy
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes: #定义存储卷
- name: html # 定义存储名称
hostPath: # 定义存储类型
path: /tmp/k8s/data/volumn # 宿主机存储路径
type: DirectoryOrCreate # 不存在路径创建路径
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /usr/share/nginx/html
应用配置
1
2
kubectl apply -f vol-nfs-deploy.yml
kubectl get pods -o wide

这样我们就创建出来了一个NFS存储,我们发现当前的POD处于node01节点

image-20220601164736619

写入文件

我们在node01节点中写入文件,验证是否能够被容器访问

1
2
3
# 登录node01节点,进入挂载目录
cd /tmp/k8s/data/volumn/
echo "This is a text to test whether the pod is persistent" > index.html

我们发现k8s自动帮我们创建了一个挂载目录

image-20220601154239098

访问测试

我们来访问以下看看刚才写入的文件是否能够被nginx访问到

1
curl 10.244.1.129

我们发现已经能够访问到数据了,说明宿主机和POD之间是互通的

image-20220601164923133

存储卷测试

共享测试

下面我们测试以下存储卷在多个容器之间的共享问题

登录nginx

我们登录Nginx并通过容器写入一些文件

1
2
3
4
5
6
kubectl exec vol-nfs-deploy-684496d8cc-xhwjb -c nginx -it -- /bin/sh
# 登录终端后执行下面的命令
ls /usr/share/nginx/html
cat /usr/share/nginx/html/index.html
echo "hello hostpath" > /usr/share/nginx/html/hello
ls /usr/share/nginx/html

这样我们就将一个文件写入了容器的挂载目录中

image-20220601165042851

登录宿主机查看

因为使用的是hostpath,我们登录pod所在的节点的宿主机目录查看文件是否存在

1
2
3
4
#登录node01节点执行以下的命令
cd /tmp/k8s/data/volumn/
ll
cat hello

发现写入的文件是存在的

image-20220601155122341

持久性测试
删除Pod

手动删除容器模拟容器销毁,用于是pod是被控制器管理的,删除后会被重建新的pod

1
2
3
kubectl get pod -o wide
kubectl delete pod vol-nfs-deploy-684496d8cc-xhwjb
kubectl get pod -o wide

我们发现删除POD后,新的POD调度到了node02这个节点上了

image-20220601165200656

访问测试

我们知道hostpath是在宿主机创建文件的,现在我们发现pod漂移到了node2节点,我们尝试访问,并登录容器查看

1
curl 10.244.2.142

发现文件存在符合我们的预期,这个因为通过NFS已经将数据从node01同步到了node02节点

image-20220601165219760

​ 通过上面测试可以看出,此前创建的index.html及其数据在Pod资源重建后依然存在,且不论pod资源调度到哪个节点。

​ 这表明在删除Pod资源时,其关联的外部存储卷并不会被一同删除,如果需要删除此类的数据,需要用户通过存储系统的管理接口手动进行。

持久化卷(PV/PVC)

概述

​ 在 Kubernetes 中,存储资源和计算资源(CPU、Memory)同样重要,Kubernetes 为了能让管理员方便管理集群中的存储资源,同时也为了让使用者使用存储更加方便,所以屏蔽了底层存储的实现细节,将存储抽象出两个 API 资源 PersistentVolumePersistentVolumeClaim 对象来对存储进行管理。

img

PersistentVolume(持久化卷)

PersistentVolume 简称 PV, 是对底层共享存储的一种抽象

​ 将共享存储定义为一种资源,它属于集群级别资源,不属于任何 Namespace,用户使用 PV 需要通过 PVC 申请,PV 是由管理员进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如说 Ceph、GlusterFS、NFS 等,都是通过插件机制完成与共享存储的对接,且根据不同的存储 PV 可配置参数也是不相同。

PersistentVolumeClaim(持久化卷声明)

PersistentVolumeClaim 简称 PVC,是用户存储的一种声明

​ 类似于对存储资源的申请,它属于一个 Namespace 中的资源,可用于向 PV 申请存储资源。PVCPod 比较类似,Pod 消耗的是 Node 节点资源,而 PVC 消耗的是 PV 存储资源,Pod 可以请求 CPU 和 Memory,而 PVC 可以请求特定的存储空间和访问模式。

StorageClass

​ 上面两种资源 PVPVC 的存在很好的解决了存储管理的问题,不过这些存储每次都需要管理员手动创建和管理,如果一个集群中有很多应用,并且每个应用都要挂载很多存储,那么就需要创建很多 PVPVC 与应用关联,为了解决这个问题 Kubernetes 在 1.4 版本中引入了 StorageClass 对象。

​ 当我们创建 PVC 时指定对应的 StorageClass 就能和 StorageClass 关联,StorageClass 会交由与他关联 Provisioner 存储插件来创建与管理存储,它能帮你创建对应的 PV 和在远程存储上创建对应的文件夹,并且还能根据设定的参数,删除与保留数据。所以管理员只要在 StorageClass 中配置好对应的参数就能方便的管理集群中的存储资源。

PV详解

PersistentVolumePV)是集群中已由管理员配置的一段网络存储。

​ 集群中的资源就像一个节点是一个集群资源,PV是诸如卷之类的卷插件,但是具有独立于使用PV的任何单个Pod的生命周期。该API对象捕获存储的实现细节,即NFSISCSI或云提供商特定的存储系统。

PV支持存储的类型
  • RBD:Ceph 块存储。
  • FC:光纤存储设备。
  • NFS:网络问卷存储卷。
  • iSCSI:iSCSI 存储设备。
  • CephFS:开源共享存储系统。
  • Flocker:一种开源共享存储系统。
  • Glusterfs:一种开源共享存储系统。
  • Flexvolume:一种插件式的存储机制。
  • HostPath:宿主机目录,仅能用于单机。
  • AzureFile:Azure 公有云提供的 File。
  • AzureDisk:Azure 公有云提供的 Disk。
  • ScaleIO Volumes:DellEMC 的存储设备。
  • StorageOS:StorageOS 提供的存储服务。
  • VsphereVolume:VMWare 提供的存储系统。
  • Quobyte Volumes:Quobyte 提供的存储服务。
  • Portworx Volumes:Portworx 提供的存储服务。
  • GCEPersistentDisk:GCE 公有云提供的 PersistentDisk。
  • AWSElasticBlockStore:AWS 公有云提供的 ElasticBlockStore。
PV的生命周期
  • Available: 可用状态,尚未被 PVC 绑定。
  • Bound: 绑定状态,已经与某个 PVC 绑定。
  • Failed: 当删除 PVC 清理资源,自动回收卷时失败,所以处于故障状态。
  • Released: 与之绑定的 PVC 已经被删除,但资源尚未被集群回收。

配置案例

创建资源清单
1
vi pv.yml

这里我们创建了两个PV,一个采用NFS存储,一个采用hostpath模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
labels:
type: nfs
spec:
capacity: #定义了一个2G大小的容量
storage: 2Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce #定义访问模式
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow #这个地方的存储类只是起到一个标识的作用,可以随便自定义
mountOptions:
- hard
- nfsvers=4.1
nfs: #NFS路径配置的声明(底层存储)
path: /tmp/k8s/data/volumn
server: 192.168.245.151
---
apiVersion: v1
kind: PersistentVolume #pv是全局范围内的,没有namespace的限制
metadata:
name: pv2
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
hostPath:
path: "/tmp/k8s/data/hostpath"
type: DirectoryOrCreate
应用配置

我们执行命令应用配置

1
2
kubectl apply -f pv.yml
kubectl get pv -o wide

我们现在已经创建了两个PV

image-20220602160321540

关键配置参数

PV作为存储资源,主要包括存储能力、访问模式、存储类型、回收策略、后端存储类型等关键信息的设置

存储能力(Capacity)

PV 可以通过配置 capacity 中的 storage 参数,对 PV 挂多大存储空间进行设置

​ 一般来说,一个 PV 对象都要指定一个存储能力,通过 PV 的 capacity 属性来设置的,目前只支持存储空间的设置,就是我们这里的 storage=10Gi,不过未来可能会加入 IOPS、吞吐量等指标的配置。

1
2
capacity:
storage: 2Gi

​ 注意:这里的存储能力大小是一定就给你底层划分这么大的空间吗,不一定,和你底层使用的存储有关,例如是ceph 块存储的话,那么它底层就可能给你划分一个2g大小的空间;但是NFS这种文件系统,它是限制不了的;

存储卷模式(volumeMode)

PV 可以通过配置 volumeMode 参数,对存储卷类型进行设置,可选项包括:

  • Filesystem: 文件系统,默认是此选项。
  • Block: 块设备

目前 Block 模式只有 AWSElasticBlockStore、AzureDisk、FC、GCEPersistentDisk、iSCSI、LocalVolume、RBD、VsphereVolume 等支持)

1
volumeMode: Filesystem
访问模式(accessModes)

PV 可以通过配置 accessModes 参数,设置访问模式来限制应用对资源的访问权限,有以下机制访问模式:

  • ReadWriteOnce(RWO): 读写权限,只能被单个节点挂载。
  • ReadOnlyMany(ROX): 只读权限,允许被多个节点挂载读。
  • ReadWriteMany(RWX): 读写权限,允许被多个节点挂载。
1
2
accessModes:
- ReadWriteOnce

​ 这里还需要注意的一点:配置文件里面accessModes(访问模式)的声明,仅仅只是一个声明而已,其实起作用的关键还是要看底层存储,例如NFS这里只声明了为ReadWriteOnce,但其实也是文件系统它是支持all模式的

一些 PV 可能支持多种访问模式,但是在挂载的时候只能使用一种访问模式,多种访问模式是不会生效的。

块存储,一般只支持单个节点挂载,不支持多个节点共享; – 只能被一台服务器去挂载,不能被多台服务器挂载;因此适用于RWO、ROX;

不过不同的存储所支持的访问模式也不相同,具体如下:

Volume Plugin ReadWriteOnce ReadOnlyMany ReadWriteMany
AWSElasticBlockStore - -
AzureFile
AzureDisk - -
CephFS
Cinder - -
FC -
FlexVolume -
Flocker - -
GCEPersistentDisk -
GlusteFS
HostPath - -
iSCSI -
PhotonPersistentDisk - -
Quobyte
NFS
RBD -
VsphereVolume - -
PortworxVolume -
ScaleIO -
StorageOS - -
挂载参数(mountOptions)

PV 可以根据不同的存储卷类型,设置不同的挂载参数,每种类型的存储卷可配置参数都不相同,如 NFS 存储,可以设置 NFS 挂载配置

​ 下面例子只是 NFS 支持的部分参数,其它参考官网的配置项。

1
2
3
mountOptions:
- hard
- nfsvers=4.1
存储类 (storageClassName)

PV 可以通过配置 storageClassName 参数指定一个存储类 StorageClass 资源

​ 具有特定 StorageClassPV 只能与指定相同 StorageClassPVC 进行绑定,没有设置 StorageClassPV 也是同样只能与没有指定 StorageClassPVC 绑定

1
storageClassName: slow
回收策略(persistentVolumeReclaimPolicy)

PV 可以通过配置 persistentVolumeReclaimPolicy 参数设置回收策略,可选项如下

  • Retain(保留): 保留数据,需要由管理员手动清理。
  • Recycle(回收): 删除数据,即删除目录下的所有文件,比如说执行 rm -rf /thevolume/* 命令,目前只有 NFS 和 HostPath 支持。
  • Delete(删除): 删除存储资源,仅仅部分云存储系统支持,比如删除 AWS EBS 卷,目前只有 AWS EBS,GCE PD,Azure 磁盘和 Cinder 卷支持删除。
1
persistentVolumeReclaimPolicy: Recycle

PVC详解

PersistentVolumeClaimPVC)是用户存储的请求。它类似于PodPod消耗节点资源,PVC消耗存储资源,Pod可以请求特定级别的资源(CPU和内存)

​ PVC作为用户对存储资源的需求申请,主要包括存储空间请求、访问模式、PV选择条件和存储类别等信息的设置

创建资源清单
1
vi pvc.yml

这样我们就创建出来了一个PVC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 2Gi
storageClassName: slow
selector:
matchLabels:
type: "nfs" #选择 pv1的PV进行绑定
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc2
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 1Gi
storageClassName: manual
selector:
matchLabels:
type: "local" #选择 pv2的PV进行绑定
应用配置

现在我们应用配置,并观察绑定前后的状态变化

1
2
kubectl apply -f pvc.yml
kubectl get pvc -o wide

我们现在已经将PVC绑定到了PV上面了

image-20220602164935522

查看PV状态

我们现在看下PV的状态

1
kubectl get pv -o wide

我们发现当前的PV已经进入绑定的状态了

image-20220602165102100

常用配置参数
筛选器(selector)

PVC 可以通过在 Selecter 中设置 Laberl 标签,筛选出带有指定 LabelPV 进行绑定。

Selecter 中可以指定 matchLabelsmatchExpressions,如果两个字段都设定了就需要同时满足才能匹配

1
2
3
selector:
matchLabels:
name: "pv1"
资源请求(resources)

PVC 设置目前只有 requests.storage 一个参数,用于指定申请存储空间的大小。

1
2
3
resources:
requests:
storage: 2Gi
存储类(storageClass)

PVC 要想绑定带有特定 StorageClassPV 时,也必须设定 storageClassName 参数,且名称也必须要和 PV 中的 storageClassName 保持一致。

​ 如果要绑定的 PV 没有设置 storageClassNamePVC 中也不需要设置

​ 当 PVC 中如果未指定 storageClassName 参数或者指定为空值,则还需要考虑 Kubernetes 中是否设置了默认的 StorageClass

  • 未启用 DefaultStorageClass:等于 storageClassName 值为空。
  • 启用 DefaultStorageClass:等于 storageClassName 值为默认的 StorageClass。

如果设置 storageClassName=””,则表示该 PVC 不指定 StorageClass。

1
storageClassName: slow
访问模式(accessModes)

PVC 中可设置的访问模式与 PV 种一样,用于限制应用对资源的访问权限。

存储卷模式(volumeMode)

PVC 中可设置的存储卷模式与 PV 种一样,分为 FilesystemBlock 两种。

StorageClass详解

StorageClass作为对存储资源的抽象定义,对用户设置的PVC申请屏蔽后端存储的细节,一方面减少了用户对存储资源细节的关注,另一方面减少了管理员手工管理PV的工作,由系统自动完成PV的创建和绑定,实现了动态的资源供应

配置案例

这里使用 NFS 存储,创建 StorageClass 示例

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
annotations:
storageclass.kubernetes.io/is-default-class: "true" #设置为默认的 StorageClass
provisioner: nfs-client
mountOptions:
- hard
- nfsvers=4
parameters:
archiveOnDelete: "true"
常用配置参数
提供者(provisioner)

在创建 StorageClass 之前需要 Kubernetes 集群中存在 Provisioner(存储分配提供者)应用,如 NFS 存储需要有 NFS-Provisioner (NFS 存储分配提供者)应用,如果集群中没有该应用,那么创建的 StorageClass 只能作为标记,而不能提供创建 PV 的作用。

1
provisioner: nfs-client
参数(parameters)

后端存储提供的参数,不同的 Provisioner 可与配置的参数也是不相同

1
2
arameters:
archiveOnDelete: "true" #删除 PV 后是否保留数据
挂载参数(mountOptions)

StorageClass 中,可以根据不同的存储来指定不同的挂载参数,此参数会与 StorageClass 绑定的 Provisioner 创建 PV 时,将此挂载参数与创建的 PV 关联。

1
2
3
mountOptions: 
- hard
- nfsvers=4
设置默认的 StorageClass

可与在 Kubernetes 集群中设置一个默认的 StorageClass,这样当创建 PVC 时如果未指定 StorageClass 则会使用默认的 StorageClass。

1
2
3
metadata:
annotations:
storageclass.kubernetes.io/is-default-class: "true"

hostPath使用案例

一般 Deployment 中使用 PVC,大部分都是静态供给方式创建,即先创建 PV,再创建 PVC 与 PV 绑定,在设置应用于 PVC 关联。

创建资源清单
1
vi pvc-hostpath.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: apps/v1
kind: Deployment
metadata:
name: pvc-hostpath-deploy
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes: #定义存储卷
- name: html # 定义存储名称
persistentVolumeClaim: # 使用PVC的方式进行挂载
claimName: pvc2 # 使用PVC2的hostpath存储
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /usr/share/nginx/html
应用配置
1
2
kubectl apply -f pvc-hostpath.yml
kubectl get pod -o wide

现在我们就创建了一个PVC的Deployment

image-20220602172743698

写入文件

我们在node01节点检查PV目录是否创建,如果创建在目录中写入文件

1
2
3
# 登录node01节点,进入挂载目录
cd /tmp/k8s/data/hostpath
echo "This is a text to test whether the pod is persistent" > index.html

我们发现k8s自动帮我们创建了一个挂载目录

image-20220602173817081

访问测试

我们来访问以下看看刚才写入的文件是否能够被nginx访问到

1
curl 10.244.1.126

我们发现已经能够访问到数据了,说明宿主机和POD之间是互通的

image-20220601154418778

共享测试

下面我们测试以下存储卷在多个容器之间的共享问题

登录nginx

我们登录Nginx并通过容器写入一些文件

1
2
3
4
5
6
kubectl exec vol-emptydir-deploy-86cd768757-5mtzx -c nginx -it -- /bin/sh
# 登录终端后执行下面的命令
ls /usr/share/nginx/html
cat /usr/share/nginx/html/index.html
echo "hello hostpath" > /usr/share/nginx/html/hello
ls /usr/share/nginx/html

这样我们就将一个文件写入了容器的挂载目录中

image-20220601154912526

登录宿主机查看

因为使用的是hostpath,我们登录pod所在的节点的宿主机目录查看文件是否存在

1
2
3
4
#登录node01节点执行以下的命令
cd /tmp/k8s/data/volumn/
ll
cat hello

发现写入的文件是存在的

image-20220601155122341

持久性测试
删除Pod

手动删除容器模拟容器销毁,用于是pod是被控制器管理的,删除后会被重建新的pod

1
2
3
kubectl get pod -o wide
kubectl delete pod pvc-hostpath-deploy-6b6ccc9499-chxdr
kubectl get pod -o wide

我们发现删除POD后,新的POD调度到了node02这个节点上了

image-20220602174139347

访问测试

我们知道hostpath是在宿主机创建文件的,现在我们发现pod漂移到了node2节点,我们尝试访问,并登录容器查看

1
curl 10.244.2.143

发现文件不存在符合我们的预期,这个因为node02节点没有数据

image-20220602174220731

再次删除容器

因为上一次删除漂移到了node2,我们再次删除让其漂移到node1

1
2
3
kubectl get pod -o wide
kubectl delete pod pvc-hostpath-deploy-6b6ccc9499-rmdcd
kubectl get pod -o wide

我们发现POD已经被调度到了node01节点上了

image-20220602174305749

访问测试

我们再次访问测试,检查是否可以访问到持久化的数据

1
curl 10.244.1.138

我们发现已经可以访问到数据了

image-20220602174346577

NFS使用案例

创建资源清单
1
vi pvc-nfs-deploy.yml

配置基本上和上面一样,只是PVC我们采用NFS的方式,我们发现使用PVC后,PVC帮我们屏蔽了底层存储设备的不一致特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: apps/v1
kind: Deployment
metadata:
name: pvc-nfs-deploy
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes: #定义存储卷
- name: html # 定义存储名称
persistentVolumeClaim: # 使用PVC的方式进行挂载
claimName: pvc1 # 使用PVC2的NFS存储
containers:
- name: nginx
image: nginx:1.12
ports:
- containerPort: 80
volumeMounts: #在容器中定义挂载存储卷的名和路径
- name: html
mountPath: /usr/share/nginx/html
应用配置
1
2
kubectl apply -f pvc-nfs-deploy
kubectl get pods -o wide

这样我们就创建出来了一个NFS存储,我们发现当前的POD处于node01节点

image-20220602174814499

写入文件

我们在node01节点中写入文件,验证是否能够被容器访问

1
2
3
# 登录node01节点,进入挂载目录
cd /tmp/k8s/data/volumn/
echo "This is a text to test whether the pod is persistent" > index.html

我们发现k8s自动帮我们创建了一个挂载目录

image-20220601154239098

访问测试

我们来访问以下看看刚才写入的文件是否能够被nginx访问到

1
curl 10.244.1.139

我们发现已经能够访问到数据了,说明宿主机和POD之间是互通的

image-20220602174851317

共享测试

下面我们测试以下存储卷在多个容器之间的共享问题

登录nginx

我们登录Nginx并通过容器写入一些文件

1
2
3
4
5
6
kubectl exec pvc-nfs-deploy-684496d8cc-xhwjb -c nginx -it -- /bin/sh
# 登录终端后执行下面的命令
ls /usr/share/nginx/html
cat /usr/share/nginx/html/index.html
echo "hello hostpath" > /usr/share/nginx/html/hello
ls /usr/share/nginx/html

这样我们就将一个文件写入了容器的挂载目录中

image-20220602174949653

登录宿主机查看

因为使用的是hostpath,我们登录pod所在的节点的宿主机目录查看文件是否存在

1
2
3
4
#登录node01节点执行以下的命令
cd /tmp/k8s/data/volumn/
ll
cat hello

发现写入的文件是存在的

image-20220601155122341

持久性测试
删除Pod

手动删除容器模拟容器销毁,用于是pod是被控制器管理的,删除后会被重建新的pod

1
2
3
kubectl get pod -o wide
kubectl delete pod pvc-nfs-deploy-7ccff99d76-8vds6
kubectl get pod -o wide

我们发现删除POD后,新的POD调度到了node02这个节点上了

image-20220602175145723

访问测试

我们知道hostpath是在宿主机创建文件的,现在我们发现pod漂移到了node2节点,我们尝试访问,并登录容器查看

1
curl 10.244.2.142

发现文件存在符合我们的预期,这个因为通过NFS已经将数据从node01同步到了node02节点

image-20220601165219760

清理环境

使用完成环境后需要清空环境用于下一次测试

查看当前PV状态

我们可以看下当前的PV状态

1
2
kubectl get pvc -o wide
kubectl get pv -o wide

我们发现当前的PV以及PVC都处于绑定状态

image-20220602175707514

删除Deployment

我们将Deployment删除

1
kubectl delete deployment pvc-nfs-deploy

这样我们就将Deployment删除了

image-20220602175846158

我们再来检查以下PV和PVC的状态

1
2
kubectl get pvc -o wide
kubectl get pv -o wide

我们发现当前的PV以及PVC都还处于绑定状态,说明PV还处于使用状态,因为PVC连接着PV

image-20220602175707514

删除PVC

最后我们将PVC删除掉

1
kubectl delete pvc pvc1 pvc2

这样我们就将PVC全部删除掉了

image-20220602180140361

检查PV状态

最后我们检查PV的的状态

1
kubectl get pv -o wide

我们发现当前的PV已经进入了可用状态

image-20220602180219236

检查数据状态

我们最后在检查下NFS中的数据

1
2
cd /tmp/k8s/data/volumn
ll

我们发现数据都已经被删除了,这是因为我们PV设置的回收策略是Recycle,PVC删除后自动删除PV的数据

image-20220602180350916

生命周期转化(PV/PVC)

PV 是 Kubernetes 集群的存储资源,而 PVC 则是对存储资源的需求,创建 PVC 需要对 PV 发起使用申请,即和 PV 进行绑定

​ PV 和 PVC 是一一对应的关系,它们二者的交互遵循如下生命周期:

img

存储供给

存储供给(Provisioning)是指为 PVC 准备可用的 PV 的一种机制。Kubernetes 支持 PV 供给方式有 静态供给动态供给 两种:

  • 静态供给: 指由集群管理员手动创建一定数量的 PV,创建 PV 时需要根据后端存储的不同,配置的参数也不同。
  • 动态供给: 指不需要集群管理员手动创建 PV,将 PV 的创建工作交由 StorageClass 关联的 Provisioner 进行创建,会根据存储的不同自动配置相关的参数。创建完成 PV 后系统会自动将 PVC 与其绑定。
存储绑定
静态模式

​ 在静态模式下,在用户定义好 PVC 后,Kubernetes 将根据 PVC 提出的“申请空间的大小”、“访问模式”从集群中寻找已经存在且满足条件的 PV 进行绑定,如果集群中没有匹配的 PV 则 PVC 将处于 Pending 等待状态,知道系统创建了符合条件的 PV 再与其绑定。PV 与 PVC 绑定后就不能和别的 PVC 进行绑定。

动态模式

​ 在动态模式下,当创建 PVC 并且指定 StorageClass 后,与 StorageClass 关联的存储插件会自动创建对应的 PV 与该 PVC 进行绑定。

资源使用

Pod使用Volume定义,将PVC挂载到容器内的某个路径进行使用。

​ Volume的类型为Persistent VolumeClaim,在容器挂载了一个PVC后,就能被持续独占使用,多个Pod可以挂载到同一个PVC上。

存储回收

完成存储卷的使用目标之后删除 PVC 对象,以便进行资源回收,不过,至于如何操作则取决于 PV 的回收策略 ,目前有三种策略:

保存策略(Retain)

​ 删除 PVC 之后,Kubernetes 系统不会自动删除 PV ,而仅是将它标识为 Released 状态,不过处于 Released 状态的 PV 不能被其他 PVC 申请所绑定,因为之前 PVC 绑定的应用数据仍然存在,需要由管理员手动清理数据,然后决定如何处理 PV 的使用。

回收策略

​ 当 PVC 删除后,此 PV 变成 Available 可用状态。不过此策略需要后端存储插件的支持

删除策略

​ 当删除 PVC 后 PV 和存储中的数据会被立即删除,不过此策略也需要后端存储插件的支持

使用注意

下面是PV/PVC使用的注意事项

请求大于PV

如果我们在PVC中的请求值大于PV中的最大值,那么绑定不会成功

创建资源清单
1
vi pvc.yml

我们创建一个请求大于PV的PVC,我们NFS的PV最大2G,而我们请求的是5G

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 5Gi
storageClassName: slow
selector:
matchLabels:
type: "nfs" #选择 pv1的PV进行绑定
应用配置

我们应用这个PVC的配置

1
2
kubectl apply -f pvc.yml
kubectl get pvc -o wide

我们发现这个PVC处于pending的状态,没有完成绑定

image-20220602182043253

我们查看下当前的PVC的详情

1
kubectl describe pvc pvc1

我们发现没有找到对应的PV,主要原因则是没有找到对应大小的PV

image-20220602182207714

多个PVC符合条件

如果有多个pv都符合pvc的要求呢,那么PVC会智能选择最匹配的PV

创建PV资源清单

我们创建一个PV的资源清单,两个PV都是1G

1
vi pv.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
apiVersion: v1
kind: PersistentVolume #pv是全局范围内的,没有namespace的限制
metadata:
name: pv1
labels:
type: nfs
spec:
capacity: #定义了一个2G大小的容量
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce #定义访问模式
persistentVolumeReclaimPolicy: Recycle
mountOptions:
- hard
- nfsvers=4.1
nfs: #NFS路径配置的声明(底层存储)
path: /tmp/k8s/data/volumn
server: 192.168.245.151
---
apiVersion: v1
kind: PersistentVolume #pv是全局范围内的,没有namespace的限制
metadata:
name: pv2
labels:
type: local
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
hostPath:
path: "/tmp/k8s/data/hostpath"
type: DirectoryOrCreate

然后我们应用配置

1
2
kubectl apply -f pv.ym
kubectl get pv -o wide

我们发现创建了两个1G的PV

image-20220602183700351

创建PVC资源清单

我们创建一个没有条件的PVC,请求有1G,符合条件的有PV1和PV2

1
vi pvc.yml
1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 1Gi
应用配置
1
2
kubectl apply -f pvc.yml
kubectl get pvc -o wide

我们看到当前的PV和PVC绑定了,我们申请1G,我们发现和PV1绑定到了一起

image-20220602183916443

我们删除PVC,然后在绑定一次试试

1
2
3
kubectl delete pvc pvc1
kubectl apply -f pvc.yml
kubectl get pvc -o wide

我们发现和PV2绑定到了一起,也就是说如果同时符合多个条件会随机找一个绑定

image-20220602184010211

容器匹配策略

如果的PV资源大小不一致,则不会产生这种问题,PVC绑定PV遵循就近原则的,容器匹配策略

  • 匹配最接近的pv容量,向大的匹配

  • 如果满足不了,pod处于pending

评论