introduction

  目前接触了几种k8s本地持久卷: hostpath, local-storage, directpv, open-local等. 梳理下这几种本地存储的特性和适用场景啥的. 本文持续补充中…

k8s原生本地持久卷

hostpath

  hostPath卷从主机节点的文件系统把文件或目录挂载到Pod中。

  几种用hostpath的场景:

  1. 运行一个需要访问docker内部的容器, 把/var/lib/docker作为hostpath挂载给pod;
  2. 容器中运行cAdivisor(容器监控), 把/sys作为hostpath挂给pod用;
  3. 允许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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: v1
kind: Pod
metadata:
name: test-webserver
spec:
containers:
- name: test-webserver
image: registry.k8s.io/test-webserver:latest
volumeMounts:
- mountPath: /var/local/aaa
name: mydir
- mountPath: /var/local/aaa/1.txt
name: myfile
volumes:
- name: mydir
hostPath:
# Ensure the file directory is created.
path: /var/local/aaa
type: DirectoryOrCreate
- name: myfile
hostPath:
path: /var/local/aaa/1.txt
type: FileOrCreate

&esmp; <注意: FileOrCreate不会创建文件的父目录, 如果父目录不存在, 则会失败>

local-storage

local-storage表示挂载的本地存储设备,如磁盘、分区或目录.

local-storage只能作为静态创建的PersistentVolume使用. 不支持动态发放. 即使用时, 需要先静态的创建好pv, 再提供给pod使用.

  与hostPath卷相比,local-storage以持久和可移植的方式使用,无需手动将pod调度到节点。通过查看PersistentVolume上的节点亲和性来了解卷的节点约束。

local卷取决于底层node的可用性, 并不适合所有应用. 如果节点状态变得不健康, 本地卷无法被pod访问. 使用这个卷的pod不能正常运行. 使用本地卷的应用必须可以容忍这种可用性的降低. 以及潜在的数据丢失, 这取决于底层磁盘的持久性特征.

  以下是一个使用local-storage的pv示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 100Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /mnt/disks/ssd1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- example-node

  使用本地卷时, 必须设置spec.nodeAffinity. Kubernetes调度器使用spec.nodeAffinity将pod调度到正确的节点.

  PV的 volumeMode 可以被设置为 “Block” (而非默认值: “Filesystem”) , 以以公开本地卷作为原始块设备.

  当使用本地卷时,建议创建一个StorageClass, 将volumeBindingMode设置为WaitForFirstConsumer. 延迟卷绑定确保pvc绑定决策也将使用Pod可能具有的任何其他节点约束进行评估,例如节点资源需求、节点选择器、Pod亲和性和Pod反亲和性。

local本地卷的sc如下:

1
2
3
4
5
6
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

  如果不使用外部静态提供程序来管理卷的生命周期, 则需要用户手动清理和删除本地PV. 我们使用时, 没有使用外部程序进行生命周期管理, 如果本地卷对应的本地磁盘被写满, 需要手动删除数据.

hostpathlocal 都是属于静态的卷, 不支持扩容, 当宿主机的磁盘写满时, 需要手动的扩充磁盘. 并且指定的持久卷无法限制使用容量, 例如一个磁盘上创建了五个卷, 这五个卷抢占磁盘资源, 直至磁盘被占满. 虽然使用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
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
┌─────────────────────────────────────────────────────────────────────────────┐
│ Master │
│ ┌───┬───┐ ┌────────────────┐ │
│ │Pod│PVC│ │ API-Server │ │
│ └───┴┬──┘ └────────────────┘ │
│ │ bound ▲ │
│ ▼ │ watch │
│ ┌────┐ ┌───────┴────────┐ │
│ │ PV │ │ Kube-Scheduler │ │
│ └────┘ ┌─┴────────────────┴─┐ │
│ ▲ │ open-local │ │
│ │ │ scheduler-extender │ │
│ │ ┌────►└────────────────────┘◄───┐ │
│ ┌──────────────────┐ │ │ ▲ │ │
│ │ NodeLocalStorage │ │create│ │ │ callback │
│ │ InitConfig │ ┌┴──────┴─────┐ ┌──────┴───────┐ ┌────┴────────┐ │
│ └──────────────────┘ │ External │ │ External │ │ External │ │
│ ▲ │ Provisioner │ │ Resizer │ │ Snapshotter │ │
│ │ watch ├─────────────┤ ├──────────────┤ ├─────────────┤ │
│ ┌─────┴──────┐ ├─────────────┴──┴──────────────┴──┴─────────────┤GRPC│
│ │ open-local │ │ open-local │ │
│ │ controller │ │ CSI ControllerServer │ │
│ └─────┬──────┘ └────────────────────────────────────────────────┘ │
│ │ create │
└──────────┼──────────────────────────────────────────────────────────────────┘

┌──────────┼──────────────────────────────────────────────────────────────────┐
│ Worker │ │
│ │ │
│ ▼ ┌───────────┐ │
│ ┌──────────────────┐ │ Kubelet │ │
│ │ NodeLocalStorage │ └─────┬─────┘ │
│ └──────────────────┘ │ GRPC Shared Disks │
│ ▲ ▼ ┌───┐ ┌───┐ │
│ │ ┌────────────────┐ │sdb│ │sdc│ │
│ │ │ open-local │ create volume └───┘ └───┘ │
│ │ │ CSI NodeServer ├───────────────► VolumeGroup │
│ │ └────────────────┘ │
│ │ │
│ │ Exclusive Disks │
│ │ ┌─────────────┐ ┌───┐ │
│ │ update │ open-local │ init device │sdd│ │
│ └────────────────┤ agent ├────────────────► └───┘ │
│ └─────────────┘ Block Device │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

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)