当前位置: 首页 > 后端技术 > Java

基于云基础设施快速部署RocketMQ5.0集群

时间:2023-04-01 13:47:11 Java

本文作者:蔡高阳,ApacheRocketMQCommitter,阿里云智能技术专家。背景上图左侧为RocketMQ4.x版本集群,属于非切换架构。NameServer可以作为无状态节点进行多副本部署,一个broker集群可以部署多组broker,每组有一个BrokerMaster和多个BrokerSlave。如果一组master在运行过程中出现故障,消息发送会路由到正常的master,普通消息可以从原来的BrokerSlave继续消费。然而,非交换架构中存在几个问题。比如定时消息或者交易消息需要Master进行两次下发。如果Master出现故障,需要人工干预来恢复Master。因此,RocketMQ5.0提出了一种自主切换的架构。在自治交换架构中增加了一个新的Controller模块,负责选择master。当一个BrokerMaster出现故障时,将选择一个合适的BrokerSlave提升为Master,无需人工干预。如果要在生产环境部署一个集群,需要规划整个集群的机器资源(比如哪些模块部署在哪些机器上,哪些机器需要什么资源规格等),然后安装JDK和其他依赖软件。每个组件还需要准备它的配置文件、启动脚本,然后启动每个组件。整个过程非常耗费人力,并且存在误操作的可能。然而,在云基础设施上部署RocketMQ面临更多的挑战:首先,在云基础设施上创建不同规格的虚拟机更加方便,因此在云基础设施上部署时,往往在一个虚拟机上只部署一个模块。实现资源隔离。然而,多节点部署也会带来更高的运营成本。系统内部组件的宕机、恢复、迁移等行为也需要得到支持。从社区的角度来看,由于社区面向不同的用户,不同的用户往往会部署在不同的云基础设施服务商上。但从IaaS层设施来看,不同云厂商提供的接口并不统一。为了解决上述问题,社区借鉴了面向接口编程的思想:不直接操作基础设施,而是通过标准接口。而Kubernetes正是这样一个容器编排的“标准接口”。因此,社区在解决RocketMQ在云基础设施中的部署问题时,选择基于Kubernetes进行部署。不同的云厂商负责从Kubernetes调度到具体的云IaaS层:将有状态的RocketMQ集群托管到Kubernetes集群,充分利用Kubernetes提供的资源。部署、升级、自愈能力也能享受到Kubernetes社区的生态红利。Kubernetes将Pod、Deployment、Service、Ingress等封装成抽象的资源。在部署RocketMQ集群时,只需要安排好相关的Kubernetes资源,最终如何安排在云基础设施上,就交给云服务商了。但是直接基于Kubernetes原生资源部署也有一些不足。比如用Kubernetes部署时,经常需要操作YAML文件,这会涉及到Deployment、StatefulSet、Service、Ingress等资源,需要大量的配置。遇到复杂的资源定义就像是“面向YAML的编程”。此外,Kubernetes在支持有状态应用的管理方面也存在局限性。RocketMQ集群的状态可以概括为两部分。一是集群拓扑关系,包括RocketMQBroker主备关系和RocketMQ不同模块之间的依赖关系(比如Broker需要依赖NameServer、Controller等);另一个是存储状态,包括Cluster名称、Broker名称、BrokerID、新的ExpandBroker元数据等。如何自动管理以上状态也是必须要解决的问题。因此社区成立了RocketMQOperator项目,支持RocketMQ集群在云基础设施上的自动化运维和管理。如上图所示,RocketMQOperator模块在最右边,它与KubernetesAPIServer进行实时交互。一方面正常部署RocketMQ集群(包括NameServer、Broker等模块),同时使用RocketMQAdminTool实时维护集群状态,如NameServer地址等.一、KubernetesOperator原理[]()KubernetesOperator是一种相对简单、灵活、编程友好的应用状态管理方案。它的工作原理分为两部分:一部分是使用自定义API资源(CRD)来描述管理状态应用,可以认为是一个面向用户的接口,用户描述需要部署的资源或维护;另一部分是自定义控制器,根据自定义资源对象的变化完成运维动作。上图中间的Operatorcontrolloop可以看作是自定义资源和Kubernetes资源之间的桥梁。它会持续监控自定义资源的状态变化,根据状态和内部逻辑更新Kubernetes资源。同时,自定义资源的状态会根据Kubernetes资源的变化进行更新。自定义对象类似于Pod或Deployment,只不过它不是Kubernetes提供的对象,而是需要用户自定义并通知给Kubernetes。CustomResource是指自定义API资源的实例,CustomResourceDefinition是指CR的定义。如上图所示,例如有一个Container类型的CRD,它定义了三个属性,分别是Container的名称、Container对应的镜像、Container监听的端口。下面CR是具体的自定义资源,名字叫nginx,镜像是DockerHub上的最新版本,监听80端口。类比我们熟悉的数据库表,Container可以看做是一张表,具体定义(Spec)可以类比为每一列的定义,每一行数据都是不同的自定义资源(CR)。自定义资源提供API对象,而真正负责将自定义资源转换为Kubernetes内部资源的工作则由自定义控制器来实现。自定义controller中有一个Informer模块,会不断调用KubernetesAPIServer的listAndWatch接口获取被监控CR的变化。CR变化事件和CR对象会被添加到DeltaFIFOQueue中,以Key-Value的形式保存在本地存储中,Key被添加到WorkQueue中。最右边的控制循环会不断从WorkQueue中取出相关的Key,并根据Key从Informer'sLocalStore中查询到对应的CR对象。接下来,将对象定义的期望状态与当前实际状态进行比较,如果有差异,则执行内部逻辑。最后,实际状态与CR定义的期望状态一致。2、RocketMQOperator设计社区在实现RocketMQOperator时,并没有直接使用controller-runtime底层接口,而是依赖OperatorSDK作为脚手架,帮助生成相关代码。在开发RocketMQOperator时,开发者只需要关注RocketMQ集群本身的编排逻辑即可。目前RocketMQOperator的模块包括NameService、Controller、Broker、TopicTransfer和Console,与RocketMQ模块基本一致。每个模块都通过不同的CRD进行组织。优点是架构和代码比较清晰。不同的对象有独立的CRD定义和对应的Controller实现。缺点是缺少对RocketMQ集群维度的描述,代码实现和配置可能存在重复。关于CRD的演进,欢迎社区同学结合自身实践提出建议。NameService模块负责Kubernetes集群中NameServer的运维、管理和控制,包括部署、伸缩、提供NameServer集群IP列表等。Broker需要向NameServer注册路由信息,因此NameServer的地址极其重要,需要作为内部状态实时维护。当NameServer扩缩容时,Broker集群可以自动感知NameServer地址的更新。上图是NameServerCR定义的例子。主要属性有:NameServer集群实例数NameServer镜像hostNetwork可以设置为true或false,true表示通过hostNetwork提供NodeIP,供Kubernetes集群外的客户端访问。Controller模块定义了Dledger控制器集群。Broker开启自主切换模式时,需要维护Controller的访问地址,它采用两种机制:第一种:Service。该方法会暴露Controller集群的统一访问地址,供Broker访问。Broker的访问请求会被路由到任意一个Controller节点,Controller节点返回Controller主节点的访问地址,Broker与Controller主节点进行通信。第二种:HeadlessService。该机制为每个Controller提供了一个访问地址,用于Controller之间的服务发现。在组成Controller集群时,Controller节点必须与具体的Controller节点进行通信,因此必须是一对一的关系。Controller的定义比较简单,只需要提供Controller的数量(数量必须是奇数),Controllerimage,其他和NameServer类似,比如资源和存储的定义。Broker模块用于定义Broker集群,维护Broker组的数量和每个组中的节点数量,处理Broker集群的运维,包括部署、扩缩容、元数据复制等。扩容时,如果新扩容的Broker没有Topic等元数据,用户流量实际上不会路由到这个Broker。因此Broker模块也负责Broker扩容后的元数据复制。Broker的定义比较复杂,包括:Broker组的数量。每组的节点数。clusterMode定义代理集群模式。默认情况下,部署一个非交换集群。如果设置为Controller,则部署自治交换集群。其余部分还包括资源、存储定义等。TopicTransfer不是直接映射到RocketMQ的模块,它定义了Topic和ConsumerGroup元数据迁移的运维操作。使用TopicTransfer迁移元数据时,首先在目标集群中创建指定的Consumer组和Topic;创建完成后,禁止向原集群写入,消息不会发送到原集群;原集群消息消费完成后,清理Metadata。在此过程中,任何一步失败都会被回滚,以保证元数据的正确迁移。Console模块负责部署RocketMQ控制台并维护其使用的NameServer地址。功能比较简单。目前RocketMQConsole是一个无状态节点,其定义方式与Deployment相同。接下来介绍几个重要的控制器实现。NameServer控制器会先判断NameService对应的StatefulSet是否存在,如果不存在,则创建或更新StatefulSet,直到NameServer节点数与预期值相同。然后列出NameServer对应的Pod地址,判断地址是否发生变化。如果是,则会更新集群中的NameServer地址,以保证Broker或其他模块能够获取到正确的NameServer地址。DledgerController在搭建Controller集群时首先创建HeadlessService作为服务发现的入口,然后判断Controller节点数是否与预期值一致,如果不一致则创建StatefulSet。创建StatefulSet时,会自动将controllerDledgerSelfId分配给每个DledgerController。待预期节点数与实际节点数一致后,Controller的Service地址将暴露给Broker访问。Broker是一个有状态的应用程序,因此BrokerController在扩展或收缩时需要执行额外的操作。BrokerController会以Broker组为单位进行调度,每个broker组有1个master节点,配置0个到更多的slave节点。Broker扩容时,会增加一组新的Broker,并根据用户配置将元数据复制到新扩容的Broker中。Broker依赖于NameServer和DLedgerController,因此会等待NameServer和DLedgerController启动并正常提供服务后,再进一步创建Broker对应的StatefulSet,直到实际节点数与预期节点数一致。如果发生扩容,会根据CR中定义的ScalePodName字段对应的Pod,将元数据(包括Topic和consumergroup)复制到新扩容的Broker中。3、快速部署RocketMQ集群首先,将RocketMQOperator项目clone到本地,解压后执行install-operator.sh脚本,完成RocketMQOperator的安装。第二步是配置名称服务CR。名称服务CR配置中有两个重要字段。一个是size,即需要部署多少个NameServer节点,另一个是hostNetwork,默认为false。此时客户端只能与Kubernetes集群中的NameServer通信。如果Kubernetes集群外的客户端需要访问RocketMQ集群,需要配置hostNetwork为true,NameServer的访问点需要配置为NameServer所在的NodeIP。第三步,配置ControllerCR。注意size需要配置为奇数。Controller的数据需要持久化存储,可以使用云服务商提供的StorageClass,不需要自己维护存储。如果你想配置自己的存储,GitHub上的RocketMQOperator项目代码提供了一个配置NFS存储的例子。第四步,配置BrokerCR。示例中配置了两组Broker,每组有一个备节点。同时设置clusterMode为Controller,启动一个自治交换架构集群。准备好以上三个模块的相关配置文件后,执行kubectlapply命令提交到Kubernetes集群。其余的部署运维动作由RocketMQOperator自动完成。部署成功后,可以通过kubectlgetpo命令查看已部署的Pod。可以看到部署了4个Broker节点,3个Controller节点,3个NameSever节点。进入一个BrokerPod,可以使用clusterlist命令查看集群状态,可以看到集群有两组Broker,每组有一个master(BID=0)和一个backup。4.未来展望RocketMQOperator将不断完善,全面支持RocketMQ5.0。后续规划主要包括以下工作:① 镜像统一。目前RocketMQOperator内部也维护了一套RocketMQ镜像,但是已经有一个RocketMQDocker项目,所以不需要再维护一套镜像。因此,未来社区希望将两边的镜像统一起来,降低管理成本。② 集群管理。RocketMQ5.0版本还提供了另一种集群部署方式——BrokerContainer点对点部署。与4.0版本传统的主备模式不同,BrokerContainer在这个过程中会同时启动一主一备,两个BrokerContainer中的Broker互为主备。当某个Container的主节点发生故障时,配对的Container中的备节点将进入SlaveActingMaster状态,负责代主节点处理定时消息或交易消息等二次消息。③ 支持部署更多RocketMQ组件,包括RocketMQSchemaRegistry、RocketMQProxy、RocketMQExporter等。