通过部署群集内数据平面构建Kubernetes原生SaaS应用

2020-10-22 09:41:28

在Pixie,我们正在开发Kubernetes本地监控系统,该系统完全在用户集群内存储和处理结果数据。这是讨论有效构建Kubernetes原生应用程序的技术和最佳实践的一系列帖子中的第一篇。在这篇文章中,我们探讨了使用完全位于集群内的空隙部署和分别在云和集群之间拆分控制和数据平面的系统之间的权衡。

为Kubernetes平台构建的一个好处是,它简化了将应用程序部署到用户环境的过程,通常只需要几个简单的步骤,如应用一组YAML或安装Helm Chart。在几分钟内,用户就可以轻松地在其群集上拥有该应用程序的运行版本。但是,现在这些应用程序完全在Prem上运行,开发人员很难进行管理。在许多情况下,推出重大更新或错误修复依赖于让用户手动更新其部署。这对开发人员来说是不可靠的,对用户来说是繁重的。

为了解决这个问题,我们提出了一个互联的本地架构,它将管理应用程序的数据和控制面的责任分别委托给在集群和开发人员管理的云环境中运行的部署。更具体地说,部署在用户集群中的应用程序只负责收集数据并使数据可访问。一旦建立了此数据层的基础,逻辑基本保持稳定,并且不会频繁更新。同时,云托管系统管理应用程序的核心功能和编排。因为云是由开发者自己管理的,所以他们可以自由地执行更新,而不需要依赖用户。这允许开发人员快速迭代其系统的功能,同时保持Prem上的数据局部性。

这种责任分立的架构在许多硬件产品中很常见,因为外部因素可能会使部署在物理设备上运行的软件的更新具有挑战性。例如,尽管有这些物理限制,Ubiqiti的UI通过将功能委托给他们的云并将他们的物理路由器保持在数据平面内,能够提供丰富的功能集。同样,WebRTC是大多数现代浏览器中内置的标准,用于处理语音和视频数据。尽管浏览器更新不频繁,但分离的数据层和控制层允许开发人员在WebRTC之上自由构建各种不同的应用程序集。这种体系结构在企业软件中仍然相对少见,但已经被流行的产品(如Harness、Streamset和Anthos)采用。

然而,设计互联的本地架构说起来容易做起来难。在构建这样的系统时,您可能会遇到的一个挑战是如何通过托管在云中的UI从用户集群上运行的应用程序查询数据。为此,我们探索了两种方法:

为简洁起见,我们将用户集群上运行的应用程序称为卫星。

在卫星上执行查询的最简单方法是让UI直接向卫星本身发出请求。要做到这一点,UI必须能够从云中获取卫星的(1)状态和(2)地址,以便它知道卫星是否可用于查询以及它应该向何处发出请求。

跟踪程序状态的常用技术是在程序(卫星)和监控系统(云)之间建立心跳序列。这通常是通过让卫星首先向云发送注册消息来实现的。在注册期间,卫星要么提供标识符,要么通过云分配标识符,该标识符用于在后续的心跳消息中标识卫星。

注册后,卫星开始向云端发送周期性的心跳信号,以表明它是活的和健康的。可以在这些心跳中发送附加信息。在我们的示例中,我们还附加了卫星的IP地址。或者,如果IP地址不受更改,也可以在注册期间发送。云记录卫星的状态和地址,以便UI可以查询。

现在,当UI想要向卫星发出请求时,它首先向云查询地址,然后直接向该地址发出请求。

太棒了!那还不算太糟。在许多情况下,许多云/分布式卫星架构已经通过心跳通信来跟踪卫星状态,因此发送额外的地址不成问题。然而.。如果您的UI在浏览器上运行,并且您的卫星正在通过HTTPS响应(很可能使用自签名证书),那么您还没有完成……。

因为卫星的SSL证书,浏览器阻止了我们的请求!用户可以继续并直接导航到卫星的地址,在那里浏览器提示用户是否要绕过无效的证书。

然而,这将需要在每个卫星上完成,并且会破坏用户的整体体验。可以为IP地址生成SSL证书,但这并不常见,大多数免费的证书颁发机构都不提供此功能。如果卫星的IP地址变化,这种方法也很复杂。

在您控制的子域下预先生成SSL证书,例如:<;uuid>;.sat ites.youdomain.com。使用任何免费的证书颁发机构都很容易完成此步骤,如果子域具有众所周知的DNS地址,则可以安全地完成此步骤。您应该确保生成比预期卫星数量更多的SSL证书。

当卫星注册到云时,应该为其分配一个未使用的SSL证书和关联的子域。SSL证书应该安全地发送到卫星,并且卫星的代理应该更新以使用新的证书。

当云从其心跳接收到卫星的IP地址时,它会更新卫星子域的DNS记录以指向该IP地址。

在执行查询时,UI现在可以安全地向卫星分配的子域发出请求,而不是直接向其IP地址发出请求,所有这些都具有有效的证书!

最后,直接向卫星发出请求比我们最初想象的更复杂(也更麻烦)。该解决方案也不能很好地扩展,因为需要预先生成SSL证书。没有固定的卫星数量,或者没有卫星数量的上限,不久之后,所有的证书都已经分配完毕,需要有人介入并手动生成更多的证书。可以动态生成证书及其DNS记录,但我们发现这些操作可能需要太长时间才能传播到所有网络。还需要注意的是,此方法可能会违反自动生成SSL的服务条款,并且容易受到通配符证书的常见安全风险的影响。

当卫星位于防火墙之后时,只能由网络内的用户查询。这进一步确保不会有敏感数据离开网络。

正如在前面的方法中看到的,让UI向云发出请求以避免任何证书错误是最容易的。但是,我们仍然希望由卫星本身来处理实际的查询执行。为了解决这个问题,我们设计了另一种方法,遵循以下一般步骤:

云必须能够同时处理对许多不同卫星的多个查询。作为响应,卫星将流式传输批量数据,服务器需要将这些数据发送给正确的请求者。有这么多消息来回飞来飞去,所有这些消息都需要包含在它们自己的请求/应答通道中,我们认为这将是消息总线的完美工作。

我们建立了一个希望消息总线满足的标准列表:

它应该快速接收和发送消息,特别是在接收端有用户等待的情况下。

它应该能够处理相对较大的消息。卫星的查询响应可以批处理成许多较小的消息,但是单个数据点的大小仍然不是很小。

类似地,由于卫星的响应可能被批处理成许多消息,因此消息总线应该能够在任何给定时间处理大量涌入的消息。

随时开通新频道应该很容易。我们可能希望为每个请求或每个卫星创建一个新频道,所有这些我们都没有固定的数字。

我们简要地考虑了Google Pub/Sub,它有严格的配额要求(每个Google项目只有10,000个主题),以及其他项目,如Apache Pulsar。但是,我们主要考虑了两个消息传递系统:Apache Kafka和NATS。卡夫卡和NATS的总体比较已经在其他博客中进行了详细的讨论。在这篇博客文章中,我们的目标是根据上面的要求对这两个系统进行比较。

我们在很大程度上依赖于其他人执行的基准测试,以基于消息大小和消息量来判断延迟。这些结果倾向于NATS。

我们还想在我们的特定用例上测试每个系统,并执行以下基准测试:

服务器上运行的名为RequestProxyer的服务接收消息并将其放在主题A上。

RequestProxyer读取有关主题B的消息,并将响应写回WebSocket。

在这种情况下,为基准记录的延迟是从RequestProxyer中接收到WebSocket消息的时间,到服务器从订阅者接收响应消息的时间。

这些基准测试在具有N1-STANDARD-4节点的3节点GKE集群上运行,具有6字节的静态消息。这些结果可能并不适用于所有环境。我们也承认这些系统不是为这个特定的用例而构建的。

我们最终选择了NATS作为我们的消息传递系统。上面别人执行的基准测试和我们自己的基准测试表明,NATS能够有效地处理我们的请求和响应消息传递模式。我们还发现,在NATS中即时创建主题非常容易,而在Kafka上创建主题可能相当复杂,因为分区必须在启动前确定。鉴于我们将支持许多短期查询,我们希望避免任何主题创建开销。这些要点,再加上NATS较低的操作复杂性,使其成为我们案件的明显赢家。重要的是要注意到,卡夫卡的系统是为了提供额外的保证而建造的,并有许多积极的方面,这可能是其他用例所必需的。

我们的查询请求管道的实际实现看起来与我们上面运行的基准测试案例非常相似。

负责处理查询请求的云服务收到消息,并在新的goroutine中启动一个RequestProxyer实例。

RequestProxyer为查询生成一个ID,并将查询及其ID转发到正确的卫星,方法是将一条消息放在卫星/<;sat_id>;NATS主题上。它等待对REPLY-<;Query-ID NATS主题的响应。

负责处理卫星通信(如心跳)的服务订阅了sat/*NATS主题。它读取查询请求,并通过其通常的通信信道将其发送到适当的卫星。卫星将响应流回此服务。然后,该服务将这些响应放在REPLY-<;query-id>;NATS主题上。

RequestProxyer接收关于Reply-<;query-id>;主题的响应,并将它们发送回UI。

值得注意的是,在这种方式下,由于数据现在是通过云传输,而不是直接从卫星传输到浏览器,因此可能会有额外的网络延迟。

在防火墙后的群集中,通过云代理请求将允许网络外用户访问数据。这既可以是积极的,也可以是消极的,因为它使应用程序更容易使用,但依赖于离开网络的潜在敏感数据。

我们在Pixie中使用了这两种方法,并且发现任何一种方法都可以让我们高效可靠地查询客户集群中的数据。通过提供这两个选项,客户可以灵活地选择最符合其安全需求的架构。我们相信这些技术对于任何内部互联架构都很有用,具体的方法应该根据总体用例和特定于系统本身的约束来选择。

总体而言,为Kubernetes原生应用程序设计一个分离的数据/控制平面架构将帮助开发人员快速迭代,尽管Kubernetes部署具有本地性质。