开源之夏个人专访与项目经验分享持续开放中,欢迎正在参与和已从开源之夏毕业的学生、导师一同加入专访行动,填写文末专访问卷,与大家分享你的开源经验与收获!
开源之夏 2024 项目经验分享第六期来自 KubeBlocks 中选学生 甘一伟 同学,他在本届活动中承担的项目是:KubeBlocks 支持 etcd Add-on
项目名称:KubeBlocks 支持 etcd Add-on
项目导师:燧木
项目描述:KubeBlocks 是目前最流行的在 Kubernetes 上管理数据库的开源项目之一,该项目以统一的 API 管理多达 33 款数据库、流计算等数据系统。etcd 是流行的高可靠、强一致 K/V 存储,通常作为一个高可用组件,在分布式系统中承担配置共享、分布式锁、服务发现等功能。本项目的目标是通过 KubeBlocks Add-on 机制,将 etcd 接入到 KubeBlocks 中,使得 KubeBlocks 支持管理 etcd。具体功能包括:etcd 基本生命周期管理、备份恢复、运行时/动态配置更新(Runtime/Dynamic Reconfiguration)、基于角色的更新策略、基于 OpenTelemetry 的可观测能力
项目链接:https://summer-ospp.ac.cn/org/prodetail/248330234
导师专访回顾:
KubeBlocks 本质上是一个 k8s operator,其用途是在 k8s 集群上对多种类的数据库进行管理,KubeBlocks 抽象了适用于管理各种数据库的 API,要将 etcd 接入到 KubeBlocks,主要内容可以分为如下三部分:
1. 了解 KubeBlocks 的工作机制
2. 熟读 KubeBlocks 的 API 接口,了解数据库在 k8s 里是怎么管理的
3. 针对 etcd 运维的各个场景,参考KubeBlocks-add-ons(https://github.com/apecloud/kubeblocks-addons) 中类似的数据库引擎和其他开源的 etcd operator ,在 KubeBlocks 上实现该能力
KubeBlocks 的工作机制
上面说到 KubeBlocks 是一个 k8s operator,这个管理数据库的 operator 其实包括很多的 controller,作为一个 Golang 项目,这些 controller 当然可以被打包成 docker image,并且作为一个 k8s 集群 pod 中某个 container 依赖的镜像去运行。在运行的过程中,controller 会不断地 Reconcile(协调)k8s 里面的资源,一遍一遍地读取当前状态的 CR(Custom Resource),与 k8s 的 api server 交互,将数据库的状态趋向于已经声明的 add-on 的状态。
KubeBlocks 提供的 API
KubeBlocks 作为一个 dbPaaS operator, 通过抽象和分层,将 k8s 提供的资源(如 pod、volume、secret、service 等)整合起来,以最合适的方式在数据库的运维层面体现,对外暴露了能够描述数据库容器组成、组件相互关系、集群拓扑结构、对外服务、数据库事件响应的 API。
要接入 etcd,我们需要关注的主要是 ComponentDefinition 和 Cluster 这两个 CRD(customer resource definition,k8s 里面自定义的资源)。前者描述了单个数据库示例的各种资源以及某种状态下需要执行的行为;后者定义了一个数据库集群的节点数、数据库实例的拓扑结构、需要暴露的服务、节点配置的资源等信息。通过这两个主要的 API 接口,我们可以编写 yaml 以及脚本,对 etcd cluster 进行描述和管理。
KubeBlocks 使用 InstanceSet 代替 StatefulSet 来作为 Workload 管理有状态的数据库(InstanceSet 作为燧木师兄的得意之作,etcd 正好可以作为 InstanceSet 的最佳实践之一 ),下面是 KubeBlocks 1.0 下部署三节点 etcd 的 InstanceSet(删去了部分杂乱的信息),究其本质,可以得知 InstanceSet 这个 Workload 其实引用了 k8s 原生的 corev1.PodTemplateSpec 这个 API 描述了 etcd 及其相关 container,并且添加了 status 等字段以监控节点状态。
etcd 作为 Raft-based 的强一致性 KV 存储,当然也会划分 Leader、Candidate、Follower 等角色赋予不同的能力使得节点状态趋于一致,而 InstanceSet 会从 kbagent(KB 1.0) 的 roleProbe 这个 lifecycleAction 中得知节点的角色,在 etcd 中,不同角色的实例会存在差异。比如在对外提供的服务能力上,通常主节点可以提供读写能力,其它节点提供只读能力(ReadIndex 优化可以 Follower 读)。在运维时,按照数据库运维最佳实践,通常先逐个升级备实例,最后升级主实例,在升级主实例前,需要先做一次 switchover,以保证数据的完整性并降低服务不可用时间。
在 KubeBlocks 接入 etcd
接入 etcd 的开发工作可以分为以下几个点:
1. 搭建 k8s 环境,KubeBlocks 部署与调试
2. etcd 集群 BootStrap
3. 配置更新
4. 支持 etcd 的基本生命周期中的 TLS 认证
5. 备份恢复
6. 基于角色的更新策略与 switchover
7. 基于 Prometheus 和 Grafana 的可观测
8. defragmentation 碎片空间回收 ops
因为篇幅有限,在此挑出两三个比较有意思(zhemo)的点来展开一下:
etcd 集群 BootStrap
etcd 集群启动的时候,需要知道自己的 hostname 以及 peer 节点的 FQDNs(Fully Qualified Domain Name,在 k8s 中可以表示一个 service 对外暴露的 dns 地址),目前 KubeBlocks 让集群的 peer 相互通信的默认方案是暴露一个默认的 headless 的 clusterIP 类型的 service,这个 service 会为每个 etcd 节点绑定一个 FQDN,比如`etcd-cluster-etcd-0.etcd-cluster-etcd-headless.default.svc.cluster.local`,etcd 需要开放 2380 端口用于 peer 之间通信。
值得一提的是 etcd 还可以支持 Multi-Cluster(异地容灾的部署方案在机房刮龙卷风的时候可以起到很好的容灾效果)。然而,KubeBlocks 默认的 headless service 只能用于集群内部通信,如果要引入 Multi-Cluster,就需要考虑用集群外部能够访问的 service,如 NodePort 和 LoadBalancer 类型的 service,笔者主要适配了 LoadBalancer 类型的 service,其原理主要是 KubeBlocks 接入云厂商提供的负载均衡器,从中获取到其分配的公网 IP,etcd 再通过 KubeBlocks 提供的 Vars 机制获取到集群的节点地址,将其作为变量写入到 configmap 中。
还有一个坑点是 etcd 的版本在`<= 3.4.22 || (>=3.5.0 && <=3.5.6)`的时候其官方镜像是基于 busybox 的,但是,高版本的 etcd 为了更安全,采用了 distroless 作为基础镜像,在 KubeBlocks 中,我们需要对不同的数据库编写不同的脚本,这依赖于基本的脚本执行环境(bash、cd、ls、grep 等 brinaries),通过与燧木导师以及 KubeBlocks 社区的各位师兄师姐沟通,决定使用`busybox:1.35-musl`作为 initContainer,复制其静态编译的 brinaries 到 emptyDir 类型的共享 volume,为 etcd container 注入一个脚本的基本执行环境。
TLS 认证
etcd 的 tls(https://etcd.io/docs/v3.4/op-guide/security/) 分为两部分,一个是 client2server,一个是 server2server,两者互不干扰。官方推荐只用一个根证书,根据不同节点的 hostname,对不同节点的证书进行签名,进而将 tls 证书文件填到配置文件中。但是根据 KubeBlocks 生成的 tls 文件启动 etcd 并开启 tls 认证后,通过 curl 与 etcd 交互会 timeout:
似乎没有什么有效信息,遂向燧木师兄求助,得到回复“peer 之间建议用自签名,让 etcd 集群能跑起来,然后通过 openssl s_client 测试外部服务端口,看哪一步出问题”。于是发现了 KB 生成的 tls 文件为 ca.crt、tls.crt 和 tls.key,并且 ca.crt 和 tls.crt 是相同的,也就是使用的是自签名的证书。结合之前看过的 etcd 文档,发现需要修改 KB 生成 tls 的逻辑,让根证书对次级证书签名,并通过 dns wildcard 匹配的方式对 KubeBlocks 默认的 headless service(`*.%s-%s-headless.%s.svc.cluster.local` )进行了匹配,使 peer 之间能够互相认证,改完后发现 etcd 的 tls 配置能够正常生效了,顺便喜提 KubeBlocks 的 good first issue 一枚。
备份恢复
在备份前会先检测需要用`etcdctl`工具生成备份的文件,并且检验是否能被`etcdutl`正常解析,如果无法被正常解析或者备份文件解析后 etcd 存在的 key 数量过少,则会跳过此次备份;恢复同理。
测试 etcd 生成的 snapshot 在 minio 这个对象存储中的备份恢复时,首先得部署 minio 并暴露 9001 端口,然后创建 access key 和 bucket 便于 etcd 的访问。KubeBlocks 的备份恢复接口主要有 BackupRepo 和 BackupPolicyTemplate,前者描述了一个备份数据的仓库的类型以及应该怎么访问,后者则是根据不同数据库实现不同的备份策略,需要将 access key 配置成 k8s secret,进而部署 backupRepo。
部署成功后,就可以手动触发备份操作,但是不知道为啥,备份一直没有开始,`kubectl describe cluster`查看集群信息的时候发现 component 的状态为 creating,但是没有进一步的报错:
向燧木师兄以及 KubeBlocks 社区的热心师兄师姐求助后,开始一步步排查问题:首先是检查了一下 pod 中的 container 是否正常运行,发现 lorry container 并未正常启动(lorry 是 KB 0.9 的 roleProbe 容器),进而继续看 KubeBlocks 的报错,发现 RBAC 相关的资源缺失。其实是 KubeBlocks 本地部署的问题,遂使用`kbcli kubeblocks install --version=0.9.0`安装完整的 KB。进一步的,如果要本地调试 KubeBlocks,可以通过`kubectl scale deployment kubeblocks --replica=0 kb-system`让名为 kubeblocks 的 `controller-manager` 这个 pod 下线,再 go run 启动本地的 controller-manager 进行 debug 即可。
学到的 debug 思路:cluster no ready->kubectl describe InstanceSet ->发现 pod 的某个 container 有问题-> k logs pod -c container->直接看 controller 的日志->看 KB 代码。
同类产品调研
项目完成后,找了一下同类的开源 operator(star 数截止至 2024.10.14),大都不像 KubeBlocks 一样定义一套 API 适配多种引擎,而是定制开发一套 etcd operator,定义了 CR 对部署在 k8s 上的 etcd cluster 进行控制。综合各 operator github 页面 README 的介绍,得到表格如下(粗略对比,不一定严谨):
可以看出,仅仅是做了 etcd add-on 的适配,KubeBlocks 和其他的 etcd 专用 Operator 在 etcd 运维能力上就可以相差无几,并且面向有状态节点做了更进一步的优化,如 switchover 以及基于角色的 serial updateStrategy 等。
--自我介绍--
ospp:请介绍一下自己。
甘一伟:大家好,我叫甘一伟,本科就读于华南师范大学软件学院,如今在西北工业大学计算机学院读研一。在本科期间,除了完成基本的课业任务之外,我还参加过 XCPC 等算法比赛。去年九月份保研至西北工业大学,做完毕设后终于有了比较多的空闲时间,又因为本身对开源也比较向往,就非常幸运地参加了 KubeBlocks 社区的开源之夏项目。
ospp:请结合你的经历分享一下你对开源的理解,开源对你的编程技能提升有什么帮助?
甘一伟:我觉得开源的本质是开放源代码,作者将代码开源到 github 或者是 gitee 这些代码仓库后,世界各地的程序员就可以轻易地获取到他人智慧的结晶,避免了重复造轮子的过程。在这个过程中,好的项目可以吸引到世界各地的人们一起参与,素未谋面,来自远方的友人自发地在互联网上共同维护一个项目,我觉得是一件很酷的事情。同时好的开源项目还会催生出良好的社区管理,可以是 github issues,可以是论坛,可以是聊天软件。我的本科毕设就是在一个开源的分布式存储系统上做一些小的优化,在遇到问题的时候,我会经常去对应的开源社区提问(现在回看我的问题其实很多都非常弱智,事实上我应该多点 RTFM 和 STFW,在此推荐提问的智慧(http://www.catb.org/~esr/faqs/smart-questions.html)),但是开源社区的 maintainer 还是非常耐心地回答了我的问题,所以我觉得绝大部分开源贡献者都是非常 nice 的,想要参与其中的话就直接提 issue/pr 吧。P.S. 在完成我的毕设后,我也将相关的代码开源了出去,让我感到 amazing 的是,竟然真的有人对这个感兴趣,并且找我聊了一下相关的问题。
开源对我的编程能力毋庸置疑肯定是有非常大的提升的,参与到开源项目的过程中,首先自己需要阅读大量的代码来理解项目的基本结构,其次因为涉及到团队协作,自己也会更加注意,尽可能地把代码写得优雅一点(虽然我写的代码离优雅还差十万八千里),并且能够在出名的开源项目上审核代码的人肯定是经过社区共同验证的,身经百战,编程能力很强的人,他们会帮我们将平时不会注意到的问题指出来,方便我们更好地改进(这次参加开源之夏我提的 PR 就被 KB Maintainers 找了几十个问题 : D
--参与开源之夏--
ospp:你是如何了解并参与开源之夏活动的呢?
甘一伟:其实在大二的时候就听说过开源之夏这个活动,但是碍于一直没有时间/觉得自己基础没打好,害怕自己达不到参加开源之夏的标准,就没有报名(事实上不用想太多),读研之前正好空出了大片空闲的时间,没想太多,鼓起勇气就报名参加了。
ospp:开源之夏活动与你之前参加的竞赛类活动有什么区别?
甘一伟:我觉得区别还是很大的,举个例子,XCPC 之类的算法竞赛一般是三人一机,在 5 个小时内尝试解决 10 来道算法题,问题都是非常明确的,不会就是不会,坐多久都没用;而参与开源之夏则是需要长期与社区交流,弄清楚自己要做的工作,一步步地去解决问题。
ospp:为什么会选择申请 KubeBlocks 社区的项目?为申请项目提前做了什么沟通和准备
甘一伟:当时其实对存储和云原生都比较感兴趣,然后就把看起来有意思的项目都投了一下(大家不要学我,最好是看准一两个再联系导师),燧木师兄回得最快,然后约了个时间视频聊了一下就开始看相关资料,所以就决定是 KubeBlocks ,着手写项目申请书了。后来深入地了解了一下,发现 KubeBlocks 做的事情是没有人做过的,通用数据库 k8s Operator 的相关工作,我觉得在目前这个数据库上云的大趋势下能够从零开始了解这个项目并为其做一些微小的贡献是很好的事情。
ospp:社区与导师为你提供了哪些支持与帮助?
甘一伟:KubeBlocks 社区和导师在我完成开源之夏项目期间提供的帮助是非常多的,首先是提交项目申请书的前后,我经常会有一些 k8s、KubeBlocks 或者是 etcd 相关的问题会与燧木师兄探讨,后面到了实际开发的时候,因为我暑假去了上海玩,正好离 ApeCloud(做 KubeBlocks 的公司)的杭州总部比较近,就问燧木师兄能不能到直接到线下去完成后续的工作,得到肯定的回复之后就到了线下,后面的体验也是非常棒,之后项目上的问题就直接和对应的 KubeBlocks Maintainers 对线了(写到这里的时候想念 ApeCloud 的人体工学椅、大显示器、零食和超级nice的小伙伴们了)。附图一张味道很怪的人参味苏打水:
--建议与寄语--
ospp:从本科到现在的硕士 ,你所在的学校中开源氛围如何?身边参与开源的同学多么?
甘一伟:有一说一,我觉得无论是本科还是硕士阶段,我接触到的同学们其实参加开源的都不是很多,不过也认识一些非常深度参与开源,长时间为各大开源社区做贡献的朋友,我觉得这是一件很 geek,很酷的事情。
ospp:你觉得参与开源对于计算机专业的大学生有何帮助?你会推荐他们参加开源之夏这样的活动吗?为什么?
甘一伟:从功利的角度,参与开源可以带来很多好处,比如说我们能够接触到一些真正有用的项目,可以大大提高自己的 coding 能力;同时,在一个开源项目上提交了多少行代码,参与了多少个 issue,它们会伴随着你的 github ID 一直保留着,会成为程序员同行了解你的一个重要途径;也可以借着参与开源这个契机与很多业界 coding 能力很强的程序员建立 connection。但是我觉得前提是我们需要把事情做好,这是一件需要投入时间与精力,在一定程度上走出舒适圈的事情。当然会推荐,我觉得开源之夏是一个对接开源社区和在校学生的一个很好的活动,对于在校大学生而言,如果对技术有一定追求,想要提高自己的编程能力和各方面软实力的话,可以尝试一下不随大流去卷学校推荐的各种 PPT 大赛,来参加一下开源之夏。这其实是一个很不错的接触开源的机会,不用自己去各大社区寻找适合自己的 good first issue,开源之夏把参与开源的门槛稍微降低了一些,为学生匹配了一名开源社区中有经验并且愿意带人的导师,可以让我们更容易参与到开源项目中来。无须多言,行动起来才是最重要的。
END
专栏编辑:HungryFish
校对:校大山、甘一伟
制图:GoodWhite
专栏投稿请联系开源小助手:kaiyuanzhixia ,或填写下方专访信息收集问卷