本文已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已不正确。

Kubernetes 的本地持久卷进入 Beta 版

Kubernetes 1.10 中的 本地持久卷 beta 功能使在您的 StatefulSet 中利用本地磁盘成为可能。您可以将直接连接的本地磁盘指定为 PersistentVolumes,并在 StatefulSets 中使用它们,使用之前仅支持远程卷类型的相同的 PersistentVolumeClaim 对象。

持久存储对于运行有状态的应用程序非常重要,Kubernetes 通过 StatefulSets、PersistentVolumeClaims 和 PersistentVolumes 支持这些工作负载。这些原语很好地支持了远程卷类型,其中可以从集群中的任何节点访问卷,但不支持本地卷,其中只能从特定节点访问卷。随着在 Kubernetes 中运行更多工作负载的需求增加,对在复制的有状态工作负载中使用本地快速 SSD 的需求也随之增加。

解决 hostPath 的挑战

以前通过 hostPath 卷访问本地存储的机制存在许多挑战。hostPath 卷很难在生产环境中大规模使用:当使用 hostPath 卷时,操作员需要关心本地磁盘管理、拓扑和各个 Pod 的调度,并且无法使用许多 Kubernetes 功能(如 StatefulSets)。现有的使用远程卷的 Helm chart 无法轻松移植以使用 hostPath 卷。本地持久卷功能旨在解决 hostPath 卷的可移植性、磁盘记账和调度挑战。

免责声明

在详细介绍如何使用本地持久卷之前,请注意本地卷不适合大多数应用程序。使用本地存储会将您的应用程序绑定到特定的节点,从而使您的应用程序更难调度。如果该节点或本地卷遇到故障且无法访问,那么该 Pod 也将无法访问。此外,许多云提供商不为本地存储提供广泛的数据持久性保证,因此在某些情况下您可能会丢失所有数据。

出于这些原因,大多数应用程序应继续使用高可用、远程可访问的持久存储。

适用的工作负载

一些适用于本地存储的用例包括

  • 可以利用数据引力进行快速处理的数据集缓存
  • 跨多个节点分片或复制数据的分布式存储系统。示例包括诸如 Cassandra 之类的分布式数据存储或诸如 Gluster 或 Ceph 之类的分布式文件系统。

适用的工作负载可以容忍节点故障、数据不可用和数据丢失。它们为集群的其余部分提供关键的、延迟敏感的基础设施服务,并且与其它工作负载相比,应以高优先级运行。

启用更智能的调度和卷绑定

管理员必须为本地持久卷启用更智能的调度。在创建任何用于本地 PersistentVolume 的 PersistentVolumeClaims 之前,必须创建一个 StorageClass,并将 volumeBindingMode 设置为“WaitForFirstConsumer”

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

此设置告知 PersistentVolume 控制器不要立即绑定 PersistentVolumeClaim。相反,系统会等待,直到需要使用卷的 Pod 被调度。然后,调度程序会选择合适的本地 PersistentVolume 进行绑定,同时考虑 Pod 的其他调度约束和策略。这可确保初始卷绑定与任何 Pod 资源要求、选择器、亲和性和反亲和性策略等兼容。

请注意,beta 版不支持动态配置。所有本地 PersistentVolume 都必须静态创建。

创建本地持久卷

对于此初始 beta 版本,本地磁盘必须首先由管理员预先分区、格式化并挂载到本地节点。还支持共享文件系统上的目录,但也必须在使用前创建。

设置本地卷后,您可以为其创建 PersistentVolume。在此示例中,本地卷挂载在节点“my-node”上的“/mnt/disks/vol1”处

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-local-pv
spec:
  capacity:
    storage: 500Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /mnt/disks/vol1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - my-node

请注意,PersistentVolume 对象中有一个新的 nodeAffinity 字段:Kubernetes 调度程序通过此字段了解此 PersistentVolume 绑定到特定节点。nodeAffinity 是本地 PersistentVolume 的必填字段。

当像这样手动创建本地卷时,唯一支持的 persistentVolumeReclaimPolicy 是“Retain”。当 PersistentVolume 从 PersistentVolumeClaim 释放时,管理员必须手动清理并重新设置本地卷以供重用。

自动化本地卷的创建和删除

手动创建和清理本地卷是一个很大的管理负担,因此我们编写了一个简单的本地卷管理器来自动执行其中的一些步骤。它位于 external-storage 仓库中,作为一个可选程序,您可以在集群中部署该程序,其中包括有关如何运行它的说明和示例部署规范。

要使用此管理器,本地卷仍必须首先由管理员在本地节点上设置并挂载。管理员需要将本地卷挂载到本地卷管理器可识别的可配置的“发现目录”中。支持共享文件系统上的目录,但必须将其绑定挂载到发现目录中。

此本地卷管理器会监视发现目录,查找任何新的挂载点。该管理器会为它检测到的任何新挂载点创建 PersistentVolume 对象,并具有适当的 storageClassName、路径、nodeAffinity 和容量。这些 PersistentVolume 对象最终可以被 PersistentVolumeClaims 声明,然后挂载到 Pod 中。

在 Pod 完成使用卷并删除其 PersistentVolumeClaim 后,本地卷管理器会通过删除其中的所有文件来清理本地挂载,然后删除 PersistentVolume 对象。这将触发发现周期:为该卷创建一个新的 PersistentVolume,并且可以由新的 PersistentVolumeClaim 重复使用。

一旦管理员最初设置好本地卷挂载,此本地卷管理器就会接管 PersistentVolume 生命周期的其余部分,而无需任何其他管理员干预。

在 Pod 中使用本地卷

在完成所有管理员工作后,用户实际上如何将本地卷挂载到其 Pod 中?幸运的是,从用户的角度来看,可以通过与任何其他 PersistentVolume 类型完全相同的方式请求本地卷:通过 PersistentVolumeClaim。只需在 PersistentVolumeClaim 对象中指定本地卷的适当 StorageClassName,系统就会处理其余的事情!

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: example-local-claim
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: local-storage
  resources:
    requests:
      storage: 500Gi

或者在 StatefulSet 中作为 volumeClaimTemplate

kind: StatefulSet
...
 volumeClaimTemplates:
  - metadata:
      name: example-local-claim
    spec:
      accessModes:
      - ReadWriteOnce
      storageClassName: local-storage
      resources:
        requests:
          storage: 500Gi

文档

Kubernetes 网站提供了有关 本地持久卷的完整文档。

未来增强功能

本地持久卷 beta 功能远未完成。一些正在开发中的值得注意的增强功能

  • 从 1.10 开始,本地原始块卷可用作 alpha 功能。这对于需要直接访问块设备并管理其自身数据格式的工作负载很有用。
  • 使用 LVM 的本地卷动态配置正在设计中,并且 alpha 实现将在未来的版本中进行。只要工作负载的性能要求可以容忍共享磁盘,这将消除当前管理员预分区、格式化和挂载本地卷的需求。

补充功能

Pod 优先级和抢占是 Kubernetes 的另一个特性,是对本地持久卷的补充。当您的应用程序使用本地存储时,它必须被调度到本地卷所在的特定节点上。您可以为本地存储工作负载赋予高优先级,这样如果该节点没有足够的空间来运行您的工作负载,Kubernetes 可以抢占优先级较低的工作负载,为您的工作负载腾出空间。

Pod 中断预算对于那些必须维持仲裁的工作负载也非常重要。为您的工作负载设置中断预算可以确保它不会因自愿中断事件(例如升级期间的节点驱逐)而降至仲裁以下。

Pod 亲和性和反亲和性确保您的工作负载保持在同一位置或分散在不同的故障域中。如果单个节点上有多个可用的本地持久卷,最好指定一个 Pod 反亲和性策略,将您的工作负载分散到不同的节点上。请注意,如果您希望多个 Pod 共享同一个本地持久卷,则无需指定 Pod 亲和性策略。调度器理解本地持久卷的位置约束,并将您的 Pod 调度到正确的节点。

参与其中

如果您对此功能有反馈,或有兴趣参与设计和开发,请加入 Kubernetes 存储特别兴趣小组 (SIG)。我们正在快速发展,并始终欢迎新的贡献者。

特别感谢来自多家公司帮助将此功能推向 Beta 版的所有贡献者,包括 Cheng Xing (verult), David Zhu (davidz627), Deyuan Deng (ddysher), Dhiraj Hedge (dhirajh), Ian Chakeres (ianchakeres), Jan Šafránek (jsafrane), Matthew Wong (wongma7), Michelle Au (msau42), Serguei Bezverkhi (sbezverk), 和 Yuquan Ren (nickrenren)。