跨区域、Kubernetes集群运行数据库实践指南

语言: CN / TW / HK

译者 | 康少京

策划 | 云昭

在众多NoSQL存储中,Cassandra 是广受企业和开发者欢迎的选择之一。它使用AmazonDynamo引入的架构方面的特性来支持Big Table 数据模型,优势非常明显:高度可扩展性和高度可用性、没有单点故障 NoSQL 列族实现、非常高的写入吞吐量和良好的读取吞吐量、二级索引支持搜索可调节的一致性和对复制的支持灵活的模式等。除此之外,它还实现了一个非常棘手的 Kubernetes 集群中运行数据库(或其他应用程序)的难题,这里供大家参考。  全球应用程序需要一个与它们服务的用户一样分布式的数据层,Apache Cassandra已经很好的做到了这一点,目前为苹果、Netflix和索尼等公司处理数据需求。传统上,分布式应用程序的数据层管理,由专门的团队来管理数千个节点的部署和操作(包括本地节点和云中的节点)。

为了减轻DevOps团队的大部分负担,我们利用Kubernetes(K8s)提供的公共控制区域,在K8ssandra中开发了许多这样的实践和模式。不过,有一个问题是,如果没有适当的注意和预先规划,跨多个区域或K8s集群运行数据库(或任何应用程序)是很棘手的。

在这里向您展示下是如何做到这一点的,我们先看看在一个单独的K8s集群上运行的单个区域K8ssandra部署。它是由六个Cassandra节点组成,分布在该区域内的三个可用性区域中,每个可用性区域中有两个Cassandra节点。在本例中,我们将使用谷歌云平台(GCP)区域名称。然而,这里的例子同样适用于其他云,甚至是在线云。

这就是我们现在的处境:

云数据库的现有部署

目标是建立两个区域,每个区域都有一个 Cassandra 数据中心。 在我们这里的云管理 K8s 部署中,这就转化为两个K8s集群——每个集群都有一个单独的控制平面,但使用一个公共的虚拟私有云 (VPC) 网络。 通过 Cassandra 集群扩展到多个数据中心,我们可以在区域中断的情况下获得冗余,并且在本地访问数据的情况下改善客户端应用程序的响应时间和延迟。

这就是我们的目标:拥有两个区域,每个区域都有自己的 Cassandra 数据中心。

表面上看,我们似乎可以通过简单地启动另一个部署相同 K8s YAML 的 K8s 集群来实现这一点。然后对可用区名称添加一些调整,我们就可以完成了,对吧?最终,这些资源的形状非常相似,都是 K8s 对象。那么,这不应该有效吗?也许。根据您的环境,这种方法可能会起作用。

如果没有使用完全分布式数据库部署,会避免很多问题。但不幸的是,事情很少那么简单。即使其中一些障碍很容易清除,还有许多其他无害的事情可能会出错并导致降级状态。您选择的云提供商、K8s 发行版、命令行标志,是的,甚至是 DNS——这些都可能导致您进入一条黑暗深渊。所以,我们来探讨一些可能遇到的最常见问题来避免这种情况。

常见障碍

即使您的某些部署一开始起来运行得很好,但当您发展到多云环境、升级到另一个 K8s 版本或开始使用不同的发行版和免费工具时,您可能会遇到一两个障碍。当涉及到分布式数据库时,它有更多的底层功能。了解 K8s是如何在一系列硬件中运行容器的,将有助于开发高级解决方案--最终它将满足您的确切需求。

Cassandra 节点需要唯一 IP 地址

您可能遇到的第一个障碍涉及基本网络。回到我们的第一个集群,让我们看一下所涉及的网络层。

在下面显示的 VPC 中,我们有一个无分类域间路由 (CIDR) 范围,代表 K8s工作实例的地址。在 K8s 集群范围内,有一个单独的地址空间,用于操作 pods 和容器运行。Pod 是共享了某些资源的容器集合,例如存储、网络和进程空间。  在某些云环境中,这些子网被绑定到特定的可用区域。因此,K8s工作器所启动的每个子网可能都有一个CIDR范围。 VPC 中可能还有其他虚拟机,但在本例中,我们将坚持使用 K8s 作为唯一租户。

具有K8s层的VPC使用的CIDR范围

在我们的例子中,有 10.100.x.x 用于节点,10.200.x.x 用于 K8s 级别。 每个K8s工作者都会获得10.200.x.x 的一部分,在该单个实例上运行Pod 的CIDR范围。

回想一下我们的目标结构,如果两个集群使用相同或重叠的CIDR地址范围会发生什么? 当你第一次接触网络时,您可能还记得这些错误信息:

尝试连接两个网络时的常见错误消息

K8s 的错误看起来不像这样。不会弹出集群无法有效沟通的警告。

如果您有一个具有 IP 空间的集群,而另一个集群有相同的IP空间或重叠的位置,那么每个集群如何知道特定数据包何时需要离开自己的地址空间,并且通过 VPC 网络路由到另一个集群,然后进入该集群的网络?

正常情况下,这里没有任何提示。有一些方法可以解决这个问题;但从更高的层面来看,如果你有重叠,那就是自找麻烦。这里的重点是,您需要了解每个集群的地址空间,然后仔细规划这些 IP 的分配和使用。这允许 Linux 内核(K8s 路由发生的地方)和 VPC 网络层根据需要转发和路由数据包。

但是,如果您没有足够的 IP怎么办?在某些情况下,您不能给每 pod提供自己的 IP 地址。因此,在这种情况下,您需要退后一步,确定哪些服务绝对必须具有唯一地址,以及哪些服务可以在同一地址空间中一起运行。例如,如果您的数据库需要能够与每个其他pod通信,那么它可能需要自己的唯一地址。但是,如果在东海岸和西海岸的应用程序层只是与他们的本地数据层通信,它们可以拥有自己的专用 K8s 集群,地址范围相同,避免冲突。

扁平化网络

在我们的参考部署中,我们将 K8s 集群中的非重叠范围专用于基础设施层,这些基础设施必须是唯一的,并且服务不会通信的重叠 CIDR 范围。最终,我们在这里所做的是扁平化网络。

有了不重叠的 IP 范围,现在可以继续将数据包路由到每个集群中的 pod。在上图中,您可以看到西海岸为 10.100,东海岸为 10.150,K8s pod 接收来自这些范围的 IP。K8s 集群有自己的 IP 空间,200 对 250,并且 pod 像以前一样被分割。

如何处理 Cassandra 数据中心之间的路由

我们有一堆 IP 地址,并且我们对这些地址具有唯一性。现在,我们如何处理这些数据的路由以及所有这些的通信和发现?发往集群 A 的数据包无法知道它们需要如何路由到集群 B。当我们尝试跨集群边界发送数据包时,本地Linux网络堆栈会发现这不是该主机或本地K8s群集内的任何主机的本地数据包,然后将数据包转发到 VPC 网络。从这里开始,我们的云提供商必须有一个路由表条目来了解这个数据包需要去哪里。

在某些情况下,这是开箱即用。 VPC 路由表使用 pod 和服务 CIDR 范围进行更新,告知应路由哪些主机数据包。在其他环境中,包括混合环境和本地环境,这可能采取通过 BGP向网络层通告路由的形式。雅虎!日本有一篇很棒的文章介绍了这种确切的部署方法。

但是,这些选项可能并不总是最好的答案,这取决于您的多集群架构在单个云提供商中的样子。它是混合云还是多云,结合了本地和两个不同的云提供商?虽然您当然可以在所有这些不同的环境中检测这些,但您可以指望它需要大量的时间和维护。

要考虑的一些解决方案

覆盖网络

一个更简单的答案是使用覆盖网络,在其中为您的应用程序构建一个单独的 IP 地址空间——在这种情况下,它是一个 Cassandra 数据库。然后,您可以利用代理、 sidecars和网关在现有的Kube网络上运行它。在这篇文章中,我们不会深入探讨这一点,但我们有一些关于如何跨 K8s 集群连接有状态工作负载的很棒的内容,这些内容将向您展示如何从高层次上做到这一点。

所以,接下来是什么?数据包在流动,但现在有一些新的 K8s诡计要处理。假设您已经准备好了网络并拥有了所有适当的路由,那么这些集群之间至少在 IP层存在一些连接。您拥有 IP 连接 Pod,并且Cluster 1 可以与 Pods 和Cluster 2 通信,但您现在还需要考虑一些新的事情。  

服务发现

在K8s 网络中,身份是暂时的。由于集群事件,Pod可能被重新安排并接收新的网络地址。在某些应用中,这不是问题。在其他情况下,比如数据库,网络地址就是身份——这可能导致意外行为。尽管 IP 地址可能会发生变化,但随着时间的推移,我们的存储以及每个 pod 所代表的数据都会保持不变。我们必须有一种方法来维护地址到应用程序的映射。这就是服务发现的切入点。

在大多数情况下,服务发现是在 K8s内通过DNS 实现的。即使 pod 的 IP 地址可能发生变化,它也可以具有基于 DNS 的持久身份,该身份会在集群时间发生时被更新。这听起来很不错,但是当我们进入多集群的世界时,我们必须确保我们的服务可以跨集群边界被发现。作为Cluster  1 中的 pod,我应该能够获取Cluster  2 中的 pod 的地址。

DNS 存根

解决这个难题的一种方法是 DNS 存根。在这个配置中,我们配置 K8s DNS 服务,将特定域后缀的请求路由到远程集群。有了完全限定的域名,我们就可以将 DNS 查找请求转发到适当的集群进行解析并最终路由。

这里的问题是,每个集群都需要通过 kubelet 标志设置单独的 DNS 后缀,这在所有 K8s 中都不是一个选项。一些用户通过使用命名空间名称作为 FQDN 的一部分来配置存根解决此问题。这是可行的,但有点像黑客,不是正确设置集群后缀。

托管 DNS

另一种类似于 DNS 存根的解决方案是使用托管 DNS 产品。 在 GCP 的情况下,有  Cloud DNS 产品,可以将本地 DNS 表项复制到 VPC 级别,供外部集群甚至同一 VPC 内的虚拟机进行解析。 此选项提供了很多好处,包括:

消除管理集群托管 DNS 服务器的开销——云 DNS 不需要扩展、监控或管理 DNS 实例,因为它是托管的 Google 服务。

每个Google K8s引擎(GKE)节点上DNS查询的本地解析——与NodeLocal DNSCache类似,云DNS将DNS响应存到本地,提供低延迟和高可扩展性DNS解析。

与 Google Cloud 的操作套件集成——提供了 DNS 监控和日志记录。

VPC 范围DNS——提供多集群、多环境和 VPC 范围的 K8s 业务解析。

用于多集群服务发现的复制托管 DNS

Cloud DNS 抽象了许多传统的开销。 云提供商将管理伸缩性、监控和安全补丁,以及您期望从托管产品中获得的所有其他方面。 对于一些提供商来说,GKE还提供了一个节点本地 DNS 缓存,它通过在较低级别运行 DNS 缓存来减少延迟,这样您就不用等待 DNS 响应。

从长远来看,如果您只在单个云中,专门用于DNS的托管服务将可以正常工作。 但是,如果您跨越多个云提供商和本地环境的集群,托管产品可能只是解决方案的一部分。

云本地计算基金会https://www.cncf.io/(CNCF) 提供了多种选择,并且有大量开源项目缺思在帮助缓解这些痛点方面取得了很大的发展,尤其是在跨云、多云、 或混合云类型的场景。

译者介绍

康少京,51CTO社区编辑,目前从事通讯类行业,底层驱动开发岗位,研究过数据结构,Python,现对操作系统和数据库等相关领域感兴趣。

原文标题:Taking Your Database Beyond a Single Kubernetes Cluster,作者:Christopher Bradford

链接:https://dzone.com/articles/taking-your-database-beyond-a-single-kubernetes-cl