2.5 存储方案
计算资源三大要素是CPU、内存和存储,在规划应用资源需求时,这三种资源需求是首先要考虑的。Kubernetes 支持多种存储插件,允许Pod 使用计算节点磁盘,它的存储方案和节点管理密不可分。存储管理相对于CPU、内存管理更复杂,因为存储包含多种类型,不同存储空间的使用方式不同,组合条件较多,因而可能引发的问题也较多。
Kubernetes 支持多种存储类型,可按照数据持久化方式分为临时存储(如emptyDir)、半持久化存储(如hostPath)和持久化存储(包含网络存储和本地存储)。
2.5.1 存储卷插件管理
Kubernetes 支持以插件的形式来实现对不同存储的支持和扩展,这些扩展基于如下三种方式:
● in-tree 插件。
● FlexVolume out-of-tree 存储插件。
● CSI out-of-tree 存储插件。
2.5.1.1 in-tree 插件
在Kubernetes 早期版本中,接口抽象仍不完备,所有存储支持均通过in-tree 插件来实现。in-tree 插件是指插件代码编译在Kubernetes 控制器和kubelet 中,归属于Kubernetes的核心代码库。Kubernetes 支持的存储插件不断增加,in-tree 插件与其之间的强依赖关系导致系统维护成本过高,且插件代码与Kubernetes 的核心代码共享同一进程,因此当插件代码异常崩溃时,会导致Kubernetes 核心模块不可用。
Kubernetes 社区已不再接受新的in-tree 存储插件,新的存储必须通过out-of-tree 插件进行支持。
out-of-tree 存储插件拥有独立的代码版仓库,并且以独立进程运行。
目前Kubernetes 提供了FlexVolume 和容器存储接口(Container Storage Interface,即CSI)两种out-of-tree 存储插件。在1.13 版本之后的版本,CSI 进入GA(General Available)阶段,现已成为默认的推荐方式。FlexVolume 还会继续维护,但新的特性只会加在CSI上。对于已经存在的in-tree 的存储插件,Kubernetes 社区的处理方式是通过CSI 将插件全部移除。
2.5.1.2 FlexVolume out-of-tree 存储插件
FlexVolume 是指Kubernetes 通过调用计算节点的本地可执行文件与存储插件进行交互。不同的存储驱动对应不同的可执行文件,该可执行文件可以实现FlexVolume 存储插件需要的attach/detach、mount/umount 等操作。部署与Kubernetes 核心组件分离的可执行文件,使得存储驱动有独立的升级和部署周期,解决了in-tree 存储插件的强耦合问题。但是in-tree 存储插件对Kubernetes 核心组件的强依赖问题依然存在:
● FlexVolume 插件需要宿主机用root 权限来安装插件驱动。
● FlexVolume 存储驱动需要宿主机安装attach、mount 等工具,也需要具有root 访问权限。
2.5.1.3 CSI out-of-tree 存储插件
Kubernetes 社区引入了一个in-tree 的CSI 存储插件,用于用户和外挂的CSI 存储驱动的交互。相对于FlexVolume 基于可执行文件的方式,CSI 通过RPC 的方式与存储驱动进行交互。在设计CSI 的时候,Kubernetes 对CSI 存储驱动的打包和部署要求很少,主要定义 了 Kubernetes 的两 个相关 模块: kube-controller-manager 和 kubelet 。kube-controller-manager 模块用于感知CSI 驱动存在,kubelet 模块用于与CSI 驱动进行交互。具体来说,Kubernetes 会针对CSI 规定以下内容:
1.kubelet 和CSI 驱动的交互
● kubelet 通过UNIX Domain Socket 向CSI 驱动发起CSI 调用(如NodeStageVolume、NodePublishVolume 等),再发起mount 卷和umount 卷。
● kubelet 通过插件注册机制发现CSI 驱动及用于和CSI 驱动交互的UNIX Domain Socket。
● 所有部署在Kubernetes 集群中的CSI 驱动都要通过kubelet 的插件注册机制来注册自己。
2.Kubernetes 主控模块(主要是kube-controller-manager)和CSI 驱动的交互
● Kubernetes 的主控模块通过UNIX Domain Socket(而不是CSI 驱动)或者其他方式进行直接交互。
● Kubernetes 的主控模块只与Kubernetes 相关的API 进行交互。
● CSI 驱动若有依赖于Kubernetes API 的操作,例如卷的创建、卷的attach、卷的快照等,需要在CSI 驱动中通过Kubernetes 的API 来触发相关的CSI 操作。
基于这样的要求,CSI 驱动的开发者可以很方便地根据实际需求进行驱动的开发和部署。除了实现CSI 定义的必备功能,用户还可以在CSI 驱动中添加其他功能,例如接口调用次数统计、磁盘健康状态使用率检测等。这些功能在in-tree 插件时期是很难添加的,但是通过CSI 驱动可以很方便地做到。
CSI 驱动一般包含 external-attacher、external-provisioner、external-resizer、external- snapshotter、node-driver-register、CSI driver 等模块,可以根据实际的存储类型和需求进行不同方式的部署。例如,对于有些网络存储,external-provisioner 和external-attacher 整个集群只取其一,但是对于与节点相关的本地存储,external-provisioner 就需要以daemonset的形式进行部署。图2-22 是一个典型的CSI 驱动部署的例子。
图2-22 CSI 驱动部署
2.5.2 存储的分类
2.5.2.1 临时存储
常见的临时存储主要有emptyDir 卷。
emptyDir 是一种经常被用户使用的卷类型,顾名思义,“卷” 最初是空的。当Pod 从节点上删除时,emptyDir 卷中的数据也会被永久删除。但当Pod 的容器因为某些原因退出再重启时,emptyDir 卷内的数据并不会丢失。
默认情况下,emptyDir 卷存储在支持该节点所使用的存储介质上,可以是本地磁盘或网络存储。emptyDir 也可以通过将 emptyDir.medium 字段设置为 “Memory” 来通知Kubernetes 为容器安装tmpfs,此时数据被存储在内存中,速度相对于本地存储和网络存储快很多。但是在节点重启的时候,内存数据会被清除;而如果存在磁盘上,则重启后数据依然存在。另外,使用tmpfs 的内存也会计入容器的使用内存总量中,受系统的CGroup限制。
emptyDir 设计的初衷主要是给应用充当缓存空间,或者存储中间数据,用于快速恢复。然而,这并不是说满足以上需求的用户都被推荐使用emptyDir,我们要根据用户业务的实际特点来判断是否使用emptyDir。因为emptyDir 的空间位于系统根盘,被所有容器共享,所以在磁盘的使用率较高时会触发Pod 的eviction 操作,从而影响业务的稳定。
2.5.2.2 半持久化存储
常见的半持久化存储主要是hostPath 卷。hostPath 卷能将主机节点文件系统上的文件或目录挂载到指定的Pod 中。对普通用户而言一般不需要这样的卷,但是对很多需要获取节点系统信息的Pod 而言,却是非常必要的。
hostPath 的用法举例如下:
● 某个Pod 需要获取节点上所有Pod 的log,可以通过hostPath 访问所有Pod 的stdout输出存储目录,例如/var/log/pods 路径。
● 某个Pod 需要统计系统相关的信息,可以通过hostPath 访问系统的/proc 目录。
使用hostPath 的时候,除设置必需的path 属性外,用户还可以有选择性地为 hostPath 卷指定类型,支持类型包含目录、字符设备、块设备等。
另外,使用hostPath 卷需要注意如下几点:
● 使用同一个目录的Pod 可能会由于调度到不同的节点,导致目录中的内容有所不同。
● Kubernetes 在调度时无法顾及由 hostPath 使用的资源。
● Pod 被删除后,如果没有特别处理,那么hostPath 上写的数据会遗留到节点上,占用磁盘空间。
2.5.2.3 持久化存储
支持持久化存储是所有分布式系统所必备的特性。针对持久化存储,Kubernetes 引入了 StorageClass、Volume、PVC(Persistent Volume Claim)、PV(Persitent Volume) 的概念,将存储独立于Pod 的生命周期来进行管理。
StorageClass 用于指示存储的类型,不同的存储类型可以通过不同的StorageClass 来为用户提供服务。StorageClass 主要包含存储插件provisioner、卷的创建和mount 参数等字段。
PVC 由用户创建,代表用户对存储需求的声明,主要包含需要的存储大小、存储卷的访问模式、stroageclass 等类型,其中存储卷的访问模式必须与存储的类型一致,包含的三种类型如表2-17 所示。
表2-17 存储卷的访问模式
PV 由集群管理员提前创建,或者根据PVC 的申请需求动态地创建,它代表系统后端的真实的存储空间,可以称之为卷空间。
用户通过创建PVC 来申请存储。控制器通过PVC 的StorageClass 和请求的大小声明来存储后端创建卷,进而创建PV,Pod 通过指定PVC 来引用存储。Pod、PVC、PV、StorageClass 等卷之间的相互关系如图2-23 所示。
图2-23 存储对象的关系
Kuberntes 目前支持的持久化存储包含各种主流的块存储和文件存储, 譬如awsElasticBlockStore、azureDisk、cinder、NFS、cephfs、iscsi 等,在大类上可以分为网络存储和本地存储两种类型。
1.网络存储
通过网络来访问的存储都可以称为网络存储。目前有多种多样的存储类型,分别对应不同的使用场景。
在Kuberntes 大火之前,很多公司和云服务厂商都基于OpenStack 构建了云服务。因此,为基于OpenStack 搭建的Kubenetes 集群提供Cinder 的块存储,成为一个自然而然的选择。下面以Cinder 存储为例来展现网络存储的工作流程,如图2-24 所示。
图2-24 用户使用Cinder 存储的工作流程
下面对图2-24 中的10 个部分分别进行介绍:
(1)创建PVC:用户创建了一个使用Cinder 存储的PVC。
(2)创建卷:当PV Controller 监听到该PVC 的创建时,调用Cinder 的存储插件,从Cinder 存储后端申请卷。
(3)创建PV:pv controller 创建PV,PV 中包含如下主要字段:
● spec.accessMode: 卷的访问模式。
● spec.capactiy.storage: 卷的大小。
● spec.cinder.volumeID : Cinder 卷的ID。
Spec 定义代码如下:
(4)绑定PVC/PV:在PV 成功创建后,PV Controller 会将PVC 和PV 进行绑定,PVC和PV 的状态都设置为Bound。
(5)创建Pod:用户创建Pod,并在其中申明使用这个PVC。
(6)调度Pod:kube-scheduler 将Pod 调度到某个节点。
(7)attach 卷:Attach-Detach Controller 检测到Pod 已经被调度到某个节点,遂将该Pod 使用的卷attach 到对应的节点。
(8)更新节点卷的attach 信息:Attach-Detach Controller 将卷的相关信息添加到节点对象的node.status.volumesAttached 中。
(9)更新节点卷的InUse 信息:当kubelet 监听到有Pod 已经调度到本机后,在汇报节点状态的时候,将Pod 使用的卷信息添加到节点对象的node.status.volumesInUse 中。
(10)挂载卷,启动容器:kubelet 从节点对象上获知该卷已经执行了attach,在节点上找到该卷的设备信息,开始对盘做mount 操作。
Pod 删除流程为上面流程的逆过程,具体如下:
(1)Pod 接收delete 请求后,kubelet 会中止容器,对磁盘进行umount 操作,并对Pod执行删除(将grace-period 设置为0)操作,这样Pod 就从API Server 中被删除了。
(2)kubelet 在汇报节点状态的时候,将该卷的相关信息从节点对象的 node.status.volumesInUse 中删除。
(3)在 Pod 被删除后,Attach-Detach Controller 会判断卷是否已经从节点对象的node.status.volumesInUse 中删除,然后将卷从节点上进行detach。
如图2-25 所示,我们通过调用OpenStack Nova 的接口对卷执行attach 操作,并通过Nova 和Cinder 将卷attach 到对应节点中。同时,需要修改Nova DB 和Cinder DB,以设置相应的节点状态和Cinder 卷状态。
图2-25 Cinder 卷的attach 流程图
在对卷进行attach 和detach 的过程中,涉及OpenStack 的多个模块,有Nova、Cinder、Qemu 等。如果频繁地对卷做attach 和detach 操作,可能会产生问题。比如Nova 的DB和Cinder 的DB 不一致,nova show instance 命令可以看到有卷attach 到该节点上,但是通过cinder show volume 可能看到Cinder 卷的状态仍为available。
要解决这样的问题,可以将Cinder 卷对接的Ceph RBD 或iSCSI 卷通过PV 暴露出来,这样在节点上可以直接与存储后端配合进行卷的 attach/detach 操作,从而达到减少其他OpenStack 模块参与的目的。 通过在集群上部署 Kubenetes 社区开源的standalone-cinder-provisioner 的Pod 来替代PV Controller 进行PV 的创建和删除操作,从而将cinder 卷的Ceph RBD 或iSCSI 信息暴露出来,如图2-26 所示。
图2-26 用户使用Cinder 存储工作流程
下面对图2-26 中的10 个部分分别进行介绍。
(1)创建PVC:用户创建了一个使用cinder 存储的PVC。
(2)创建卷:当Standalone-cinder-provisioner 监听到有PVC 创建时,从Cinder 申请卷。
(3)创建PV:如下面的PV spec 定义代码所示,它除了带有accessModes、capacity等信息,还带有keyring 信息及存储后端ceph 的monitors。
(4)绑定PVC 和PV:PV Controller 将PVC 和PV 做绑定,修改PVC 和PV 状态。
(5)创建Pod:用户创建Pod,其中申明使用这个PVC。
(6)调度Pod:Pod 被调度到某个节点。
(7)更新节点卷的attach 信息:Attach-detach controller 监听到Pod 已经被调度到某个节点, 开始执行卷的 attach 操作, 并将该卷信息添加到节点对象的node.status.volumesAttached 里。该attach 操作是个空操作,仅用于表明该卷已经用于某个Pod 节点。如果有其他Pod 再次使用该PVC,但是调度到不同的节点上,那么该Pod 就会因报错而无法启动。以上是通过Attach-Detach Controller 的方式,来保证RBD 的卷的RWO属性,即只允许该卷在一个节点上被mount。
(8)更新节点卷的InUse 信息:当kubelet 监听到有Pod 被调度到节点上时,就会汇报节点状态,并将Pod 使用的卷信息添加到节点对象的node.status.volumesInuse 中。
(9)attach 卷:kubelet 检查在node.status.volumesAttached 中是否存在卷信息,并开始对磁盘做mount 操作。在mount 之前,对该卷进行attach 操作。
(10)mount 卷并启动容器:对磁盘执行mount 操作。
删除Pod 为上面流程的逆过程,具体过程如下:
(1)在Pod 接收delete 请求后,kubelet 会中止容器,并对磁盘进行umount 操作。在umount 操作之前,将该卷进行detach,并对Pod 执行删除(将grace-period 设置为0)操作,Pod 就从API Server 中被删除了。kubelet 在汇报节点状态时,将该卷的相关信息从节点对象的node.status.volumesInUse 中删除。
(2)Pod 被删除后,Attach-Detach Controller 将卷从节点上进行detach。
使用访问模式为RWO 的网络存储时通常会碰到一个问题,这个问题可以通过一个例子来说明:用户利用deployment 来部署Pod,Pod 使用某个accessMode 为RWO 的PVC。Pod 运行后,节点出现故障而进入NotReady 状态。经过指定时间后,节点控制器对该Pod进行驱逐,但由于节点组件无法正常工作,所以 Pod 无法执行删除操作,Pod 进入terminationg 状态,PVC 对应的卷也不会从出问题的节点上执行detach 命令。当deployment controller 发现Pod 处在terminating 状态时会创建新的Pod,但是由于使用的卷没有detach,所以新的 Pod 就不能使用该 PVC。这种情况需要人工介入进行处理,或通过专门的controller 对NotReady 的节点进行处理,例如将节点移出集群,并从对应的cloud provider中删除该节点。
2.本地存储
本地存储并没有如网络存储一般的可靠性,而是需要使用本地存储的应用做数据备份,但是具有高性能和低成本的优势,因此是一个很通用的需求。在Kubernetes 支持的存储类型中,本地存储得到支持的时间比较晚。与网络存储相比,本地存储有一个很大的不同:网络存储可以attach 到不同的节点上,但是本地存储与节点是一一绑定的,即如果某个本地存储的PVC 和PV 绑定,那么Pod 就必须调度到该PV 所在的节点。因此,不能在基于网络存储需求设计的存储实现流程上,通过简单地增加一个存储插件来支持本地存储。
本地存储的实现:每个本地存储的PV 都需要对应节点上的某块物理磁盘空间。这块空间可以是独立的磁盘,也可以是基于磁盘的分区,还可以是基于LVM 创建出来的逻辑卷。由于本地存储PV 是基于节点上真实的物理磁盘的,所以当磁盘损坏或丢失时会影响本地存储的使用。因此,磁盘的监控很重要,要有及时汇报的机制,即磁盘出现问题时及时通知集群管理员和使用该本地存储的用户。
社区支持本地存储的过程经历了三个阶段。
第一阶段,集群的节点上需要部署一个社区开源项目External Storage 提供的、基于本地存储的daemonSet local-volume-provisioner。该daemonSet 会根据configmap 的配置,在特定路径下查找是否存在需要提供本地存储的PV 的磁盘或卷,并创建PV。当用户创建一个PVC 后,pv controller 会将该PVC 与找到的合适的PV 进行绑定。PVC 一旦绑定,使用该PVC 的Pod 就必须调度到PV 所对应的节点上。
这样的实现方式存在明显的不足:PVC 和PV 绑定的逻辑比较简单,只是考虑了卷的大小和卷模式是否满足需求。但是Pod 有CPU、内存、nodeSelector、亲和性和反亲和性的需求,因此一旦节点被选择的PV 固定,Pod 就很有可能永远都无法进行调度。
第二阶段是PVC 对PV 的选择,它通过kube-scheduler 的某个predicate 进行考量。当Pod 选择了一个满足所有需求的节点后,通过kube-scheduler 和PV Controller 共同完成PVC的绑定。在实现过程中,需要在StorageClass 上增加字段waitForFirstConsumer,用来指示除PV Controller 外是否还需要其他模块提前对PVC/PV 进行处理,这里的其他模块目前主要指kube-scheduler。
在该阶段,使用本地存储的具体的工作流程如图2-27 所示。
图2-27 使用本地存储的工作流程
下面对图2-27 中的9 个部分分别进行介绍。
(1)创建PV:通过Local-volume-provisioner daemonset 创建本地存储的PV。
(2)创建PVC:用户创建PVC,由于它处于pending 状态,所以kube-controller-manager并不会对该PVC 做任何操作。
(3)创建Pod:用户创建Pod。
(4)Pod 挑选节点:kube-scheduler 开始调度Pod,通过PVC 的resources.request.storage和volumeMode 选择满足条件的PV,并且为Pod 选择一个合适的节点。
(5)更新PV:kube-scheduler 将PV 的pv.Spec.claimRef 设置为对应的PVC,并且设置annotation pv.kubernetes.io/boound-by-controller 的值为 “yes”。
(6)PVC 和PV 绑定:pv_controller 同步PVC 和PV 的状态,并将PVC 和PV 进行绑定。
(7)监听PVC 对象:kube-scheduler 等待PVC 的状态变成Bound。
(8)Pod 调度到节点:如果PVC 的状态变为Bound 则说明调度成功,而如果PVC 一直处于pending 状态,超时后会再次进行调度。
(9)mount 卷并启动容器:kubelet 监听到有Pod 已经调度到节点上,对本地存储进行mount 操作,并启动容器。
Pod 被创建后,用户可能会因为升级等原因将Pod 删除再创建,而此时由于PVC 已经是Bound 状态,所以重新创建的Pod 只能被调度到本地存储PV 所指定的节点上。这样可能会引发如下问题:
(1)如果PV 所指向的节点出现问题,处在NotReady 状态,导致Pod 无法调度该节点,那么Pod 就会无节点可调度,处于pending 状态。
(2)如果在Pod 重建之前,有其他Pod 也调度到PV 指向的节点上,导致节点的CPU或者内存不足,那么新建的Pod 也无节点可以调度了。
为避免出现以上问题,可以设定Pod 优先级,通过抢占资源来保证Pod 可以被调度到需要的节点。
另外,PVC 和PV 的绑定需要kube-scheduler 的参与。Pod 如果直接指定了nodeName,就不会被kube-scheduler 调度,PVC 也不会和PV 绑定,因此Pod 的容器就不会被kubelet运行起来。
本地存储的PV 需要在集群中提前部署,部署成功后PV 对应的分区大小及其使用模式(Filesystem 或者Block)就会固定下来,这会导致使用上不够灵活。而且用户如果不需要PV 具有的磁盘空间,那么还会造成空间的浪费。
第三阶段,支持本地存储PV 的动态创建。
本地存储PV 的动态创建是一个很重要的需求,但是目前Kubernetes 社区并没有一套针对本地存储PV 动态创建的成熟解决方案,主要原因是CSI 驱动需要汇报节点上相关存储的资源信息,以便用于调度。但是机器的厂家不同,其汇报方式也不同。例如,有的厂家的机器节点上具有nvme、SSD、HDD 等多种存储介质,希望将这些存储介质分别进行汇报。这种需求有别于其他存储类型的CSI 驱动对接口的需求,因此如何汇报节点的存储信息,以及如何让节点的存储信息应用于调度,目前并没有形成统一的意见。集群管理员可以基于节点存储的实际情况对开源CSI 驱动和调度进行一些代码修改,再进行部署和使用。
在这种模式下,使用本地存储的具体的工作流程如图2-28 所示。
图2-28 使用本地存储动态分配卷的工作流程
下面对图2-28 中的11 个部分分别进行介绍。
(1)创建PVC:用户创建PVC,PVC 处于pending 状态。
(2)创建Pod:用户创建Pod。
(3)Pod 选择节点:kube-scheduler 开始调度Pod,通过PVC 的pvc.spec.resources.request.storage 等选择满足条件的节点。
(4)更新PVC:选择节点后,kube-scheduler 会给PVC 添加包含节点信息的annotation: volume.kubernetes.io/selected-node: <节点名字>。
(5)创建卷:运行在节点上的容器external-provisioner 监听到PVC 带有该节点相关的annotation,向相应的CSI 驱动申请分配卷。
(6)创建PV:PVC 申请到所需的存储空间后,external-provisioner 创建PV,该PV的pv.Spec.claimRef 设置为对应的PVC。
(7)PVC 和PV 绑定:kube-controller-manager 将PVC 和PV 进行绑定,状态修改为Bound。
(8)监听PVC 状态:kube-scheduler 等待PVC 变成Bound 状态。
(9)Pod 调度到节点:当PVC 的状态为Bound 时,Pod 才算真正调度成功了。如果PVC 一直处于Pending 状态,超时后会再次进行调度。
(10)Mount 卷:kubelet 监听到有Pod 已经调度到节点上,对本地存储进行mount操作。
(11)启动容器:启动容器。
本地存储的动态分配方式提高了分配空间的灵活性,但是也带来一个问题:如果将磁盘空间作为一个存储池(例如LVM)来动态分配,那么在分配出来的逻辑卷空间的使用上,可能会受到其他逻辑卷的I/O 干扰,因为底层的物理卷可能是同一个。而第二阶段的静态部署方式中,如果PV 后端的磁盘空间是一块独立的物理磁盘,则I/O 就不会受到干扰。
通过以上三个阶段的发展,目前Kubernetes 已经基本形成对本地存储的支持。但是由于本地存储和节点的强绑定性,还是会产生一些问题,这些问题需要删除PVC 才可以解决。例如,运行 StatefulSet Pod 所在的节点不正常工作时,就需要删除 PVC,以恢复StatefulSet 的Pod。
因此,使用本地存储的业务,需要意识到本地存储服务的优势和劣势,针对存储数据进行多份备份,以便在出现Kubernetes 不能自动解决的问题的时候,能够自动化地(比如采取删除PVC 等方式)让业务Pod 重新部署。
本地存储是将节点上除根分区外的其他磁盘分区提供给用户使用,这取决于使用本地存储的方式,可以将物理磁盘以分区或者LVM 逻辑卷的方式给集群提供本地存储的能力。集群的机器类型不同,物理磁盘数量、磁盘介质类型、磁盘容量等也会不同。如何实现高效灵活的管理,是一个很重要的问题。
对于本地存储的实践,笔者推荐如下的管理和部署方式:
● 不同介质类型的磁盘, 需要设置不同的 StorageClass, 以便让用户做区分。StorageClass 需要设置磁盘介质的类型,以便用户了解该类存储的属性。
● 在本地存储的PV 静态部署模式下,每个物理磁盘都尽量只创建一个PV,而不是划分为多个分区来提供多个本地存储PV,避免在使用时分区之间的I/O 干扰。
● 本地存储需要配合磁盘检测来使用。当集群部署规模化后,每个集群的本地存储PV 可能会超过几万个,如磁盘损坏将是频发事件。此时,需要在检测到磁盘损坏、丢盘等问题后,对节点的磁盘和相应的本地存储PV 进行特定的处理,例如触发告警、自动cordon 节点、自动通知用户等。
● 对于提供本地存储节点的磁盘管理,需要做到灵活管理和自动化。节点磁盘的信息可以归一、集中化管理。在local-volume-provisioner 中增加部署逻辑,当容器运行起来时,拉取该节点需要提供本地存储的磁盘信息,例如磁盘的设备路径,以Filesystem 或Block 的模式提供本地存储,或者是否需要加入某个LVM 的虚拟组(VG)等。local-volume-provisioner 根据获取的磁盘信息对磁盘进行格式化,或者加入某个VG,从而形成对本地存储支持的自动化闭环。