本文发表时间已超过一年。较旧的文章可能包含过时内容。请检查页面信息自发布以来是否已发生变化。
本地存储:存储容量跟踪、分布式供应和通用临时卷进入 Beta
Kubernetes 中的 “通用临时卷” 和 “存储容量跟踪” 功能在 Kubernetes 1.21 中升级为 Beta 版。加上 CSI external-provisioner 中的分布式供应支持,开发和部署在节点本地管理存储的容器存储接口 (CSI) 驱动程序变得容易得多。
这篇博文解释了这类驱动程序之前的工作方式,以及如何使用这些功能来简化驱动程序。
我们正在解决的问题
存在本地存储的驱动程序,例如用于传统磁盘的 TopoLVM 和用于 持久内存 的 PMEM-CSI。它们在较旧的 Kubernetes 版本上也能正常工作并已准备好使用,但实现这一点并不容易。
需要中央组件
第一个问题是卷供应:它通过 Kubernetes 控制面处理。某些组件必须对 PersistentVolumeClaims (PVC) 作出反应并创建卷。通常,这由 CSI external-provisioner 的中心部署和一个 CSI 驱动程序组件处理,该组件随后连接到存储后端。但对于本地存储,没有这样的后端。
TopoLVM 通过其不同组件之间通过创建和响应 custom resources 来通过 Kubernetes API 服务器相互通信的方式解决了这个问题。因此,尽管 TopoLVM 基于 CSI(一个独立于特定容器编排器的标准),但 TopoLVM 仅在 Kubernetes 上工作。
PMEM-CSI 创建了其自己的存储后端,通过 gRPC 调用进行通信。保护该通信依赖于 TLS 证书,这使得驱动程序的部署更加复杂。
通知 Pod 调度器关于容量的信息
下一个问题是调度。当卷独立于 Pod 创建时(“立即绑定”),CSI 驱动程序必须选择一个节点,而无需了解将要使用该卷的 Pod。拓扑信息随后强制这些 Pod 在创建卷的节点上运行。如果该节点的其他资源(如 RAM 或 CPU)已耗尽,则 Pod 无法启动。可以通过在 StorageClass 中配置卷创建应等待第一个使用该卷的 Pod(volumeBinding: WaitForFirstConsumer
)来避免这种情况。在该模式下,Kubernetes 调度器会根据其他约束尝试性地选择一个节点,然后 external-provisioner 被要求创建一个卷,使其在该节点上可用。如果本地存储耗尽,供应器可以请求另一次调度尝试。但如果缺乏可用容量信息,调度器可能总是选择相同的不合适节点。
TopoLVM 和 PMEM-CSI 都使用调度器扩展器解决了这个问题。这可行,但在部署驱动程序时配置起来很困难,因为 kube-scheduler 与驱动程序之间的通信非常依赖于集群的设置方式。
重新调度
本地存储的一个常见用例是临时空间。相比于持久卷,更适合这种用例的是为 Pod 创建并随 Pod 一同销毁的临时卷。支持 CSI 驱动程序使用临时卷的初始 API(因此称为“CSI 临时卷”)被设计用于轻量级卷,其中卷创建不太可能失败。卷创建发生在 Pod 永久调度到节点之后,这与传统供应在将 Pod 调度到节点之前尝试创建卷的方式形成对比。必须修改 CSI 驱动程序以支持“CSI 临时卷”,这已为 TopoLVM 和 PMEM-CSI 完成。但是,由于 Kubernetes 中该功能的设计,如果节点上的存储容量耗尽,Pod 可能会永久卡住。调度器扩展器试图避免这种情况,但不能 100% 可靠。
Kubernetes 1.21 中的增强功能
分布式供应
从为 Kubernetes 1.20 发布的 external-provisioner v2.1.0 开始,供应可以通过与 CSI 驱动程序一起部署在每个节点上的 external-provisioner 实例处理,然后这些实例相互协作来供应卷(“分布式供应”)。不再需要中心组件,因此节点之间也无需通信,至少在供应方面如此。
存储容量跟踪
调度器扩展器仍然需要某种方式来了解每个节点上的容量。当 PMEM-CSI 在 v0.9.0 中切换到分布式供应时,这是通过查询本地驱动程序容器公开的度量数据来完成的。但对于用户来说,完全消除对调度器扩展器的需求更好,因为这样驱动程序的部署会更简单。存储容量跟踪功能于1.19 中引入,并在 Kubernetes 1.21 中升级为 Beta 版,实现了这一点。它通过在 CSIStorageCapacity
对象中发布容量信息来实现。调度器本身随后使用这些信息来过滤掉不合适的节点。由于信息可能不完全最新,Pod 仍然可能被分配到存储不足的节点,但这只是可能性较低,一旦信息得到刷新,对 Pod 的下一次调度尝试应该会更好地工作。
通用临时卷
因此,CSI 驱动程序仍然需要能够从糟糕的调度决策中恢复,这对于“CSI 临时卷”来说被证明是不可能实现的。“通用临时卷”是另一个在 1.21 中升级为 Beta 版的功能,它没有这个限制。该功能添加了一个控制器,该控制器将创建和管理与 Pod 生命周期相同的 PVC,因此正常的恢复机制也适用于它们。现有的存储驱动程序将能够处理这些 PVC,而无需任何新逻辑来处理这种新场景。
已知限制
通用临时卷和存储容量跟踪都会增加 API 服务器的负载。这是否是问题很大程度上取决于工作负载的类型,特别是多少 Pod 拥有卷以及这些卷需要创建和销毁的频率。
没有尝试对调度决策如何影响存储容量进行建模。这是因为影响会因存储系统处理存储的方式而有很大差异。其结果是,即使节点上只有足够容量供一个 Pod 使用,多个带有未绑定卷的 Pod 仍可能被分配到同一节点。调度应该能够恢复,但如果调度器对存储了解更多,效率会更高。
因为存储容量由运行中的 CSI 驱动程序发布,而集群自动扩缩器需要关于尚未创建的节点的信息,所以它目前不会为需要卷的 Pod 扩缩集群。有一个关于如何提供该信息的想法,但该领域还需要更多工作。
分布式快照和扩缩目前不受支持。应该可以修改相应的 Sidecar,external-snapshotter 和 external-resizer 的跟踪 issue 也已打开,只需要一些志愿者。
对于具有多个卷的 Pod,从糟糕的调度决策中恢复可能会失败,特别是当这些卷是节点本地的卷时:如果一个卷可以创建,然后另一个卷的存储不足,则第一个卷将继续存在并强制调度器将 Pod 放到该卷所在的节点上。有一个关于如何处理这种情况的想法,回滚卷的供应,但这仍处于非常早期的构思阶段,甚至还没有合并为 KEP。目前最好避免创建具有多个持久卷的 Pod。
启用新功能和后续步骤
随着该功能在 1.21 版本中进入 Beta 阶段,无需采取额外措施即可启用它。通用临时卷无需更改 CSI 驱动程序即可工作。欲了解更多信息,请参阅文档和之前关于它的博文。API 在 Alpha 和 Beta 之间没有任何变化。
对于另外两个功能,external-provisioner 文档解释了 CSI 驱动程序开发者必须如何改变其驱动程序的部署方式以支持存储容量跟踪和分布式供应。这两个功能是独立的,因此只启用其中一个也没关系。
如果你正在使用这些新功能,SIG Storage 希望听到你的反馈。可以通过电子邮件、Slack(频道 #sig-storage
)以及在常规 SIG 会议中联系我们。对你的工作负载的描述对于验证设计决策、设置性能测试以及最终将这些功能推广到 GA 将非常有用。
致谢
非常感谢为这些功能做出贡献或提供反馈的社区成员,包括 SIG Scheduling、SIG Auth 以及当然还有 SIG Storage 的成员!