MO干货 | Matrixone-Operator 设计与实现

作者:吴叶磊 MO研发工程师

目录

Part 1.MatrixOne-Operator 设计

Part 2.集群 API 设计

Part 3.控制器实现

Part 4.应用状态管理

Part 5.总结


Part 1 MatrixOne-Operator 设计

尽管 K8S 原生提供了 StatefulSet API 来服务有状态应用的编排,但由于不同有状态应用的应用层状态难以进行统一抽象,因此 K8S 原生并不支持管理应用状态。为了解决这一问题,Operator 模式应运而生。一个典型的 K8S Operator 由 API 和控制器(Controller)两部分构成:

API

通常通过 K8S 的 CustomResourceDefinition (CRD)对象进行声明,在提交一个 K8S CRD 到 K8S 的 api-server 后,api-server 就会在自身注册一个对应的 Restful API。所有的 K8SClient 都能以类似操作原生资源的方式对这个新声明的 API 进行 GET, LIST, POST, DELETE 等操作。按照惯例,每个 API 对象内的 .spec 结构由用户管理,用于声明对象的期望状态, .status 结构则由下文的控制器管理,用于对外暴露对象的实际状态

控制器

控制器是一段持续运行的代码,它监视(watch)一系列 K8S 对象,包括我们刚刚定义的 API 对象。然后,根据这些对象的期望状态和从现实中收集到的实际状态(注意:这里的实际状态是从现实中收集到再写进 .status 的,而不是直接来自 .status )执行自动化操作,驱动实际状态期望状态转移。这个过程会持续循环进行,被形象地称作“控制循环“(control loop),有些地方也会用一个更具古典乐风味的词“调谐循环”(reconciliation loop),巧妙地和 K8S 的“编排”(Orchestration)一词保持了风味统一。

下图以简化后的 MatrixOneCluster API 作为例子,概括性地描述了这个过程:

MatrixOne-Operator 不仅提供MatrixOneCluster 这样用于管理 MO 集群的负载型 API,还提供备份恢复这样的任务型 API 和对象存储桶这样的资源型 API。每种 API 和它们的控制器在设计时都有独特的考量,但万变不离其宗,所有的 API 和控制器都是以上述模式进行构建的。接下来,我们将继续探索每个 API 设计中的取舍。

Part 2 集群 API 设计


一个分布式 MO 集群由日志服务、事务节点、计算节点和 Proxy 等多个组件组成,其中计算节点还有明确的异构需求来实现针对负载的机型优化和跨云、云边一体等能力。将整个集群的管理都集中到一个 API 对象中声明再使用一个控制器进行管理虽然便于使用,但却是代码维护的噩梦。因此,MatrixOne-Operator 在设计之初就明确了“松耦合的细粒度 API”这一原则,设计了 LogSet、CNSet、ProxySet、BucketClaim 等职责明确的 API 和彼此独立的控制器。为了保持易用性,又引入了 MatrixOneCluster API。负责 MatrixOneCluster 控制器不重复其他控制器的工作——当一套集群需要一个 LogSet 提供日志服务时,MatrixOneCluster 控制器仅仅是创建一个 LogSet 对象,其余的工作则委托给 LogSet 控制器。

在这样的设计下,虽然 API 很多,但用户始终只需要关心 MatrixOneCluster API,而 MatrixOne-Operator 的开发者也在添加特性或解决问题时,问题域也往往不大于一个细粒度的 API 和控制器。

当然,多个 API 对象间会有某些依赖关系,比如事务节点和计算节点都依赖运行于日志服务中的 HAKeeper 来获取集群信息进行服务发现。这要求部署集群时首先启动日志服务并完成 HAKeeper 的 bootstrap 才能继续启动事务节点和计算节点。这类逻辑固然可以由 MatrixOneCluster 控制器实现,但这也意味着泄露了其他控制器的业务知识,各个控制的实现仍然产生了耦合。因此,在 mo-operator 中,我们将所有组件间产生依赖的业务逻辑都在依赖方进行实现,被依赖方仅仅通过约定俗成的 .status 字段对外暴露自身状态。举例来说,控制器在调谐一个 CNset 时,会主动等待 CNSet 指向的 LogSet 就绪再进行后续操作,而 LogSet 控制器和上层的 MatrixOneCluster 控制器都不需要感知到这件事。

松耦合细粒度的 API 能够很好地适应 CN 的异构编排场景。在 MatrixOne-Operator 中,除了在 MatrixOneCluster 中声明多个 CN 组来进行异构编排这种便捷用法之外,还能直接创建一个 CNSet 来加入现有集群,这意味着新的 CNSet 可以部署在另一套 K8S 集群中,配合网络层面的支持,就能进行跨云或云边场景下的 MO 编排。

在各个控制器的迭代过程中,MatrixOne-Operator 也倾向于通过添加新的 API 对象来添加新特性。比如,在实现对象存储管理时,MatrixOne-Operator 需要保证不同集群使用的对象存储路径之间没有交集并且在集群销毁后自动清理。MatrixOne-Operator 的解决方案就是新增一个 BucketClaim API,参考 K8S PersistentVolumeClaim 的控制逻辑,在独立的控制器中完成一个对象存储路径的生命周期管理,避免复杂的竞态条件处理和代码耦合问题。

Part 3 控制器实现

K8S 提供了controller-runtime包帮助开发者实现自己的控制器,但为了通用性,接口设计是相对底层的:

Reconcile(ctx context.Context, req Request) (Result, error)

控制器需要实现Reconcile接口,再通过 controller-runtime 的接口进行注册,声明要监听的对象以及一些监听的过滤规则,controller-runtime 就会在每次对象发生变化或者重试Reconcile时调用控制器的Reconcile方法,并在req参数中传入目标对象的标识符。这个接口内会存在不少模板代码,用伪代码来表示通常是:

func 调谐(对象 A 的 Namespace+Name) {获取对象 A 的Specif 对象 A 正在被删除 {执行清理逻辑更新清理进度到 A.status移除对象 A 上的 finalizer} else {为 A 添加 finalizer执行调谐逻辑更新调谐进度到 A.status}
}

类似的逻辑在各种社区控制器实现中反复出现,并且开发者需要关心很多业务之外的内容:正确地处理 finalizer 以确保资源不会泄露、将进度和错误及时更新到 status 中来提升可见度以及更细节的 logger 需要带 context 和 kubeClient 需要带 cache 等问题。

由于不需要考虑通用性,MatrixOne-Operator 内进行了更特化的抽象,设计了 Actor 接口:

type Actor[T client.Object] interface {  Observe(*Context[T]) (Action[T], error)  Finalize(*Context[T]) (done bool, err error)  
}type Action[T client.Object] func(*Context[T]) error

在背后,通用的控制器框架逻辑会处理好所有类似上文模板代码的逻辑和细节,在 Context[T] 内准备好当前需要 reconcile 的对象和已经处理好上下文的 Logger, EventRecorder, KubeClient 对象。最后:

  • 在调谐一个未被删除的对象时,调用 Actor.Observe 让真正的业务逻辑执行调谐;
  • 在调谐一个删除中对象时,调用 Actor.Finalize 执行业务逻辑内的资源清理行为,不断重试,直到 Finalize 返回完成,才移除对象的 finalizer。

一个对象的状态机如下:

在这个流程下,一个控制器对 API 对象生命周期管理中的创建和销毁两部分的实现非常直白。无非是结合 MO 的运维知识,调用 K8S 的 API 申请存储、部署工作负载、配置服务发现;或是反过来,在 API 处于删除阶段时,将创建的外部资源全部销毁。对象的更新后的调谐操作也是常规的 diff 逻辑,以MatrixOneCluster的.cnSets字段为例,调谐流程可以用下面的伪代码表示:

func sync(c MatrixOneCluster) {existingCNSets := 收集这个集群的所有CNSetfor _, desired := range c.spec.CNSets {cnSet := 构建CNSet(desired)if _, ok := existingCNSets[cnSet.Name]; ok {// 1. CNSet 存在,更新 CNSet....// 2. 标记这个 cnSet 是期望状态中需要的delete(existingCNSets, cnSet.Name)} else {// CNSet 不存在,创建....}}for _, orphan := range existingCNSets {// 对于实际存在但期望状态中不存在的 CNSet,进行清理}
}

比较容易出错的是ConfigMap/ Secret 的更新逻辑,MO 和很多应用一样,需要配置文件并且每次配置更新时需要重启重新读取配置,而配置文件通常用 K8S 原生的 ConfigMap 对象进行存储。有一个容易踩坑的地方在于 ConfigMap 对象的内容是可变的,而大部分应用往往只在启动时读取一次 ConfigMap 内的配置文件,后续不会再 reload。因此,查看 Pod 当前引用的 ConfigMap 内的内容并不能确定 Pod 目前使用的配置(有可能在启动后 ConfigMap 内容发生了变化)。另外,假如想在 ConfigMap 变化后滚动更新应用,一个常见的做法是将 ConfigMap 的内容做一个 Hash 填到 PodTemplate 的 Annotation 中,每次更新 ConfigMap 变化后更新这个 Annotation 触发应用的滚动更新。但这种做法也会因为原地修改 ConfigMap 而出现非预期的情况:

以上图为例,假设 Annotation 中的 ConfigMap Hash 从 123 更新成了 321,而 321 启动后因为 ConfigMap 配置有问题而无法 Ready,这时候在合适的策略配置下,滚动更新会卡住避免故障范围扩大。然而,尚未更新的 Pod 内也已经读到了新版本的 ConfigMap,只要发生一次容器重启或 Pod 重建,就会立刻发生问题。这和更新镜像或其他字段的行为显然不一样,更新其他字段时,绿色的 Pod 还属于旧的 ReplicaSet/ControllerRevision,重启或重建都不会使用新版本的配置启动,故障范围是可控的。

问题的根源在于 ConfigMap 的内容并不在 Pod 的 spec 内,直接修改 ConfigMap 的内容和 Pod 的“不可变基础设施”原则是冲突的。

因此,MatrixOne-Operator 中将所有 Pod 内会引用的对象都设计成不可变的,以 ConfigMap 为例,每次通过 CRD 更新某个组件的配置,MatrixOne-Operator 都会生成一个新的 ConfigMap 并将组件的所有副本滚动更新到这个新的 ConfigMap 上:

基于这个原则,任意时刻我们都可以通过当前的 Pod Spec 明确所有 Pod 内的信息。滚动更新的问题也迎刃而解。

Part 4 应用状态管理

除了应用本身的生命周期管理外,MatrixOne-Operator 还有一个重要职责是管理应用本身的状态。但是,分布式系统通常都会基于心跳或类似的机制管理自身的应用状态,为什么 Operator 中还要多此一举呢?

原因在于 Operator 的代码内拥有关于自动化运维的知识,比如 Operator 明确地知道在滚动更新过程中接下来哪一个 Pod 要被重建/重启了。因此可以提前调整应用内的状态,比如将 Pod 上的负载进行迁移,最小化滚动更新的影响。这类应用状态的管理逻辑有两种常见的实现方式:

  1. 借助 Pod 本身的各类生命周期钩子,比如 InitContainer、PostStart Hook 和 PreStop Hook ,在这些钩子内同步应用状态;
  2. 在 Operator 的调谐循环内调用应用接口调整应用状态;

方式一实现起来比较简单,方式二更自活自由,能更好地应对复杂场景。举个例子,在缩容 CNSet 时,要先将被缩容的 CN Pod 上的 session 迁移到其他 CN Pod 上再停止 CN Pod。假如这个操作放在 Pod PreStop Hook 中,那就是无法撤销的。而实际场景中,确实存在一组 CN 先被缩容,在缩容完成前又再扩容上去的场景(尤其是在开启了自动伸缩后),这时候,Operator 内的调谐循环就可以计算出此时可以直接复用仍然在下线中的 CN,调用 MO 内部的管理接口将 CN 将这个 CN 重新恢复成服务状态,不再向其他 CN 迁移 session 并且重新向 Proxy 接受新 session,而不需要再扩容出一个新的 CN。

Part 5 总结

作为扩展 K8S 编排能力的主流选项,Operator 模式发展到今天已经拥有成熟的基础库和工具链,社区中也有大量的成熟开源项目可供参考,在 K8S 上开发一个 Operator 已经不再是一个新鲜的话题。但真正的复杂度永远藏在实际业务的细节中,解决这些问题需要对结合对 K8S 和自身业务系统领域知识的充分理解。MatrixOne 作为一款云原生分布式数据库,其中很多设计理念和领域知识与其他云原生系统存在共通之处。希望这篇短文不仅能帮助你了解 mo-operator 的设计实现,也能给你在设计自己的 Operator 时提供经验参考。


About MatrixOne

MatrixOne 是一款基于云原生技术,可同时在公有云和私有云部署的多模数据库。该产品使用存算分离、读写分离、冷热分离的原创技术架构,能够在一套存储和计算系统下同时支持事务、分析、流、时序和向量等多种负载,并能够实时、按需的隔离或共享存储和计算资源。云原生数据库MatrixOne能够帮助用户大幅简化日益复杂的IT架构,提供极简、极灵活、高性价比和高性能的数据服务。

MatrixOne企业版和MatrixOne云服务自发布以来,已经在互联网、金融、能源、制造、教育、医疗等多个行业得到应用。得益于其独特的架构设计,用户可以降低多达70%的硬件和运维成本,增加3-5倍的开发效率,同时更加灵活的响应市场需求变化和更加高效的抓住创新机会。在相同硬件投入时,MatrixOne可获得数倍以上的性能提升

关键词:超融合数据库、多模数据库、云原生数据库、国产数据库。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/315898.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

没有文件服务器,头像存哪里合适

没有文件服务器,头像存哪里合适 视频在bilibili:没有文件服务器,头像存哪里合适 1. 背景 之前有同学私信我说,他的项目只是想存个头像,没有别的文件存储需求,不想去用什么Fastdfs之类的方案搭建文件服务…

安卓常用组件(启停活动页面、活动之间传递信息、收发应用广播、操作后台服务)

启停活动页面 Activity的启动和结束 页面跳转可以使用startActivity接口,具体格式为startActivity(new Intent(this, 目标页面.class));。 关闭一个页面可以直接调用finish();方法即可退出页面。 Activity的生命周期 页面在安卓有个新的名字叫活动,因…

Linux系统编程---线程池并发服务器

模型原理分析: 线程池的关键优势在于它减少了每次任务执行时创建和销毁线程的开销 线程池的组成主要分为 3 个部分,这三部分配合工作就可以得到一个完整的线程池: 1. 任务队列,存储需要处理的任务,由工作的线程来处理…

关于google search console工具提交sitemap.xml无法抓取的问题解决办法

其实这个问题很好解决。 第一种情况:利用工具为我们的网站自动生成静态的sitemap.xml文件。这种可以检查下是否完整,然后上传到根目录下去,再去google search console提交我们的网站地图。 第二种情况:同样利用工具自动生成动态s…

时间序列模型(含python程序实现)

常用按时间顺序排列的一组随机变量来表示一个随机事件的时间序列,简记为 用表示该随机序列的n个有序观察值,称之为序列长度为n的观察值序列。 常用的时间序列模型 时间序列的预处理 拿到一个观察值序列后,首先要对它的纯随机性和平稳性进行…

Unity 问题之 开发应用在设备上运行闪屏花屏问题的分析处理

Unity 问题之 开发应用在设备上运行闪屏花屏问题的分析处理 目录 Unity 问题之 开发应用在设备上运行闪屏花屏问题的分析处理 一、简单介绍 二、问题现象 三、问题分析 四、使用空后处理,解决闪屏花屏的显示问题 五、空后处理完整代码 一、简单介绍 Unity 在…

秋招后端开发面试题 - Java语言基础(下)

目录 Java基础下前言面试题toString() 、String.valueof()、(String)?hashCode() 方法?hashCode 和 equals 方法判断两个对象是否相等?为什么重写 equals 时必须重写 hashCode 方法?String、StringBuffer、StringBuilder?String …

自己写的爬虫小案例

网址:aHR0cDovL2pzc2NqZ3B0Lmp4d3JkLmdvdi5jbi8/dXJsPS92aWV3L3dvcmtpbmdVbml0L3dvcmtpbmdVbml0Lmh0bWw 这串代码能够爬取勘察单位企业的详细信息。 import requests import time import csv f open(勘察单位公司信息.csv,w,encodingutf-8,newline) csv_writer …

2.Neo4j的搭建启动

Graph Database 图数据库 版本对应关系 官网都是高版本,推荐使用下载地址可以找到社区老版本: https://we-yun.com/doc/neo4j/ neo4j.bat 启动脚本 cypher-shell.bat 执行CQL语句的。 import文件夹可以放入excel,csv等数据文件,导入到…

SQLite的DBSTAT 虚拟表(三十六)

返回:SQLite—系列文章目录 上一篇:SQLite运行时可加载扩展(三十五) 下一篇:SQLite—系列文章目录 1. 概述 DBSTAT 虚拟表是一个只读的同名虚拟表,返回 有关用于存储内容的磁盘空间量的信息 的 SQLite 数据库。 示例用例…

Valentina Studio Pro for Mac:强大的数据库管理工具

Valentina Studio Pro for Mac是一款功能全面、操作高效的数据库管理工具,专为Mac用户设计,旨在帮助用户轻松管理各种类型的数据库。 Valentina Studio Pro for Mac v13.10激活版下载 该软件拥有直观的用户界面,使得数据库管理变得简单直观。…

网络基础(day3)

【 理论重点】 网络是什么&#xff1f; &#xff08;网络是载体&#xff0c;目的是传输互联网中的数据&#xff0c;数据是终端产生<手机、电脑、服务器等>。&#xff09; 如何组件网络&#xff08;良性网络架构&#xff09;&#xff1f;有网络架构思维&#xff0c;得按层…

数据分析:甲基化分析-从DNA methylation的IDAT文件到CpG site的Beta values

介绍 DNA Methylation和疾病的发生发展存在密切相关&#xff0c;它一般通过CH3替换碱基5‘碳的H原子&#xff0c;进而调控基因的转录。常用的DNA methylation是Illumina Infinium methylation arrays&#xff0c;该芯片有450K和850K&#xff08;也即是EPIC&#xff09;。 该脚…

Java中一个汉字究竟占几个字节?

前言 在今天&#xff0c;“Java中一个汉字占几个字符”的问题&#xff0c;让我提起了兴趣 在我的记忆中&#xff0c;一个字符应该是占两个字符的。但看了他人的回答 发现自己对这方面了解非常片面&#xff0c;于是痛定思痛潜心学习&#xff0c;写下这篇博客 总结不足文章目录 …

使用Pandas从Excel文件中提取满足条件的数据并生成新的文件

目录 一、引言 二、环境准备 三、读取Excel文件 四、数据筛选 五、保存为新的Excel文件 六、案例与代码总结 七、进阶用法与注意事项 八、结语 在数据处理的日常工作中&#xff0c;我们经常需要从大量数据中筛选出满足特定条件的数据集。Pandas是一个强大的Python数据分…

网络编程——TCP

socket socket类型 流式套接字(SOCK_STREAM) TCP 提供了一个面向连接、可靠的数据传输服务&#xff0c;数据无差错、无重复、无丢失、无失序的发送且按发送顺序接收。内设置流量控制&#xff0c;避免数据流淹没慢的接收方。数据被看作是字节流&#xff0c;无长度限制。 数据报…

SpringBoot - java.lang.NoClassDefFoundError: XXX

问题描述 以 json-path 为例&#xff1a;java.lang.NoClassDefFoundError: com/jayway/jsonpath/Configuration 原因分析 编译不报错&#xff0c;但是运行时报错。 遇到这样类似的问题&#xff0c;首先就要想到是不是 Jar 包冲突引起的&#xff0c;或者引入的不是理想的 Jar…

机器学习(三)之监督学习2

前言&#xff1a; 本专栏一直在更新机器学习的内容&#xff0c;欢迎点赞收藏哦&#xff01; 笔者水平有限&#xff0c;文中掺杂着自己的理解和感悟&#xff0c;如果有错误之处还请指出&#xff0c;可以在评论区一起探讨&#xff01; 1.支持向量机&#xff08;Support Vector Ma…

Agent AI智能体在未来,一定与你我密不可分

随着Agent AI智能体的逐渐成熟&#xff0c;人工智能应用的不断深入与拓展&#xff0c;相信在不久的将来&#xff0c;他与你我的生活一定是密不可分的。 目录 ​编辑 1 Agent AI智能体是什么&#xff1f; 2 Agent AI在语言处理方面的能力 2.1 情感分析示例 2.2 文本分类任…

电脑已经有了一个Windows10,再多装一个Windows10组成双系统

前言 前段时间已经讲过一次双Windows系统的安装教程&#xff0c;但是小白重新去看了一下&#xff0c;发现写的内容太多&#xff0c;怕小伙伴看了之后一脸萌。 所以今天咱们就重新再来讲讲&#xff1a;在同一台机器上安装Windows10双系统的教程。 注意哦&#xff01;这里的Wi…