共计 6302 个字符,预计需要花费 16 分钟才能阅读完成。
本文作者:蔡低垂,Apache RocketMQ Committer, 阿里云智能技术专家。
背景
上图左侧为 RocketMQ 4.x 版本集群,属于非切换架构。NameServer 作为无状态节点能够部署多份,broker 集群能够部署多组 broker,每一组有一个 Broker Master 和多个 Broker Slave。运行过程中如果某一组 master 故障,音讯发送会路由到失常的 master 上,一般音讯能够从原 Broker Slave 持续生产。
但非切换架构存在若干问题,比方定时音讯或事务音讯须要由 Master 进行二次投递,如果 Master 故障,则须要人工染指将 Master 从新复原。因而,RocketMQ 5.0 提出了自主切换架构。
自主切换架构新增了一个 Controller 模块,负责选主。当某个 Broker Master 故障,会抉择适合的 Broker Slave 晋升为 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 集群的状态可演绎为两块。其一为集群拓扑关系,包含 RocketMQ Broker 主备关系以及 RocketMQ 不同模块之间的相互依赖(比方 Broker 须要依赖 NameServer、Controller 等);其二为存储状态,包含 Cluster 名称、Broker 名称、Broker ID、新扩容 Broker 元数据等。
如何自动化治理以上状态也是必须解决的问题。
于是社区成立了 RocketMQ Operator 我的项目,用于撑持 RocketMQ 集群在云基础设施上的自动化运维与治理。
如上图所示,最右侧为 RocketMQ Operator 模块,实时与 Kubernetes API Server 进行交互。一方面会将 RocketMQ 集群(包含 NameServer、Broker 等模块)失常部署,同时也会利用 RocketMQ Admin Tool 实时地保护集群状态,比方 NameServer 地址等。
一、Kubernetes Operator 原理
[]()
Kubernetes Operator 是一种绝对简略灵便且编程敌对的治理利用状态的解决方案。其工作原理分为两局部:一部分是利用自定义 API 资源(CRD)形容治理状态利用,能够认为是一个面向用户的接口,用户形容须要部署或运维的资源;另一部分是自定义控制器,依据自定义资源对象的变动实现运维动作。
上图两头的 Operator 管制循环能够视作自定义资源和 Kubernetes 资源之间的桥梁,它会一直监听自定义资源的状态变动,依据状态以及外部逻辑更新 Kubernetes 资源。同时也会依据 Kubernetes 资源的变动更新自定义资源的状态。
自定义对象与 Pod 或 Deployment 相似,只是它并不是 Kubernetes 外部提供的对象,而是须要用户自定义,并告知 Kubernetes。Custom Resource 指自定义 API 资源的实例,Custom Resource Definition 指 CR 的定义。
如上图,比方有一个类型为 Container 的 CRD,定义了三个属性,别离是 Container 名称、Container 对应的 image 和 Container 监听的端口。上面的 CR 为具体自定义资源,其名称为 nginx,image 为 DockerHub 上的最新版本,监听 80 端口。与大家比拟相熟的数据库表进行类比,Container 能够认为是一张表,具体定义(Spec)能够类比为每一列的定义,每一行数据即不同的自定义资源(CR)。
自定义资源提供 API 对象,真正负责将自定义资源转换成 Kubernetes 外部资源的工作则由自定义控制器实现。
自定义控制器里有 Informer 模块,会一直地调用 Kubernetes API Server 的 listAndWatch 接口,以取得所监听 CR 的变动。CR 的变动事件和 CR 对象会被退出 Delta FIFO Queue,以 Key-Value 的形式保留在本地存储,并将 Key 退出 WorkQueue。最右侧的管制循环会一直地从 WorkQueue 取出相干 Key,依据 Key 从 Informer 的 Local Store 查问对应的 CR 对象。接下来将对象定义的冀望状态与目前理论状态进行比对,如果有差别,则执行外部逻辑。最终使得理论状态与 CR 定义的冀望状态达到统一。
二、RocketMQ Operator 设计
社区在实现 RocketMQ Operator 时,并非间接通过 controller-runtime 底层接口,而是依赖 Operator SDK 作为脚手架,帮忙生成相干代码。开发人员在进行 RocketMQ Operator 开发时,只须要专一 RocketMQ 集群自身的编排逻辑。
目前 RocketMQ Operator 的模块有 Name Service、Controller、Broker、TopicTransfer 和 Console,与 RocketMQ 模块基本一致。各模块通过不同 CRD 进行编排,其长处为架构、代码比拟清晰,不同对象均有独立的 CRD 定义和对应的 Controller 实现。毛病为短少 RocketMQ 集群维度的形容,代码实现、配置上可能存在反复。对于 CRD 的演进,欢送社区同学联合各自的实际提出倡议。
Name Service 模块负责 NameServer 在 Kubernetes 集群的运维管控操作,包含部署、扩缩容、提供 NameServer 集群 IP 列表等。Broker 须要向 NameServer 注册路由信息,因而 NameServer 的地址极为重要,须要作为外部状态实时地进行保护。当 NameServer 进行扩缩容时,Broker 集群可能主动感知 NameServer 地址的更新。
上图为 NameServer CR 定义示例,次要属性有:
- NameServer 集群实例数
- NameServer 镜像
- hostNetwork 能够设置为 true 或 false,true 则示意通过 hostNetwork 提供 Node IP,供 Kubernetes 集群内部的客户端拜访。
Controller 模块定义了 Dledger controller 集群。当 Broker 启用自主切换模式时,须要保护 Controller 的拜访地址,其中采纳了两种机制:
第一种:Service。该形式会裸露 Controller 集群的对立拜访地址供 Broker 拜访。Broker 的拜访申请会路由到任意 Controller 节点,Controller 节点会返回 Controller 主节点的拜访地址,Broker 再与 Controller 主节点进行通信。
第二种:Headless Service。该机制为每一个 Controller 的提供拜访地址,用于 Controller 间进行服务发现。在组建 Controller 集群时,Controller 节点必须与具体的某个 Controller 节点进行通信,因而必须为一对一关系。
Controller 的定义绝对简略,只需提供 Controller 数量(数量必须为奇数)、Controller image,其余与 NameServer 相似,比方资源、存储的定义。
Broker 模块用于定义 Broker 集群,保护 Broker 组数量以及每组的节点数量,同时负责解决 Broker 集群的运维操作,包含部署、扩缩容以及元数据复制。扩容时,如果新扩容的 Broker 没有 Topic 等元数据,用户流量实际上不会路由到此 Broker。因而,Broker 模块还会负责 Broker 扩容后进行元数据复制。
Broker 的定义绝对简单,包含:
- Broker 组的数量。
- 每组的节点数。
- clusterMode 定义了 broker 集群模式,默认部署非切换集群,设置为 Controller 则部署自主切换集群。
- 其余还包含资源、存储定义等。
TopicTransfer 不是与 RocketMQ 间接映射的模块,它定义了 Topic 和 Consumer Group 元数据迁徙运维操作。应用 TopicTransfer 迁徙元数据时,首先在指标集群创立指定的 Consumer group 和 Topic;创立实现后禁写原集群,使得音讯不会发送到原集群;期待原集群的音讯生产实现后,会将原集群的元数据进行清理。在此过程中,任何一步失败均会进行回滚,确保元数据正确迁徙。
Console 模块负责部署 RocketMQ 控制台以及保护其用到的 NameServer 地址,性能绝对简略,目前 RocketMQ Console 为无状态节点,其定义形式与 Deployment 的雷同。
接下来介绍几个重要的控制器实现。
NameServer 控制器首先会判断 Name Service 对应的 StatefulSet 是否存在,如果不存在,则创立或更新 StatefulSet,直至 NameServer 节点数与期望值雷同。而后列出 NameServer 对应的 Pod 地址,并判断地址是否产生了变动。如果是,则会将集群中的 NameServer 地址进行更新,从而保障 Broker 或其余模块可能获取到正确的 NameServer 地址。
Dledger Controller 先创立 Headless Service 用作组建 Controller 集群时服务发现的入口,接着判断 Controller 节点数量是否与期望值统一,如果不统一,则创立 StatefulSet。创立 StatefulSet 时会主动为每个 Dledger Controller 调配 controllerDledgerSelfId。冀望节点数目与理论节点数目统一后,才会裸露 Controller 的 Service 地址,供 Broker 拜访。
Broker 是有状态利用,因而在扩容或缩容时须要 Broker Controller 进行额定动作。Broker Controller 会以 Broker 组为单位进行调度,每一个 broker 组有 1 个 master 节点,并配置 0 到多个 slave 节点。当 Broker 进行扩容时,会新增一组 Broker 并依照用户配置复制元数据到新扩容的 Broker。
Broker 依赖 NameServer 和 DLedger Controller,因而会期待 NameServer、DLedger Controller 启动实现且两者均失常提供服务后,才会进一步创立 Broker 对应的 StatefulSet,直到理论节点数目与冀望节点数统一。
如果呈现扩容状况,则会依据在 CR 定义 的 ScalePodName 字段对应 Pod 将元数据(包含 Topic、生产组)拷贝到新扩容的 Broker。
三、疾速部署 RocketMQ 集群
首先,将 RocketMQ Operator 我的项目克隆到本地,解压后执行 install-operator.sh 脚本即可实现 RocketMQ Operator 的装置。
第二步,配置 Name Service CR。Name Service CR 配置较为重要的字段有两个,其一为 size,即须要部署了多少个 NameServer 节点,其二为 hostNetwork,默认 false,此时客户端只能在 Kubernetes 集群内与 NameServer 进行通信。如果 Kubernetes 集群外的客户端须要拜访到 RocketMQ 集群,须要将 hostNetwork 配为 true,NameServer 的接入点须要配置为 NameServer 所在的 Node IP。
第三步,配置 Controller CR。留神 size 须要配置为奇数。Controller 的数据须要长久化存储,能够利用云服务提供商提供的 StorageClass,无需自行保护存储。如果心愿配置本人的存储,GitHub 上 RocketMQ Operator 我的项目代码提供了配置 NFS 存储的相干示例。
第四步,配置 Broker CR。示例中配置了两组 Broker,每组有一个备节点,同时将 clusterMode 设置为 Controller,启动自主切换架构集群。
筹备好以上三个模块的相干配置文件之后,执行 kubectl apply 命令提交给 Kubernetes 集群。其余的部署、运维等动作均交由 RocketMQ Operator 主动实现。
胜利部署后,能够通过 kubectl get po 命令查看部署的 Pod。能够看到部署了 4 个 Broker 节点、Controller 和 NameSever 节点各 3 个。
进入一个 Broker Pod,能够应用 clusterlist 命令查看集群状态,能够看到集群有两组 Broker,每一组各有一主(BID=0)一备。
四、将来瞻望
RocketMQ Operator 将不断完善,全面反对 RocketMQ 5.0,后续布局次要蕴含以下工作:
① 镜像对立。目前 RocketMQ Operator 外部也保护了一套 RocketMQ 镜像,然而曾经有 RocketMQ Docker 我的项目,没必要再保护一套镜像。因而,将来社区心愿将对两边镜像进行对立,升高治理老本。
② 集群治理。RocketMQ 5.0 版本还提供了另外一种集群部署形式—— BrokerContainer 对等部署。与 4.0 版本传统的主备形式不同,BrokerContainer 会在过程中同时启动一主一备,有两个 BrokerContainer 中的 Broker 互为主备。某一个 Container 的主节点故障时,则配对的 Container 中的备节点会进入 Slave Acting Master 状态,负责代理主节点进行定时音讯或事务音讯等二级音讯的解决。
③ 反对部署更多 RocketMQ 组件,包含 RocketMQ Schema Registry、RocketMQ Proxy、RocketMQ Exporter 等。