在 Kubernetes 上扩展成百上千个数据库集群
在 Kubernetes 上扩展成百上千个数据库集群 作者:Brian Morrison II | 2023年9月27日
容器技术在应用程序的构建、部署和分发方面产生了深远的影响。
利用容器化应用,你无需再担心依赖关系的冲突或经典的“在我的机器上没问题”的问题。此外,Kubernetes 简化了容器化应用的部署与扩展。如果一个容器崩溃,Kubernetes 可以轻松地启动一个新的容器来处理负载!
但你是否曾考虑过能否在 Kubernetes 上运行数据库?
简短回答是“可以”。事实上,我们在 PlanetScale 广泛使用 Kubernetes 来支持全球数十万的数据库。但是,无论数据库工作负载大小,在将数据库部署到 Kubernetes 时都需要特别注意。
接下来,我们将探讨如何在 Kubernetes 上部署数据库,以及 PlanetScale 如何做到这一点。
Kubernetes 简介
Kubernetes 是一种容器编排工具,被世界上一些最大的企业用来管理其容器化应用的集群。
在 Kubernetes 集群中,多个服务器(即 Kubernetes 所称的“节点”)被配置为协同工作,以确保部署到这些节点的容器始终在线且可用。Kubernetes 中的最小可部署单元被称为 Pod,它代表一个容器或一组容器。当一个 Pod 由于某种原因崩溃时,系统足够智能以启动一个新的 Pod 实例以保持应用程序在线,这可以发生在同一个节点上,也可以发生在不同节点上。
这一过程被称为“控制循环”(Control Loop)。
控制循环由 Kubernetes 调度器管理。调度器使用配置文件(以 YAML 编写),该文件定义了为了确保应用程序在线需要运行的 Pod。调度器通过对比配置文件中定义的内容与实际部署在 Kubernetes 中的内容,并采取必要措施来解决差异。
这种设置方式非常适用于应用程序,但如果运行数据库的 Pod 崩溃会发生什么?
在 Kubernetes 上运行数据库的问题
将数据库部署到 Kubernetes 的最大问题是:数据库是有状态的。
应用程序的工作负载是否有状态(Stateful)是指与该工作负载相关的数据是否是临时性的。部署到 Kubernetes 的应用程序应考虑这一点。如果 Pod 中的容器崩溃,存储在容器中的任何数据很可能会丢失。而对于数据库来说,数据库中的数据状态非常重要,这使得将数据库部署到 Kubernetes 时需要额外的措施。
根据官方文档,StatefulSets 是在 Kubernetes 上运行数据库的推荐方式。
StatefulSets 允许你定义一组 Pod,这些 Pod 能够保持其数据状态,不管它是否在线。Kubernetes 通过将持久存储(Persistent Storage)连接到 Pod 来读取和写入数据,并确保每次 Pod 上线时都会以相同的顺序、名称和网络地址运行。这和常规 Deployments 不同,后者只关心保持一定数量的 Pod 在线,而不在意它们的启动顺序。
无论何时要将数据库部署到 Kubernetes,都应该使用 StatefulSets,不过还需要考虑其他因素才能正确运行数据库。
将 MySQL 部署到 Kubernetes 时需要注意的问题
将一个良好架构的 MySQL 部署到 Kubernetes 比简单地运行一堆 MySQL Pod 要复杂得多。以下是一些需要解决的关键问题:
1. 复制与节点故障
MySQL 可以启用复制以允许数据在 StatefulSets 中的多个 Pod 同步,但如果节点发生故障,会出现什么问题?
2. 数据源的真实性
在复制环境中,你如何确认哪个 Pod 拥有最新的数据?应用程序如何决定应该查询哪个 Pod?
3. 备份与恢复
备份是必要的,以确保在发生意外写入时能够回滚。然而,在有多个 MySQL 实例运行的环境中,你如何决定备份应该从哪里来?如何确保备份完整?又该如何执行恢复?
PlanetScale 如何管理数十万 MySQL 数据库集群
到目前为止,文章讨论的大多数内容属于在 Kubernetes 上运行数据库的最佳实践。而在 PlanetScale,我们的做法有所不同。
利用 Kubernetes 上的 Vitess
众所周知,PlanetScale 使用 Vitess 来管理和操作我们的数据库,但这背后还有更多故事。
Vitess 是一个开源的、兼容 MySQL 的项目,旨在突破 MySQL 的传统能力极限。Vitess 为 MySQL 提供了额外的组件,例如无状态代理(称为 VTGate)和拓扑服务器。在 Kubernetes 环境中,这两个组件可以协同工作,以确定存在多少 MySQL 实例、如何访问它们,以及在水平分片配置中哪些 Pod 存储了所请求的数据。在 Vitess 中,这些 MySQL Pod 被称为“tablet”,它实际上是一个运行 MySQL 的 Pod,同时运行一个名为 vttablet 的旁车进程。管理平面(vtctld)负责管理这些 Pod,并可以通知整体配置的任何拓扑变化。
上述所有功能都封装于 Vitess 中,同时引入了一个问题:如何管理 Vitess?
这时我们引入 **PlanetScale 的 Vitess Operator**。
运用 PlanetScale 的 Vitess Operator
Kubernetes 提供了大量自动化功能,但有些工作负载需要比 Kubernetes 的默认逻辑更多的处理环节。“Operator”允许开发者通过添加自定义资源扩展 Kubernetes,从而改进控制循环。Vitess Operator 解决了之前提到的复杂问题,使 Kubernetes 能够简化这些管理任务。
正是 Kubernetes、Vitess、PlanetScale Operator 和我们的全球基础设施的组合,使我们能够扩展并管理数十万 MySQL 数据库集群。
新数据库部署
当用户需要在我们的基础设施中部署新数据库时,第一步是通过 API 向我们的定制编排层发出请求,要求创建数据库。
如果请求有效,编排器会定义一种我们用来描述 PlanetScale 数据库规范的自定义资源定义。前文提到的 Operator 会通过 Kubernetes 的内置机制(特别是控制循环与 API)来检测当前状态与所需状态之间的不匹配。之后,Operator 会创建运行和操作 Vitess 集群所需的资源。
一旦这个过程完成,编排器会通知系统其创建完成,用户即可开始使用这个数据库。
存储管理
运行数据库的最佳实践是使用 StatefulSets,因为 Kubernetes 可以自动追踪状态。然而我们选择了不同的做法:
我们使用 Vitess Operator 的逻辑来启动直接连接到云存储的 Pod,并利用持久卷声明(PVC)。由于我们已经有一个路由机制(VTGate),我们无需担心特定 Pod 的名称或地址。
直接连接到云存储还允许我们以编程方式管理存储的容量。随着数据库使用量的增加,数据库所需的存储通常也会增加。为解决这一问题,我们的监控机制可以检测到数据库所在的云存储容量接近耗尽时,利用云提供商的 API 自动分配额外空间,确保数据库不会因为容量问题而停止服务。
这一切都发生在幕后,用户无需担心配额或空间不足的问题。
数据库备份
无论数据库运行在哪里,备份都是不可忽视的一部分,因为备份可以挽救整个企业。
为了减少备份对生产数据库性能的影响,我们使用 Vitess 创建一个仅用于备份的特殊类型的 tablet。备份时,我们会将最新版本的备份恢复到该 tablet,复写自备份以来发生的所有更改,然后基于这些数据创建新的备份。这不仅大幅减少了对生产数据库的影响,还能够自动验证现有备份的完整性。
故障缓解
尽管我们的大多数数据库运行在 AWS 或 GCP 这样可靠的平台上,但我们仍然需要应对一些无法控制的故障。
借助 Kubernetes 和 Vitess Operator,我们能够自动处理 Pod 或容器停止时的情况。数据库在 Kubernetes 中的配置定义了任何时候应该运行的资源数量和类型。如果检测到偏差,Operator 会自动启动新的资源以确保系统稳定运行。
此外,对于我们的付费生产数据库,我们还保护用户免受可用区(AZ)中断的影响。例如,对于 Scaler Pro 级别的数据库,我们会自动在同一云区域内的三个可用区中部署 Vitess 集群。这包括服务数据库数据的 tablet,以及将查询路由到正确 tablet 的 VTGate 实例。这意味着如果某个可用区发生故障,Vitess Operator 会自动检测到并采取必要的基础设施变化来保持数据库在线。
实际上,我们甚至不支持少于三个可用区的云区域!
通过 Kubernetes 和 Vitess 的结合,PlanetScale 能够以可扩展的架构稳定运行百万级 MySQL 数据库,同时为用户提供优越的体验与更高的可靠性。
关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接
本文链接:https://choupangxia.com/2025/09/13/kubernetes-database/