我正在参加「掘金·启航计划」
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的编写。
因为整个流程都是监听 k8s master api 来完成的,所以让你的应用有自愈的特性成为了可能,而且官方出版的
client-go 这个包拥有完善的限流,队列,事件监听等组件,让你不用担心对 k8s master api 造成压力。
当然社区里面普遍推荐用 kubebuilder 来生成项目一整套开发到部署的脚手架,具体可以看教程尝试写一个。
crd + crd 的 controller 就是某个应用的 operatore 了。
为什么还需要 apiserver
我们基于上面的开发流程,很快就发现,我们的应用是面向用户的,而不是面向命令行的,我们可以 kubectl apply -f cr.yaml 很快的在 k8s 上运行我们的应用,并且应用除了执行我们的业务逻辑,还有具有自愈,备份(看你需求)等特性,但是我们的用户太局限了(仅开发者和集群运维人员),所以你的应用大部分情况下还是得暴露 REST API 接口的形式为前端(或其他系统)对接。
添加 apiserver 的方法
-
既然开发可以执行
kubectl apply -f cr.yaml部署应用,那么我写一个 httpServer 代替执行kubectl apply xxx命令即可,例如你可以直接接收到前端传来的参数调用cmd exec执行命,或者更有经验可以想用 client-go 这个 sdk 的 dynamic client 来封装 yaml 发送的 master api, 这些都是可行的,但是各有各的问题。 -
聚合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