云原生的诞生是为了解决传统应用在架构、故障处理、系统迭代等方面的问题,而开源则为企业打造云原生的架构贡献了中坚力量。本文作者在全身心投入开源以及每日参与云原生的过程中,对开源行业和云原生流系统解决方案有了不一样的思考与实践。
作者 | 李鹏辉 责编 | 唐小引
出品 | 新程序员
随着业务与环境的变化,云原生的趋势越来越明显。现在正是企业从云计算向云原生转型的时代,云原生理念经过几年落地实践的打磨已经得到了企业的广泛认可,云上应用管理更是成为企业数字化转型的必选项。可以说,现在的开发者,或正在使用基于云原生技术架构衍生的产品和工具,或正是这些产品和工具的开发者。
本文出自《新程序员·云原生和全面数字化实践》
云原生成为基础战略
那么,什么是云原生?每个人都有不同的解释。我认为,首先,云原生就是为了在云上运行而开发的应用,是对于企业持续快速、可靠、规模化地交付业务的解决方案。云原生的几个关键词,如容器化、持续交付、DevOps、微服务等无一不是在诠释其作为解决方案的特性与能力,而Kubernetes更以其开创性的声明式API和调节器模式,奠定了云原生的基础。
其次,云原生是一种战略。云原生的诞生是为了解决传统应用在架构、故障处理、系统迭代等方面存在的问题。从传统应用到云,与其说是一次技术升级,不如说将其视为战略转型。企业上云面临应用开发、系统架构、企业组织架构,甚至商业产品的全面整合,是否加入云原生大潮一次是将从方方面面影响企业长期发展的战略性决策。
搭配开源底色的云原生
近几年诞生的与架构相关的开源项目大部分采用云原生架构设计,开源为企业打造云原生的架构贡献了中坚力量。
开源技术与生态值得信任,云可以给用户带来好的伸缩性,降低资源浪费。云原生和开源的关系也可以从以CNCF为主的开源基金会持续推进云原生的发展中略窥一二。许多开源项目本身就是为云原生架构而生的,这是用户上云会优先考虑的基础软件特点。
以Apache软件基金会为例,它是一个中立的开源软件孵化和治理平台。Apache软件基金会在长期的开源治理中,总结出的Apache之道(Apache Way)被大家奉为圭臬,其中“社区大于代码”广为流传,即没有社区的项目是难以长久的。一个社区和代码保持高活跃度的开源项目,经过全世界开发者在多种场景的打磨,可以不断完善、频繁地升级迭代,并诞生丰富的生态以满足不同的用户需求。云原生大潮与当前开源大环境两种因素叠加,就会使那些伴随技术环境不断升级的优秀技术推陈出新、脱颖而出,不适应时代的技术会渐渐落后,甚至被淘汰。正如我之前所说,云原生是战略性决策,企业的战略性决策必定会首选最先进、最可靠的技术。
为云而生的消息流数据系统
前文讲述了云原生环境下开源的重要性,那么一个云原生的开源项目需要如何去设计、规划和演进?云原生时代的企业数字化转型应如何选择消息和流系统?在本文中,我将以自己全身心投入的开源云原生消息和流数据系统Apache Pulsar的设计和规划为例进行剖析。希望能够为大家提供参考思路,并为寻求消息和流数据系统解决方案带来启发。
回顾历史:消息与流的双轨制
消息队列通常用于构建核心业务应用程序服务,流则通常用于构建包括数据管道等在内的实时数据服务。消息队列拥有比流更长的历史,也就是开发者们所熟悉的消息中间件,它侧重在通信行业,常见的系统有RabbitMQ和ActiveMQ。相对来说,流系统是一个新概念,多用于移动和处理大量数据的场景,如日志数据、点击事件等运营数据就是以流的形式展示的,常见的流系统有Apache Kafka和AWS Kinesis。
由于之前的技术原因,人们把消息和流分为两种模型分别对待。企业需要搭建多种不同的系统来支持这两种业务场景(见图1),由此造成基础架构存在大量“双轨制”现象,导致数据隔离、数据孤岛,数据无法形成顺畅流转,治理难度大大提升,架构复杂度和运维成本也都居高不下。
图1 企业搭建不同的系统支持业务场景导致的“双轨制”
基于此,我们亟须一个集成消息队列和流语义的统一实时数据基础设施,Apache Pulsar由此而生。消息在Apache Pulsar主题上存储一次,但可以通过不同订阅模型,以不同的方式进行消费(见图2),这样就解决了传统消息和流“双轨制”造成的大量问题。
图2 Apache Pulsar集成消息队列与流语义
实现天然云原生的关键要素
上文提到,云原生时代带给开发者的是能够快速扩缩容、降低资源浪费,加速业务推进落地。有了类似Apache Pulsar这种天然云原生的消息和流数据基础设施,开发者可以更好地聚焦在应用程序和微服务开发,而不是把时间浪费在维护复杂的基础系统上。
为什么说Apache Puslar是“天然云原生”?这与在当初设计原型的底层架构有关。存储计算分离、分层分片的云原生架构,极大地减轻了用户在消息系统中遇到的扩展和运维困难,能在云平台以更低成本给用户提供优质服务,能够很好地满足云原生时代消息系统和流数据系统的需求。
生物学有一个结论,叫“结构与功能相适应”。从单细胞原生生物到哺乳动物,其生命结构越来越复杂,具备的功能也越来越高级。基础系统同理,“架构与功能相适用”体现在Apache Pulsar上有这样几点:
存储计算分离架构可保障高可扩展性,可以充分发挥云的弹性优势。
跨地域复制,可以满足跨云数据多备的需求。
分层存储,可充分利用如AWS S3等的云原生存储,有效降低数据存储成本。
轻量化函数计算框架Pulsar Functions,类似于AWS Lambda平台,将FaaS引入Pulsar。而Function Mesh是一种Kubernetes Operator,助力用户在Kubernetes中原生使用Pulsar Functions和连接器,充分发挥Kubernetes资源分配、弹性伸缩、灵活调度等特性。
基础架构:存储计算分离、分层分片
上文说到,Pulsar在诞生之初就采用了云原生的设计,即存储计算分离的架构,存储层基于Apache软件基金会开源项目BookKeeper。BookKeeper是一个高一致性、分布式只追加(Append-only)的日志抽象,与消息系统和流数据场景类似,新的消息不断追加,刚好应用于消息和流数据领域。
Pulsar架构中数据服务和数据存储是单独的两层(见图3),数据服务层由无状态的Broker节点组成,数据存储层则由Bookie节点组成,服务层和存储层的每个节点对等。Broker仅负责消息的服务支持,不存储数据,这为服务层和存储层提供了独立的扩缩容能力和高可用能力,大幅减少了服务不可用时间。BookKeeper中的对等存储节点,可以保证多个备份被并发访问,也保证了即使存储中只有一份数据可用,也可以对外提供服务。
图3 Pulsar架构
在这种分层架构中,服务层和存储层都能够独立扩展,提供灵活的弹性扩容,特别是在弹性环境(如云和容器)中能自动扩缩容,动态适应流量峰值。同时,显著降低集群扩展和升级的复杂性,提高系统的可用性和可管理性。此外,这种设计对容器也非常友好。
Pulsar将主题分区按照更小的分片粒度来存储(见图4)。这些分片被均匀打散,将会分布在存储层的Bookie节点上。这种以分片为中心的数据存储方式,将主题分区作为一个逻辑概念,分为多个较小的分片,并均匀分布和存储在存储层中。这样的设计可以带来更好的性能、更灵活的扩展性和更高的可用性。
图4 分片存储模型
从图5可见,相比大多数消息队列或流系统(包括Apache Kafka)均采用单体架构,其消息处理和消息持久化(如果提供了的话)都在集群内的同一个节点上。此类架构设计适合在小型环境部署,当大规模使用时,传统消息队列或流系统就会面临性能、可伸缩性和灵活性方面的问题。随着网络带宽的提升、存储延迟的显著降低,存储计算分离的架构优势变得更加明显。
图5 传统单体架构vs存储计算分层架构
读写区别
接着上述内容,我们来看一下消息的写入、读取等方面的区别体现在哪里。
首先看写入。图6左侧是单体架构的应用,数据写入leader,leader将数据复制到其他follower,这是典型的存储计算不分离的架构设计。在图6右侧则是存储计算分离的应用,数据写入Broker,Broker并行地往多个存储节点上写。假如要求3个副本,在选择强一致性、低延迟时两个副本返回才算成功。如果Broker有leader的角色,就会受限于leader所在机器的资源情况,因为leader返回,我们才能确认消息成功写入。
图6 单体架构与分层架构写入对比
在右侧对等的分层架构中,三个中任意两个节点在写入后返回即为成功写入。我们在AWS上进行性能测试时发现,两种结构在刷盘时的延迟也会有几毫秒的差距:在单机系统中落在leader上的topic会有延迟,而在分层架构中受到延迟影响较小。
在实时数据处理中,实时读取占据了90%的场景(见图7)。在分层架构中,实时读取可以直接通过Broker的topic尾部缓存进行,不需要接触存储节点,能够在很大程度上提升数据读取的效率和实时性。
图7 单体架构与分层架构读取实时数据对比
架构也导致了读取历史数据时的区别。从图8可见,在单体架构中,回放消息时直接找到leader,从磁盘上读取消息。在存储计算分离的架构上,需要将数据加载到Broker再返回客户端,以此保证数据读取的顺序性。当读取数据对顺序性没有严格要求时,Apache Pulsar支持同时并行从多个存储节点读取数据段,即使是读取一个topic的数据也可以利用多台存储节点的资源提升读取的吞吐量,Pulsar SQL也是利用这种方式来读取的。
图8 单体架构与分层架构读取历史数据对比
IO隔离
BookKeeper内部做了很好的数据写入和读取的IO隔离。BookKeeper可以指定两类存储设备,图9左侧是Journal盘存放writeheadlog,右侧才是真正存储数据的地方。即使在读取历史数据时,也会尽可能地保证写入的延迟不会受到影响。
图9 BookKeeper的IO隔离
如果利用云平台的资源,Pulsar的IO隔离可以让用户选择不同的资源类型。由于Journal盘并不需要存放大量的数据,很多云用户会根据自己的需求配置来达到低成本、高服务质量的目的,如Journal盘使用低存储空间、高吞吐低延迟的资源,数据盘选择对应吞吐可以存放大量数据的设备。
扩缩容
存储计算分离允许Broker和BookKeeper分别进行扩缩容,下面为大家介绍扩缩容topic的过程。假设n个topic分布在不同的Broker上,新的Broker加入能够在1s内进行topic ownership的转移,可视为无状态的topic组的转移。这样,部分topic可以快速地转移至新的Broker。
对于存储节点来说,多个数据分片散布在不同的BookKeeper节点上,扩容时即新加入一个BookKeeper,并且这种行为不会导致历史数据的复制。每一个topic在经历一段时间的数据写入后,会进行分片切换,即切换到下一个数据分片。在切换时会重新选择Bookies放置数据,由此达到逐渐平衡。如果有BookKeeper节点挂掉,BookKeeper会自动补齐副本数,在此过程中,topic不会受到影响。
跨云数据多备
Pulsar支持跨云数据多备(见图10),允许组成跨机房集群来进行数据的双向同步。很多国外用户在不同的云厂商部署跨云集群,当有一个集群出现问题时,可以快速切换到另外的集群。异步复制只会产生细微的数据同步缺口,但可以获得更高的服务质量,同时订阅的状态也可以在集群间同步。
图10 跨云数据多备
进入无服务器架构时代
Pulsar Functions与Function Mesh让Pulsar跨入了无服务器架构时代。Pulsar Functions是一个轻量级的计算框架,主要是为了提供一个部署和运维都能非常简单的平台。Pulsar Functions主打轻量、简单,可用于处理简单的ETL作业(提取、转化、加载)、实时聚合、事件路由等,基本可以覆盖90%以上的流处理场景。Pulsar Functions借鉴了无服务器架构(Serverless)和函数即服务(FaaS)理念,可以让数据得到“就近”处理,让价值得到即时挖掘(见图11)。
图11 单条Pulsar Function消息流转
Pulsar Functions只是单个应用函数,为了让多个函数关联在一起,组合完成数据处理目标,诞生了Function Mesh(已开源)。Function Mesh同样采用无服务器架构,它也是一种Kubernetes Operator,有了它,开发者就可以在Kubernetes上原生使用Pulsar Functions和各种Pulsar连接器,充分发挥Kubernetes资源分配、弹性伸缩、灵活调度等特性。例如,Function Mesh依赖Kubernetes的调度能力,确保Functions的故障恢复能力,并且可以在任意时间适当调度Functions。
Function Mesh主要由Kubernetes Operator和Function Runner两个组件组成。Kubernetes Operator监测Function Mesh CRD、创建Kubernetes资源(即StatefulSet),从而在Kubernetes运行Function、连接器和Mesh。Function Runner负责调用Function和连接器逻辑,处理从输入流中接收的事件,并将处理结果发送到输出流。目前,Function Runner基于Pulsar Functions Runner实现。
当用户创建Function Mesh CRD时(见图12),Function Mesh控制器从Kubernetes API服务器接收已提交的CRD,然后处理CRD并生成相应的Kubernetes资源。例如,Function Mesh控制器在处理Function CRD时,会创建StatefulSet,它的每个Pod都会启动一个Runner来调用对应的Function。
图12 Function Mesh处理CRD过程
Function Mesh API基于现有Kubernetes API实现,因此Function Mesh资源与其他Kubernetes原生资源兼容,集群管理员可以使用现有Kubernetes工具管理Function Mesh资源。Function Mesh采用Kubernetes Custom Resource Definition(CRD),集群管理员可以通过CRD自定义资源,开发事件流应用程序。
用户可以使用kubectl CLI工具将CRD直接提交到Kubernetes集群,而无须使用pulsar-admin CLI工具向Pulsar集群发送Function请求。Function Mesh控制器监测CRD并创建Kubernetes资源,运行自定义的Function、Source、Sink或Mesh。这种方法的优势在于Kubernetes直接存储并管理Function元数据和运行状态,从而避免在Pulsar现有方案中可能存在的元数据与运行状态不一致的问题。
结语
在本文中,我分享了自己在云原生环境下,对于开源行业的思考和云原生流平台解决方案的技术实践。作为一名全身心投入的开源人,我很高兴看到近几年有越来越多的人认可开源理念并成为开源开发者与贡献者,开源行业正在蓬勃发展。我希望能和无数的开发者一样,在开源道路上一往无前,助力更多企业加速云原生和数字化进程。
本文出自《新程序员·云原生和全面数字化实践》。在《新程序员003》中,我们聚焦“云原生时代的开发者”与“全面数字化转型”两大主题。阿里、字节跳动、网易、快手、亚马逊等互联网大厂的云原生技术的赋能者,从技术定义、技术应用、实践案例分享等方面,以直击内核的硬核输出全面解析云原生,帮助开发者在云原生时代快速找到适合自身发展的技术范式。
同时,我们也将对微软、英特尔、华为、施耐德、西门子等首批开启数字化转型的企业展开报道,通过十多位技术专家分享的鲜活案例,一窥金融、新零售、工业物联网等领域的数字化转型成果,帮助更多关注数字化转型的开发者从先驱者的经验中获得启迪。
阅读更多相关技术文章及行业资讯,欢迎扫描下方二维码或点击【阅读原文】订阅《新程序员003》纸质书+电子书。
全年订阅(001-004期)尊享更多会员福利
点击图片订阅(001-004期)