k8s本地持久化存储
introduction
目前接触了几种k8s本地持久卷: hostpath, local-storage, directpv, open-local等. 梳理下这几种本地存储的特性和适用场景啥的. 本文持续补充中…
k8s原生本地持久卷
hostpath
hostPath卷从主机节点的文件系统把文件或目录挂载到Pod中。
几种用hostpath的场景:
- 运行一个需要访问docker内部的容器, 把/var/lib/docker作为hostpath挂载给pod;
- 容器中运行cAdivisor(容器监控), 把/sys作为hostpath挂给pod用;
- 允许Pod指定的hostPath是否应该在Pod运行之前存在,是否应该创建它,以及它应该作为什么存在.
k8s官方文档提供的type值如下:
Value | Behavior |
---|---|
Empty string (default) is for backward compatibility, which means that no checks will be performed before mounting the hostPath volume. | |
DirectoryOrCreate |
If nothing exists at the given path, an empty directory will be created there as needed with permission set to 0755, having the same group and ownership with Kubelet. |
Directory |
A directory must exist at the given path |
FileOrCreate |
If nothing exists at the given path, an empty file will be created there as needed with permission set to 0644, having the same group and ownership with Kubelet. |
File |
A file must exist at the given path |
Socket |
A UNIX socket must exist at the given path |
CharDevice |
A character device must exist at the given path |
BlockDevice |
A block device must exist at the given path |
使用hostpath的卷, 需要注意, 原因如下:
- HostPaths可以公开特权系统凭据(例如Kubelet)或特权api(例如容器运行时套接字),这可以用于容器逃逸或攻击集群的其他部分。
- 由于每个节点上的文件不同,具有相同配置的pod(例如从PodTemplate创建的pod)在不同节点上的行为可能不同.
- 在宿主机上创建的文件或目录只能由根用户写。要么需要在特权容器中以root身份运行进程,要么需要修改主机上的文件权限,以便能够写入hostPath卷. 使用特权容器也会带来一定的安全隐患.
使用FileOrCreate
配置的hostpath示例:
1 | apiVersion: v1 |
&esmp; <注意: FileOrCreate不会创建文件的父目录, 如果父目录不存在, 则会失败>
local-storage
local-storage
表示挂载的本地存储设备,如磁盘、分区或目录.
local-storage
只能作为静态创建的PersistentVolume使用. 不支持动态发放. 即使用时, 需要先静态的创建好pv, 再提供给pod使用.
与hostPath卷相比,local-storage
以持久和可移植的方式使用,无需手动将pod调度到节点。通过查看PersistentVolume上的节点亲和性来了解卷的节点约束。
local
卷取决于底层node的可用性, 并不适合所有应用. 如果节点状态变得不健康, 本地卷无法被pod访问. 使用这个卷的pod不能正常运行. 使用本地卷的应用必须可以容忍这种可用性的降低. 以及潜在的数据丢失, 这取决于底层磁盘的持久性特征.
以下是一个使用local-storage
的pv示例:
1 | apiVersion: v1 |
使用本地卷时, 必须设置spec.nodeAffinity
. Kubernetes调度器使用spec.nodeAffinity
将pod调度到正确的节点.
PV的 volumeMode
可以被设置为 “Block” (而非默认值: “Filesystem”) , 以以公开本地卷作为原始块设备.
当使用本地卷时,建议创建一个StorageClass
, 将volumeBindingMode
设置为WaitForFirstConsumer
. 延迟卷绑定确保pvc绑定决策也将使用Pod可能具有的任何其他节点约束进行评估,例如节点资源需求、节点选择器、Pod亲和性和Pod反亲和性。
local
本地卷的sc如下:
1 | apiVersion: storage.k8s.io/v1 |
如果不使用外部静态提供程序来管理卷的生命周期, 则需要用户手动清理和删除本地PV. 我们使用时, 没有使用外部程序进行生命周期管理, 如果本地卷对应的本地磁盘被写满, 需要手动删除数据.
hostpath
和 local
都是属于静态的卷, 不支持扩容, 当宿主机的磁盘写满时, 需要手动的扩充磁盘. 并且指定的持久卷无法限制使用容量, 例如一个磁盘上创建了五个卷, 这五个卷抢占磁盘资源, 直至磁盘被占满. 虽然使用local
卷时, 会设置capacity
, 但实际上这个字段不会生效.
个人认为local
卷与hostpath
相比, 只是多提供了一个节点亲和的特性, 不需要手动调度pod到指定节点, 其余没有太大区别. k8s原生的本地存储都支持当PV被删除, 当再指定同样的路径时, pod仍然可以读取之前的数据, 这是因为数据是直接写在磁盘中的.
external csi
open-local
open-local 是阿里巴巴开源的一款本地存储管理系统。通过 open-local,在 Kubernetes 上使用本地存储就像使用集中式存储一样简单。目前 open-local 支持以下存储特性:本地存储池管理、存储卷动态分配、存储调度算法扩展、存储卷扩容、存储卷快照、存储卷监控、存储卷 IO 限流、原生块设备及临时卷。
use case
- 应用本身已支持多副本高可用,希望使用本地盘以提高存储资源利用率,提高数据读写性能,如 HBase、MinIO 等。
- 应用期望数据卷具备容量隔离能力,避免出现诸如日志打满系统盘的情况。
- 应用需要大量本地存储并依赖节点保持,如 etcd、zookeeper、Elasticsearch 等。
- 集群本地磁盘数量众多,希望通过调度器实现有状态应用的自动化部署。
- 通过存储快照能力为数据库类应用备份瞬时数据等。
架构
1 | ┌─────────────────────────────────────────────────────────────────────────────┐ |
open-local 包含四个组件:
1)scheduler-extender:作为 Kube-Scheduler 的扩展组件,通过 Extender 方式实现,新增本地存储调度算法。
2)CSI 插件:按照 CSI(Container Storage Interface) 标准实现本地磁盘管理能力,包含创建/删除/扩容存储卷、创建/删除快照、暴露存储卷 metrics 等能力。
3)agent:运行在集群中的每个节点,根据配置清单初始化存储设备,并通过上报集群中本地存储设备信息以供 Scheduler-Extender 决策调度。
4)controller:获取集群存储初始化配置,并向运行在各个节点的 Agent 下发详细的资源配置清单。
open-local 包含两个 CRD
1)NodeLocalStorage:open-local 通过 NodeLocalStorage 资源上报每个节点上的存储设备信息,该资源由 controller 创建,由每个节点的 agent 组件更新其 status。该 CRD 属于全局范围的资源。
2)NodeLocalStorageInitConfig:open-local controller 可通过 NodeLocalStorageInitConfig 资源创建每个 NodeLocalStorage 资源。NodeLocalStorageInitConfig 资源中包含全局默认节点配置和特定节点配置,若节点的 node label 满足表达式则使用特定节点配置,否则使用默认配置。
依赖
open-local部署依赖宿主机的lvm
, 当动态创建一个持久卷时, open-local会在指定的VG(volume group)中创建一个逻辑卷, 该逻辑卷包含指定的size, 并将该逻辑卷挂给pod使用. PV扩容同理, open-local会使用lvextend
对逻辑卷扩容.
todo
TODO
其余待补充
Refer to
Storage | Kubernetes
如何通过 open-local 玩转容器本地存储? | 龙蜥技术-阿里云开发者社区 (aliyun.com)