Kubernetes 不是通灵的:分布式有状态工作负载

Kubernetes 不是通灵的:分布式有状态工作负载

翻译自 Kubernetes Is Not Psychic: Distributed Stateful Workloads

通过在集群中的各个节点上使用分布式数据存储替换可互换的无状态副本,有状态工作负载可以安全地运行。

如果您的业务围绕任何类型的交易展开,您可能会觉得商业世界的其他部分已经完全迁移到云端而将您甩在了后面。

关系数据库对于现代商业的每一种形式都至关重要,从购物到金融服务再到流媒体娱乐——但它们仍然是技术时代的产物。关系数据库的强大之处在于其绝对的简单性:行和表定义的体系结构自 1970 年代 SQL 出现以来几乎没有变化。但这种简单性也带来了巨大的责任:保持这些行和表中的数据一致、隔离和持久。

传统的 SQL 数据库是可靠的主力,但它们基本上也是有状态的。然而,事实证明,在分布式应用程序中运行有状态工作负载是一项严峻的挑战,这也是事务数据库加入云原生队伍的速度较慢的原因。

不是宠物,不是牛。

关系数据库必须保证数据的有效性,克服云提供商中断、电源故障和其他任何可以想象的灾难。它的基本工作是在工作负载的整个生命周期中维护状态。

传统的 SQL 数据库被设计为在单个物理服务器上运行,或者至多在有限数量的服务器上运行。这种“宠物”场景非常适合维护状态。然而,云原生应用程序通过设计分布在虚拟服务器的“牛群”中,这是一个由无状态节点、pod 和集群组成的容器的短暂环境,这些节点会根据工作负载需求而启动(和关闭)。这些可以在不再需要时过期,或者有时它们会失效;无论哪种方式,它们都会很快被替换。但是有状态的工作负载对牛并不友好。特别是关系数据库必须具有持久和持久的存储,以保证数据的一致性和可用性。

Kubernetes 可以做很多事情,但持久存储不是其中之一

Kubernetes 不提供确保即使 pod 或节点出现故障或重新启动也能存储数据的内置支持。虽然 Kubernetes 提供了将存储卷附加到容器的机制,但在分布式环境中管理和维护持久存储并不容易。

这是因为该平台本身旨在管理容器化应用程序,而不是承担主要存储职责。 Kubernetes 自己的本地存储解决方案——例如本地存储、hostPath 卷和 emptyDir 卷——是临时的,因此不适合维护状态。在极有可能发生节点故障的情况下,这可能会导致数据丢失或不一致,从而损害数据库的完整性。

Kubernetes 的短暂性并不是使运行有状态工作负载出现问题的唯一因素。根据需求扩大(和缩小)工作负载也是一个问题。扩展无状态工作负载非常简单:您只需添加更多副本。但是,对于有状态工作负载,扩展需要更复杂的操作,例如在集群中添加或删除节点、重新平衡工作负载以及确保节点之间的数据一致性。

Kubernetes 不是通灵的

Kubernetes 本质上是一个生成和编排可互换副本的引擎。这根本不适用于像事物一样的有状态的工作负载,它具有独特的状态,如写入。

因此,分布式有状态工作负载必须与运行相同应用程序的其他节点紧密协调,以确保数据和事务可靠性的原子性、一致性、隔离性和持久性。这种保证很难在 Kubernetes 中提供。在分布式应用程序中实现它们可能需要手动解决方法和分片,引入管理复杂性,更不用说在更改或更新应用程序时成为主要的派对扫把星。

除了复杂性之外,这很可能还会引入延迟。如果一个节点或集群发生故障,Kubernetes 需要时间来分配故障转移节点或集群来承担替代主导角色并保持提要数据应用程序的准确性。当涉及到备份和恢复等服务时,这一点尤为明显。

最后,Kubernetes 不是通灵的。它无法检测环境是否使用单个数据库实例、领导者/追随者数据库集群或共享领导者配置。这意味着构建手动脚本来指示 Kubernetes 如何在您的数据库和应用程序的其余部分之间进行干预——或者这意味着采购和集成第三方工具来为您完成这项工作。这意味着要在预先为您做更多工作加上持续维护或增加支出和复杂性(以及持续维护)之间做出选择。无论哪种方式,都会增加更多的复杂性。应用程序架构师要做什么?

分发您的数据(基础)

因此,挑战在于如何在无法保证节点和 Pod 寿命的 Kubernetes 环境中实现有状态分布式应用程序(和数据库)的数据一致性和可用性。此外,要在不将容器绑定到特定数据存储的情况下实现这一点,此举会扼杀整个可移植性概念。

答案是,不要复制您的数据——分发它!使用一个单一的逻辑数据库,它本身是建立在分布式架构上的——也就是分布式 SQL 数据库。

构建在 Kubernetes 之上的分布式 SQL 数据库采用自定义架构来处理有状态的分布式工作负载。它是同样熟悉的 SQL,但现在能够支持在集群中的各个节点上存储数据。这意味着数据可以保存在不同的区域中以确保可用性。这些节点能够在不产生冲突的情况下接收和协调它们之间的读写请求,从而确保 ACID-complaint 分布式事务。

在真正的分布式 SQL 数据库中,所有节点都将被编程为就数据状态达成一致。实现这一目标需要以基于共识的复制协议为核心,例如 RAFT 共识协议。这样的协议可以提供类似 ACID 的保证,因为它确保数据是一致的,无论您的有状态应用程序与哪个节点对话。

RAFT 之所以有效,是因为它确保在执行这些更改之前,一定数量的副本就任何更改达成一致。例如,如果您的体系结构集群到三个节点,则需要三个节点的法定人数来保证数据的准确性。然后,该协议将与一个领导节点一起工作,该领导节点协调其组中的所有写入,并在失败时被替换。

最后一个关键要素是一个高效的键值存储引擎,它能够与数据读写的共识协议一起工作。在这种情况下,“高效”是什么样子的?它应该具有快速批量数据加载和摄取、定期垃圾收集系统以减少磁盘上数据大小等功能,以及利用 SQL 标准中的关键功能(如跟踪历史数据)的能力。

总结

Kubernetes 是一个用于管理容器化工作负载的强大平台,但长期以来它并不是运行有状态工作负载的最佳选择。然而,通过重新考虑数据放置——用跨集群中各个节点的分布式数据存储替换大量可互换的无状态副本——有状态的工作负载可以无风险地运行。

换句话说,由于分布式 SQL,关系数据库终于可以加入云端应用程序堆栈的其余部分。传递薯条和蘸酱!

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注