一、volume概述
为了保证数据的持久性,必须保证数据存放在外部存储。在普通的docker容器中,为了实现数据的持久性存储,我们在宿主机和容器内做映射,可以保证在容器的生命周期结束,数据依旧可以实现持久性存储。但是在k8s中,由于pod分布在各个不同的节点之上,并不能实现不同节点之间持久性数据的共享,并且,在节点故障时,可能会导致数据的永久性丢失。为此,k8s就引入了外部存储卷的功能。
Volume是Pod中能够被多个容器访问的共享目录。Kubernetes的Volume概念、用途和目的与Docker的Volume比较类似,但两者不能等价。首先,Kubernetes中的Volume定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下;其次,Kubernetes中的Volume中的数据也不会丢失。最后,Kubernetes支持多种类型的Volume,例如Gluster、Ceph等先进的分布式文件系统。
除了可以让一个Pod里的多个容器共享文件、让容器的数据写到宿主机的磁盘上或者写文件到网络存储中,Kubernetes的Volume还扩展出了一种非常有实用价值的功能,即容器配置文件集中化定义与管理,这是通过ConfigMap这个新的资源对象来实现的,后面我们会详细说明。
k8s支持的存储卷类型:
使用kubectl explain pod.spec.volumes
查看k8s支持的存储类型
常用类型:
emptyDir(临时目录):Pod删除,数据也会被清除,这种存储成为emptyDir,用于数据的临时存储。
hostPath (宿主机目录映射):
本地的SAN (iSCSI,FC)、NAS(nfs,cifs,http)存储
分布式存储(glusterfs,rbd,cephfs)
云存储(EBS,Azure Disk)
k8s要使用存储卷,需要2步:
1)在pod定义volume,并指明使用哪个存储设备
2)在容器使用volume mount进行挂载
二、volume示例
emptyDir
一个emptyDir 第一次创建是在一个pod被指定到具体node的时候,并且会一直存在在pod的生命周期当中,从它的名称就可以看出,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为这是Kubernetes自动分配的一个目录。pod中的容器都可以读写这个目录,这个目录可以被挂在到各个容器相同或者不相同的的路径下。当一个pod因为任何原因被移除的时候,这些数据会被永久删除。注意:一个容器崩溃了不会导致数据的丢失,因为容器的崩溃并不移除pod.
emptyDir 磁盘的作用:
- 临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留。
- 长时间任务的中间过程CheckPoint的临时保存目录。
- 一个容器需要从另一个容器中获取数据的目录(多容器共享目录)。
默认的,emptyDir 磁盘会存储在主机所使用的媒介上,可能是SSD,或者网络硬盘,这主要取决于你的环境。当然,我们也可以将emptyDir.medium的值设置为Memory来告诉Kubernetes 来挂在一个基于内存的目录tmpfs,因为tmpfs速度会比硬盘块度了,但是,当主机重启的时候所有的数据都会丢失。
emptydir示例
[root@k8s-master ~]# kubectl explain pods.spec.volumes.emptyDir #查看emptyDir存储定义
[root@k8s-master ~]# kubectl explain pods.spec.containers.volumeMounts #查看容器挂载方式
[root@k8s-m1 k8s-volumes]# cat pod-emptydir-vol.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-emptydir
labels:
tier: frontend
annotations:
margu.com/create-by: "margu_168"
spec:
containers:
- name: myapp
image: nginx
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: html
mountPath: /data/
command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done']
volumes:
- name: html
emptyDir: {}
[root@k8s-m1 k8s-volumes]# kubectl apply -f pod-emptydir-vol.yaml
pod/pod-emptydir created
[root@k8s-m1 k8s-volumes]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-emptydir 2/2 Running 0 19s 10.244.42.188 k8s-m1 <none> <none>
在上面,我们在一个pod定义了2个容器,其中一个容器是输入日期到index.html中,然后验证访问nginx的html是否可以获取日期。以验证两个容器之间挂载的emptyDir实现共享。如下访问验证:
[root@k8s-m1 k8s-volumes]# curl 10.244.42.187
Wed Jun 28 02:14:33 UTC 2023
Wed Jun 28 02:14:35 UTC 2023
Wed Jun 28 02:14:37 UTC 2023
Wed Jun 28 02:14:39 UTC 2023
Wed Jun 28 02:14:41 UTC 2023
hostPath
hostPath为在Pod上挂载宿主机上的文件或目录,就是把pod所在的宿主机之上的脱离pod中的容器名称空间的之外的宿主机的文件系统的某一目录或者文件和pod建立关联关系,在pod删除时,存储数据不会丢失。(hostPath可以实现持久存储,但是在node节点故障时,也会导致数据的丢失)hostpath的用途有以下几个方面:
- 容器应用程序生成的日志文件需要永久保持时,可以使用宿主机的高速文件系统进行存储。
- 需要访问宿主机上Docker引擎内部数据结构的容器应用时,可以通过定义hostPath为宿主机/var/lib/docker目录,使容器内部应用可以直接访问Docker的文件系统。
在使用这种类型的Volume时,需要注意以下几点:
- 在不同的Node上具有相同配置的Pod可能会因为宿主机上的目录和文件不同而导致对Volume上目录和文件的访问结构不一致。
- 如果使用了资源配额管理,则Kubernetes无法将hostPath在宿主机上使用的资源纳入管理。
hostpath示例:
[root@k8s-master volumes]# kubectl explain pods.spec.volumes.hostPath #查看hostPath存储类型定义
[root@k8s-m1 k8s-volumes]# cat pod-hostpath-vol.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-hostpath
spec:
containers:
- name: myapp
image: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
hostPath:
path: /data/pod/hostpath
type: DirectoryOrCreate
[root@k8s-m1 k8s-volumes]# kubectl apply -f pod-hostpath-vol.yaml
pod/pod-hostpath created
[root@k8s-m1 k8s-volumes]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-emptydir 2/2 Running 0 21m 10.244.42.188 k8s-m1 <none> <none>
pod-hostpath 1/1 Running 0 16s 10.244.42.189 k8s-m1 <none> <none>
#在相应节点查看该目录是否被自动创建
[root@k8s-m1 k8s-volumes]# ll -d /data/pod/hostpath/
drwxr-xr-x 2 root root 6 Jun 28 10:45 /data/pod/hostpath/
注意:由于type设置的DirectoryOrCreate,所以 /data/pod/hostpath/ 目录会自动创建,不用手动创建。
type类型:
- DirectoryOrCreate 宿主机上不存在创建此目录
- Directory 必须存在挂载目录
- FileOrCreate 宿主机上不存在挂载文件就创建
- File 必须存在文件 3、在node节点上创建挂载目录
#访问测试
[root@k8s-m1 k8s-volumes]# echo node1.margu.com > /data/pod/hostpath/index.html
[root@k8s-m1 k8s-volumes]# curl 10.244.42.189
node1.margu.com
#删除pod,再重建,验证是否依旧可以访问原来的内容
[root@k8s-m1 k8s-volumes]# kubectl delete -f pod-hostpath-vol.yaml
pod "pod-hostpath" deleted
[root@k8s-m1 k8s-volumes]# kubectl apply -f pod-hostpath-vol.yaml
pod/pod-hostpath created
[root@k8s-m1 k8s-volumes]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-hostpath 1/1 Running 0 19s 10.244.42.190 k8s-m1 <none> <none>
[root@k8s-m1 k8s-volumes]# curl 10.244.42.190
node1.margu.com
subPath
使用场景
- 1个pod中可以运行多个容器,有时候希望将不同容器的路径挂载在存储卷volume的子路径,这个时候需要用到subpath
- volume支持将configMap/Secret以文件形式挂载在容器中,但是会覆盖掉挂载路径下原有的文件,如何支持选定configMap/Secret的每个key-value挂载在容器中,且不会覆盖掉原目录下的文件,这个时候也可以用到subpath
1个pod中多个容器时使用subPath
有时候,在单个 Pod 中多个container使用同一个volume。 volumeMounts.subPath 属性可用于指定所引用的卷内的子路径,而不是其根路径。
下面例子展示了如何配置某包含 LAMP 堆栈(Linux Apache MySQL PHP)的 Pod 使用同一共享卷。 此示例中的 subPath 配置不建议在生产环境中使用。 PHP 应用的代码和相关数据映射到卷的 html 文件夹,MySQL 数据库存储在卷的 mysql 文件夹中:
例如volume路径为 /data/pod/subpath 那么他们存储路径为/data/pod/subpath/html /data/pod/subpath/mysql 目录下:
[root@k8s-m1 k8s-volumes]# cat pod-subpath-vol.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-lamp
spec:
containers:
- name: mysql
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
volumeMounts:
- mountPath: /var/lib/mysql
name: subpath
subPath: mysql
- name: php
image: php:7.0-apache
volumeMounts:
- mountPath: /var/www/html
name: subpath
subPath: html
volumes:
- name: subpath
hostPath:
path: /data/pod/subpath
type: DirectoryOrCreate
[root@k8s-m1 k8s-volumes]# kubectl apply -f pod-subpath-vol.yaml
pod/my-lamp created
[root@k8s-m1 k8s-volumes]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-lamp 2/2 Running 0 8m 10.244.42.191 k8s-m1 <none> <none>
pod-hostpath 1/1 Running 0 25m 10.244.42.190 k8s-m1 <none> <none>
[root@k8s-m1 k8s-volumes]# echo node1.margu.com-subpath > /data/pod/subpath/html/index.html
[root@k8s-m1 k8s-volumes]# curl 10.244.42.191
node1.margu.com-subpath
在config/secret中使用subPath
有些时候我们希望将 config/secret 作为文件挂载到容器中的某个目录下而不覆盖挂载目录下的文件。
[root@k8s-m1 k8s-volumes]# cat volume-configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap-subpath
data:
config.ini: "hello"
config.conf: "world"
[root@k8s-m1 k8s-volumes]# cat configmap-subpath-pod.yml
apiVersion: v1
kind: Pod
metadata:
name: configmap-subpath
spec:
containers:
- name: test
image: busybox
command: ["/bin/sh","-c","sleep 3600s"]
volumeMounts:
- name: config-test
mountPath: /etc/config.ini # 最终在容器中的文件名
subPath: config.ini #要挂载的confmap中的key的名称
- name: config-test
mountPath: /etc/config.conf
subPath: config.conf
volumes:
- name: config-test
configMap:
name: configmap-subpath
[root@k8s-m1 k8s-volumes]# kubectl apply -f volume-configmap.yml
configmap/configmap-subpath created
[root@k8s-m1 k8s-volumes]# kubectl apply -f configmap-subpath-pod.yml
pod/configmap-subpath created
[root@k8s-m1 k8s-volumes]# kubectl exec -it configmap-subpath -- /bin/sh
/ # ls -al /etc/config.*
-rw-r--r-- 1 root root 5 Jun 28 05:58 /etc/config.conf
-rw-r--r-- 1 root root 5 Jun 28 05:58 /etc/config.ini
nfs
nfs 使得我们可以挂载一些需要共享的文件或者文件夹到我们的Pod中,和emptyDir不同的是,emptyDir会被删除当我们的Pod被删除的时候,但是nfs不会被删除,仅仅是解除挂在状态而已,这就意味着NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递。并且,nfs可以同时被多个pod挂在并进行读写。
注意:必须先保证NFS服务器正常运行在我们进行挂在nfs的时候。
安装nfs,并配置nfs服务
[root@k8s-m1 k8s-volumes]# yum install -y nfs-utils rpcbind
[root@k8s-m1 k8s-volumes]# mkdir /data/volumes -pv
#注意网段添加正确
[root@k8s-m1 k8s-volumes]# echo -e "/data/volumes 192.168.2.0/24(rw,sync,no_root_squash)" > /etc/exports
[root@k8s-m1 k8s-volumes]# exportfs -arv
[root@k8s-m1 k8s-volumes]# systemctl restart nfs
[root@k8s-m1 k8s-volumes]# showmount -e
Export list for k8s-m1:
/data/volumes 192.168.2.0/24
在其他节点上安装nfs-utils,进行简单测试挂载
[root@k8s-m2 tmp]# showmount -e
clnt_create: RPC: Program not registered
[root@k8s-m2 tmp]# systemctl restart nfs
[root@k8s-m2 tmp]# showmount -e
Export list for k8s-m2:
/data/volumes 192.168.2.0/24
[root@k8s-m2 tmp]# mkdir /nfs
[root@k8s-m2 tmp]# mount -t nfs k8s-m1:/data/volumes /nfs ##无报错一般就是成功的
[root@k8s-m2 tmp]# mount #查看输出
......
nfsd on /proc/fs/nfsd type nfsd (rw,relatime)
192.168.2.140:/data/volumes on /nfs type nfs4 (rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.2.142,local_lock=none,addr=192.168.2.140)
创建nfs存储卷的使用清单
[root@k8s-m1 k8s-volumes]# cat nfs-vol-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-vol-nfs
spec:
containers:
- name: myapp
image: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
nfs:
path: /data/volumes
server: 192.168.2.140 #需要先做名称解析或者直接使用IP地址
[root@k8s-m1 k8s-volumes]# kubectl apply -f nfs-vol-pod.yaml
pod/pod-vol-nfs created
[root@k8s-m1 k8s-volumes]# kubectl get po
NAME READY STATUS RESTARTS AGE
pod-vol-nfs 1/1 Running 0 2m31s 10.244.11.36 k8s-m3 <none> <none>
在nfs服务器上创建index.html
[root@k8s-m1 k8s-volumes]# echo "nfs volume pod " > /data/volumes/index.html
[root@k8s-m1 k8s-volumes]# curl 10.244.11.36
nfs volume pod
#删除nfs相关pod,再重新创建,测试数据的是否持久化存储
[root@k8s-m1 k8s-volumes]# kubectl delete -f nfs-vol-pod.yaml
pod "pod-vol-nfs" deleted
[root@k8s-m1 k8s-volumes]# kubectl apply -f nfs-vol-pod.yaml
pod/pod-vol-nfs created
[root@k8s-m1 k8s-volumes]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-vol-nfs 1/1 Running 0 6s 10.244.11.9 k8s-m3 <none> <none>
[root@k8s-m1 k8s-volumes]# curl 10.244.11.9
nfs volume pod
其他类型的Volume
iscsi:使用iSCSI存储设备上的目录挂载到Pod中
flocker:使用Flocker来管理存储卷
glusterfs:使用开源GlusterFS网络文件系统的目录挂载到Pod中。
rbd:使用Ceph块设备共享存储(Rados Block Device)挂载到Pod中。
gitRepo:通过挂载一个空目录,并从GIT库clone一个git repository以供Pod使用。
secret:一个secret volume用于为Pod提供加密的信息,你可以将定义在Kubernetes中的secret直接挂载的volume总是不会持久化的。
三、persistent volume概述
为什么需要Persistent Volume(PV)
之前提到的Volume是被定义在Pod上的,属于计算资源的一部分,使用是有局限性的。然而,网络存储是相对独立于计算资源而存在的一种实体资源。比如在使用虚拟机的情况下,我们通常会先定义一个网络存储,然后从中划出一个“网盘”并挂接到虚拟机上。Persistent Volume(PV)和与之相关联的Persistent Volume Claim(PVC)也起到了类似的作用。它屏蔽底层存储实现的细节,让用户方便使用,同时让管理员方便管理。
- Persistent Volume(持久卷,简称PV)是集群内,由管理员提供的网络存储的一部分。就像集群中的节点一样,PV也是集群中的一种资源。它也像Volume一样,是一种volume插件,但是它的生命周期却是和使用它的Pod相互独立的。PV不是被定义在pod上,而是独立在pod之外被定义的。PV这个API对象,捕获了诸如NFS、ISCSI、或其他云存储系统的实现细节。
- Persistent Volume Claim(持久卷声明,简称PVC)是用户的一种存储请求。它和Pod类似,Pod消耗Node资源,而PVC消耗PV资源。Pod能够请求特定的资源(如CPU和内存)。PVC能够请求指定的大小和访问的模式(可以被映射为一次读写或者多次只读)。
PV目前支持的类型包括:gcePersistentDisk、AWSElasticBlockStore、AzureFile、AzureDisk、FC(Fibre Channel)、Flocker、NFS、iSCSI、RBD(Rados Block Device)、CephFS、Cinder、GlusterFS、VsphereVolume、Quobyte Volumes、VMware Photon、Portworx Volumes、ScaleIO Volumes和HostPath
PVC允许用户消耗抽象的存储资源,用户也经常需要各种属性(如性能)的PV。集群管理员需要提供各种各样、不同大小、不同访问模式的PV,而不用向用户暴露这些volume如何实现的细节。因为这种需求,就催生出一种StorageClass资源。
StorageClass提供了一种方式,使得管理员能够描述他提供的存储的等级。集群管理员可以将不同的等级映射到不同的服务等级、不同的后端策略。
pv和pvc的区别
PersistentVolume(持久卷)和PersistentVolumeClaim(持久卷申请)是k8s提供的两种API资源,用于抽象存储细节。管理员关注于如何通过pv提供存储功能而无需关注用户如何使用,同样的用户只需要挂载pvc到容器中而不需要关注存储卷采用何种技术实现。
pvc和pv的关系与pod和node关系类似,前者消耗后者的资源。pvc可以向pv申请指定大小的存储资源并设置访问模式,这就可以通过Provision -> Claim 的方式,来对存储资源进行控制。
pv类型
一般有两种PV的提供方式:静态或者动态
静态–>直接固定存储空间:
集群管理员创建一些 PV。它们携带可供集群用户使用的真实存储的详细信息。 它们存在于Kubernetes API中,可用于消费。
动态–>通过存储类进行动态创建存储空间:
当管理员创建的静态 PV 都不匹配用户的 PVC 时,集群可能会尝试动态地为 PVC 配置卷。此配置基于StorageClasses:PVC 必须请求存储类,并且管理员必须已创建并配置该类才能进行动态配置。
pv生命周期
Binding(绑定)
用户创建一个PVC(或者之前就已经就为动态供给创建了),指定要求存储的大小和访问模式。master中有一个控制回路用于监控新的PVC,查找匹配的PV(如果有),并把PVC和PV绑定在一起。如果一个PV曾经动态供给到了一个新的PVC,那么这个回路会一直绑定这个PV和PVC。另外,用户总是至少能得到它们所要求的存储,但是volume可能超过它们的请求。一旦绑定了,PVC绑定就是专属的,无论它们的绑定模式是什么。
如果没找到匹配的PV,那么PVC会无限期得处于unbound未绑定状态,一旦PV可用了,PVC就会又变成绑定状态。比如,如果一个供给了很多50G的PV集群,不会匹配要求100G的PVC。直到100G的PV添加到该集群时,PVC才会被绑定。注意,PV和PVC是一一对应的,一个PV只能被一个PVC绑定。
Using(使用)
当系统为用户创建的PVC绑定PV后,表明用户成功申请了存储资源。用户在pod中定义PVC类型的volume,当创建POD实例时系统将与PVC绑定的PV挂载到POD实例。如果PV支持多种访问模式(accessModes),用户需要pod的PVC volume中指定期望的类型。注意,pod与PVC必需位于相同namespace之下。
Releasing(释放)
当用户使用PV完毕后,他们可以通过API来删除PVC对象。当PVC被删除后,对应的PV就被认为是已经是“released”了,但还不能再给另外一个PVC使用。前一个PVC的数据还存在于该PV中,必须根据策略来处理掉。
Reclaiming(回收)
PV的回收策略告诉集群,在PV被释放之后集群应该如何处理该PV。
Retain
保护被PVC释放的PV及其上数据,并将PV状态改成”released”,不将被其它PVC绑定。集群管理员手动通过如下步骤释放存储资源:
手动删除PV,但与其相关的后端存储资源如(AWS EBS, GCE PD, Azure Disk, or Cinder volume)仍然存在。
手动清空后端存储volume上的数据。
手动删除后端存储volume,或者重复使用后端volume,为其创建新的PV。
Delete
删除被PVC释放的PV及其后端存储volume。对于动态PV其”reclaim policy”继承自其”storage class”,默认是Delete。集群管理员负责将”storage class”的”reclaim policy”设置成用户期望的形式,否则需要用户手动为创建后的动态PV编辑”reclaim policy”。
Recycle
如果PV卷支持再利用,再利用会在PV卷上执行一个基础的擦除操作(rm -rf /thevolume/*),使得它可以再次被其他PVC声明利用。(Recycle回收政策已弃用)
pv介绍
Capacity(容量)
一般来说,PV会指定存储的容量,使用PV的capacity属性来设置。当前,存储大小是唯一能被设置或请求的资源。未来可能包含IOPS,吞吐率等属性。
其次比较重要的是PV的accessModes属性,目前有以下类型。
- ReadWriteOnce:读写权限,并且只能被单个Node挂载。
- ReadOnlyMany:只读权限,允许被多个Node挂载。
- ReadWriteMany:读写权限,允许被多个Node挂载。
在CLI中,访问模式可以简写为:
- RWO - ReadWriteOnce
- ROX - ReadOnlyMany
- RWX - ReadWriteMany
最后说说PV的状态。PV是有状态的对象,它的状态有以下几种。
- Available:空闲状态。
- Bound:已经绑定到某个PVC上。
- Released:对应的PVC已经被删除,但资源还没有被集群收回。
- Failed:PV自动回收失败。
Class
一个PV可以有一种class,通过设置storageClassName属性来选择指定的StorageClass。有指定class的PV只能绑定给请求该class的PVC。没有设置storageClassName属性的PV只能绑定给未请求class的PVC。
过去,使用volume.beta.kubernetes.io/storage-class注解,而不是storageClassName属性。该注解现在依然可以工作,但在Kubernetes的未来版本中已经被完全弃用了。
四、persistent volume示例
以nfs类型的pv做测试。
#格式查看
[root@k8s-m1 k8s-volumes]# kubectl explain pv
[root@k8s-m1 k8s-volumes]# kubectl explain pv.spec
配置nfs存储
[root@k8s-m1 k8s-volumes]# mkdir -p /data/volumes/v{1,2,3,4,5}
[root@k8s-m1 k8s-volumes]# vim /etc/exports
/data/volumes 192.168.2.0/24(rw,sync,no_root_squash)
/data/volumes/v1 192.168.2.0/24(rw,no_root_squash)
/data/volumes/v2 192.168.2.0/24(rw,no_root_squash)
/data/volumes/v3 192.168.2.0/24(rw,no_root_squash)
/data/volumes/v4 192.168.2.0/24(rw,no_root_squash)
/data/volumes/v5 192.168.2.0/24(rw,no_root_squash)
[root@k8s-m1 k8s-volumes]# exportfs -arv
exporting 192.168.2.0/24:/data/volumes/v5
exporting 192.168.2.0/24:/data/volumes/v4
exporting 192.168.2.0/24:/data/volumes/v3
exporting 192.168.2.0/24:/data/volumes/v2
exporting 192.168.2.0/24:/data/volumes/v1
exporting 192.168.2.0/24:/data/volumes
[root@k8s-m1 k8s-volumes]# showmount -e
Export list for k8s-m1:
/data/volumes/v5 192.168.2.0/24
/data/volumes/v4 192.168.2.0/24
/data/volumes/v3 192.168.2.0/24
/data/volumes/v2 192.168.2.0/24
/data/volumes/v1 192.168.2.0/24
/data/volumes 192.168.2.0/24
定义PV(非常注意:在创建PV时不要指定namespace空间PV)
这里定义5个PV,并且定义挂载的路径以及访问模式,还有PV划分的大小。
[root@k8s-master volumes]# cat pv-demo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv001
labels:
name: pv001
spec:
nfs:
path: /data/volumes/v1
server: 192.168.2.140
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv002
labels:
name: pv002
spec:
nfs:
path: /data/volumes/v2
server: 192.168.2.140
accessModes: ["ReadWriteOnce"]
capacity:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv003
labels:
name: pv003
spec:
nfs:
path: /data/volumes/v3
server: 192.168.2.140
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv004
labels:
name: pv004
spec:
nfs:
path: /data/volumes/v4
server: 192.168.2.140
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 4Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv005
labels:
name: pv005
spec:
nfs:
path: /data/volumes/v5
server: 192.168.2.140
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 5Gi
[root@k8s-master volumes]# kubectl apply -f pv-demo.yaml
persistentvolume/pv001 created
persistentvolume/pv002 created
persistentvolume/pv003 created
persistentvolume/pv004 created
persistentvolume/pv005 created
[root@k8s-m1 k8s-volumes]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 1Gi RWO,RWX Retain Available 10m
pv002 2Gi RWO Retain Available 10m
pv003 2Gi RWO,RWX Retain Available 10m
pv004 4Gi RWO,RWX Retain Available 10m
pv005 5Gi RWO,RWX Retain Available 10m
标签说明:
Capacity(容量)
一般来说,一个 PV 对象都要指定一个容量,通过 PV 的 capacity属性来设置的,目前只支持存储空间的设置,就是我们这里的 storage=1Gi
AccessModes(访问模式)
AccessModes 是用来对 PV 进行访问模式的设置,用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
ReadWriteMany(RWX):读写权限,可以被多个节点挂载
persistentVolumeReclaimPolicy(回收策略)
我这里指定的 PV 的回收策略为 Recycle,目前 PV 支持的策略有三种:
Retain(保留)- 保留数据,需要管理员手工清理数据
Recycle(回收)- 清除 PV 中的数据,效果相当于执行 rm -rf /thevoluem/*
Delete(删除)- 关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)将被删除。
不过需要注意的是,目前只有 NFS 和 HostPath 两种类型支持回收策略。但是一般来说我们还是设置为 Retain 这种策略保险一点。
状态
一个 PV 的生命周期中,可能会处于5种不同的阶段:
Available(可用):表示可用状态,还未被任何 PVC 绑定
Bound(已绑定):表示 PVC 已经被 PVC 绑定
Released(已释放):PVC 被删除,但是资源还未被集群重新声明
Terminating (正在停止):PVC正常停止中
Failed(失败): 表示该 PV 的自动回收失败
定义PVC
示例
定义pv时,我们可以不用指定命名空间,因为pv是全局的。而pvc是命名空间级别的,哪个命名空间的pod需要使用pv,就需要就需要用pvc进行绑定。
这里定义了pvc的访问模式为多路读写,该访问模式必须在前面pv定义的访问模式之中。定义PVC申请的大小为2Gi,此时PVC会自动去匹配多路读写且大小为2Gi的PV,匹配成功获取PVC的状态即为Bound
[root@k8s-m1 k8s-volumes]# cat pod-vol-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
namespace: default
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: Pod
metadata:
name: pod-vol-pvc
spec:
containers:
- name: myapp
image: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
persistentVolumeClaim:
claimName: mypvc
[root@k8s-m1 k8s-volumes]# kubectl apply -f pod-vol-pvc.yaml
persistentvolumeclaim/mypvc created
pod/pod-vol-pvc created
[root@k8s-m1 k8s-volumes]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 1Gi RWO,RWX Retain Available 20m
pv002 2Gi RWO Retain Available 30m
pv003 2Gi RWO,RWX Retain Bound default/mypvc 30m
pv004 4Gi RWO,RWX Retain Available 30m
pv005 5Gi RWO,RWX Retain Available 30m
[root@k8s-m1 k8s-volumes]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc Bound pv003 2Gi RWO,RWX 27s
测试访问
在nfs存储服务器上相应的目录创建index.html,并写入数据,通过访问Pod进行查看,可以获取到相应的页面。
[root@k8s-m1 k8s-volumes]# echo pvc test > /data/volumes/v3/index.html
[root@k8s-m1 k8s-volumes]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
configmap-subpath 1/1 Running 2 161m 10.244.42.136 k8s-m1 <none> <none>
my-lamp 2/2 Running 0 5h29m 10.244.42.191 k8s-m1 <none> <none>
pod-hostpath 1/1 Running 0 5h47m 10.244.42.190 k8s-m1 <none> <none>
pod-vol-nfs 1/1 Running 0 125m 10.244.11.9 k8s-m3 <none> <none>
pod-vol-pvc 1/1 Running 0 3m10s 10.244.42.141 k8s-m1 <none> <none>
[root@k8s-m1 k8s-volumes]# curl 10.244.42.141
pvc test
更多关于kubernetes的知识分享,请前往博客主页。编写过程中,难免出现差错,敬请指出