【k8s】PersistentVolume解决数据持久化问题(十三)
PersistentVolume 简称为 PV,是 Kubernetes 对存储设备的抽象,由系统管理员维护,需要描述清楚存储设备的类型、访问模式、容量等信息。PersistentVolumeClaim 简称为 PVC,代表 Pod 向系统申请存储资源,它声明对存储的要求,Kubernetes 会查找最合适的 PV 然后绑定。StorageClass 抽象特定类型的存储系统,归类分组 PV 对象,
文章目录
ConfigMap 和 Secret 中的 Volume
【k8s】 ConfigMap/Secret 管理应用配置(五) 提到了 Volume 存储卷的概念。它使用字段 volumes 和 volumeMounts 将配置信息挂载到 Pod 中供进程使用。
本篇笔记会讲述 Volume 的高级用法,看看 Kubernetes 管理存储资源的 API 对象 PersistentVolume、PersistentVolumeClaim、StorageClass,然后使用本地磁盘来创建实际可用的存储卷。
一、PersistentVolume
Pod 里的容器是由镜像产生的,而镜像文件本身是只读的,进程要读写磁盘只能用一个临时的存储空间,一旦 Pod 销毁,临时存储也就会立即回收释放,数据也就丢失了。为了保证即使 Pod 销毁后重建数据依然存在,Kubernetes 就顺着 Volume 的概念,延伸出了 PersistentVolume 对象,它专门用来表示持久存储设备,但隐藏了存储的底层实现,我们只需要知道它能安全可靠地保管数据就可以了(由于 PersistentVolume 这个词很长,一般都把它简称为 PV
)。
作为存储的抽象,PV
实际上就是一些存储设备、文件系统,比如 Ceph、GlusterFS、NFS
,甚至是本地磁盘,管理它们已经超出了 Kubernetes 的能力范围,所以,一般会由系统管理员单独维护,然后再在 Kubernetes 里创建对应的 PV
。
要注意的是,PV
属于集群的系统资源,是和 Node 平级的一种对象,Pod
对它没有管理权,只有使用权。
1.PersistentVolumeClaim 和 StorageClass
PV
无法直接挂载到 Pod 里面使用,因为不同的存储设备的差异巨大:速度快慢、共享/独占读写、容量大小…这么多种存储设备,只用一个 PV 对象来管理还是有点太勉强了,不符合“单一职责”的原则,让 Pod 直接去选择 PV 也很不灵活。
于是 Kubernetes 就又增加了两个新对象,PersistentVolumeClaim
和 StorageClass
,用的还是中间层
的思想,把存储卷的分配管理过程再次细化。
PersistentVolumeClaim
简称 PVC。它是用来向 Kubernetes 申请存储资源的 API 对象,用来给 Pod 使用,相当于 Pod 的存储代理,代表 Pod 向系统申请 PV。一旦资源申请成功,Kubernetes 就会把 PV 和 PVC 关联在一起,这个动作叫做绑定(bind)
。
但是,系统里的存储资源非常多,如果要 PVC 去直接遍历查找合适的 PV 也很麻烦,所以就要用到 StorageClass
。
StorageClass
抽象了特定类型的存储系统(比如Ceph、NFS
),在 PVC
和 PV
之间充当协调人
的角色,帮助 PVC 找到合适的 PV。也就是说它可以简化 Pod 挂载虚拟盘
的过程,让 Pod 看不到 PV 的实现细节。
二、使用 YAML 描述 PersistentVolume
Kubernetes 里有很多种类型的 PV
,最基础的是本机存储 HostPath
。它和 Docker 中使用 docker run -v
使用的参数 -v
非常相似。
因为 Pod 会在集群的任意节点上运行,所以首先,我们要作为系统管理员在每个节点上创建一个目录,它将会作为本地存储卷挂载到 Pod 里:在 /tmp
目录下建立名字为 host-10mb-pv
的文件夹,表示一个容量为 10 MB 的存储设备。
目前只能用 kubectl api-resources
kubectl explain
查看 PV 的字段说明,手动编写 PV 的 YAML 描述文件。
apiVersion: v1
kind: PersistentVolume
metadata:
name: host-10m-pv
spec:
storageClassName: host-manual
accessModes:
- ReadWriteOnce
capacity:
storage: 10Mi
hostPath:
path: /tmp/host-10mib-pv/
storageClassName
是对存储类型的抽象 StorageClass 的名字。这个 PV 是手动管理的,名字可以任意起。
accessModes
定义了存储设备的访问模式。简单来说就是虚拟盘的读写权限,和 Linux 的文件访问模式差不多,目前 Kubernetes 里有 4 种:
ReadWriteOnce
:存储卷可读可写,但只能被一个节点上的 Pod 挂载ReadOnlyMany
:存储卷只读不可写,可以被任意节点上的 Pod 多次挂载ReadWriteMany
:存储卷可读可写,也可以被任意节点上的 Pod 多次挂载ReadWriteOncePod
:卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用 ReadWriteOncePod 访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本
前三种 3 种访问模式限制的对象是节点而不是 Pod,因为存储是系统级别的概念,不属于 Pod 里的进程。显然,本地目录只能是在本机使用,所以这个 PV 使用了 ReadWriteOnce。
在命令行接口(CLI)中,访问模式也使用以下缩写形式:
RWO - ReadWriteOnce
ROX - ReadOnlyMany
RWX - ReadWriteMany
RWOP - ReadWriteOncePod
capacity
表示存储设备的容量,这里我设置为 10MB
。Kubernetes 里定义存储容量使用的是国际标准,要写成 Ki/Mi/Gi
。
hostPath
指定了存储卷的本地路径,也就是我们在节点上创建的目录。
用这些字段把 PV 的类型、访问模式、容量、存储
位置都描述清楚,一个存储设备就创建好了。
三、使用 YAML 描述 PersistentVolumeClaim
有了 PV,就表示集群里有了这么一个持久化存储可以供 Pod 使用,我们需要再定义 PVC 对象,向 Kubernetes 申请存储。
下面这份 YAML 就是一个 PVC
,要求使用一个 5MB
的存储设备,访问模式是 ReadWriteOnce
:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: host-5mib-pvc
spec:
storageClassName: host-manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Mi
PVC
的内容与 PV
很像,但它不表示实际的存储,而是一个申请
或者声明
,spec
里的字段描述的是对存储的期望状态
。所以 PVC 里的storageClassName
accessModes
和 PV 是一样的,但不会有字段 capacity
,而是要用 resources.requests.storage
表示希望要有多大的容量。
这样,Kubernetes 就会根据 PVC 里的描述,去找能够匹配 StorageClass
和符合容量的 PV,然后把 PV 和 PVC绑定
在一起,实现存储的分配。
四、在 Kubernetes 中使用 PersistentVolume
创建 PV 对象,然后查看它的状态
$ kubectl apply -f host-path-pv.yaml
persistentvolume/host-10m-pv created
$ kubectl get pv -o wide
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE VOLUMEMODE
host-10m-pv 10Mi RWO Retain Available host-manual 8s Filesystem
可以看到,这个 PV 的容量是 10 MiB,访问模式是 RWO(ReadWriteOnce)
,StorageClass
是自定义的 host-manual
,状态是可用(Available),可以随时分配给 Pod 使用。
创建PVC申请存储资源
$ kubectl apply -f host-path-pvc.yaml
persistentvolumeclaim/host-5mib-pvc created
$ kubectl get pvc -o wide
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE VOLUMEMODE
host-5mib-pvc Bound host-10m-pv 10Mi RWO host-manual 5s Filesystem
$ kubectl get pv -o wide
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE VOLUMEMODE
host-10m-pv 10Mi RWO Retain Bound default/host-5mib-pvc host-manual 14m Filesystem
一旦 PVC 对象创建成功,Kubernetes 就会立即通过 StorageClass resources
等条件在集群里查找符合要求的 PV
,如果找到合适的存储对象就会把它俩绑定
(bind)在一起,绑定后的状态就是 Bound。
PVC 对象申请的是 5MB,但现在系统里只有一个 10MB 的 PV,没有更合适的对象,所以 Kubernetes 也只能把这个 PV 分配出去。
如果把 PVC 的申请容量改大一些,超过现有可用 PV 的容量,PVC 就会一直处于 Pending 状态
,这意味着 Kubernetes 没有在系统中找到合适的存储,无法分配资源,只有等满足要求的 PV 出现才能完成绑定。
五、为 Pod 挂载 PersistentVolume
有了持久化存储就可以为 Pod 挂载存储卷。先要在 spec.volumes
定义存储卷,然后在 containers.volumeMounts
挂载进容器。因为我们用的是 PVC,所以要在 volumes 里用字段 persistentVolumeClaim
指定 PVC 的名字。
下面就是 Pod 的 YAML 描述文件,把存储卷挂载到了 Nginx 容器的 /tmp
目录:
apiVersion: v1
kind: Pod
metadata:
name: host-pvc-pod
spec:
volumes:
- name: host-pvc-vol
persistentVolumeClaim:
claimName: host-5mib-pvc
containers:
- name: ngx-pvc-pod
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: host-pvc-vol
mountPath: /tmp
现在启动这个 Pod:
$ kubectl apply -f ngx-pvc-pod.yaml
pod/host-pvc-pod created
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
host-pvc-pod 1/1 Running 0 21s 10.10.1.70 k8s-worker01 <none> <none>
我们在 Pod 中创建一个文件,然后取宿主机上看看文件是否被创建:
$ kubectl exec -it host-pvc-pod -- sh
/ # cd /tmp/
/tmp # echo yeah! > file.text
登录宿主机:
[root@k8s-work01 ~]# ls /tmp/host-10mib-pv/
file.text
[root@k8s-work01 ~]# cat /tmp/host-10mib-pv/file.text
yeah!
确实在 worker 节点的本地目录有一个 file.text
文件。因为 Pod 产生的数据已经通过 PV 存在了磁盘上,所以如果 Pod 删除后再重新创建,挂载存储卷时会依然使用这个目录,数据保持不变,也就实现了持久化存储。不过还有一点小问题,因为这个 PV 是 HostPath 类型,只在本节点存储,如果 Pod 重建时被调度到了其他节点上,那么即使加载了本地目录,也不会是之前的存储位置,持久化功能也就失效了。
HostPath
类型的 PV 一般用来做测试,或者是用于 DaemonSet
这样与节点关系比较密切的应用。下篇笔记就要学习网络共享存储了。
下图表示了Pod 和 PVC/PV 的关系画成了图(省略了字段 accessModes),你可以从图里看出它们是如何联系起来的:
资料
Kubernetes 持久卷官方文档
Introducing Single Pod Access Mode for PersistentVolumes
总结
- PersistentVolume 简称为 PV,是 Kubernetes 对存储设备的抽象,由系统管理员维护,需要描述清楚存储设备的类型、访问模式、容量等信息。
- PersistentVolumeClaim 简称为 PVC,代表 Pod 向系统申请存储资源,它声明对存储的要求,Kubernetes 会查找最合适的 PV 然后绑定。
- StorageClass 抽象特定类型的存储系统,归类分组 PV 对象,用来简化 PV/PVC 的绑定过程。
- HostPath 是最简单的一种 PV,数据存储在节点本地,速度快但不能跟随 Pod 迁移。
更多推荐
所有评论(0)