本文关键点
大多数在 Kubernetes 之上构建应用程序的开发人员仍然主要依赖于无状态协议和设计。问题是,只关注无状态设计会忽略分布式系统中最难的部分:管理状态数据。
挑战不是设计和实现服务本身,而是服务之间的空间管理:数据的一致性保证、可靠的通信、数据的复制和故障转移、组件故障的检测和恢复、分片、路由、共识算法等等。
自 Kubernetes 和 Akka 在应用程序软件栈各自负责不同的层和功能以来,两者就可以很好地协作了。Kubernetes 允许粗粒度的容器级弹性和可伸缩性管理。Akka 允许细粒度的实体级弹性和可伸缩性管理。
Akka 在过去两年中发展迅速。如今,它的月下载量约为 500 万次,而两年前是 50 万次。
在今年夏天在纽约举行的 QCon 大会上,Jonas Bonér 发表了会议上最受欢迎的演讲之一,他专注于设计事件优先的微服务。在 InfoQ 的问答中,我们请 Bonér 解释“从整体大型系统设计中引入坏习惯”对于服务设计来说是一条没有出路的路,他认为自己的 Akka 框架适用于云计算本地栈。
InfoQ:在 QCon 上,您说过,“当开始微服务之旅时,应该注意不要最终弄成一堆紧耦合的‘微型独体应用(microliths)’,因为你可能会从大型独体应用的设计中将一些坏习惯带到微服务中,使服务之间产生非常紧密的耦合。”,您可以进一步帮我们解释一下吗?
Jonas Bonér:在云本地应用程序开发领域,我仍然看到了对无状态协议(通常是同步协议)的强烈依赖。
大多数在 Kubernetes 之上构建应用程序的开发人员仍然主要依赖于无状态协议和设计。
它们包含容器,并且常常保留旧的体系结构、设计、习惯、模式、实践和工具——这些都是为运行在强大的 RDBMS 之上的单节点系统的整体世界而设计的。
问题是,只关注无状态设计会忽略分布式系统中最难的部分:管理状态数据。
忽略最困难的部分,把它的责任从应用程序层中剥离出来,这听起来可能是个好主意。有时确实如此,然而,随着应用程序越来越以数据为中心和数据驱动,通过一种高效、性能和可靠的方式管理、处理、转换和丰富接近应用程序本身的数据,从而获得数据的所有权比以往任何时候都更加重要。
许多应用程序无法为每次数据访问或存储支付往返数据库的费用,它们需要近实时地连续处理数据,从永无止境的数据流中挖掘知识。为了可伸缩性、低延迟和吞吐,这些数据在存储之前通常还需要以分布式方式进行处理。
InfoQ:有状态服务一直被认为是主流容器采用的最大障碍。告诉我们为什么这是一个如此棘手的领域?
Bonér:将容器视为逻辑上相同的单元(这些单元可以被替换、启动和移动)的策略非常适合于无状态服务,无需太多思考,但是与您想要管理分布式有状态服务和数据库的方式相反。
首先,有状态实例并不是简单可替换的,因为每个实例都有自己需要考虑的状态。
其次,有状态副本的部署通常需要在副本之间进行协调,比如引导依赖顺序、版本升级、模式更改等等。
第三,复制需要时间,从复制开始的机器将承受比平时更大的负载,因此,如果在负载下启动一个新的副本,实际上可能会导致整个数据库或服务崩溃。
解决这个问题的一种方法,是将状态管理委托给 Kubernetes 集群之外的云服务或数据库。也就是说,如果我们想使用 Kubernetes 以统一的方式管理您的所有基础设施,那么我们该怎么做呢?
此时,Kubernetes 对运行非云本地的有状态服务的问题的回答是 StatefulSet 的概念,它确保每个 pod 都有一个稳定的标识和专用磁盘,这些磁盘在重启时(甚至在重新调度到另一台物理机器之后)会保留数据。因此,现在在 Kubernetes 上部署分布式数据库、流数据管道和其他有状态服务是可能的(尽管仍然很有挑战性)。
我们需要的是新一代的工具,这些工具允许开发人员构建真正的云本地有状态服务,这些服务只具有 Kubernetes 提供给无状态服务的基础设施需求。这并不是反对使用像 Kubernetes 和 istia 这样的低级基础设施工具(它们显然带来了大量的价值),而是呼吁基础设施和应用程序层之间进行更紧密的协作,以维护整体的正确性和安全性保证。
InfoQ:请告诉我们 Akka 适用于 Kubernetes 软件栈有状态服务需求的地方。
Bonér:实际上,困难的部分不是设计和实现服务本身,而是管理服务之间的空间。所有的困难在于:数据的一致性保证、可靠的通信、数据的复制和故障转移、组件故障的检测和恢复、分片、路由、协商一致性算法等等。把所有这些都缝合在一起,并长期保持,你自己做是非常非常困难的。
端到端正确性、一致性和安全性对于不同的服务意味着不同的东西,它们完全依赖于用例,不能完全外包给基础设施。我们需要的是云计算的编程模型,搭配一个可以做繁重工作的运行时,让我们专注于商业价值的构建,而不是陷入网络编程的复杂性和故障模式,我相信 Akka 搭配 Kubernetes 就是可以成为这样的解决方案。
Akka 是一个开源项目,创建于 2009 年,旨在成为分布式系统和云计算的结构和编程模型。
Akka 在最真实的意义上是云本地的,在“云本地”一词出现之前,它构建出来直接在云中运行。
Akka 基于 Actor 模型,并建立在Reactive Manifesto中概述的原则之上,该宣言将 Reactive 系统定义为一组架构设计原则,这些原则旨在满足当今和未来系统所面临的需求。
在 Akka 中,工作和状态单元称为参与者,可以看作是有状态、容错、隔离和自治的组件或实体。就资源而言,这些参与者/实体是非常轻量级的,您可以在一台机器上轻松地同时运行数百万个参与者/实体,并使用异步消息传递进行通信。它们具有自动自修复的内置机制,默认情况下是可分发和位置透明的。这意味着它们可以根据需要在集群中伸缩、复制和移动,针对应用程序的使用方式作出反应,这对参与者/实体的用户来说是透明的。
InfoQ:那么当 Kubernetes 和 Akka 一起使用时,它们之间在哪里做关注点的分离呢?
Bonér:一种方法是,Kubernetes 非常擅长管理和编排软件的“盒子”(容器),但是管理这些盒子只会让你走到一半。同样重要的是你在盒子里放了什么,这是 Akka 可以帮助你的。
Kubernetes 和 Akka 结合得非常好,它们各自负责应用程序软件栈中的不同层和功能。Akka 是编写应用程序的编程模型,它支持运行时,帮助管理业务逻辑、数据一致性和完整性、操作语义、
分布式和本地工作流和通信、与其他系统的集成等。Kubernetes 是一种运维工具,以统一的方式管理大量的容器实例,帮助管理容器的生命周期、容器的版本控制和分组、容器间的路由通信、管理容器之间的安全性、身份验证和授权等。
从本质上说,Kubernetes 的工作是为应用程序提供足够的计算资源,将外部流量发送到应用程序中的某个节点,并管理诸如访问控制之类的东西;而 Akka 的工作则是决定如何在给予它的所有计算资源中分配工作。
InfoQ: Akka 采用了一种“让它崩溃”的方法,失去一个参与者并不重要,因为另一个参与者会接手工作。您能解释一下它在容器环境中是如何工作的吗?管理员需要进行干预吗?
Bonér:传统的基于线程的编程模型只提供一个控制线程,所以如果这个线程异常崩溃,您就有麻烦了。这意味着您需要在这个线程中显式地处理所有错误。异常不会在线程之间传播,也不会在网络中传播,因此甚至无法发现有什么失败。但是丢失线程,或者在最坏的情况下,丢失整个容器是非常昂贵的。更糟糕的是,使用同步协议可能导致这些故障跨整个应用程序产生级联效应。我们可以做得更好。
在 Akka 中,你在所谓的“主管层次结构”中设计应用程序,参与者在其中监视彼此的健康状况并管理彼此的失败。如果一个参与者失败了,它的错误将被隔离并包含在其中,并被具体化为一条消息,该消息将被异步发送(如果需要的话,将跨网络发送)给它的监督参与者,后者可以在一个安全健康的上下文中处理失败,并根据声明定义的规则自动重启失败的参与者。这自然会产生一种非防御性的编程方式和一种快速失败(和恢复)的方法,也称为“让它崩溃”。
这听起来可能与 Kubernetes 的角色重叠,确实 Kubernetes 和 Akka 都有助于管理弹性和可伸缩性,但是在应用程序软件栈中具有不同的粒度级别。你还可以从细粒度与粗粒度弹性和可伸缩性的角度来研究这两种技术。
Kubernetes 允许对弹性和可伸缩性进行粗粒度容器级别的管理,其中将容器作为一个整体进行复制、重新启动或伸缩。Akka 允许对弹性和可伸缩性进行细粒度的实体级管理(与应用程序紧密协作),其中每个服务本身就是一组实体副本,这些副本在需要时被复制、重新启动和伸缩,由 Akka 运行时自动管理,而不需要运维人员或 Kubernetes 进行干预。
InfoQ:您能给我们介绍一下 Akka 的应用吗?你现在每月能看到有多少次下载?
Bonér:Akka 在过去两年中发展迅速。如今,它的月下载量约为 500 万次,而两年前是 50 万次。如果您对项目的一些早期历史和里程碑感兴趣,这张信息图中有一些很酷的数据点。
关于受访者
Jonas Bonér 是 Lightbend 的创始人和 CTO,他是 Akka 项目的创造者、响应宣言的发起者和合著者,也是 Java 的拥护者。
查看英文原文:Stateful Service Design Considerations for the Kubernetes Stack
评论 1 条评论