Kubernetes Operator简介

在对一些复杂应用进行Kubernetes容器化的时候,经常能看到xxx operator,如ETCD Operator、Prometheus Operator、Spark Operator,那么Operator是什么、如何使用它,本文对Operator进行简析。

Operator介绍

Operator可以看作是一种解决数据库等复杂应用容器化问题的模式。Operator遵循Kubernetes声明式API和Controller的设计理念,被用来扩展Kubernetes API,利用定制资源管理应用及其组件,对复杂的有状态应用,如数据库、缓存和监控系统等,进行创建、配置和管理。Operator 基于 Kubernetes 的资源和控制器概念之上构建,但同时又包含了应用程序特定的领域知识。

Kubernetes资源和控制器

在Kubernetes中所有操作的内容都被称为“资源“,如Pod、NameSpace、Statefulset等,可以使用 kubectl api-resources 命令查看Kubernetes支持的资源。
Kubernetes中所有的资源都拥有一些必备的属性字段,这些字段里有代表Kubernetes资源版本的apiVersion,指定资源类型和角色的kind,标定资源元数据的metadata,以及资源的期望状态spec。
Kubernetes作为一个“容器编排”平台,其核心是对资源对象进行”编排“,而控制器(controller)则负责此工作。控制器对资源对象的操作可以看作是一个状态机操作,不断对资源对象进行轮询,比较其当前状态(status)和期望状态(spec)并做出反应,使资源对象的状态满足其期望的要求。Kubernetes核心组件controller manager中包含对各个内建资源进行管理的contoller,如node controller、service controller、namespace controller等。
作为Kubernetes中重要的设计,如下图所示,Controller从API Server同步资源对象的期望状态并且在资源对象的期望状态和实际运行状态之间进行调谐,从而实现两者的最终一致性。Kubernetes系统中的各种组件,包括Scheduler,Kubelet以及各种资源对象的Controller都以这种统一的模式运行着。

Kubernetes CRD

Kubernetes中已经内置了Deployment,StatefulSet,Job、Cronjob等丰富的编排对象,但在实际使用中,面对纷繁复杂的应用场景,尤其是针对Etcd、Redis、ES等复杂的有状态应用,现有的编排对象在面对这些应用的如主从配置、集群扩缩容等需求时显得力不从心。Kubernetes在v1.7版本引入了CRD(Custom Resource Definition),允许用户将应用的复杂需求抽象成自定义资源对象并且做到像原生对象一样操作它们。
在Kubernetes中扩展CRD的一般流程为:

  • 向Kubernetes API服务注册一个带特定scheme的资源
  • 借助Kubernetes RBAC等机制对资源在访问控制、多租户等安全性方面做出保证
  • 开发自定义的controller对资源进行释义和管理,持续调谐资源对象处于预期状态

在Kubernetes中注册一个种类为foo的crd资源的yaml模板如下所示:

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: foos.samplecontroller.k8s.io
spec:
group: samplecontroller.k8s.io
version: v1alpha1
names:
kind: Foo
plural: foos
scope: Namespaced

Kubernetes Controller开发框架

使用上述模板注册CRD,可使得自定义资源被Apiserver识别并存储在etcd中,而为了对资源进行操纵以满足实际需求则需要开发针对自定义资源的controller。在Kubernetes客户端,如client-go,包含了可便捷开发controller的现成框架。
如上图所示,该controller框架分为两大部分:

  1. Common Part
    该部分已由kubernetes client实现,完成了与apiserver的通信、授权、资源监听、资源对象反射、和本地缓存等通用功能(图中步骤1-4)。同时提供调用接口(图中步骤5),在监听到资源对象发生变化时通知用户进行处理。

  2. Special Part
    此部分由用户自己处理,用户对资源的实际操作逻辑在该部分实现。通过实现AddFunc、UpdateFunc、DeleteFunc可在关注资源新建、发生改变和删除的时候获得通知,针对不同类型的事件填充对应的处理代码对自定义对象进行处理。

构建Operator

Operator作为解决复杂应用容器化的一种思路,通过将专业领域知识注入Kubernetes完成对复杂有状态应用的自动部署和管理。例如,在对于数据类有状态应用,不同的数据库如Redis、MySql等,其部署、扩缩容、备份方法各有区别,Operator针对这些复杂的场景进行专业的处理。简单理解,Operator=CRD+Controller,CRD完成资源的定义,Controller监听CRD的CRUD事件根据业务逻辑进行自定义处理。
创建自定义Operator需要如下资源:

  1. Custom Resource(CR)spec,定义我们要观测的应用对象,以及为CR定义的API
  2. Custom Controller,用来观测CR
  3. Custom Code,决定Custom Controller如何协调CR
  4. Operator 框架,管理Custom Controller
  5. Deployment,定义Operator和自定义资源

上述内容核心的两步是CR spec的设计和controller的开发。CR spec反映应用具体的需求,如对于redis集群来说,redis集群的分片数、每个分片的从副本数、每个redis节点的内存大小等均是redis自定义资源的spec部分。controller解析CR spec,协调调用Kubernetes内建资源(Statefulset、ConfigMap、Service等)和特定应用处理逻辑,实现对容器化应用的自动化管理。

Operator可以通过手工编写代码和spec实现,除此之外也可以通过Operator工具来实现。目前最常用的Operator开源工具有CoreOS的Operator Framework(https://operatorframework.io/)和Kubernetes社区的kubebuilder(https://book.kubebuilder.io/)。这些工具允许通过CLI命令生成spec、controller以及operator框架(具体使用方式参照官方文档),使用者只需着重于spec定义和编写 自定义
协调代码。
Operator工具生成的框架中,对Kubernetes客户端提供的controller框架进行了进一步封装,仅保留Reconcile部分并生成同名函数供用户填充。在Operator框架中,关注资源发生变化(create、update、delete)均会触发Reconcile函数。从Reconcile函数的传入参数可以取得关注资源的key(req.NamespacedName),用户需要从本地缓存中取出资源的实际值并且自行判断触发事件并做出反应。需要注意的是由于operator框架使用了本地缓存,可能导致Reconcile函数中读取的资源值与实际apiserver中的有偏差(本地缓存同步apiserver需要时间),所以要求Reconcile函数处理操作符合幂等性。

func (r *DemoReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ctx := context.Background()
log := r.Log.WithValues("cronjob", req.NamespacedName)

var crdObj CRDDEMO
if err := r.Get(ctx, req.NamespacedName, &crdObj); err != nil {
log.Error(err, "unable to object")
return ctrl.Result{}, ignoreNotFound(err)
}

//+ your code

return ctrl.Result{}, err
}

Operator与Helm

Operator解析CR spec,为自定义资源生成所需的Service、Configmap、Statefulset等,这一点让我们想到了另一个Kubernetes应用的编排工具——Helm。那么Operator与Helm有何不同,这里引用一篇博文(https://my.oschina.net/u/3330830/blog/1632262) 中的说法:

Helm是为了配置分离,Operator则是针对复杂应用的自动化管理。Operator本质上是针对特定的场景去做有状态服务,或者说针对拥有复杂应用的应用场景去简化其运维管理的工具。Helm的话,它其实是一个比较普适的工具,想法也很简单,就是把你的K8S资源模板化,方便共享,然后在不同的配置中重用。
其实Operator做的东西Helm大部分也可以做。用Operator去监控更新etcd的集群状态,也可以用定制的Chart做同样的事情。只不过你可能需要一些更复杂的处理而已,例如在etcd没有建立起来时候,你可能需要一些init Container去做配置的更新,去检查状态,然后把这个节点用对应的信息给拉起来。删除的时候,则加一些PostHook去做一些处理。所以说Helm是一个更加普适的工具。两者甚至可以结合使用,比如stable仓库里就有etcd-operator chart。

作者简介

孟玉立,中国民生银行信息科技部开源软件支持组工程师,目前主要负责Kubernetes、Redis的源码研究和工具开发等相关工作。