一、概念介绍
当前,kubernetes已经成为了容器调度平台的事实标准,随着它的生态的不断发展,k8s默认的基础资源模型已经无法支持各业务领域下的复杂的自动化场景。
因此CoreOS的工程师于2016年提出的Operator的概念,它基于k8s的资源和控制器的概念之上构建,同时也包含了应用程序特定的领域知识;operator主要用来创建、配置和管理复杂的有状态应用,如数据库、缓存和监控系统等,它通过与Kubernetes API的紧密交互,提供了一种一致的方法来自动处理应用程序的各种操作流程,并且不需要任何人工干预。
用句通俗的话来讲,Operator是打包,运行和管理k8s应用程序的一种方式。
二、Operator工作原理
在实现某个Operator之前,有几个需要提前掌握的核心概念:
CRD
从字面翻译就是自定义资源的定义。CRD定义了用户的应用业务模型,用户根据自身实际的业务需求,完全定制所需要的资源对象,如RedisCluster,PrometheusServer等这些都是可以被K8s直接操作和管理的自定义的资源对象。
CR
自定义资源,是CRD的一个具体实例,是具体的可管理的K8s资源对象,可以对其进行创建,删除,修改,查询等生命周期管理操作,同时CR对象一般还会包含运行时的状态,用于观察和判断CR对象的真正所处于的状态。
工作队列
controller的核心组件。它会监控集群内的资源变化,并把相关的对象,以及事件变化,作为一个事件存储到队列中。
Controller
operator机制的核心,它会循环处理工作队列中的事件,按照逻辑协调应用当前状态至期望状态。Controller如果检查到一个CR对象被创建了,会根据定义的逻辑来处理CR,让CR对象的状态以及CR对象所负责的业务逻辑逐步的向期望的状态上靠近,最终达到期望的效果。
如图所示:
webhook
实质是一种 HTTP回调,会注册到apiserver上。当apiserver 检测到发生特定事件时,会先查询已注册的webhook,并转发相应的消息。按照处理类型的不同,一般可以将其分为两类:一类可能会修改传入对象,称为mutating webhook;一类则会只读传入对象,称为validating webhook。
具体的工作流程:
① 用户创建一个自定义资源 (CRD);
② apiserver收到创建CRD请求后,把该请求转发给 webhook;
③ webhook一般会先完成该CRD的缺省值设定和参数检验。webhook处理完之后,相应的CR会被写入数据库,返回给用户;
④ controller会在后台监测该自定义资源,按照业务逻辑,处理与该自定义资源相关联的操作;
⑤ 上述处理一般会引起集群内的状态变化,controller会监测这些关联的变化,把这些变化记录到CRD的状态中。
总体来说一个Operator通常由两部分组成:
一个或多个Kubernetes自定义资源定义(CRD),它们描述了一种新的资源,包括应该具有哪些字段。可能会同时创建多个CRD,例如etcd-cluster-operator同时使用EtcdCluster和EtcdPeer来封装不同的概念。
一个运行中的软件,读取自定义资源并作出响应。
Operator通常使用一个简单的Deployment资源,部署到K8s集群中。理论上,只要operator能够与集群的Kubernetes API通信,它就可以在任何地方运行。
但是,为了操作方便,大多数用户选择在集群中运行operator。通常情况下会使用自定义Namespace将operator与其他资源分隔开来。用户如果使用这种方法来运行operator,还需要做一些事情:
① 一个容器镜像,其中包含 Operator 可执行文件;
② 一个 Namespace;
③ Operator的ServiceAccount,授予读取自定义资源的权限,并配置它要管理的资源(例如 Pod);
④ 用于 Operator 容器的 Deployment;
⑤ ClusterRoleBinding 和 ClusterRole 资源,绑定到 ServiceAccount;
⑥ Webhook 配置。
三、开发语言和框架选择
Go语言拥有非常成熟的工具集。除了Go语言,其他语言(如 Java、Rust、Python 和其他语言)通常也会提供用于连接Kubernetes API或者专门用于构建Operator的工具。这些工具的成熟度和支持水平各有差别。
如果团队已经习惯使用 Go,那么Go生态系统丰富的工具是最佳的选择。如果团队还没有使用Go,那么就需要做出权衡,要么在学习和培训更成熟的生态系统工具方面付出代价,要么选择不成熟但团队熟悉其底层语言的生态系统。
从零开始开发一个operator是非常困难的,好在社区提供了不少成熟的框架,目前主流的operator Framework主要有两个:Kubebuilder以及operator SDK。这两个框架本质上没有太大的区别,它们的核心都是使用官方的 controller-tools 和 controller-runtime,只不过细节上稍有差异,比如 kubebuilder有着更为完善的测试与部署以及代码生成的脚手架等;而operator-sdk对ansible operator这类上层操作的支持更好一些。
四、Kubebuilder演练
本案例选择开源项目kruise下的SidecarSet。SidecarSet的功能就是负责给Pod插入sidecar容器,例如可以插入一些监控,日志采集等来丰富这个Pod的功能,然后根据插入的状态、Pod的状态反过来更新SidercarSet以记录这些辅助容器的状态。
初始化
新建项目,运行“kubebuilder init –domain=kruise.io”,domain参数指定了后续注册CRD对象的Group域名。
创建API
运行命令:
kubebuilder create api --group apps --version v1alpha1 --kind SidecarSet --namespace=false
这个命令不仅会创建API,还会生成Controller的框架。group, version, kind 这三参数基本上对应了CRD元信息的三个重要组成部分。namespaced 参数则用于指定刚刚创建的 CRD 是全局唯一的(如 node)还是namespace 唯一的(如 Pod)。这里用了false,即指定SidecarSet为全局唯一的。
生成框架后,还需要开发人员自己填充代码。
开发人员需要重点关注蓝色字体部分,sidecarset_types.go文件是定义CRD的地方,需要开发人员亲自填充,sidecar_controller.go文件用于描述定义controller,也需要填充。
填充CRD
自动生成的CRD位于 “pkg/apis/apps/v1alpha1/sidecarset_types.go”,通常还需要进行两个操作:
<1>调整注释:
code generator依赖注释生成代码,因此有时需要调整注释。以下列出了本次实战中SidecarSet需要调整的注释:
+genclient:nonNamespaced: 生成非namespace对象;
+kubebuilder:subresource:status: 生成 status子资源;
+kubebuilder:printcolumn:name=“MATCHED”,type=‘integer’,JSONPath=\”.status.matchedPods\”,description=“xxx”: kubectl get sidecarset:后续展示相关。
<2>填充字段:
填充字段是令用户的CRD实际生效,实现具体定义的重要部分。
SidecarSetSpec: 填充 CRD 描述信息;
SidecarSetStatus: 填充 CRD 状态信息。
实际填充如下:
填充完运行make重新生成代码即可。
SidecarSet的功能是给Pod注入Sidecar,为了完成该功能,开发人员在SidecarSetSpec(左图) 定义了两个主要信息:一个是用于选择哪些Pod需要被注入的Selector;一个是定义Sidecar容器的Containers。
在 SidecarSetStatus(右图)中定义了状态信息,MatchedPods反映的是该SidecarSet 实际匹配了多少Pod,UpdatedPods反映的是已经注入了多少,ReadyPods反映的则是有多少Pod已经正常工作了。
生成webhook框架
<1> 生成 mutating webhook,运行:
“kubebuilder alpha webhook –group apps –version v1alpha1 –kind SidecarSet –type=mutating –operations=create”
“kubebuilder alpha webhook –group core –version v1 –kind Pod –type=mutating –operations=create”
<2> 生成 validating webhook,运行:
“kubebuilder alpha webhook –group apps –version v1alpha1 –kind SidecarSet –type=validating –operations=create,update”
上面几条命令生成了webhook框架,还需要手工填写一部分代码,具体看截图:
标有蓝色字体的代码文件需要填充代码。
填充webhook:
生成的 webhook handler 分别位于:
pkg/webhook/default_server/sidecarset/mutating/xxx_handler.go
pkg/webhook/default_server/sidecarset/validating/xxx_handler.go
pkg/webhook/default_server/pod/mutating/xxx_handler.go
需要改写、填充的一般包括以下两个部分:
① 是否需要注入K8s client:
取决于除了传入的CRD以外是否还需要其它资源。在本示例中,不仅要关注SidecarSet,同时还要注入Pod,因此需要注入 K8s client;
②填充webhook的关键方法:
即 mutatingSidecarSetFn 或 validatingSidecarSetFn。由于待操作资源对象指针已经传入,因此直接调整该对象属性即可完成hook的工作。
实际填充如下:
先上图左侧的sidecarset mutating,首先是setDefaultSidecarSet把默认值设置好,这也是mutaing最常做的事情。
图右侧validating也是对SidecarSet一些字段进行校验。
填充controller
生成的 controller 框架位于 pkg/controller/sidecarset/sidecarset_controller.go。
主要有三点需要进行修改:
① 修改权限注释
框架会自动生成形如 //+kuberbuilder:rbac;groups=apps,resources=deployments/status,verbs=get;update;path 的注释,用户可以按照自己的需求修改,该注释最终会生成rbac规则;
② 增加入队逻辑
缺省的代码框架会填充CRD本身的入队逻辑(如SidecarSet对象的增删改都会加入工作队列),如果需要关联资源对象的触发机制(如SidecarSet也需关注Pod的变化),则需手工新增它的入队逻辑;
③ 填充业务逻辑
修改Reconcile函数,循环处理工作队列。Reconcile函数主要完成“根据Spec完成业务逻辑”和“将业务逻辑结果反馈回status”两部分。需要注意的是,如果Reconcile函数出错返回err,默认会重新入队。
实际填充如下:
addPod中先获取该Pod对应的SidecarSet,并将其加入到工作队列中以便Reconcile函数进行处理。Reconcile将SidercarSet取出之后,根据Selector选择匹配的Pod,最后根据Pod当前的状态信息计算出集群的状态,然后回填到CRD的状态中。
以上就是基于kubebuilder框架的SidecarSet的一个简单实现过程。
结语
只有具备过硬的综合素质,才能开发出一个优秀的operator,仅熟悉封装组件,是远远不够的。咱们以中间件为例,来分析一下团队需要具备的综合素质:
① 首先,对于架构师而言,要具备中间件架构设计能力,以及要有中间件丰富运维经验;
② 其次,对于研发人员而言,要具备operator的研发能力,以及能结合周边的监控、告警、日志、容器存储、容器网络等系统,打造完善的中间件的opetator能力;
③ 最后,对于运维人员而言,需要能理解operator本身的能力,以及利用好operator的能力。
随着云原生及其整个生态的发展,operator这种自动化与云交互的方式,将会发挥出更大的云原生价值。
作者:孙洪轩
往期回顾
浅谈数据库发展历史
带你揭开WIFI的面纱
监控的分布式探索
使用Maven插件快速发布Docker镜像
微信公众号
EBCloud
赶快扫码关注我们吧!
原创文章,作者:EBCloud,如若转载,请注明出处:https://www.sudun.com/ask/32442.html