1 存储卷概述

Pod本身具有生命周期,这就带了一系列的问题,第一,当一个容器损坏之后,kubelet会重启这个容器,但是文件会丢失-这个容器会是一个全新的状态;第二,当很多容器在同一Pod中运行的时候,很多时候需要数据文件的共享。Docker支持配置容器使用存储卷将数据持久存储于容器自身文件系统之外的存储空间之中,它们可以是节点文件系统或网络文件系统之上的存储空间。相应的,kubernetes也支持类似的存储卷功能,不过,其存储卷是与Pod资源绑定而非容器。

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

img

2 Kubernetes支持的存储卷类型

Kubernetes支持非常丰富的存储卷类型,包括本地存储(节点)和网络存储系统中的诸多存储机制,还支持SecretConfigMap这样的特殊存储资源。例如,关联节点本地的存储目录与关联GlusterFS存储系统所需要的配置参数差异巨大,因此指定存储卷类型时也就限定了其关联到的后端存储设备。通过命令# kubectl explain pod.spec可以查看当前kubernetes版本支持的存储卷类型。常用类型如下:

  • 非持久性存储

  • emptyDir

  • hostPath

  • 网络连接性存储

  • SAN:iscsi

  • NFS:nfs、cfs

  • 分布式存储

  • glusterfs、cephfs、rbd

  • 云端存储

  • awsElasticBlockStore、azureDisk、gitRepo

3 存储卷的使用方式

在Pod中定义使用存储卷的配置由两部分组成:一是通过.spec.volumes字段定义在Pod之上的存储卷列表,其支持使用多种不同类型的存储卷且配置参数差别很大;另一个是通过.spce.containers.volumeMounts字段在容器上定义的存储卷挂载列表,它只能挂载当前Pod资源中定义的具体存储卷,当然,也可以不挂载任何存储卷。

在Pod级别定义存储卷时,.spec.volumes字段的值为对象列表格式,每个对象为一个存储卷的定义,由存储卷名称(.spec.volumes.name <string>)或存储卷对象(.spec.volumes.VOL_TYPE <Object>)组成,其中VOL_TYPE是使用的存储卷类型名称,它的内嵌字段随类型的不同而不同。下面示例定义了由两个存储卷组成的卷列表,一个为emptyDir类型,一个是gitRepo类型。

1
2
3
4
5
6
7
8
9
......
volumes:
- name: data
emptyDir: {}
- name: example
gitRepo:
repository: https://github.com/ikubernetes/k8s_book.git
revision: master
directory:

无论何种类型的存储卷,挂载格式基本上都是相同的,通过命令# kubectl explain pod.spec.containers.volumeMounts 可以进行查看。在容器中顶一个挂载卷时的通用语法形式如下:

1
2
3
4
5
6
......
volumeMounts:
- name <string> -required- #指定要挂载的存储卷的名称,必选字段
mountPath <string> -required- #挂载点路径,容器文件系统的路径,必选字段
readOnly <boolean> #是否挂载为只读卷
subPath <string> #挂载存储卷时使用的子路径,及mountPath指定的路径下使用一个子路径作为其挂载点。

示例,容器myapp将上面定义的data存储卷挂载于/var/log/myapp,将examply挂载到/webdata/example目录。

1
2
3
4
5
6
7
8
9
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
volumeMounts:
- name: data
mountPath: /var/log/myapp/
- name: example
mountPath: /webdata/example/

4 存储卷使用示例

下面的所有示例的资源清单文件都放在新建的storage目录中。

1
2
[root@k8s-master ~]# mkdir storage
[root@k8s-master ~]# cd storage/

4.1 emptyDir 存储卷

emptyDir存储卷是Pod对象生命周期中的一个临时目录,类似于Docker上的“docker 挂载卷”,在Pod对象启动时即被创建,而在Pod对象被移除时会被一并删除(永久删除)。Pod中的容器都可以读写这个目录,这个目录可以被挂载到各个容器相同或者不相同的路径下。注意:一个容器崩溃了不会导致数据的丢失,因为容器的崩溃并不移除Pod

emptyDir的作用:

  1. 普通空间,基于磁盘的数据存储

  2. 作为从崩溃中恢复的备份点

  3. 存储那些需要长久保存的数据,例如web服务中的数据

emptyDir字段说明:

1
2
3
[root@k8s-master ~]# kubectl explain pod.spec.volumes.emptyDir
medium <string>: #此目录所在的存储介质的类型,可取值为“default”或“Memory”,默认为default,表示使用节点的的默认存储介质;Memory表示使用基于RAM的临时的文件系统temfs,空间受限于内存,但性能非常好,通常用于为容器中的应用提供缓存空间。
sizeLimit <string> #当前存储卷的空间限额,默认值为nil,表示不限制;不过,在medium字段值为“Memory”时建议务必定义此限额。

emptyDir示例:

这里定义了一个Pod资源对象(vol-emptydir-pod),在其内部定义了两个容器,其中一个容器是辅助容器sidecar,每隔10秒生成一行信息追加到index.html文件中;另一个是nginx容器,将存储卷挂载到站点家目录。然后访问nginxhtml页面验证两个容器之间挂载的emptyDir实现共享。

img

1)编辑资源清单文件

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
[root@k8s-master storage]# vim vol-emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: vol-emptydir-pod
spec:
volumes: #定义存储卷
- name: html #定义存储卷的名称
emptyDir: {} #定义存储卷的类型
containers:
- name: nginx
image: nginx:1.12
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

2)创建并查看状态

1
2
3
4
5
[root@k8s-master storage]# kubectl apply -f vol-emptydir.yaml 
pod/vol-emptydir-pod created
[root@k8s-master storage]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
vol-emptydir-pod 2/2 Running 0 63s 10.244.2.79 k8s-node2 <none> <none>

3)访问测试

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
[root@k8s-master storage]# curl 10.244.2.79
vol-emptydir-pod Wed Oct 9 03:32:43 UTC 2019
vol-emptydir-pod Wed Oct 9 03:32:53 UTC 2019
vol-emptydir-pod Wed Oct 9 03:33:03 UTC 2019
vol-emptydir-pod Wed Oct 9 03:33:13 UTC 2019
vol-emptydir-pod Wed Oct 9 03:33:23 UTC 2019
......

#进入vol-emptydir-pod中的sidecar容器中查看挂载目录下的index.html文件
[root@k8s-master storage]# kubectl exec vol-emptydir-pod -c sidecar -it -- /bin/sh
/ # ls
bin etc html media opt root sbin sys usr
dev home lib mnt proc run srv tmp var
/ # ls /html
index.html
/ # cat /html/index.html
vol-emptydir-pod Wed Oct 9 03:32:43 UTC 2019
vol-emptydir-pod Wed Oct 9 03:32:53 UTC 2019
vol-emptydir-pod Wed Oct 9 03:33:03 UTC 2019
......

#进入vol-emptydir-pod中的nginx容器中查看挂载目录下的index.html文件
[root@k8s-master storage]# kubectl exec vol-emptydir-pod -c nginx -it -- /bin/sh
# cat /usr/share/nginx/html/index.html
vol-emptydir-pod Wed Oct 9 03:32:43 UTC 2019
vol-emptydir-pod Wed Oct 9 03:32:53 UTC 2019
vol-emptydir-pod Wed Oct 9 03:33:03 UTC 2019
......

4.2 hostPath 存储卷

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

img

hostPath字段说明:

1
2
3
4
5
6
7
8
9
10
11
12
[root@k8s-master storage]# kubectl explain pod.spec.volumes.hostPath
path <string> -required- #指定工作节点上的目录路径
type <string> #指定存储卷类型

type类型如下:
DirectoryOrCreate 指定的路径不存在时自动创建其权限为0755的空目录,属主和属组为kubelet
Directory 必须存在的目录路径
FileOrCreate 指定的路径不存在时自动创建其权限为0644的空文件,属主和属组为kubelet
File 必须存在的文件路径
Socket 必须存在的Socket文件路径
CharDevice 必须存在的字符设备文件路径
BlockDevice 必须存在的块设备文件路径

hostPath示例:

1)编辑资源清单文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@k8s-master storage]# vim vol-hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-vol-hostpath
namespace: default
spec:
containers:
- name: myapp
image: nginx:1.15
imagePullPolicy: IfNotPresent
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
hostPath:
path: /data/pod/volume1
type: DirectoryOrCreate

2)创建并查看状态

1
2
3
4
5
[root@k8s-master storage]# kubectl apply -f vol-hostpath.yaml 
pod/vol-hostpath-pod created
[root@k8s-master storage]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
vol-hostpath-pod 1/1 Running 0 7s 10.244.1.83 k8s-node1 <none> <none>

3)通过上面查看pod被调度到节点1上面,查看节点1的目录并创建测试文件

1
2
3
[root@k8s-node1 ~]# ll /data/pod/volume1/
总用量 0
[root@k8s-node1 ~]# echo "<h1>kubernetes hostPath test</h1>" >> /data/pod/volume1/index.html

4)访问测试,及删除测试

1
2
3
4
5
6
7
8
9
[root@k8s-master storage]# curl 10.244.1.83
<h1>kubernetes hostPath test</h1>

#删除pod资源再次查看节点1上面的文件
[root@k8s-master storage]# kubectl delete -f vol-hostpath.yaml
pod "vol-hostpath-pod" deleted
[root@k8s-node1 ~]# ll /data/pod/volume1/
总用量 4
-rw-r--r-- 1 root root 34 10月 9 16:09 index.html

4.3 nfs 存储卷

nfs存储卷用于将事先存在的NFS服务器上导出的存储空间挂载到Pod中供容器使用。与emptyDir不同的是,当pod资源删除时emptyDir也会被删除,而NFSPod对象删除时仅是被卸载而非删除。这就意味NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递,并且NFS可以同时被多个Pod挂载并进行读写。

nfs字段说明:

1
2
3
4
[root@k8s-master ~]# kubectl explain pod.spec.volumes.nfs
server <string> -required- #NFS服务器的IP地址或主机名,必选字段
path <string> -required- #NFS服务器导出(共享)的文件系统路径,必选字段
readOnly <boolean> #是否以只读方式挂载,默认为false

nfs示例:

1)首先准备一个nfs服务器

1
2
3
4
5
6
7
8
9
10
[root@storage ~]# yum -y install nfs-utils    #安装软件
[root@storage ~]# mkdir -p /data/k8s/v1 #创建共享目录
[root@storage ~]# vim /etc/exports #编辑配置文件配置共享目录
/data/k8s/v1 192.168.1.0/24(rw,no_root_squash)
[root@storage ~]# systemctl start rpcbind #启动rpcbind服务(nfs依赖服务)
[root@storage ~]# systemctl start nfs #启动nfs

[root@k8s-node1 ~]# showmount -e 192.168.1.34 #k8s节点测试能否正常访问到nfs服务器
Export list for 192.168.1.34:
/data/k8s/v1 192.168.1.0/24

2)编辑资源清单文件

1
[root@k8s-master storage]# vim vol-nfs.yaml

3)创建并查看状态

1
2
3
4
5
6
7
8
[root@k8s-master storage]# kubectl apply -f vol-nfs.yaml 
pod/vol-nfs-pod created
[root@k8s-master storage]# kubectl get pods
NAME READY STATUS RESTARTS AGE
vol-nfs-pod 1/1 Running 0 45s
[root@k8s-master storage]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
vol-nfs-pod 1/1 Running 0 51s 10.244.2.80 k8s-node2 <none> <none>

4)测试验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@k8s-master storage]# kubectl exec -it vol-nfs-pod redis-cli
127.0.0.1:6379> set mykey "hello test"
OK
127.0.0.1:6379> get mykey
"hello test
127.0.0.1:6379> bgsave
Background saving started
127.0.0.1:6379> exit

#为了测试其数据持久化效果,下面删除Pod资源vol-nfs-pod,并于再次重新创建后检测数据是否依然能够访问
[root@k8s-master storage]# kubectl delete -f vol-nfs.yaml
pod "vol-nfs-pod" deleted
[root@k8s-master storage]# kubectl apply -f vol-nfs.yaml
pod/vol-nfs-pod created
[root@k8s-master storage]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
vol-nfs-pod 1/1 Running 0 47s 10.244.1.84 k8s-node1 <none> <none>
[root@k8s-master storage]# kubectl exec -it vol-nfs-pod redis-cli
127.0.0.1:6379> get mykey
"hello test"
127.0.0.1:6379>

通过上面测试可以看出,此前创建的mykey及其数据在Pod资源重建后依然存在,且不论pod资源调度到哪个节点。这表明在删除Pod资源时,其关联的外部存储卷并不会被一同删除。如果需要删除此类的数据,需要用户通过存储系统的管理接口手动进行。

5 PVC与PV

5.1 介绍

前面提到Kubernetes提供那么多存储接口,但是首先Kubernetes的各个Node节点能管理这些存储,但是各种存储参数也需要专业的存储工程师才能了解,由此我们的Kubernetes管理变的更加复杂。由此kubernetes提出了PVPVC的概念,这样开发人员和使用者就不需要关注后端存储是什么,使用什么参数等问题。如下图:

img

PV:

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

PVC:

PersistentVolumeClaimPVC)是用户存储的请求。它类似于PodPod消耗节点资源,PVC消耗存储资源。Pod可以请求特定级别的资源(CPU和内存)。权限要求可以请求特定的大小和访问模式。

虽然PersistentVolumeClaims允许用户使用抽象存储资源,但是常见的是,用户需要具有不同属性(如性能)的PersistentVolumes,用于不同的问题。集群管理员需要能够提供多种不同于PersistentVolumesPersistentVolumes,而不仅仅是大小和访问模式,而不会使用户了解这些卷的实现细节。对于这些需求,存在StorageClass资源。

StorageClass为管理员提供了一种描述他们提供的存储的“类”的方法。不同的类可能映射到服务质量级别,或备份策略,或者由集群管理员确定的任意策略。Kubernetes本身对于什么类别代表是不言而喻的。这个概念有时在其它存储系统中称为“配置文件”

5.2 生命周期

PV是集群中的资源。PVC是对这些资源的请求,也是对资源的索赔检查。PVPVC之间的相互作用遵循这个生命周期:

1
Provisioning—>Binding—>Using—>Releasing—>Recycling

供应准备Provisioning

PV有两种提供方式:静态或者动态

  • Static:集群管理员创建多个PV。它们携带可供集群用户使用的真实存储的详细信息。它们存在于Kubernetes API中,可用于消费。

  • Dynamic:当管理员创建的静态PV都不匹配用户的PersistentVolumesClaim时,集群可能会尝试为PVC动态配置卷。此配置基于StorageClasses:PVC必须请求一个类,并且管理员必须已经创建并配置该类才能进行动态配置。要求该类的声明有效地位自己禁用动态配置。

绑定Binding

用户创建PVC并指定需要的资源和访问模式。在找到可用PV之前,PVC会保持未绑定状态。

使用Using

用户可在Pod中像volume一样使用PVC

释放Releasing

用户删除PVC来回收存储资源,PV将变成“released”状态。由于还保留着之前的数据,这些数据要根据不同的策略来处理,否则这些存储资源无法被其它PVC使用

回收Recycling

PV可以设置三种回收策略:保留(Retain)、回收(Recycle)和删除(Delete)。

5.3 创建PV

字段说明:

PersistentVolume Spec主要支持以下几个通用字段,用于定义PV的容量、访问模式、和回收策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@k8s-master ~]# kubectl explain pv.spec
capacity <map[string]string> #当前PV的容量;目前,capacity仅支持空间设定,将来应该还可以指定IOPS和throughput。

accessModes <[]string> #访问模式;尽管在PV层看起来并无差异,但存储设备支持及启用的功能特性却可能不尽相同。例如NFS存储支持多客户端同时挂载及读写操作,但也可能是在共享时仅启用了只读操作,其他存储系统也存在类似的可配置特性。因此,PV底层的设备或许存在其特有的访问模式,用户使用时必须在其特性范围内设定其功能。参考:https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes
- ReadWriteOnce:仅可被单个节点读写挂载;命令行中简写为RWO。
- ReadOnlyMany:可被多个节点同时只读挂载;命令行中简写为ROX。
- ReadWriteMany:可被多个节点同时读写挂载;命令行中简写为RWX。

persistentVolumeReclaimPolicy <string> #PV空间被释放时的处理机制;可用类型仅为Retain(默认)、Recycle或Delete,具体说明如下。
- Retain:保持不动,由管理员随后手动回收。
- Recycle:空间回收,即删除存储卷目录下的所有文件(包括子目录和隐藏文件),目前仅NFS和hostPath支持此操作。
- Delete:删除存储卷,仅部分云端存储系统支持,如AWS EBS、GCE PD、Azure Disk和Cinder

volumeMode <string> #卷模型,用于指定此卷可被用作文件系统还是裸格式的块设备;默认为Filesystem。

storageClassName <string> #当前PV所属的StorageClass的名称;默认为空值,即不属于任何StorageClass。

mountOptions <[]string> #挂载选项组成的列表,如ro、soft和hard等。

5.4 创建PVC

字段说明:

PersistentVolumeClaim是存储卷类型的资源,它通过申请占用某个PersistentVolume而创建,它与PV是一对一的关系,用户无须关系其底层实现细节。申请时,用户只需要指定目标空间的大小、访问模式、PV标签选择器和StorageClass等相关信息即可。PVCSpec字段的可嵌套字段具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
[root@k8s-master ~]# kubectl explain pvc.spec
accessModes <[]string> #当前PVC的访问模式,其可用模式与PV相同

resources <Object> #当前PVC存储卷需要占用的资源量最小值;目前,PVC的资源限定仅指其空间大小

selector <Object> #绑定时对PV应用的标签选择器(matchLabels)或匹配条件表达式(matchEx-pressions),用于挑选要绑定的PV;如果同时指定了两种挑选机制,则必须同时满足两种选择机制的PV才能被选出

storageClassName <string> #所依赖的存储卷的名称

volumeMode <string> #卷模型,用于指定此卷可被用作于文件系统还是裸格式的块设备;默认为“Filesystem”

volumeName <string> #用于直接指定要绑定的PV的卷名

5.5 在Pod中使用PVC

Pod资源中调用PVC资源,只需要在定义volumes时使用persistentVolumeClaims字段嵌套指定两个字段即可。具体如下:

1
2
3
[root@k8s-master ~]# kubectl explain pod.spec.volumes.persistentVolumeClaim
claimName <string> -required- #要调用的PVC存储卷的名称,PVC卷要与Pod在同一名称空间中
readOnly <boolean> #是否将存储卷挂载为只读模式,默认为false。

5.6 示例使用PVC和PV

说明:下面示例中,准备了一台NFS Server创建了几个共享目录提供给Kubernetes作为PV使用。在创建PV的同时指定了不同的大小和不同的访问权限,然后在创建PVC时候指定了大小为6Gi,故满足条件的PV只有pv003~pv005,这里通过标签选择器选择了pv003。Pod中的容器使用了MySQL,并将MySQL的数据目录挂载到PV上。示例图如下:

img

1)准备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
(1)创建存储卷对应的目录
[root@storage ~]# mkdir /data/volumes/v{1..5} -p

(2)修改nfs的配置文件
[root@storage ~]# vim /etc/exports
/data/volumes/v1 192.168.1.0/24(rw,no_root_squash)
/data/volumes/v2 192.168.1.0/24(rw,no_root_squash)
/data/volumes/v3 192.168.1.0/24(rw,no_root_squash)
/data/volumes/v4 192.168.1.0/24(rw,no_root_squash)
/data/volumes/v5 192.168.1.0/24(rw,no_root_squash)

(3)查看nfs的配置
[root@storage ~]# exportfs -arv
exporting 192.168.1.0/24:/data/volumes/v5
exporting 192.168.1.0/24:/data/volumes/v4
exporting 192.168.1.0/24:/data/volumes/v3
exporting 192.168.1.0/24:/data/volumes/v2
exporting 192.168.1.0/24:/data/volumes/v1

(4)使配置生效
[root@storage ~]# showmount -e
Export list for storage:
/data/volumes/v5 192.168.1.0/24
/data/volumes/v4 192.168.1.0/24
/data/volumes/v3 192.168.1.0/24
/data/volumes/v2 192.168.1.0/24
/data/volumes/v1 192.168.1.0/24

2)创建PV;这里创建5PV,存储大小各不相等,是否可读也不相同

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
(1)编写资源清单文件
[root@k8s-master storage]# vim pv-nfs-demo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-001
labels:
name: pv001
spec:
nfs:
path: /data/volumes/v1
server: 192.168.1.34
readOnly: false
accessModes: ["ReadWriteOnce","ReadWriteMany"]
capacity:
storage: 2Gi
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-002
labels:
name: pv002
spec:
nfs:
path: /data/volumes/v2
server: 192.168.1.34
readOnly: false
accessModes: ["ReadWriteOnce"]
capacity:
storage: 5Gi
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-003
labels:
name: pv003
spec:
nfs:
path: /data/volumes/v3
server: 192.168.1.34
readOnly: false
accessModes: ["ReadWriteOnce","ReadWriteMany"]
capacity:
storage: 10Gi
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-004
labels:
name: pv004
spec:
nfs:
path: /data/volumes/v4
server: 192.168.1.34
readOnly: false
accessModes: ["ReadWriteOnce","ReadWriteMany"]
capacity:
storage: 15Gi
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-005
labels:
name: pv005
spec:
nfs:
path: /data/volumes/v5
server: 192.168.1.34
readOnly: false
accessModes: ["ReadWriteOnce","ReadWriteMany"]
capacity:
storage: 20Gi
persistentVolumeReclaimPolicy: Retain

(2)创建PV
[root@k8s-master storage]# kubectl apply -f pv-nfs-demo.yaml
persistentvolume/pv-nfs-001 created
persistentvolume/pv-nfs-002 created
persistentvolume/pv-nfs-003 created
persistentvolume/pv-nfs-004 created
persistentvolume/pv-nfs-005 created

(3)查看PV
[root@k8s-master storage]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-nfs-001 2Gi RWO,RWX Retain Available 2s
pv-nfs-002 5Gi RWO Retain Available 2s
pv-nfs-003 10Gi RWO,RWX Retain Available 2s
pv-nfs-004 15Gi RWO,RWX Retain Available 2s
pv-nfs-005 20Gi RWO,RWX Retain Available 2s

3)创建PVC,绑定PV

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
39
40
41
42
43
44
45
46
47
(1)编写资源清单文件
[root@k8s-master storage]# vim vol-nfs-pvc.yaml
#创建PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 6Gi #指定PVC大小为6Gi
selector: #这里通过标签选择器指定了所使用的pv卷为key为name,value为pv003的pv资源
matchLabels:
name: pv003
---
#创建Pod
apiVersion: v1
kind: Pod
metadata:
name: pvc-mysql
labels:
app: mysql
spec:
containers:
- name: pvc-mysql-pod
image: mysql:latest
imagePullPolicy: IfNotPresent
ports:
- name: mysqlport
containerPort: 3306
volumeMounts:
- name: mysqldata
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "mysql"
volumes:
- name: mysqldata
persistentVolumeClaim: #通过该字段定义使用pvc
claimName: nfs-pvc #指定pvc的名称
readOnly: false #关闭只读

(2)创建PVC和Pod
[root@k8s-master storage]# kubectl apply -f vol-nfs-pvc.yaml
persistentvolumeclaim/nfs-pvc created
pod/pvc-mysql created

4)查询验证pvpvc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@k8s-master storage]# kubectl get pvc    #查看pvc,可以看到该pvc使用的是pv-nfs-003资源
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nfs-pvc Bound pv-nfs-003 10Gi RWO,RWX 12s

[root@k8s-master storage]# kubectl get pv #查看pv,可以看出pv-nfs-003资源的状态从Availabel变成了Bound
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-nfs-001 2Gi RWO,RWX Retain Available 64m
pv-nfs-002 5Gi RWO Retain Available 64m
pv-nfs-003 10Gi RWO,RWX Retain Bound default/nfs-pvc 64m
pv-nfs-004 15Gi RWO,RWX Retain Available 64m
pv-nfs-005 20Gi RWO,RWX Retain Available 64m

[root@k8s-master storage]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pvc-mysql 1/1 Running 0 31s 10.244.2.84 k8s-node2 <none> <none>

[root@storage ~]# ls /data/volumes/v3/ #查看nfs服务器的pv3对应的共享目录,里面生成了mysql的数据。
auto.cnf ca-key.pem ib_buffer_pool ibtmp1 performance_schema server-key.pem
binlog.000001 ca.pem ibdata1 #innodb_temp private_key.pem sys
binlog.000002 client-cert.pem ib_logfile0 mysql public_key.pem undo_001
binlog.index client-key.pem ib_logfile1 mysql.ibd server-cert.pem undo_002

5)测试验证

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#(1)进入到pod连接容器mysql并创建一个数据库
[root@k8s-master ~]# kubectl exec -it pvc-mysql -- mysql -u root -pmysql
......
mysql>
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.01 sec)

mysql> create database volumes;
Query OK, 1 row affected (0.00 sec)

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| volumes |
+--------------------+
5 rows in set (0.00 sec)

mysql> exit
Bye


#(2)删除pvc和pod和pv
[root@k8s-master storage]# kubectl delete -f vol-nfs-pvc.yaml #删除pvc
persistentvolumeclaim "nfs-pvc" deleted
pod "pvc-mysql" deleted
[root@k8s-master storage]# kubectl delete -f pv-nfs-demo.yaml #删除pv(如果有pv在被使用的状态,需要先删除pvc方可删除pv)
persistentvolume "pv-nfs-001" deleted
persistentvolume "pv-nfs-002" deleted
persistentvolume "pv-nfs-003" deleted
persistentvolume "pv-nfs-004" deleted
persistentvolume "pv-nfs-005" deleted
[root@storage ~]# ls /data/volumes/v3/ #上面删除了pv和pvc,可以看出存储服务器上面的数据还是存在
auto.cnf ca-key.pem ib_buffer_pool ibtmp1 performance_schema server-key.pem volumes
binlog.000001 ca.pem ibdata1 #innodb_temp private_key.pem sys
binlog.000002 client-cert.pem ib_logfile0 mysql public_key.pem undo_001
binlog.index client-key.pem ib_logfile1 mysql.ibd server-cert.pem undo_002

#(3)重新创建pv和pvc和pod验证数据
[root@k8s-master storage]# kubectl apply -f pv-nfs-demo.yaml
persistentvolume/pv-nfs-001 created
persistentvolume/pv-nfs-002 created
persistentvolume/pv-nfs-003 created
persistentvolume/pv-nfs-004 created
persistentvolume/pv-nfs-005 created
[root@k8s-master storage]# kubectl apply -f vol-nfs-pvc.yaml
persistentvolumeclaim/nfs-pvc created
pod/pvc-mysql created
[root@k8s-master ~]# kubectl exec -it pvc-mysql -- mysql -u root -pmysql
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| volumes |
+--------------------+
5 rows in set (0.00 sec)


###
测试说明:
如果删除pvc不删除pv,重新创建同样的pvc,那么pvc状态会处于Pending状态,因为pv的当前状态为Released。这也和上面定义的回收策略息息相关。

本站由 卡卡龙 使用 Stellar 1.29.1主题创建

本站访问量 次. 本文阅读量 次.