查看原文
其他

ClickHouse集群|Operator跨Kubernetes集群管理

左启刚 eBay技术荟 2022-12-29

作者|左启刚

编辑|林颖

供稿|Unified Monitoring Platform Team

本文共6467字,预计阅读时间10分钟

更多干货请关注“eBay技术荟”公众号


1  背景

作为对Metrics和Logs的补充,监控团队引入了Events监控类型。和Metrics相比,它对基数没有限制;和Logs相比,它适合做聚合分析。Events支持的监控数据需要有预定义的Schema,典型的应用对象有traces和OLAP(Online Analytical Processing)数据。为了支持Events类型,监控团队开发了如图1所示的系统,Events信号从左到右经过采集模块(Agent)、接受模块(Ingress)和存储模块(ClickHouse集群),并通过查询模块(Egress)和报警模块向用户开放使用。通过图1希望大家能对Events监控系统有一个整体认识,方便后面内容的介绍。

图1 Events监控系统架构图

(点击可查看大图)


了解到Events监控系统后,一个很自然的问题是,Events监控系统需要具备哪些特性呢?从不同的角度分析,会得到不同的答案。从Events监控系统自身出发,Events的监控属性必然要求高可用,同时为了应对不断增加的新用户流量以及老用户流量的增长,还需要具备可伸缩性和高性能;从用户出发,系统需要容易集成和使用;从运维人员出发,需要减少人工步骤,提高自动化程度。

相比于高可用、可伸缩、高性能、容易集成等话题,运维自动化被讨论得比较少。因此,本文将会覆盖Events监控系统的运维自动化,主要是ClickHouse集群的运维自动化,其中涉及到定制资源定义(Custom Resource Definition,简称CRD)[1]、ClickHouse、ClickHouse Operator[2]和跨可用区(Available Zone,简写AZ)[3]部署。


2  问题

通过背景介绍,我们知道Events监控系统是一个面向多用户的高可用、可伸缩系统。系统运行在Kubernetes环境下,部署方式如图2所示。

图 2 跨AZ部署

(点击可查看大图)


表1简单介绍了系统各模块的功能,其中,Ingress、Egress、Alert Service为无状态应用,使用Deployment进行部署及GTM进行流量管理;Ruler和Alertmanager为有状态应用,使用StatefulSet进行部署;ClickHouse集群虽然为有状态服务,但并不适合使用StatefulSet进行部署。

表1 Events监控系统模块功能简介

(点击可查看大图)



2.1 ClickHouse集群管理

2.1.1 ClickHouse分布式特性

为了理解StatefulSet管理ClickHouse集群的困难,我们需要从运维角度理解ClickHouse集群的分布式特性。

1)ClickHouse目前没有节点发现功能,需要通过手动配置集群节点来解决。

如下示例所示,定义了一个名为‘logs’的集群,它由两个分片(shard)组成,每个分片包含两个副本(replica)。其中,分片是指包含数据不同部分的服务器,副本是存储复制数据的服务器。

(点击可查看大图)


2)为了满足zoo_path和replica_name组合的唯一性,需要为每个ClickHouse服务器配置不同的macros。

只有Replicated*MergeTree表支持副本,创建Replicated*MergeTree时,需要指定zoo_path(Zookeeper中该表的路径)和replica_name(Zookeeper中该表的副本名称)。如下示例所示,zoo_path和replica_name可以包含宏替换的占位符,即大括号里的部分,它们会被配置文件里‘macros’下同名配置的值替换掉。

(点击可查看大图)


‘macros’示例如下所示:

(点击可查看大图)


3)ClickHouse集群具有灵活的部署能力,但需要使用者根据业务需求,准备相应的集群节点配置文件。

在每个分片中,可以配置一个或多个副本。不同分片可配置不同数量的副本。同一个物理ClickHouse集群可以划分成多个逻辑集群,如‘logs’集群中的4台物理机器,生成如下所示的3个逻辑集群实现读写分离:’logs’、’logs-read’和’logs-write’,其中‘logs-read’集群包含一半副本,‘logs-write’集群包含另一半的副本。

(点击可查看大图)


ClickHouse集群节点配置文件的复杂性使得无法用StatefulSet进行管理。比如,一个4分片2备份的ClickHouse集群,可能的编排方式是:
  • 按备份优先分配,得到(sts-0, sts-4), (sts-1,sts-5), (sts-2,sts-6), (sts-3,sts-7)

  • 按分片优先分配,得到(sts-0, sts-1), (sts-2,sts-3), (sts-4,sts-5), (sts-6,sts-7)

若修改规模为一个2分片3备份的ClickHouse集群,那么可能的编排方式是:
  • 按备份优先分配,得到(sts-0, sts-2, sts-4), (sts-1, sts-3, sts-5)

  • 按分片优先分配,得到(sts-0, sts-1, sts-2), (sts-3, sts-4, sts-5)

不管是按备份优先分配还是按分片优先分配,都有节点(红色字体标注)改变了分片,这可能导致原分片数据丢失以及现分片内数据不一致。


2.2.2 ClickHouse Operator

对于StatefulSet、Deployment完成不了的工作,Kubernetes提供了Operator模式。Operator模式会封装你编写的(Kubernetes本身提供功能以外的)任务自动化代码。在这个过程中,使用定制资源(Custom Resource)来描述自动化任务的目标。使用Operator可以自动化的事件包括:
  • 按需部署应用;

  • 获取/还原应用状态的备份;

  • 处理应用代码的升级以及相关改动;

  • 发布一个service(要求不支持Kubernets API的应用也能发现它)等。

Operator模式是如此的受欢迎,以至于很多应用软件都出现了开源Operator,ClickHouse也不例外。由Altinity开源的ClickHouse Operator,提供了如下特性:
  • 根据定制资源描述创建ClickHouse集群;

  • 自定义存储配置;

  • 自定义pod模板;

  • 自定义service模板;

  • ClickHouse配置和设置;

  • 灵活的模板;

  • ClickHouse集群扩展;

  • ClickHouse版本升级;

  • 导出ClickHouse metrics到Prometheus。

这些功能满足了运维人员在单个Kubernetes集群中创建、扩展和升级ClickHouse集群的基本需求,但不能满足如下需求:

1)跨Kubernetes集群部署ClickHouse集群(因为不同AZ上对应不同Kubernetes集群,为了讨论的方便,这里以及后面对AZ的讨论都用Kubernetes集群替换)

2)在ClickHouse物理集群上划分逻辑集群

因此,直接使用开源ClickHouse Operator不能满足管理Events ClickHouse集群的需求。


2.2 Schema管理

Events监控系统的设计初衷是向整个公司的开发人员提供服务的,因此Schema有如下特点:

①Schema数量众多且持续增长,不同组用户拥有不同Schema,同一组用户也会拥有不同Schema;

②Schema间的数据量差异巨大,有的Schema日数据量达到百亿条记录,有的Schema日数据量只有区区几千条记录;

③Schema需要支持添加新的字段;

④Schema数据量是动态变化的,可能由小变大;

⑤Schema数据保存时长要求不同,大部分用户使用默认时长,少部分用户则希望增加时长。


Schema的这些特性,对运维能力提出了如下挑战:

①存储众多且不断增长的Schema;

②如何确保添加新字段的过程中,不会影响Schema数据的插入和查询;

③一个大数据量Schema需要独占一个ClickHouse集群,多个小数据量Schema需要共享一个ClickHouse集群,那么如何管理Schema与ClickHouse集群之间的对应关系;

④Schema数据量由小变大,以至于需要单独ClickHouse集群,如何迁移Schema到单独的ClickHouse集群;

⑤提供定制化的Schema数据保存时长设置。

同时,我们的ClickHouse服务器使用本地持久卷(local persistent volume)作为ClickHouse的数据目录,在ClickHouse服务器需要重建本地持久卷时,如何确保Schema被及时重建。对以上Schema的运维工作,如果手工去完成的话,将是一件无比艰巨的任务。但应用Operator模式,将Schema定义成自动化任务的目标(定制资源),并通过某种方式给出Schema和ClickHouse集群之间的对应关系,然后通过Operator封装在ClickHouse集群上创建Schema的自动化任务,将会使得Schema的管理变得轻松愉快。


2.3 总结

通过上述分析,我们知道Events监控系统有两个运维问题,并且可以应用Operator模式解决:
1)ClickHouse集群管理

- 创建、更新、扩展ClickHouse集群;

- 支持跨Kubernetes集群部署ClickHouse集群;

- 支持在ClickHouse集群上划分逻辑集群。

2)Schema管理

- 存储Schema对象;

- 维护Schema和ClickHouse集群的对应关系;

- 在ClickHouse集群上创建Schema;

- 添加新字段不影响Schema数据插入和查询;

- 支持Schema在ClickHouse集群间的迁移;

- 定制化Schema数据保存时长;

- ClickHouse服务器重建本地持久卷时,及时重建Schema。


3  解决方案

下面开始讨论Operator模式是如何解决上面两个问题的。在开始之前,你需要了解Operator是如何工作的。
首先,你需要为你的自动化任务定义一个CRD,并将该CRD配置到Kubernetes集群中,CRD允许用户创建新的资源类别同时又不必添加新的API服务器。假设我们有一个名为EventsSchema的CRD,用于在ClickHouse集群上创建Schema任务的定义。然后,你可以创建EventsSchema的对象,创建操作会使用你所设定的名字和模式定义创建一个新的定制资源,Kubernetes API负责为你的定制资源提供存储和访问服务。假设创建了一个名为demo的定制资源。接下来,你需要实现一个Operator,由Operator的控制器代码执行操作使现实与定制资源匹配。完成开发工作后,将Operator打包成容器镜像,部署到Kubernetes集群上。Operator处于运行状态后,会获取定制资源demo,在ClickHouse集群上创建demo中描述的Schema;后面如果修改了定制资源demo,Operator控制器也会修改ClickHouse集群上Schema。
因此,应用Operator的过程可以分解为:1)定义CRD,配置到Kubernetes集群;2)实现Operator,并打包部署Operator到Kubernetes集群,Operator的核心是控制器;3)创建CRD对象来驱动Operator工作,使现实和CRD对象匹配。


3.1 ClickHouse集群管理

开源ClickHouse Operator已经实现了Kubernetes集群内的ClickHouse集群管理,欠缺的是跨Kubernetes集群部署和在ClickHouse集群上划分逻辑集群。因此,ClickHouse集群管理的工作变成了围绕欠缺功能设计CRD并实现Operator。

3.1.1 CRD
ClickHouse Operator使用ClickHouseInstallation来定义创建ClickHouse集群任务的定义,在创建ClickHouseInstallation定制资源时,可以设置集群规模和ClickHouse版本、自定义pod、自定义存储配置、自定义service以及配置和设置ClickHouse。通过修改ClickHouseInstallation定制资源,来扩展ClickHouse集群或升级ClickHouse版本。通过删除ClickHouseInstallation定制资源,来删除ClickHouse集群。我们以ClickHouseInstallation为基础,设计了如表2所示的3个CRD。由于CRD包含太多字符,就不放在这里了,以图片示意。
表2 ClickHouse集群管理的CRDs

(点击可查看大图)


3.1.2 Operator
使用Kubebuilder框架[4],创建一个联邦ClickHouse Operator,在其中实现3个CRD的控制器,实现如下功能:
1)FederatedClickHouseInstallation(简写FCHI)控制器
a.创建/更新FCHI定制资源时,FCHI控制器为FCHI定制资源在目标Kubernetes集群内创建/更新/删除ClickHouseInstallation定制资源,并结合CombinedFederateClickHouseInstallation定制资源和CombinedFederatedClickHouseCluster定制资源生成集群节点配置文件。由各Kubernetes集群内的ClickHouse Operator根据ClickHouseInstallation定制资源创建/更新/删除ClickHouse集群。b.删除FCHI定制资源时,FCHI控制器删除FCHI定制资源在目标Kubernetes集群内的ClickHouseInstallation定制资源,以及为FCHI定制资源生成的集群节点配置文件。由各Kubernetes集群内的ClickHouse Operator根据ClickHouseInstallation定制资源被删除事件执行删除ClickHouse集群。
2)CombinedFederateClickHouseIn-stallation(简称CFCHI)控制器

CFCHI控制器检查CFCHI定制资源的合法性,在CFCHI定制资源不合法时,将其标记为不可用。

3)CombinedFederatedClickHouse-Cluster(简称CFCHC)控制器

CFCHC控制器检查CFCHC定制资源的合法性,在CFCHC定制资源不合法时,将其标记为不可用。


在以上描述中,需要指出的是:

①3个CRD的控制器的工作方式都被配置成周期性触发,也就是说即使没有更改定制资源,控制器也会周期性执行自动化任务。

②FCHI控制器生成的集群节点配置文件通过configmap挂载到ClickHouse服务器上,替换ClickHouse Operator生成的集群节点配置文件。

③在做ClickHouse集群升级时,FCHI控制器确保只有升级完一个Kubernetes集群之后,才会升级下一个Kubernetes集群,从而保证了不会有分片的所有备份都在升级的情况。

④在对ClickHouse集群做扩容时,FCHI控制器确保只有在ClickHouse集群扩容完成后,才会生成包含扩容节点的集群节点配置文件,避免ClickHouse服务器在未完成扩容之前就被加入到集群中。

⑤在对ClickHouse集群做缩容时,FCHI控制器确保在ClickHouse集群缩容前,生成缩容之后的集群节点配置文件,避免ClickHouse集群在缩容过程中包含被删除掉的ClickHouse服务器。

3.1.3 总结
至此,我们实现了跨Kubernetes集群的ClickHouse集群部署,并使用CFCHI定制资源组合多个FCHI使得其中的ClickHouse服务器相互可见,然后通过创建CFCHC定制资源实现在CFCHI内划分逻辑集群。


3.2 Schema管理

3.2.1 CRD
为了解决2.3节中总结的Schema管理问题,设计了如表3所示的3个CRD。
表3 Schema管理的CRDs

(点击可查看大图)


3.2.2 Operator
在3.1.2节创建的联邦ClickHouse Operator中实现SchemaTable控制器。Schema-ClickHouseCluster和SchemaRetention存在的目的是为SchemaTable提供信息,不需要实现控制器。

SchemaTable控制器

a.查找与SchemaTable定制资源同名的SchemaClickHouseCluster定制资源,如果不存在,则使用名为default的SchemaClick-HouseCluster定制资源。组合SchemaTable定制资源和SchemaClickHouseCluster定制资源得到Schema的复制表、缓存表、分布式表和表集群。然后比较表集群中ClickHouse服务器上实际存在的复制表、缓存表、分布式表与Schema定义的区别,根据不同情况执行不同的操作:

    i)表在ClickHouse服务器上不存在,直接创建

   ii)Schema定义的表新增了字段,对于复制表和分布式表执行Alter命令添加新的字段,对于缓存表执行删除命令后重新创建

   iii)分布式表修改了ClickHouse集群参数,删除分布式表之后重建

b.查找与SchemaTable定制资源同名的SchemaRetention定制资源,如果不存在,则使用名为default的SchemaRetention定制资源。组合SchemaTable定制资源和SchemaRetention定制资源得到Schema的复制表和复制表数据保存时长。同时根据a步骤,可以得到Schema的表集群。然后对表集群中的所有ClickHouse服务器,查询system.parts表中超出保存时长的partition,对于查询返回结果执行drop partition命令。
SchemaTable控制器的工作方式也被配置成周期性触发,也就是说即使没有更改定制资源,控制器也会周期性执行自动化任务。因此,对于重建本地持久卷的ClickHouse服务器,在SchemaTable控制器周期性地执行自动化任务时会发现表不存在,从而重建。自此,关于Schema管理还剩两个问题没有得到解答,因为这两个问题需要外部模块的协同。
1)添加新字段不影响Schema数据插入和查询

在SchemaTable控制器添加新字段的过程中,存在删除缓存表的操作,因此需要插入端具备重试机制。在表集群的所有ClickHouse服务器完成添加新字段之前,插入端和查询端都不应该使用新的字段。

2)支持Schema在ClickHouse集群间的迁移

用一个例子来说明如何支持Schema在ClickHouse集群间的迁移。如图3所示,有两个ClickHouse集群clusterA和clusterB。在最开始,在clusterA上存在两个Schema,分别为events1和events2。现在想将events1迁移到clusterB,该怎么做呢?

a.首先通过CFCHC创建ClickHouse集群clusterAB,

b.然后修改events1的SchemaClickHouse Cluster定制资源,写集群和读集群为clusterA,表集群为clusterAB。

c.等events1的表在clusterAB创建完成后,修改events1的SchemaClickHouseCluster定制资源,写集群为clusterB,读集群和表集群为clusterAB。这时候,events1的数据都写到了clusterB,

d.等过了events1的保存时长后,clusterA上将不再有events1的数据。这时候,修改events1的SchemaClickHouseCluster定制资源,写集群、读集群和表集群都设置为clusterB

e.最后将clusterA上的events1表删除掉。

这样就完成了Schema在ClickHouse上的迁移。上述过程以一个API调用的形式存在,只需要向API提供Schema和想迁移到的ClickHouse集群就能自动执行。

图3 Schema在ClickHouse集群间的迁移

(点击可查看大图)


3.2.3 总结

我们用SchemaTable存储用户Schema,用SchemaClickHouseCluster存储Schema和ClickHouse集群的关系,用SchemaRetention存储Schema数据保存时长。然后由SchemaTable控制器负责在表集群的ClickHouse服务器上创建Schema及添加新字段。通过将SchemaTable控制器的工作方式配置成周期性触发,能够处理ClickHouse服务器重建本地持久卷导致的表丢失。并解释了添加新字段不会影响Schema数据插入和查询,以及如何在ClickHouse集群间迁移Schema。


3.3 Operator部署

图4 Operator部署方式

(点击可查看大图)


完成联邦ClickHouse Operator开发工作之后,采用图4所示的部署方式。最上层是一个跨AZ的API服务器,在上面配置FederatedClickHouseInstallation、CombinedFederateClickHouseInstallation、CombinedFederatedClickHouseCluster、SchemaTable、SchemaClickHouseCluster和SchemaRetention这6个CRD。联邦ClickHouse Operator(见图中Federated ClickHouse Operator)部署在多个Kubernetes集群上,工作于主从模式,同一时刻只会有一个联邦ClickHouse Operator的pod在工作。联邦ClickHouse Operator监听在跨AZ部署的API服务器上,以及多个Kubernetes集群的API服务器上。ClickHouseInstallation配置在Kubernetes集群内的API服务器上,ClickHouse Operator监听在Kubernetes集群内的API服务器。
通过这样的部署方式,我们可以只通过跨AZ的API服务器这一个接口,创建定制资源,由联邦ClickHouse Operator完成运维工作。


4  案例


本节将通过一个用户的故事,一探联邦ClickHouse Operator是如何支撑系统工作的:

1)用户创建Schema A

用户通过UI提交创建Schema A的请求,该请求会为Schema A创建SchemaTable定制资源,同时检查Schema A的SchemaClickHouseCluster定制资源是否存在,如果不存在,会用默认ClickHouse集群为Schema A创建SchemaClickHouseCluster定制资源。定制资源创建完成后,联邦ClickHouse Operator为Schema A在它配置的表集群上创建复制表、缓存表和分布式表。

2)用户发送Schema A的Events数据,Ingress负责接受和写入ClickHouse集群

Schema的名称会和Events数据一起发送。同时,Ingress集成了服务发现功能,能够获得给定Schema的写集群及写集群的ClickHouse服务器信息,因此Ingress可以将Events数据写入正确的ClickHouse集群。

3)运行一段时间后,因为需求变化,用户需要在Schema A上添加新的字段

用户通过UI提交在Schema A上添加新字段的请求,该请求会修改Schema A的SchemaTable定制资源,以反映添加的新字段。定制资源修改完成后,联邦ClickHouse Operator为Schema A在它配置的表集群上的复制表、缓存表和分布式表中添加新字段。注意,在表集群完成添加新字段之前,新字段对Ingress是不可见的。

4)因为业务的增长,Schema A的数据量猛增,需要迁移到一个新的ClickHouse集群

根据Schema A的数据量估算所需的ClickHouse集群规模,然后通过创建一个新FCHI定制资源来得到新的ClickHouse集群。新ClickHouse集群创建完成后,按照3.3.2中所展示的方式将Schema A迁移到新ClickHouse集群。

5)又过了一段时间,用户想增加Schema A数据的保存时间

用户需要提供增加数据保存时间的合理说明,然后由我们为Schema A创建SchemaRetention定制资源,并指定数据保存时长。

通过上述Schema A的例子,可以发现联邦ClickHouse Operator基本实现了运维的自动化、人工介入少而且操作简单。


5  总结

本文首先介绍了监控团队的Events监控系统,然后对Events系统的运维自动化展开讨论,分析了ClickHouse集群管理和Schema管理存在的困难,提出使用Operator模式解决问题的思路,并详细介绍了我们设计的CRD、控制器和Operator部署方案。最终,对跨AZ环境下的ClickHouse集群管理和Schema管理,通过Operator模式实现了运维自动化。


参考资料:

[1]https://kubernetes.io/zh/docs/concepts/extend-kubernetes/api-extension/custom-resources/

[2]https://github.com/Altinity/clickhouse-operator

[3]https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-regions

[4]https://book.kubebuilder.io/



往期精彩

分享|eBay边缘节点的云原生网络实践

亿展宏图 第七篇|动态图算法

干货|eBay Feature测试环境上k8s的实践

从Druid到ClickHouse|eBay广告平台数据OLAP实战


点击阅读原文,一键投递

       eBay大量优质职位虚席以待

       我们的身边,还缺一个你

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存