如何为你的 Operator 添加 ApiServer

292 阅读4分钟

我正在参加「掘金·启航计划」

Operator

众所周之, kubernetes 除了 pod,deployment,service 等这类内置资源之外,还能额外为业务自定义资源种类(CRD),也就构成了 云原生(Cloud Native) 这么一个生态,基于 k8s 为底座,开发各类云原生应用。 当然,有了自定义资源还没有什么作用,需要定义 Controller 才可以让 CRD 发挥真正的价值,比如按照业务开发的思维,页面有一个用户登陆的表单

apiVersion: xxx
Kind: User
username: abc
password: 123
action: login

假设没有 Controller,服务端收到这个表单可能也无法作出相应的动作,比如生成用户 token 返回前端,所以如果有了对应的 Controller, 那么我们就可以完成实际的功能了

// controller 逻辑伪代码

crd.username := request.Get("username")
crd.passwd := request.Get("password")
crd.action := request.Get("action")

if action == login {
    db.model(user).select(uid).
    where("username = ?", username).
    where("passwd = ?", hash(passwd)).
    first()
}

crd.token := generateToken(uid)
updateUserCRD(crd)

那么前端再次查询这个 CRD 就可以获取到 token 调用系统其他 API 了。
当然上面是一个比方,下来介绍实际的 CRD
例子中我们定义了 kind 是 Pipeline, Group 是 devops, Version 是 v1alpha1

Group Kind Version (GVK) 是 k8s 资源的核心概念,需要查询一下资料深入理解

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.7.0
  creationTimestamp: null
  name: pipelines.devops.devops.io
spec:
  group: devops.devops.io
  names:
    kind: Pipeline
    listKind: PipelineList
    plural: pipelines
    singular: pipeline
  scope: Namespaced
  versions:
  - name: v1alpha1
    schema:
      openAPIV3Schema:
        description: Pipeline is the Schema for the pipelines API
        properties:
          apiVersion:
            description: 'APIVersion defines the versioned schema of this representation
              of an object. Servers should convert recognized schemas to the latest
              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
            type: string
          kind:
            description: 'Kind is a string value representing the REST resource this
              object represents. Servers may infer this from the endpoint the client
              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
            type: string
          metadata:
            type: object
          spec:
            description: PipelineSpec defines the desired state of Pipeline
            properties:
              foo:
                description: Foo is an example field of Pipeline. Edit pipeline_types.go
                  to remove/update
                type: string
            type: object
          status:
            description: PipelineStatus defines the observed state of Pipeline
            type: object
        type: object
    served: true
    storage: true
    subresources:
      status: {}

可以看到, 我们利用 k8s CustomResourceDefinition 这个 kind 定义了我们自己的 group (devops), kind(pipeline), version(v1alpha1), 然后也展示了这个资源支持哪些字段:有通用的 apiversion,kind,metadata,spec,status 这些,这些也是必须的,我们能自定义的就是 spec 里面的内容,有一个 foo 字段,其中 type 是 string 类型。

我们有了 crd, 那么我们创建的 cr 也就是上面的 pipeline 这个资源,就会被 k8s 存入自己的存储(etcd),然后被 controller 组件监听到执行逻辑,但是k8s默认的控制器只会对它内置的资源进行操作,比如监听到一个pod的资源, 那么它就会读取 pod 的spec字段创建容器等,那么我们同时需要一个自定义的 controller 来处理我们的自定义资源。处理完成之后一般会更新 cr 的 status 字段来表示我已经处理完成了。

这仅仅是一个简单的描述,省略了调度,准入控制等流程。因为需要我们开发的内容就是crd的定义以及controller的编写。

image.png 因为整个流程都是监听 k8s master api 来完成的,所以让你的应用有自愈的特性成为了可能,而且官方出版的 client-go 这个包拥有完善的限流,队列,事件监听等组件,让你不用担心对 k8s master api 造成压力。

当然社区里面普遍推荐用 kubebuilder 来生成项目一整套开发到部署的脚手架,具体可以看教程尝试写一个。

crd + crd 的 controller 就是某个应用的 operatore 了。

为什么还需要 apiserver

我们基于上面的开发流程,很快就发现,我们的应用是面向用户的,而不是面向命令行的,我们可以 kubectl apply -f cr.yaml 很快的在 k8s 上运行我们的应用,并且应用除了执行我们的业务逻辑,还有具有自愈,备份(看你需求)等特性,但是我们的用户太局限了(仅开发者和集群运维人员),所以你的应用大部分情况下还是得暴露 REST API 接口的形式为前端(或其他系统)对接。

添加 apiserver 的方法

  1. 既然开发可以执行 kubectl apply -f cr.yaml 部署应用,那么我写一个 httpServer 代替执行 kubectl apply xxx 命令即可,例如你可以直接接收到前端传来的参数调用 cmd exec 执行命,或者更有经验可以想用 client-go 这个 sdk 的 dynamic client 来封装 yaml 发送的 master api, 这些都是可行的,但是各有各的问题。

  2. 聚合API: 官方明确有表示,k8s master apiserver 是可以扩展的,可以参考 kubernetes.io/zh/docs/con…, 这也是当前一部分云原生应用的做法,坑的是,官方给的 demo 并没有太多参考性,好在社区也有一个类似 kubebuilder 的脚手架工具 apiserver-builder, 但是这个项目仍然是一个 alpha 阶段的项目,而且 README 明确说明了 Unless you absolutely need apiserver-aggregation, you are recommended to use Kubebuilder instead of apiserver-builder for building Kubernetes APIs

除非绝对需要apiserver-aggregation,否则建议使用Kubebuilder而不是apiserver-builder来构建Kubernetes api