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

Kubernetes 的本地持久卷进入 Beta 阶段

Kubernetes 1.10 中的 本地持久卷 beta 功能使得在有状态应用(StatefulSets)中利用本地磁盘成为可能。您可以将直接连接的本地磁盘指定为持久卷(PersistentVolumes),并在有状态应用中使用它们,就像之前仅支持远程卷类型的持久卷声明(PersistentVolumeClaim)对象一样。

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

解决 hostPath 的挑战

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

免责声明

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

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

合适的工作负载

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

  • 利用数据引力实现快速处理的数据集缓存
  • 在多个节点上分片或复制数据的分布式存储系统。示例包括 Cassandra 等分布式数据存储,或 Gluster 或 Ceph 等分布式文件系统。

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

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

管理员必须为本地持久卷启用更智能的调度。在为本地持久卷创建任何 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 版中不受支持。所有本地 PersistentVolumes 都必须静态创建。

创建本地持久卷

对于这个最初的 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 是本地 PersistentVolumes 的必填字段。

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

本地卷创建和删除自动化

手动创建和清理本地卷是一项繁重的管理负担,因此我们编写了一个简单的本地卷管理器来自动化其中一些部分。它可以在 external-storage 仓库 中作为可选程序提供,您可以将其部署到集群中,包括如何运行它的说明和示例部署规范。

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

此本地卷管理器监视发现目录,查找任何新的挂载点。管理器会为检测到的任何新挂载点创建具有适当 storageClassName、路径、nodeAffinity 和容量的 PersistentVolume 对象。这些 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)。我们正在迅速发展,并始终欢迎新的贡献者。

特别感谢来自多家公司(包括 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))的所有贡献者,他们帮助将此功能推向 beta 阶段。