- 《架构师》2021年11月
- InfoQ中文站
- 9493字
- 2022-06-30 16:30:54
推荐文章 | Article
Kubernetes上分布式系统的演化
作者 Bilgin Ibryam 译者 张卫滨
现代分布式应用
我想为这次演讲预先设置一些背景,在这里当我提到分布式系统时,我所指的是由多个组件组成的系统,可能会有数百个这样的组件。这些组件可能是有状态的、无状态的或者是无服务器的。除此之外,这些组件可以使用不同的语言创建,运行在混合环境之中,开发时使用的是开源技术和开发标准,支持互操作性。我相信你也可以使用闭源的软件创造这样的系统,或者在AWS和其他的地方创建它们。具体到这次演讲,我会特别关注Kubernetes生态系统,以及如何在Kubernetes平台上创建这样的系统。
我们从分布式系统的需求开始。我所想的是我们想要创建一个应用或者服务,并编写一些业务逻辑。那么,我们需要从平台和运行时环境得到哪些支撑以构建分布式系统呢?从基础来讲,最初的需求是我们需要一些生命周期管理的能力。当我们使用任意语言编写应用的时候,我们希望能够稳定地打包和部署应用,进行回滚和健康检查。并且能够将应用放到不同的节点上,进行资源隔离、扩展、配置管理,以及所有类似这样的事情。这些都是我们创建分布式应用所需要的最基本的东西。
第二个基石是网络相关的。我们有了一个应用之后,就希望它能够可靠地连接到其他的服务上,不管其他的服务在集群内还是在集群外部。我们希望能有诸如服务发现、负载均衡等能力,我们还希望能够进行流量转移,不管是基于不同的发布策略还是其他的一些原因。在此之后,我们希望能够与其他的系统进行弹性通信,不管是通过重试、超时、断路器还是其他方式来实现。当然,还要有安全保障,要有足够的监控、跟踪、可观察性等等。
有了网络之后,下一件事就是我们希望能够与不同的API和端点进行对话,也就是资源绑定:与其他协议和不同的数据格式进行对话。甚至可能从一种数据格式转换成另外一种数据格式。在这里我还想要包括像轻量级过滤(light filtering)这样的事情,也就是当订阅一个主题的时候,我们可能只关心特定的事件。
你认为最后一类会是什么呢?它就是状态。当我提到状态和状态化抽象的时候,我讨论的不是实际的状态管理,比如数据库或文件系统所做的事情。我更多的是讨论开发人员对那些幕后依赖状态的功能的抽象。也许,你需要进行工作流管理。可能你想要管理长时间运行的进程,或者要进行时间调度,或者要实现一些cron job来定期运行服务。也许,你想要实现分布式缓存、保证幂等性或者能够回滚。所有的这些都是开发人员层面的原语(primitive),但是在幕后,它们依赖于一些状态。我们希望能拥有这些抽象来创建健壮的分布式系统。
我们将会使用这个分布式系统原语的框架来评估在Kubernetes和其他项目上这些内容的变化。
单体架构:传统中间件的能力
假设我们从单体架构开始,考虑如何获取这些能力。在这种情况下,当我提到单体的时候,在分布式应用这个上下文中,我想到的是ESB。ESB的功能是非常强大的,当检查我们的需求列表时,我会说ESB对所有有状态的抽象都有很好的支持。
借助ESB,我们可以实现长期运行的进程的编排、实现分布式事务、回滚和幂等性。此外,ESB还提供了出色的资源绑定的能力,它们具备上百个连接器,支持转换、编排,甚至网络方面的能力。最后,ESB甚至可以实现服务发现和负载均衡。
它具备网络连接可靠性方面所有的功能,所以它能够实现重试。可能从本质来讲,ESB并不是分布式的,所以它不需要非常高级的网络和发布功能。ESB缺乏的主要是生命周期管理。因为它是一个单运行时,第一个问题就是你会被限制使用单一的语言。这通常对应真正的运行时在构建中所使用的语言,比如Java或.NET等。然后,因为它是一个单运行时,我们无法很容易地进行声明式部署或自动放置(automatic placement)。部署是非常庞大和重量级的,所以通常会涉及到人工的交互。而这样一个单体架构所带来的另外一个困难就是扩展性。“我们无法扩展单个组件。”
最后同样重要的是,关于隔离,不管是资源隔离还是故障隔离,在单体架构下都是无法实现的。从我们所需的框架来看,ESB单体架构并不合格。
云原生架构:微服务和Kubernetes
接下来,我建议我们看一下云原生架构以及这些需求是如何变化的。如果我们从一个比较高的层面来看这些架构的变化的话,就会发现云原生可能是从微服务运动开始的。微服务允许我们将一个单体应用按照业务域进行拆分。实践证明,容器和Kubernetes实际上是管理这些微服务的绝佳平台。接下来,我们看一下Kubernetes有哪些具体的特性和能力,使其成为微服务领域特别有吸引力的方案。
最初,能够进行健康探测是Kubernetes流行起来的原因。在实践中,这意味着当我们在一个pod中部署容器的时候,Kubernetes将会检查进程的健康状况。通常情况下,这样的进程模型是不够好的。我们仍然可能会有一个正在运行的进程,但它的状态并不健康。这就是为何还需要使用就绪状态(readiness)和活跃状态(liveness)检查的原因。在启动的时候,Kubernetes将会进行就绪检查来确定应用程序何时能够开始接受流量。它还会进行活跃性检查,以持续检查应用的健康状况。在Kubernetes之前,这种方式并不流行,但是如今几乎所有的语言、框架和运行时环境都有健康检查的功能,通过一个端点可以快速实现。
Kubernetes引入的另外一件事情就是应用程序的生命周期管理,在这里我的意思是,你不用再去控制服务何时启动以及何时关闭了。我们可以相信平台能够帮我们做到这一点。Kubernetes能够启动应用,能够关掉应用,也能将其在不同的节点间进行转移。要实现这一点,我们就必须正确地实现平台在启动和关闭时告诉我们的事件。
Kubernetes使之流行起来的另外一件事情就是部署以及以声明式的方式进行部署。这意味着,我们不必再去自己启动服务并根据日志判断它是否已经启动完毕。我们也不必再去手动升级实例,Kubernetes的声明式部署就可以帮我们做到这一点。根据你所选择的策略,它能够停掉旧的实例并启动新的实例。除此之外,如果出现问题的话,它还能够回滚。
另外一件事情就是声明所需的资源。当创建服务的时候,我们会将其容器化。有一项好的实践是告诉平台该服务需要多少CPU和内存。Kubernetes会利用这些信息为我们的工作负载找到最合适的节点。在Kubernetes出现之前,我们必须根据自己的标准手动将实例放到节点中。现在,我们可以根据自己的偏好设置指导Kubernetes,它将会为我们做出最佳的决策。
如今,在Kubernetes上,我们可以支持多种方式的配置管理。我们的应用程序运行时不需要进行任何的配置查找。Kubernetes会确保配置内容将会最终出现在工作负载所在的节点上。配置被会映射为一个卷或者环境变量,供应用程序使用。
实际上,我刚才提到的这些具体的能力也是互相关联的。例如,如果你希望实现服务的自动放置,那么就需要将服务的资源需求告诉Kubernetes。然后,你需要告诉它使用什么样的部署策略。为了让该策略能够正常运行,应用程序必须要实现来自环境的事件。它必须要实现健康检查。一旦我们将所有的最佳实践准备就绪并使用好这些能力,那么我们的应用就会成为一个优秀的云原生公民,它就可以在Kubernetes上实现自动化(这代表了在Kubernetes上运行工作负载的基础模式)。最后,关于在pod中如何组织容器、配置管理和行为等方面还有其他的模式。
我想简要讨论的下一个话题是关于工作负载的。从生命周期的角度来看,我们希望能够运行不同的工作负载。我们在Kubernetes上也可以做到这一点。运行十二要素应用(Twelve-Factor App)和无状态微服务是非常简单的。Kubernetes可以做到这一点。但这并不是唯一的工作负载类型。也许你还会有有状态的工作负载,在Kubernetes上我们可以通过stateful set实现它。
我们想要运行的另外一种工作负载可能是单例应用。我们想要在整个集群中运行该应用的唯一一个实例,我们希望它是一个可靠的单例应用。当它出现故障时,应该会再次重新启动。因此,你可以根据需要,以及这个应用应该最少要有一个实例还是最多有一个实例的语义,在stateful set和replica set之间做出选择。还有一种工作负载就是job和cron job,借助Kubernetes,它们也是可以实现的。
如果我们将Kubernetes所有的这些特性映射到我们的需求上,那么可以说Kubernetes满足了生命周期的要求。通常情况下,我所创建的需求列表的内容是由Kubernetes迄今为我们提供的功能所驱动的。这是所有平台的预期能力,Kubernetes能够为部署所提供的特性包括配置管理、资源隔离以及故障隔离。除此之外,它还支持不同的工作负载,不过无服务器本身除外。
这就是Kubernetes为开发人员提供的所有功能,那么我们该如何扩展Kubernetes?如何让它为我们提供更多的特性呢?因此,我想要描述我们目前常用的两种方式。
进程外的扩展机制
首先要提及的就是pod的概念,它是一种用来在节点上部署容器的抽象机制,pod能够为我们保证如下两点:
- 第一个就是部署方面的保证:pod中的所有容器最终将会位于相同的节点上。这意味着它们彼此之间可以使用localhost、基于文件系统的异步方式或者其他IPC机制进行通信。
- pod给我们的另外一个保证是关于生命周期的,pod中所有的容器并不是平等的。
根据使用的是init容器还是应用容器,你会得到不同的保证。例如,init是在开始时运行的,当pod启动的时候,它们会一个接一个地按顺序执行。只有当前面的容器成功完成后,后面的容器才会运行。它们有助于实现由容器驱动的类似工作流的逻辑。
而应用容器则是并行运行的。它们会在pod的整个生命周期中运行,这是sidecar模式的基础。sidecar可以运行多个容器,它们相互协作,共同为用户提供价值。这就是我们目前能够看到的扩展Kubernetes以获取额外能力的主要机制。
为了阐述下面的能力,我必须要简单介绍一下Kubernetes内部的运行方式。它是基于一种叫做调谐循环(reconciliation loop)的机制实现的。调谐循环的理念就是将期望状态驱动到实际状态。在Kubernetes中,有很多功能都是基于这种机制实现的。例如,当我想要两个pod实例的时候,这就是系统的期望状态。Kubernetes会有一个持续运行的控制循环,它会检查是否有两个pod的实例在运行。如果实际运行的实例数量不是两个的话,它会计算差异,看实例的数量是只有一个还是超过了两个。它会确保最终有两个实例。
这方面的例子还有很多,有些是关于replica set或stateful set的。资源定义会映射到具体的控制器,每种资源定义都会有一个控制器。控制器能够确保现实状况会匹配期望,你甚至可以编写自定义的控制器。
当在pod中运行应用的时候,我们无法在运行时加载配置文件的变化。但是,我们可以编写自定义的控制器,让它监控config map的变化并重新启动pod和应用,这样我们就能够获取到配置的变更了。
实践证明,即便Kubernetes有一个很好的资源集合,它也无法满足我们所有不同的需求,因此Kubernetes引入了自定义资源定义(custom resource definition)的概念。这意味着我们可以建模自己的需求,并定义Kubernetes中运行的API。它与其他的Kubernetes原生资源是共存的。我们可以使用任何语言编写自己的控制器,它们只需要理解我们的模型即可。我们可以使用Java实现一个ConfigWatcher,描述我们前面所阐述的内容。这也就是operator模式,一个与自定义资源定义一起运行的控制器。如今,我们看到已经有了很多的operator,这是扩展Kubernetes实现额外能力的第二种方式。
接下来,我想要介绍几个在Kubernetes之上构建的几个平台,它们大量使用了sidecar和operator来为开发者提供额外的能力。
什么是服务网格?
我们首先从服务网格开始,那什么是服务网格呢?
我们有两个服务,服务A想要调用服务B,而且可能会使用任意的语言。假设这就是我们的工作负载。服务网格使用了sidecar控制器并将一个代理注入到了我们的服务旁边。这样在pod中就有了两个容器。代理是透明的,我们的应用完全不会感知到代理的存在,但是它会拦截所有传入和传出的流量。另外,代理还充当了数据防火墙的角色。
这些服务代理的集合代表了数据平面,它们通常是非常小且无状态的。为了获取所有的状态和配置,它们会依赖于控制平面。控制平面是有状态的部分,它会保持所有的配置,收集指标,做出决策并与数据平面进行交互。实际上,我们还需要另外一个组件,也就是将数据输入到集群的API网关。有些服务网格有自己的API网关,有些则会使用第三方的网关。但是,如果你查看一下所有的这些组件的话,会发现它们都提供了我们想要的能力。
API网关主要的关注点在于抽象我们的服务实现。它隐藏了细节并提供了边界能力。服务网格所做的事情则恰好相反。在某种程度上,它增强了服务中的可见性和可靠性。联合起来,我们可以说API网关和服务网格满足网络方面的所有需求。为了获取Kubernetes之上的网络能力,单纯使用服务是不够的,我们还需要服务网格。
什么是Knative?
我想讨论的下一个话题是Knative,这是由谷歌在几年前发起的项目。它是在Kubernetes之上的一个层,提供了无服务器的能力,它包括两个主要的模块:
- Knative Serving:主要关注请求-答复的交互
- Knative Eventing:更多地用于事件驱动的交互。
我们来简单体会一下Knative Serving是什么。借助Knative Serving,我们会定义一个服务,但是它与Kubernetes服务不同,这是一个Knative服务。使用Knative服务定义完工作负载之后,就会得到一个具备无服务器特性的部署(deployment)。我们不需要建立和运行实例,请求抵达时,它可以从零开始启动。这样我们就具有了无服务器的能力,它可以快速扩展,也可以收缩至零。
Knative Eventing为我们提供了一个完全声明式的事件管理系统。假设我们有一些想要集成的外部系统和一些外部的事件生产者,在底层,我们让自己的应用运行在一个容器中,它具有一个HTTP端点。借助Knative Eventing,我们可以启动一个broker,它可以触发一个映射到Kafk的broker,也可以触发内存或云服务。除此之外,我们还可以启动一个导入器(importer),将其连接到外部系统上并将事件导入到我们的broker中。比如,这些导入器可以基于Apache Camel,它有数百个连接器。
事件进入到broker之后,我们就可以通过YAML声明的形式,让我们的容器订阅这些事件。在我们的容器中,并不需要任何的消息客户端,如Kafka客户端。我们的容器会以HTTP POST的形式得到事件。这是一个完全由平台托管的消息基础设施。作为开发人员,我们只需要在容器中编写业务逻辑,而不需要处理任何消息相关的逻辑。
从我们的需求来看,Knative满足了其中的一些。从生命周期的角度来看,它给了我们的工作负载无服务器的能力,所以能够收缩至零,并从零开始进行扩展。从网络的角度来看,它与服务网格有一些重叠,Knative也能进行流量转移。从绑定的角度来看,它对使用Knative导入器的绑定提供了很好的支持。它还能为我们提供Pub/Sub或点到点的交互,甚至某些顺序化(sequencing)处理的功能。它满足了一些类别的需求。
什么是Dapr?
另外一个使用sidecar和operator的项目是Dapr,它是由微软在最近发起的一个项目,并且正在快速流行起来。1.0版本已经被认为可以投入生产环境使用了。这是一个sidecar形式的分布式系统工具集,Dapr中的任何内容都是以sidecar的形式提供的,并且具有一组他们称之为构建块(building block)或者能力的特性。
那Dapr的这些能力是什么呢?第一组能力是关于网络的。Dapr能够实现服务发现以及服务间的点到点集成。类似的,它还能够进行跟踪、可靠性通信、重试以及恢复。第二组能力是关于资源绑定的:
- 它有大量到云API和不同系统的连接器
- 还能实现消息的发布/订阅以及其他逻辑。
有意思的是,Dapr还引入了状态管理的概念。除了Knative和服务网格提供的功能之外,Dapr还在状态存储之上进行了抽象。除此之外,我们还可以在存储机制的支持下与Dapr进行基于键值的交互。
从较高的层级来看,这种架构是将我们的应用放在最顶层,而应用可以采用任意的语言。我们可以使用Dapr提供的客户端库,但并不强制要求这样做。我们可以使用语言本身的特性来发起HTTP和gRPC调用sidecar。它与服务网格差异在于Dapr sidecar并不是一个透明的代理。它是一个显式的代理,我们必须要在应用中调用它,并通过HTTP或gRPC进行交互。根据我们所需要的能力,Dapr可以与其他的系统进行交互,比如云服务。
在Kubernetes上,Dapr以sidecar的形式进行部署,它也可以在Kubernetes之外运行(它并非只能用在Kubernetes中)。除此之外,它还有一个operator,sidecar和operator是主要的扩展机制。还有一些其他的组件用来管理证书、处理基于actor的模型以及注入sidecar。我们的工作负载与sidecar进行交互,并且要完成与其他服务的对话,从而实现与不同云供应商的交互性。它还为你提供了额外的分布式系统的能力。
如果让我总结一下这些项目为我们提供了什么的话,那么我们可以说ESB是分布式系统的早期形态,在这里我们具有中心化的控制平面和数据平面,但是无法很好地进行扩展。在云原生中,我们依然有一个中心化的控制平面,但是数据平面是去中心化的,而且是通过良好的隔离能够实现高度的可扩展性。
我们始终会需要Kubernetes进行良好的生命周期管理,在此之上,我们可能会需要一个或多个附加组件。也许我们需要Istio实现高级的网络,需要使用Knative实现无服务器的工作负载,或者需要Dapr进行集成。这些框架能够与Istio和Envoy进行很好的协作。从Dapr和Knative的角度来看,我们可能必须要从中选择一个。将它们联合起来,我们就以云原生的方式提供了过去ESB上所拥有的功能。
未来云原生的新趋势:生命周期的趋势
在接下来的部分中,我提供了一个带有一定倾向性的项目列表,我认为这些领域的开发状态非常令人兴奋。
我想首先从生命周期开始。借助Kubernetes,我们可以为应用实现非常有用的生命周期管理,但是对于复杂的生命周期管理来讲,这可能是不够的。例如,如果你有一个更复杂的有状态应用,那么Kubernetes中的部署原语对你的应用程序来说就是不够的。
在这些场景下,我们就可以使用operator模式。我们可以使用operator进行部署和升级,也可以进行备份,可能会是将服务存储到S3上。除此之外,你可能还会发现Kubernetes中的健康检查机制不够好,比如仅有活跃性检查和就绪检查还是不够的。在这种情况下,我们可以借助operator实现更智能的活跃性检查和就绪性检查,并基于此进行恢复。
第三个领域是自动扩展和调优。你可以实现一个能够更好地理解你的应用的operator,从而能够在平台上自动调优。如今,有两个主要的框架来编写operator,分别是来自Kubernetes特别兴趣小组的Kubebuilder以及Red Hat所创建的operator框架所包含的Operator SDK。它包含下面几个组成部分:
Operator SDK允许我们编写operator,Operator Lifecycle Manager能够管理operator的生命周期,而OperatorHub则可以发布operator。如果感兴趣的话,我们可以看到上百个管理数据库、消息队列和监控工具的operator。从生命周期角度来看,operator可能是在Kubernetes生态系统中最活跃的开发领域。
网络的新趋势:Envoy
我选择的另外一个项目是Envoy。服务网格接口的引入能够让我们更容易地切换不同的服务网格实现。在部署方面,对Istio有了一些整合。现在,你不需要部署七个pod了,而只需要部署一次。更有意思的是Envoy项目在数据平面上所发生的变化。我们看到越来越多的第七层协议添加到了Envoy中。
服务网格添加了对更多协议的支持,比如MongoDB、ZooKeeper、MySQL、Redis,以及最近的Kafka。我看到Kafka社区现在正在进一步改善他们的协议,以便于对服务网格更加友好。我们可以期待,将会有更紧密的集成以及更多的能力。最可能出现的情况是,会有一些桥接能力。我们可以在应用程序的本地发起一个HTTP调用,而代理将会在幕后使用Kafka。我们还可能会在应用程序之外,在sidecar中为Kafka协议进行转换和加密。
另外一个令人兴奋的进展是引入了HTTP缓存。现在,Envoy可以进行HTTP缓存。在我们的应用中,不需要使用任何的缓存客户端。所有的这些都会在sidecar中透明地完成。这里会有窃听过滤器(tap filter),所以我们可以窃听流量并得到流量的副本。最近,随着WebAssembly的引入,这意味着如果你想为Envoy写一些自定义的过滤器,我们不必用C++来编写,也不必编译整个Envoy运行时。我们可以用WebAssembly编写过滤器,并在运行时部署。其中大部分功能仍在进行中。他们没有固步自封,这说明数据平面和服务网格没有裹足不前,只支持HTTP和gRPC。他们有兴趣支持更多的应用层协议,从而让我们支撑更多的用例。最主要的是,随着WebAssembly的引入,我们现在可以在sidecar上编写自定义逻辑。这种方式很好,只要你不把一些业务逻辑放在那里就行。
绑定的新趋势:Apache Camel
Apache Camel是一个进行集成的项目,它用很多的连接器,能够连接到使用企业级集成模式的不同系统中。Camel 3.0版本已经深度集成了Kubernetes,并使用了相同的原语,比如我们提到的operator。
我们可以在Camel中使用Java、JavaScript或YAML等语言编写集成逻辑。最新版本引入了一个在Kubernetes中运行并能理解我们的集成需求的Camel operator。当我们在编写Camel应用程序时,会将其部署到一个自定义的资源中,然后operator就能知道如何构建容器或找到依赖。根据平台的能力,无论是单纯的Kubernetes,还是与Knative结合的Kubernetes,它都能决定使用什么服务以及如何实现我们的集成需求。在我们的运行时之外,会有相当多的智能处理,但是它们都在operator中,所有的这一切运行都非常快。为什么我将其称之为一个绑定方面的新趋势呢?主要是因为Apache Camel的能力都是由它提供的连接器实现的。这里有趣的一点是它如何与Kubernetes深度整合。
状态的新趋势:Cloudstate
我想要讨论的另一个项目是Cloudstate以及状态相关的新趋势。Cloudstate是Lightbend的一个项目,主要专注于无服务器和函数驱动的开发。在最新版本中,他们正在使用sidecar和operator与Kubernetes进行深度集成。
这里的理念在于,当我们编写函数的时候,在函数中所要做的就是使用gRPC获取状态,然后与状态进行交互。所有的状态管理是在一个sidecar中进行的,这个sidecar与其他的sidecar形成了集群。借助它,我们可以实现事件溯源、CQRS、键值查找和消息功能。
从我们的应用程序的角度来看,我们并不了解所有这些复杂的事情。我们要做的只是调用一个本地的sidecar,而sidecar会处理这些复杂的问题。在幕后,它可以使用两个不同的数据源,而且它拥有所有的有状态抽象,而这是我们作为开发人员所需要的。
到目前为止,我们已经看到了云原生生态系统的现状和一些仍在进行中的最新进展。我们该如何理解这一切呢?
多运行时的微服务已然出现
如果我们看一下微服务在Kubernetes上的样子,就会发现我们需要一些平台性的功能。此外,我们还需要使用Kubernetes的功能,主要用于生命周期管理。然后,我们很可能会透明地使用一些服务网格,比如Envoy,以得到增强的网络能力,不管是流量路由、弹性、增强的安全性,还是为了监控的目的。除此之外,根据我们的使用场景,可能还需要Dapr或Knative,这取决于工作负载。所有的这些都是进程外的附加能力。我们剩下的就是编写业务逻辑了,不过这不是在这些基础设施之上,而是在另外一个单独的运行时中。最有可能出现的是,未来的微服务将是多个容器组成的多运行时,其中有些是透明的,有些是显式使用的。
智能sidecar和哑管道
如果看得再深入一点会是什么样子呢,我们使用某种高级别的语言编写业务逻辑。至于是什么语言并不重要,它不一定必须是Java,因为我们可以使用任意的语言来开发自定义逻辑。
业务逻辑与外部世界的所有交互都是通过sidecar进行的,sidecar会与平台集成并进行生命周期管理。它为外部系统进行网络抽象,并提供高级绑定能力和状态抽象。sidecar不需要我们进行开发。我们可以直接选取它,并使用YAML或JSON进行配置,然后就可以使用它了。这意味着我们可以很容易地更新sidecar,因为它不会嵌入到我们的运行时中。这样的话,打补丁和更新会更容易。它为我们的业务逻辑实现了多语言运行时。
微服务之后将是什么呢?
回到我最初的问题,微服务之后将是什么呢?
如果我们看一下架构是如何发展的,会发现从较高的层次来看,应用架构是从单体应用开始的。然而,微服务为我们提供了如何将单体应用拆分成独立的业务域的指导原则。从此之后,出现了无服务器和函数即服务(FaaS),我们可以进一步将系统拆分为操作(operation),这带来了极强的扩展性,因为我们可以独立扩展每个操作了。
我想说的是,也许FaaS并不是最好的模型,因为函数并不是实现复杂服务的最佳模型,在这种情况下,如果多个操作与同一个数据集进行交互的话,我们希望能够将它们放到一起。如果是这样的话,也许可以考虑我所说的Mecha架构多运行时,在这种架构中,我们将业务逻辑放到一个容器中,并将所有基础设施相关的关注点作为一个单独的容器。它们共同代表了一个多运行时微服务。这可能是更为合适的模型,因为它具有更好的属性。
我们可以得到微服务的所有好处,但我们依然将所有的领域、所有的限界上下文放到了同一个地方。我们在一个单独的容器中放置了所有的基础设施和所需的分布式应用,在运行时,我们会将它们组合在一起。目前,与之最接近的可能是Dapr。他们正在遵循这种模式。如果你只对网络方面感兴趣,使用Envoy可能也会接近这种模式。
作者介绍:
Bilgin Ibryam是红帽公司的产品经理和前架构师,是Apache软件基金会的提交者和成员。他是一位开源布道者、博主和演讲者,并且是Kubernetes Patterns和Camel Design Patterns技术图书的作者。Bilgin目前的工作重点是分布式系统、事件驱动架构,以及可重复的云原生应用开发模式和实践。请通过 @bibryam关注他,以了解未来类似主题的更新。
原文链接:Article: The Evolution of Distributed Systems on Kubernetes