乐趣区

关于后端:LindormOperator云原生实践

简介:Kubernetes 的 CRD 机制(CustomResourceDefinition)反对通过自定义的 controller 来治理资源的生命周期,这样就能够像操作 pod,deployment 一样来不便的治理运维一些简单的资源对象。随同着云原生这股技术浪潮,lindorm-operator 充分利用 k8s 底座的特点与劣势,在云环境(包含公共云、公有云和混合云)极大晋升了 Lindorm 数据库的生产和运维效率背景介绍:随着 Kubernetes 应用的越来越宽泛,k8s 治理的 native 的对象资源有时并不能满足用户的需要,为了进步可扩展性,自 v1.7 以来,Kubernetes 引入了 CRD 机制(CustomResourceDefinition),简略的来说它容许用户定义本人的对象资源注册到集群中,并且通过自定义的 controller 来治理这些资源的生命周期,这样就能够像操作 pod,deployment 一样来不便的治理运维一些简单的资源对象。随同着云原生这股技术浪潮,lindorm-operator 充分利用 k8s 底座的特点与劣势,在云环境(包含公共云、公有云和混合云)极大晋升了 Lindorm 数据库的生产和运维效率。本文以 lindorm-operator 我的项目为例,深刻介绍了如何在 Kubernetes 中基于 Operator 模式实现自定义资源对象和控制器。Lindorm 部署架构演进史    Lindorm 是面向物联网、互联网、车联网等设计和优化的云原生多模超交融数据库,反对宽表、时序、文本、对象、流、空间等多种数据的对立拜访和交融解决。

      Lindorm 的整个部署架构大抵能够分为三个阶段,On 物理机 –> On ECS –> On K8S , 晚期输入时大部分是 on 物理机的部署状态,在 2020 年私有云商业化时开始以 On ECS 状态输入,尽管实现服务全面上云,然而整个架构上并没有全面云原生化,到了 2021 年左右在混合云输入状态构建上开始全面拥抱云原生,所有组件都 On k8s 部署,生产层只依赖 Operator,实现了真正意义上云原生化。尽管倒退工夫不长,然而管控架构基本上走的比拟靠前,充沛汲取云原生演进路线上的红利,让 Lindorm 生产效率越来越高。其余两种状态本文就不做重点介绍了,次要介绍 On k8s 模式的设计细节和实际。Operator 技术体系    Operator 能够看成是 CRD 和 Controller 的一种组合特例,Operator 是一个特定的应用程序的控制器,通过扩大 Kubernetes API 资源以代表 Kubernetes 用户创立、配置和治理简单应用程序的实例,通常蕴含资源模型定义和控制器,通过 Operator 通常是为了实现某种特定软件(通常是有状态服务)的自动化运维。controller 根本工作原理:

  上图图展现了 controller 的工作原理,实际上是一个事件监听变更再变更的循环流程,以一个 pod 变更为例对照该图阐明下 go-client 的外围组件和咱们的 controller 是如何配合工作的:go-client 在初始化时,Reflector 会先 List API 取得所有的 Pod,并放入本地缓存 Informer 初始化实现之后,Reflector 开始监听 Pod 变更相干的工夫; 如果此时 pod_1 被删除,那么 Reflector 会监听到这个事件 Reflector 将 pod_1 被删除 的这个事件的 callbacks 通过一个本地队列(DeltaFIFO) 存入 workqueueworker(实际上就是咱们业务的 controller)从队列中 取出事件处理 Controller 通过比照资源对象的理论状态和最终状态的差别,执行业务逻辑,并一直调谐达到最终状态。尽管 client-go 曾经封装了 controller-runtime 和 controller-tools,用于疾速构建 Operator,然而从头开始去构建一个 CRD 控制器并不容易,须要对 Kubernetes 的 API 有深刻理解,还须要本人实现和 api-server 的一些交互细节。为了解决这个问题,社区就推出了对应的简略易用的 Operator 框架,比拟支流的是 kubebuilder 和 Operator Framework,这两个框架的应用基本上差异不大,咱们能够依据本人习惯抉择一个即可。Lindorm+Operator 的问题与挑战    Lindorm 之前的部署状态是 On ecs 的,管控的架构基于 pengine,通过工作流编排来实现实例的生命周期治理,这套架构须要管控侧须要感知很多调度细节。而 Lindorm 和 Operator 结合能把资源的调度,节点的弹性伸缩能力,平安隔离等个性更好的下沉到 K8S 集群,充分利用云原生技术红利实现一套 Lindorm CR 定义和一个 Operator 组件能在各种简单环境轻松交付 lindorm 实例。为达成这个指标,从相应零碎的设计,开发,构建,部署,交付,监控及运维等整个利用生命周期各环节都须要被重塑。从 Lindorm 集群自身的治理上看,外围要求次要有以下几个:体验降级:实例创立,监控报警,排障定位等须要做到通顺与简略,整体流程要比之前效率更高弹性按需扩缩容能力:依据业务负载灵便动态分配或开释资源容错 & 平安能力:宕机迁徙,故障隔离,负载平衡等可观测性:丰盛且细粒度的监控指标,可长久化的提供查问多状态集群兼容:目前 Lindorm 状态十分丰盛,有单机版,集群版,多可用区版本,基于存储池的 serverless 版本,须要在一套 operator 架构下实现多种状态编排    尽管 K8S 为咱们提供了根底的 workload、存储,网络的调度和治理能力,然而如何将这些能力充分利用起来是 Operator 面临的挑战,LindormOperator 是集群内实例生命周期治理和运维的入口,Lindorm 集群的生产和运维就是 Operator 通过正当的编排调度 K8S 的原生资源来实现的,如下图所示 operator 会继续监听 K8S 原生资源和 Lindorm CR 的状态变动,并做出响应的动作,通过一直的 reconcile 使 Lindorm 集群最终达到咱们想要的状态,所以 operator 是 Lindorm 生产层的要害组件,对于 workload,存储相干技术计划的选型和思考间接影响着 Lindorm 的性能是否在 K8S 上充分发挥,上面会围绕这些问题和挑战重点介绍 lindorm-operator 的一些次要设计逻辑和技术选型的一些思考。

 技术计划整体架构     Lindorm 的部署架构经验了物理机 -> 虚拟化 (On ecs) -> 云原生化(On k8s) 的逐渐演进,目前在私有云的售卖状态是 On ECS,私有云基于存储池的 On K8s 云原生架构也已将上线,整体架构如下图所示:

     其中,LindormCluster 是由 CRD(CustomResourceDefinition)定义的自定义资源,用于形容用户冀望的 Lindorm 集群状态,集群的编排和调度逻辑则由下列组件负责:Lindorm-Controller 是一组 Kubernetes 上的自定义控制器。这些控制器会一直比照 LindormCluste 对象中记录的冀望状态与 Lindorm 集群的理论状态,并调整 Kubernetes 中的资源以驱动 Lindorm 集群满足冀望状态;Lindorm-webhook 是一个 Kubernetes api 的拦截器,会对外部传入的 Lindorm CR 做一些参数校验或者减少默认参数配置,也可拦挡局部高位操作,比方集群开释等。实例部署拓扑 & 生命周期治理   实例资源编排肯定是基于部署拓扑的,为了保障内核组件架构的一致性,On K8S 架构下内核组件的部署拓扑和私有云目前售卖状态根本保持一致,具体如下图所示:

      实例创立和弹性变配都是通过批改 Lindorm CR 实现,只有通过 kube api 能够很不便的批改 Lindorm 资源,从管控视角和运维视角就有统一的体验,能够防止很多反复建设的工作,下图就是 Lindorm 实例变更时各个组件的交互逻辑。

以实例生产和变更流程为例,阐明外部的流程:用户通过 API Server CURD LindormCluster,客户端能够是 kubectl,也能够是其余各种状态的 k8s ClientLindormCluster controller 依据用户写入的 LindormCluster,依据依赖关系 别离创立出 LindormZK, LindormStore,LindormTable, LindormTSDB, LindormSearch 资源子资源的 Controller 组合出所需的 configMap,StatefulSet,CommonService,HeadlessService,Ingress,并顺次创立这些资源。子资源的 controller 监听 各自资源(statefulSet, service,ingress) 的状态, 并通过 API Server 更新到 etcdLindormCluster Cluster 承受到子资源的更新 event,更新 Lindorm Cluster 的 Status,通过 API Server 更新的 etcd 用户通过 GET LindormCluster/status 获取到整个集群的状态信息

  外围设计要点将 Lindorm 资源从部署拓扑角度进行拆分,每个组件都有独立的 Controller,这样组件间的事件就互相独立,晋升 reconcile 流程的稳定性;Lindorm CRD 定义尽量简化,对外只裸露资源相干的参数,将编排细节内聚在 Operator 外面充分利用 K8S 的调度策略实现 pod 的打散,保障同类引擎的 pod 扩散在不同 pod,防止大量节点异样导致计算引擎服务异样对于有状态服务敞开驱赶策略,不容许 pod 漂移,这样能够尽量大量 pod 被删除的危险架构劣势节约老本通过 Operator 调度具备更高效的资源变配能力,疾速弹性可通过存储池化调度,大大降低存储老本体验降级交付老本大大降低,可实现标准化交付流程屏蔽底层硬件,升高运维老本,当节点出现异常时可通过 pod 漂移重布解决实例生产速度和效率大大晋升,用户体感更好充分利用底座调度能力,管控调度逻辑更清晰,让开发更丝滑升高复杂度晋升管控开发迭代效率,组件依赖缩小没有简单的元数据依赖关系技术选型的思考     Lindorm 作为一款数据库产品在迁徙到 k8s 的过程中也遇到了不少问题,因为 k8s 的调度设计对无状态的服务是有人造的敌对性,但对于 Lindorm 这种重存储的产品来说会面临诸多挑战,上面别离从 workload,存储和网络三个方面来介绍 lindorm 在 k8s 上实际的一些思考。Workload 选型     在谈 workload 选型前咱们能够先思考下数据库这种重存储、有状态的服务对部署的根本要求有哪些,在我看来  最根本的个性次要有 4 个:(1)  稳固的网络:Pod 须要具备稳固的网络标识,因为这个数据可能会被长久化;(2)  稳固的存储:存储不随 pod 的状态变动而有所扭转;(3)  平安可控的降级:灰度可控的降级和重启(4)运维可控:具备指定 pod 降级的能力,有状态节点运维会波及主从节点切换,须要按需降级 K8S 内置了 Pod、Deployment、StatefulSet 等负载类型(Workload)。Pod 是 k8s 最小的调度、部署单位,由一组容器组成,它们共享网络、数据卷等资源。通过 Pod 胜利将业务过程容器化了,然而 Pod 自身并不具备高可用、主动扩缩容、滚动更新等个性,因而为了解决以上挑战,Kubernetes 提供了更高级的 Workload,次要有 Deployment 和 Statefuleset,前者次要针对无状态服务场景,生成的 pod 名称是变动的,无稳固的网络标识,而 statefulset 为每个 Pod 提供惟一的名称、固定的网络身份标识、长久化数据存储、有序的滚动更新公布机制。基于 StatefulSet 能够比拟不便的将 etcd、zookeeper 等组件较繁多的有状态服务进行容器化部署。应用 Statefulset 益处是能够复用局部已有的能力,实现横向扩大的能力。然而业务诉求是多样化的,以 Lindorm 为例,Statefulset 尽管能满足各个节点根本的部署和扩大需要,然而对于运维场景的非凡需要就无奈满足,比方 Pod 不重建、反对原地更新,或者按需降级等等。为了满足业务上述的原地更新、指定 Pod 更新等高级个性需要,阿里的开源我的项目 Openkruise 就蕴含一系列 Kubernetes 增强型的控制器组件,包含 CloneSet、Advanced StatefulSet、SideCarSet 等,CloneSet 是个专一解决无状态服务痛点的 Workload,反对原地更新、指定 Pod 删除、滚动更新过程中反对 Partition,Advanced StatefulSet 顾名思义,是个加强版的 StatefulSet,同时反对原地更新、暂停和最大不可用数。这些个性给运维带来了极大的便当,Lindorm 在有状态节点的编排上也是用了 Openkruise 存储层技术选型     存储是 Lindorm 数据库系统十分重要的一部分,存储要求稳固、可用、性能、牢靠。在用户看来存储就是一块盘或者一个目录,用户不关怀盘或者目录如何实现,用户要求十分“简略”,就是稳固,性能好。为了可能提供稳固牢靠的存储产品,各个厂家推出了各种各样的存储技术和概念。无论是开源的存储我的项目还是商业的存储产品,评估办法具备普适性,次要从以下几个方面来思考:数据可靠性数据可靠性是指数据不失落的概率。通常状况下,存储产品会给出几个 9 的数据可靠性,或者给出最多容许故障盘 / 节点个数。存储采纳不同的数据冗余策略,提供的可靠性是不一样的。数据可用性数据可用性和数据可靠性很容易被混同,可用性指的是数据是否在线。比方存储集群断电,这段时间数据是不在线,然而数据没有失落,集群恢复正常后,数据能够失常拜访。存储性能次要分为容量型场景和时延敏感的在线场景   Lindorm 在 k8s 上是容器化部署,可选的存储计划从大的方面能够分为本地存储和网络存储:本地存储:如 HostPath,emptyDir 等,这种存储的特点是数据保留在集群的节点上,不能跟着 pod 漂移,节 点宕机数据不可用,然而长处是读写性能高;网络存储:Ceph、Glusterfs、NFS,OSS 等类型,这些存储卷的特点是数据不在集群的某个节点上,而是在远端的存储服务上,应用存储卷时须要将存储服务挂载到本地应用,性能上较本地存储差。Lindorm 作为一款高性能的多模数据库,网络存储会大大降低读写性能,而且网络存储的带宽也极易造成性能瓶颈,所以在这个大方向上 Lindorm 首选还是本地存储。然而选用本地存储就会丢失 K8S 对于存储 pv 治理的很多便利性,所以为了保障在应用本地盘的同时有能做到调度的便当,Lindorm 在比照以下多种技术计划后应用了 Pod 独占本地盘的模式:HostPath 模式:须要治理具体的物理盘,包含感知具体盘符,格式化,目录创立等,从目录创立到挂载是一个动态过程,也就是只能 ” 写死“,扩展性太差,个别很少应用这种计划      Local PV:local pv 的要害核心技术点是容量隔离(lvm、xfs quota)、IO 隔离、动静 provision 等问题(1)基于 volumeGroup: VG 屏蔽了多个物理磁盘,pod 应用时只需思考空间大小的问题,帮咱们屏蔽了底层硬盘带来的复杂性。然而这种计划也导致一份残缺的数据被散布到多个磁盘上,任何一个磁盘上的数据都是不残缺,也无奈进行还原,一块磁盘的抖动影响面会成倍放大。(2)pod 独占盘:每个 pod 绑定的 pv 独占一块盘的存储,这种模式能够防止下面基于 volumeGroup 造成的数据扩散,pv 数据与盘强相干,有利于故障隔离。比较简单的实现形式就是,为每一块盘创立一个独立的 pv,pod 申明对指定的 pv 的绑定,就能实现 pod 的单盘的独占。在 K8S 的实现角度能够通过动态 PV 或者动静 pv 实现这种绑定,然而动静 PV 须要依赖 k8s csi 插件的反对。动态 PV:

动静 PV:

      回到 Lindorm 的场景,咱们的数据节点根本要求是高性能和高牢靠,并且在存储池的部署场景下,存储节点是多租状态,对于故障隔离的要求尤其高,因而咱们抉择了本地盘独占的存储计划,在调度上,ASI 上曾经实现了基于此种场景的 CSI 插件,能够满足此种场景;针对其余不满足条件的规范 ACK,咱们也能够应用动态 pv 实现,只是须要提前创立好相干 pv。网络技术选型      网络架构是 Kubernetes 中较为简单、让很多用户头疼的方面之一。Kubernetes 网络模型自身对某些特定的网络性能有肯定要求,但在实现方面也具备肯定的灵活性。因而,业界已有不少不同的网络计划来满足特定的环境和要求。在基于 CNI 实现的各种 Kubernetes 的网络解决方案中(例如 Flannel,Calico 等),按数据包的收发模式实现可分为 underlay 和 overlay 两类。前者是间接基于底层网络,实现互联互通,领有良好的性能,后者是基于隧道转发,它是在底层网络的根底上,加上隧道技术,构建一个虚构的网络,因而存在肯定的性能损失。对于不同网络插件实现的技术细节本文就不扩大来讲了。

      Lindorm 在考量网络分案选型时充沛联合了 Lindorm 本身的业务特点和部署状态来思考,从技术上来看,以后风行的几种 CNI 的网络插件等都能满足 lindorm 的根本诉求,然而这种 Overlay 的网络会有肯定的性能损失,因而在综合性能和调度便利性上 Lindorm 在不同的部署场景心愿抉择的不同的网络计划。在基于 Lindorm 存储池状态下,因为存储池的 Node 是属于咱们强管控的资源,其网络资源都是提前布局好的,并且存储池 Pod 在部署状态上也充沛做了打散,数据节点独占 Node,因而为了最大限度利用节点的网络性能,存储池没有用基于 CNI 的计划,间接应用 HostNet,Operator 可能在节点调度上保障不会存在端口抵触的场景。而引擎层因为基于弹性 eci 部署,应用了 underlay 网络 terway。容错运维能力设计  Lindorm 的局部容错能力曾经内置到内核中,例如故障主动转移,备份与复原都由内核实现,在日常运维中更多是重启局部节点或者调整集群参数来修复线上问题,目前反对的运维能力次要有根底运维和配置变更两类。根底运维     LindormOperator 还没有配套的白屏化的运维控制台(能力正在建设中),次要的运维操作都通过黑屏形式操作;对于一些标准化的运维动作曾经实现了一个巡检 CronJob,能够对集群的状态做巡检并做出对应的运维操作,例如集群的容量水位爱护就通过这种形式实现,水位超过 90% 会触发写爱护,爱护引擎不会被写挂,后续还会在此基础上反对更多的能力。

主动运维次要流程如下:1. 集群中减少 cronjob 角色,由 lindorm-operator 创立,定期拉起 pod 运行巡检工作;2.cornjob 检测异样下发 lindorm-operator 的 customResource;3. 加强 sre 能力,sre 通通过拉取 sre operator 执行特定的运维动作;集群动静配置更新      在日常运维过程中大部分动作是批改集群的参数解决问题,基于此 Lindorm-operator 中实现一套动静配置更新的机制,惯例做法是登录到容器中批改配置而后重启容器,然而当节点比拟多时效率就会很低。实现思路如下图所示,LindormOperation 的的 CRD 定义了具体的操作类型和操作的相干参数,并创立对应的资源,Lindorm 的所有 POD 都会内置一个 SER Container,这个 Container 实际上也是一个“小 operator”,会监听以后所在 pod 的 operation 事件,而后进行解决,配置的批改就是通过 SRE Container 批改和 MainContainer 共享的 Volume 下的配置文件实现,对于须要重启能力失效的配置变更,Sre 还会触发 MainContainer 的重启。

Operator 高可用建设 Operator 可观测性对于 Operator 的可观测次要蕴含两局部,一个是集群 reconcile 事件的 trace,一部分是性能指标的监控 Trace 可观测性:  咱们对每一个组件的 Controller 的 Trace 日志都定义了一个独立的 event,能依据 trace 检索每一次 reconcile 事件的步骤细节,能够帮忙研发疾速定位问题。Metric 可观测性:   Operator 在启动的时候就开启了监控数据采集端口,反对 promethues 的 servie monitor 拉取 operator 的性能数据,次要包含根底的性能指标和 Controller Runtime 指标,并可根据此监控配置 promethues 报警,在集群呈现事件异样时能够提前感知。宕机复原    Lindorm Operator 是一个齐全无状态的服务,通过 Deployment 部署到 k8s 集群中,具备 readness 和 liveness 检测,在服务异样时能够从新被拉起,同时所有的 reconcile 逻辑都做了幂等性保障,这样即便 Operator 异样宕机在复原后也能从新处理事件。Leader Election    在 Kubernetes 中,通常 kube-schduler 和 kube-controller-manager 都是多正本进行部署的来保障高可用,而真正在工作的实例其实只有一个。这里就利用到 leaderelection 的选主机制,保障 leader 是处于工作状态,并且在 leader 挂掉之后,从其余节点选取新的 leader 保障组件失常工作。Lindorm operator 通过 Kubebuiler 构建,反对配置化的 leadelection   基本原理其实就是利用通过 Kubernetes 中 configmap,endpoints 或者 lease 资源实现一个分布式锁,抢 (acqure) 到锁的节点成为 leader,并且定期更新(renew)。其余过程也在一直的尝试进行抢占,抢占不到则持续期待下次循环。当 leader 节点挂掉之后,租约到期,其余节点就成为新的 leader。通过这个个性就能够在 Operator 多正本部署时主动实现主备切换,大大晋升可用性。总结     Lindorm 作为一款多模数据库其组件数量和编排复杂度在数据库产品中是比拟少见的,之前的 On ecs 的架构整体编排逻辑十分重,而通过拥抱云原生技术,通过 Operator 将大部分编排细节大大简化了,通过 Statefulset 拉起外围组件,用 ConfigMap 治理配置,通过 Service 裸露服务,全面拥抱 k8s 技术生态让 Lindorm 的交付体系更加的标准化,可移植;能同时满足混合云和私有云场景输入。原文链接:https://click.aliyun.com/m/10… 本文为阿里云原创内容,未经容许不得转载。

退出移动版