k8s入门教程十二:存储管理volume、PV/PVC

kubernetes存储管理按照发展的历程,涉及到有Volume,PV(Persistent Volume)和PVC(PersistentVolumeClaims),和StorageClass,Volume是最早提出的存储卷,主要解决容器和数据存储的依赖关系,抽象底层驱动以支持不同的存储类型;使用Volume需要了解底层存储细节,因此提出了PV,Persistent Volume是由k8s管理员定义的存储单元,应用端使用PersistentVolumeClaims声明去调用PV存储,进一步抽象了底层存储;随着PV数量的增加,管理员需要不停的定义PV的数量,衍生了通过StorageClass动态生成PV,StorageClass通过PVC中声明存储的容量,会调用底层的提供商生成PV。本文介绍Volume的使用,下篇文章介绍PV,PVC和StorageClass。

  • Volume 存储卷,独立于容器,后端和不同的存储驱动对接
  • PV Persistent Volume持久化存储卷,和node类似,是一种集群资源,由管理员定义,对接不同的存储
  • PVC PersistentVolumeClaims持久化存储声明,和pod类似,作为PV的使用者
  • StorageClass 动态存储类型,分为静态和动态两种类型,通过在PVC中定义存储类型,自动创建所需PV

存储概述

kubernetes容器中的数据是临时的,即当重启重启或crash后容器的数据将会丢失,此外容器之间有共享存储的需求,所以kubernetes中提供了volume存储的抽象,volume后端能够支持多种不同的plugin驱动,通过.spec.volumes中定义一个存储,然后在容器中.spec.containers.volumeMounts调用,最终在容器内部以目录的形式呈现。

kubernetes内置能支持多种不同的驱动类型,大体上可以分为四种类型:

  1. 公/私有云驱动接口,如awsElasticBlockStore实现与aws EBS集成
  2. 开源存储驱动接口,如ceph rbd,实现与ceph rb块存储对接
  3. 本地临时存储,如hostPath
  4. kubernetes对象API驱动接口,实现其他对象调用,如configmap,每种存储支持不同的驱动

本地临时存储

本地临时存储包括hostPath、emptyDir等。

emptyDir临时存储

emptyDir是一种临时存储,pod创建的时候会在node节点上为容器申请一个临时的目录,跟随容器的生命周期,如容器删除,emptyDir定义的临时存储空间也会随之删除,容器发生意外crash则不受影响,同时如果容器发生了迁移,其上的数据也会丢失,emptyDir一般用于测试,或者缓存场景。注意:一个容器崩溃了不会导致数据的丢失,因为容器的崩溃并不移除pod.

emptyDir 的一些用途:

  • 缓存空间,例如基于磁盘的归并排序。
  • 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
  • 在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: busybox:1.27
name: test-container
command: ["/bin/bash","-c","sleep 3600"]
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}

进入pod,查看文件

1
2
3
4
5
6
7
8
9
[root@master ~]# k apply -f 1.yaml
pod/test-pd created
[root@master ~]# k exec test-pd -it -- sh
/ # df -h |grep cache
/dev/sda3 95.5G 16.5G 79.0G 17% /cache
/ # cd /cache
/cache # echo 000 >a.txt
/cache # cat a.txt
000

那创建的文件是放在哪里呢?先使用 k get pods -o wide 查看pods所在的node,然后登陆这台node,查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@node2 ~]# docker inspect k8s_test-container_test-pd_default_88417898-cfed-44a8-8522-715d29f7e009_0
"Mounts": [
{
"Type": "bind",
"Source": "/var/lib/kubelet/pods/88417898-cfed-44a8-8522-715d29f7e009/volumes/kubernetes.io~empty-dir/cache-volume",
"Destination": "/cache",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
。。。
[root@node2 ~]# cat /var/lib/kubelet/pods/88417898-cfed-44a8-8522-715d29f7e009/volumes/kubernetes.io~empty-dir/cache-volume/a.txt
000

可以看到是bind到机器上面的。当删除完这个pod时,这个文件也是会自动删除的。

hostPath主机存储

hostPath 卷能将node宿主机节点文件系统上的文件或目录挂载到您的 Pod 中。例如,hostPath 的一些用法有:

  • 运行一个需要访问 Docker 引擎内部机制的容器;请使用 hostPath 挂载 /var/lib/docker 路径。
  • 在容器中运行 cAdvisor 时,以 hostPath 方式挂载 /sys
  • 允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在。

除了必需的 path 属性之外,用户可以选择性地为 hostPath 卷指定 type。支持的 type 值如下:

取值 行为
空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。
DirectoryOrCreate 如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 Kubelet 相同的组和所有权。
Directory 在给定路径上必须存在的目录。
FileOrCreate 如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 Kubelet 相同的组和所有权。
File 在给定路径上必须存在的文件。
Socket 在给定路径上必须存在的 UNIX 套接字。
CharDevice 在给定路径上必须存在的字符设备。
BlockDevice 在给定路径上必须存在的块设备。

当使用这种类型的卷时要小心,因为:

  • 具有相同配置(例如从 podTemplate 创建)的多个 Pod 会由于节点上文件的不同而在不同节点上有不同的行为。
  • 当 Kubernetes 按照计划添加资源感知的调度时,这类调度机制将无法考虑由 hostPath 使用的资源。
  • 基础主机上创建的文件或目录只能由 root 用户写入。您需要在特权容器中以 root 身份运行进程,或者修改主机上的文件权限以便容器能够写入 hostPath 卷。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: nginx:1.7.9
name: test-container
volumeMounts:
- mountPath: /test-pd
name: test-volume
volumes:
- name: test-volume
hostPath:
path: /data
type: Directory

就是共享本机的目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@master volume]# kubectl create -f hostPath.yaml
pod/test-pd created
[root@master volume]# kubectl exec test-pd -it -- ls -l /test-pd
total 0
[root@master ~]# k exec -it test-pd -- sh
# df -h |grep test-pd
/dev/sda3 96G 29G 67G 31% /test-pd
[root@master volume]# date >/data/1.log
[root@master volume]# kubectl exec test-pd -it -- ls -l /test-pd
total 4
-rw-r--r-- 1 root root 29 Feb 9 11:35 1.log
[root@master volume]# kubectl exec test-pd -it -- cat /test-pd/1.log
Sun Feb 9 19:35:33 CST 2020

删除这个pod时,文件不会删除掉,还要保存到node上。

PV与PVC

PV 的全称是:PersistentVolume(持久化卷),是对底层的共享存储的一种抽象,PV 由管理员进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如 Ceph、GlusterFS、NFS 等,都是通过插件机制完成与共享存储的对接。

PVC 的全称是:PersistentVolumeClaim(持久化卷声明),PVC 是用户存储的一种声明,PVC 和 Pod 比较类似,Pod 消耗的是节点,PVC 消耗的是 PV 资源,Pod 可以请求 CPU 和内存,而 PVC 可以请求特定的存储空间和访问模式。对于真正使用存储的用户不需要关心底层的存储实现细节,只需要直接使用 PVC 即可。

生命周期

PV是集群中的资源。PVC是对这些资源的请求,也是对资源的索赔检查。 PV和PVC之间的相互作用遵循这个生命周期:Provisioning ——-> Binding ——–>Using——>Releasing——>Recycling

Provisioning

这里有两种PV的提供方式:静态或者动态。

  • Static:集群管理员创建多个PV。 它们携带可供集群用户使用的真实存储的详细信息。 它们存在于Kubernetes API中,可用于消费。
  • Dynamic:当管理员创建的静态PV都不匹配用户的PersistentVolumeClaim时,集群可能会尝试为PVC动态配置卷。 此配置基于StorageClasses:PVC必须请求一个类,并且管理员必须已创建并配置该类才能进行动态配置。 要求该类的声明有效地为自己禁用动态配置

Binding绑定

在动态配置的情况下,用户创建或已经创建了具有特定数量的存储请求和特定访问模式的PersistentVolumeClaim。 主机中的控制回路监视新的PVC,找到匹配的PV(如果可能),并将它们绑定在一起。 如果为新的PVC动态配置PV,则循环将始终将该PV绑定到PVC。 否则,用户总是至少得到他们要求的内容,但是卷可能超出了要求。 一旦绑定,PersistentVolumeClaim绑定是排他的,不管用于绑定它们的模式。

如果匹配的卷不存在,PVC将保持无限期。 随着匹配卷变得可用,PVC将被绑定。 例如,提供许多50Gi PV的集群将不匹配要求100Gi的PVC。 当集群中添加100Gi PV时,可以绑定PVC。

PVC 保护的目的是确保由 pod 正在使用的 PVC 不会从系统中移除,因为如果被移除的话可能会导致数据丢失 当启用PVC 保护 alpha 功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的 删除将被推迟,直到 PVC 不再被任何 pod 使用。

PV访问模式

PersistentVolume可以以资源提供者支持的任何方式挂载到主机上。例如,NFS 可以支持多个读/写客户端,但特定的 NFS PV 可能 以只读方式导出到服务器上。每个 PV 都有一套自己的用来描述特定功能的访问模式。

  • ReadWriteOnce——该卷可以被单个节点以读/写模式挂载
  • ReadOnlyMany——该卷可以被多个节点以只读模式挂载
  • ReadWriteMany——该卷可以被多个节点以读/写模式挂载
  • 在命令行中,访问模式缩写为:
    • RWO - ReadWriteOnce
    • ROX - ReadOnlyMany
    • RWX - ReadWriteMany

下表是一些常用的 Volume 插件支持的访问模式:

Volume Plugin ReadWriteOnce ReadOnlyMany ReadWriteMany
AWSElasticBlockStore - -
AzureFile
AzureDisk - -
CephFS
Cinder - -
CSI depends on the driver depends on the driver depends on the driver
FC -
FlexVolume depends on the driver
Flocker - -
GCEPersistentDisk -
Glusterfs
HostPath - -
iSCSI -
Quobyte
NFS
RBD -
VsphereVolume - - (works when Pods are collocated)
PortworxVolume -
ScaleIO -
StorageOS - -

回收策略Reclaiming

  • Retain(保留):手动回收
  • Recycle(回收):基本擦除( rm -rf /thevolume/* )
  • Delete(删除):关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷) 将被删除
  • 当前,只有 NFS 和 HostPath 支持回收策略。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策略

状态

卷可以处于以下的某种状态:

  • Available(可用):一块空闲资源还没有被任何声明绑定
  • Bound(已绑定):卷已经被声明绑定
  • Released(已释放):声明被删除,但是资源还未被集群重新声明
  • Failed(失败):该卷的自动回收失败 命令行会显示绑定到 PV 的 PVC 的名称

hostPath实例演示

创建PV

pv-volume.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data"

创建好了显示如下:

1
2
3
4
5
[root@master pv]# kubectl create -f pv-volume.yaml
persistentvolume/task-pv-volume created
[root@master pv]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
task-pv-volume 10Gi RWO Retain Available manual 5s

可以看到这个PV为10G,访问模式为RWO,卸载后保留,目前的STATUS为可用状态。

创建PVC

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi

要求的资源要大于3G,类型为manual,这里一定要跟创建PV时对应上。

1
2
3
4
5
6
7
8
[root@master pv]# kubectl create -f pv-claim.yaml
persistentvolumeclaim/task-pv-claim created
[root@master pv]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
task-pv-volume 10Gi RWO Retain Bound default/task-pv-claim manual 4m11s
[root@master pv]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
task-pv-claim Bound task-pv-volume 10Gi RWO manual 13s

主要看状态,变成Bound了。查看pvc的状态,也可以看到分配到了10G的资源。

创建POD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-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: task-pv-storage
nodeSelector:
kubernetes.io/hostname: node1

注意,创建的PV是在master上,目录是在/data下,但是POD被调度到node1上,这样来访问一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@master ~]# kubectl get pods -o wide task-pv-pod
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
task-pv-pod 1/1 Running 0 4m41s 10.44.0.5 node1 <none> <none>
[root@master ~]# curl 10.44.0.5
this is a node1
[root@master ~]# kubectl exec task-pv-pod -- ls -l /usr/share/nginx/html
total 4
-rw-r--r-- 1 root root 16 Feb 11 06:09 index.html
drwxr-xr-x 2 nobody root 6 Feb 10 13:45 nfs0
drwxr-xr-x 2 nobody root 24 Feb 10 13:35 nfs1
drwxr-xr-x 2 nobody root 24 Feb 10 13:35 nfs2
drwxr-xr-x 2 nobody root 6 Feb 10 12:58 nfs3
[root@master ~]# cat /data/index.html
Mon Feb 10 19:26:20 CST 2020

创建成功之后,可以看到一个curl 与 cat出现的内容不一样。POD里面的的文件内容是node1上面的,而不是master节点上面的。所以hostPath只能调度到自己的机器上。

参考链接

https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/

  • 本文作者: wumingx
  • 本文链接: https://www.wumingx.com/k8s/kubernetes-volume-pvc.html
  • 本文主题: k8s入门教程十二:存储管理volume、PV/PVC
  • 版权声明: 本博客所有文章除特别声明外,转载请注明出处!如有侵权,请联系我删除。
0%