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

Kubernetes 1.14:本地持久卷 GA

本地持久卷 功能已在 Kubernetes 1.14 中升级为 GA。它最初在 Kubernetes 1.7 中作为 alpha 版本引入,然后在 Kubernetes 1.10 中 beta 版本推出。GA 里程碑表示 Kubernetes 用户可以依赖该功能及其 API 进行生产环境使用。GA 功能受 Kubernetes 弃用策略保护。

什么是本地持久卷?

本地持久卷表示直接连接到单个 Kubernetes 节点的本地磁盘。

Kubernetes 提供了一个强大的卷插件系统,使 Kubernetes 工作负载可以使用各种块和文件存储来持久化数据。这些插件中的大多数都启用远程存储——这些远程存储系统持久化数据,独立于数据来源的 Kubernetes 节点。远程存储通常无法提供与本地直接连接存储一致的高性能保证。有了本地持久卷插件,Kubernetes 工作负载现在可以使用相同的卷 API 来使用高性能本地存储,应用程序开发人员已经习惯了这些 API。

它与 HostPath 卷有什么不同?

为了更好地了解本地持久卷的好处,将其与 HostPath 卷进行比较非常有用。HostPath 卷将主机节点文件系统中的文件或目录挂载到 Pod 中。类似地,本地持久卷将本地磁盘或分区挂载到 Pod 中。

最大的区别在于 Kubernetes 调度程序知道本地持久卷属于哪个节点。使用 HostPath 卷时,引用 HostPath 卷的 Pod 可能会被调度程序移动到不同的节点,从而导致数据丢失。但是对于本地持久卷,Kubernetes 调度程序确保使用本地持久卷的 Pod 始终调度到同一节点。

虽然可以通过持久卷声明 (PVC) 或直接在 Pod 定义中内联引用 HostPath 卷,但本地持久卷只能通过 PVC 引用。这提供了额外的安全优势,因为持久卷对象由管理员管理,从而阻止 Pod 访问主机上的任何路径。

其他好处包括支持在挂载期间格式化块设备,以及使用 fsGroup 的卷所有权。

GA 的新功能是什么?

自 1.10 版本以来,我们主要专注于提高该功能的稳定性和可扩展性,以便它可以在生产环境中使用。

唯一的主要功能新增是能够指定原始块设备,并让 Kubernetes 自动格式化和挂载文件系统。这减少了之前必须在将其提供给 Kubernetes 之前格式化和挂载设备的负担。

GA 的局限性

在 GA 中,本地持久卷不支持动态卷配置。但是,有一个外部控制器可用于帮助管理节点上单个磁盘的本地持久卷生命周期。这包括创建持久卷对象、清理和重用应用程序释放后的磁盘。

如何使用本地持久卷?

工作负载可以使用与远程存储后端相同的 PersistentVolumeClaim 接口请求本地持久卷。这使得跨集群、云和本地环境轻松替换存储后端。

首先,应创建一个 StorageClass,将 volumeBindingMode: WaitForFirstConsumer 设置为启用 卷拓扑感知调度。此模式指示 Kubernetes 等待绑定 PVC,直到使用它的 Pod 被调度。

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

然后,可以配置和运行外部静态配置程序,为节点上的所有本地磁盘创建 PV。

$ kubectl get pv
NAME                CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM  STORAGECLASS   REASON      AGE
local-pv-27c0f084   368Gi      RWO            Delete           Available          local-storage              8s
local-pv-3796b049   368Gi      RWO            Delete           Available          local-storage              7s
local-pv-3ddecaea   368Gi      RWO            Delete           Available          local-storage              7s

之后,工作负载可以通过创建 PVC 和 Pod 或带有 volumeClaimTemplates 的 StatefulSet 来开始使用 PV。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: local-test
spec:
  serviceName: "local-service"
  replicas: 3
  selector:
    matchLabels:
      app: local-test
  template:
    metadata:
      labels:
        app: local-test
    spec:
      containers:
      - name: test-container
        image: registry.k8s.io/busybox # updated after publication (previously used k8s.gcr.io/busybox)
        command:
        - "/bin/sh"
        args:
        - "-c"
        - "sleep 100000"
        volumeMounts:
        - name: local-vol
          mountPath: /usr/test-pod
  volumeClaimTemplates:
  - metadata:
      name: local-vol
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "local-storage"
      resources:
        requests:
          storage: 368Gi

一旦 StatefulSet 启动并运行,PVC 都将被绑定

$ kubectl get pvc
NAME                     STATUS   VOLUME              CAPACITY   ACCESS MODES   STORAGECLASS      AGE
local-vol-local-test-0   Bound    local-pv-27c0f084   368Gi      RWO            local-storage     3m45s
local-vol-local-test-1   Bound    local-pv-3ddecaea   368Gi      RWO            local-storage     3m40s
local-vol-local-test-2   Bound    local-pv-3796b049   368Gi      RWO            local-storage     3m36s

当不再需要磁盘时,可以删除 PVC。外部静态配置程序将清理磁盘,并使 PV 再次可用。

$ kubectl patch sts local-test -p '{"spec":{"replicas":2}}'
statefulset.apps/local-test patched

$ kubectl delete pvc local-vol-local-test-2
persistentvolumeclaim "local-vol-local-test-2" deleted

$ kubectl get pv
NAME                CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                            STORAGECLASS   REASON      AGE
local-pv-27c0f084   368Gi      RWO            Delete           Bound       default/local-vol-local-test-0   local-storage              11m
local-pv-3796b049   368Gi      RWO            Delete           Available                                    local-storage              7s
local-pv-3ddecaea   368Gi      RWO            Delete           Bound       default/local-vol-local-test-1   local-storage              19m

您可以在 Kubernetes 网站上找到该功能的完整文档

有哪些合适的用例?

本地持久卷相对于远程持久存储的主要优势是性能:与远程存储系统相比,本地磁盘通常提供更高的 IOPS 和吞吐量以及更低的延迟。

但是,在使用本地持久卷时,需要考虑重要的限制和注意事项

  • 使用本地存储会将您的应用程序绑定到特定节点,从而使您的应用程序更难调度。使用本地存储的应用程序应指定高优先级,以便在必要时可以抢占不需要本地存储的较低优先级 Pod。
  • 如果该节点或本地卷遇到故障并变得无法访问,则该 Pod 也将变得无法访问。可能需要手动干预、外部控制器或运算符来从这些情况中恢复。
  • 虽然大多数远程存储系统都实现了同步复制,但大多数本地磁盘产品不提供数据持久性保证。这意味着磁盘或节点的丢失可能会导致该磁盘上的所有数据丢失

出于这些原因,本地持久存储只应考虑用于在应用程序层处理数据复制和备份的工作负载,从而使应用程序能够抵御节点或数据故障和不可用性,尽管在单个磁盘级别缺乏此类保证。

良好工作负载的示例包括软件定义存储系统和复制数据库。其他类型的应用程序应继续使用高可用性、可远程访问、持久的存储。

Uber 如何使用本地存储

M3,Uber 的内部指标平台,大规模试点本地持久卷,以评估M3DB ——一个由 Uber 创建的开源分布式时序数据库。M3DB 的一个显着特点是它能够将其指标分片到分区中,按三倍的系数复制它们,然后将副本均匀地分散在不同的故障域中。

在本地持久卷的试点之前,M3DB 完全在 Uber 管理的环境中运行。随着时间的推移,内部用例出现了,需要能够在依赖性较少的环境中运行 M3DB。因此,团队开始探索各种选择。作为一个开源项目,我们希望为社区提供一种尽可能轻松地运行 M3DB 的方法,使用开源堆栈,同时满足 M3DB 对高吞吐量、低延迟存储以及自身扩展能力的要求。

Kubernetes 本地持久卷接口以其高性能、低延迟保证迅速成为构建的完美抽象。使用本地持久卷,单个 M3DB 实例可以轻松处理高达每秒 60 万次的写入。这为通常每秒处理数百万个指标的集群留下了充足的峰值空间。

由于 M3DB 也能优雅地处理单个节点或卷的丢失,因此本地持久卷的有限数据持久性保证不是问题。如果节点发生故障,M3DB 会找到合适的替代节点,并且新节点开始从其两个对等节点流式传输数据。

由于 Kubernetes 调度程序对卷拓扑的智能处理,M3DB 能够以编程方式将其副本均匀地分散在所有可用云区域中的多个本地持久卷中,或者在本地集群的情况下,分散在所有可用的服务器机架中。

Uber 的运营经验

如上所述,虽然本地持久卷提供了许多好处,但在生产环境中提交使用之前,也需要仔细规划和仔细考虑约束。在考虑 M3DB 的本地卷策略时,Uber 需要考虑一些事项。

首先,我们必须考虑 Kubernetes 集群中节点的硬件配置文件。例如,每个节点集群将有多少个本地磁盘?它们将如何分区?

本地静态配置程序提供指南来帮助回答这些问题。最好能够为每个本地卷分配一个完整的磁盘(用于 IO 隔离),并为每个卷分配一个完整的分区(用于容量隔离)。这在我们的云环境中更容易实现,我们可以混合和匹配本地磁盘。但是,如果在本地使用本地卷,硬件约束可能会成为限制因素,具体取决于可用磁盘的数量及其特性。

在首次测试本地卷时,我们希望深入了解中断(自愿和非自愿)对使用本地存储的 Pod 的影响,因此我们开始测试一些故障场景。我们发现,当本地卷不可用但节点仍然可用时(例如在对磁盘进行维护时),使用该本地卷的 Pod 将会卡在 ContainerCreating 状态,直到它可以挂载该卷。如果节点不可用,例如如果它从集群中移除或被驱逐,那么使用该节点上的本地卷的 Pod 会卡在 Unknown 或 Pending 状态,具体取决于节点是否被正常移除。

从这些中间状态恢复 Pod 意味着必须删除将 Pod 绑定到其本地卷的 PVC,然后删除 Pod,以便可以重新调度它(或者等待节点和磁盘再次可用)。我们在构建 M3DB 的 operator 时考虑了这一点,该 operator 在 Pod 重新调度时会更改集群拓扑,以便新的 Pod 可以优雅地从其余两个对等节点流式传输数据。我们最终计划完全自动化删除和重新调度过程。

Pod 状态的警报可以帮助引起人们对卡住的本地卷的注意,并且特定于工作负载的控制器或 operator 可以自动修复它们。由于这些限制,最好将具有本地卷的节点排除在自动升级或修复之外,事实上,一些云提供商明确提到这是一种最佳实践。

本地和云之间的可移植性

本地卷在 Uber 决定使用 Kubernetes 为 M3DB 构建编排方面发挥了重要作用,部分原因是它是一种在本地和云环境中工作方式相同的存储抽象。远程存储解决方案在云提供商之间具有不同的特性,并且一些用户可能更喜欢完全不使用自己数据中心的网络存储。另一方面,本地磁盘相对普及,并提供更可预测的性能特征。

通过在云中使用本地磁盘编排 M3DB(在那里更容易启动并运行 Kubernetes),我们更有信心我们仍然可以使用我们的 operator 在本地环境中运行 M3DB 而无需进行任何修改。当我们继续研究如何在本地运行 Kubernetes 时,解决了这样一个重要的悬而未决的问题,真是让人松了一口气。

本地持久卷的下一步是什么?

正如我们在 Uber 的 M3DB 中看到的那样,本地持久卷已成功在生产环境中使用。随着本地持久卷的采用持续增加,SIG Storage 继续寻求反馈,以改进该功能。

最常见的要求之一是需要一个控制器来帮助从失败的节点或磁盘中恢复,这目前是一个手动过程(或者必须构建到 operator 中)。SIG Storage 正在研究创建一个通用的控制器,它可以被具有简单和类似恢复过程的工作负载使用。

另一个受欢迎的要求是支持使用 lvm 进行动态配置。这可以简化磁盘管理并提高磁盘利用率。SIG Storage 正在评估该功能可行性的性能权衡。

参与其中

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

特别感谢所有帮助将此功能推向 GA 的贡献者,包括 Chuqiang Li (lichuqiang)、Dhiraj Hedge (dhirajh)、Ian Chakeres (ianchakeres)、Jan Šafránek (jsafrane)、Michelle Au (msau42)、Saad Ali (saad-ali)、Yecheng Fu (cofyc) 和 Yuquan Ren (nickrenren)。