【Sigma敏捷版系列文章】如何利用apiserver-builder自定义Kubernetes API

背景知识

了解一下k8s API

k8s中的API可以抽象为两个阶段:存储和调和。
存储(Storage):
Kubernetes API公开了用于存储声明期望集群状态的资源对象的操作
调和(Reconciliation):
通常称为控制器(controller)的进程集合监视写入资源的对象进行调和
如图:

API具有如下特征:
1、Declarative(声明式)
Kubernetes API被设计为:对象的期望状态被发送到API服务器,该服务器集群用于协调实际状态与期望的状态
2、Level based (基于等级)
基于等级的实现会依据当前最新的状态,而忽略先前的期望状态。例如执行Deployment更新镜像的时候,如果当前设置的是镜像A没有更新完成时,这时候又更新Deployment设置为镜像B,则会按照最新的B进行更新。
3、Asynchronous (异步)
API是按照异步方式执行,在按照期望值进行调和的过程中,已经返回了请求的结果信息。这就意味着用户请求时候不会返回错误的信息。如果调和的过程中发生错误(如镜像版本错误),则会设置到状态的信息字段上面。

K8s中自定义资源

Kubernetes提供了两种向群集添加自定义资源的方法:
1、自定义资源定义(CRD):更易于使用,在某些情况下,它们不需要任何编程。
自定义资源定义(CRDS)允许用户创建新类型的资源而无需添加其他API服务器。您不需要了解API Aggregation就可以使用CRD。
2、API聚合:需要编程,但允许更多的API行为控制,如数据如何存储以及API版本之间的转换。
Kubernetes提供了这两种选择来满足不同用户的需求,从而既不易于使用又不会降低灵活性。

无论是通过CRD还是API聚合安装,新资源都称为“自定义资源”,以将它们与内置Kubernetes资源(如Pod)区分开来。
今天我们介绍就是汇聚层API的构建方案:apiserver-builder。同时也是一种可以独立部署k8s式API服务的自动构建方式

概述

apiserver-builder基于k8s中的api-machinery之上开发全功能Kubernetes API,同时具备项目独立部署的特点。 这意味着允许扩展API在Kubernetes之外开发,并作为一个包单独安装。
功能特点:
1、可以作为新资源的类型定义,控制器,测试和文档的工具
2、可以作为独立构建和运行扩展控制平面的工具。
3、从控制器轻松list-watch和更新Kubernetes API类型
4、轻松添加新资源和子资源
5、为大多数接口属性提供了默认值,并且可以被覆盖

开发实践

安装apiserver构建工具

1.下载最新的alpha版本:
https://github.com/kubernetes-incubator/apiserver-builder/releases/download/v1.9-alpha.4/apiserver-builder-v1.9-alpha.4-linux-amd64.tar.gz
2.export PATH=$PATH:/root/apiserver-builder/bin
3.创建一个Go工程(demo)在GOPATH/src/目录下
4.创建copyright
在GOPATH/src/demo工程目录下创建文件boilerplate.go.txt
内容:

/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

初始化工程

初始化工程中将会设置apiserver最初的代码结构,包括go vendor库,这个是从下载的apiserver-builder二进制tar包复制过来的。
1.设置domin作为api group

#apiserver-boot init repo --domain demo.alibaba.com

我们可以看到生成的代码结构:

2.开始创建API资源:
API 资源定义包括group (类似package),version (版本:v1alpha1, v1beta1, v1),和Kind (资源类型)
运行“apiserver-boot create group version resource” 命令进行创建

#apiserver-boot create group version resource --group demo --version v1alpha1  --kind Bird

注意:资源类型需要首字母大写

3.本地可以运行apiserver和controller:

#apiserver-boot run local


4.通过kubectl验证:

kubectl --kubeconfig=kubeconfig api-versions

5.创建示例资源

kubectl --kubeconfig=kubeconfig create -f sample/bird.yaml

这样我们通过apiserver-builder轻松的扩展了k8s资源接口

扩展资源说明

资源定义

资源定义主要包括3部分
1、Metadata:元数据信息
Name (唯一key值)
Annotations (描述信息key-value键值对)
Labels (用于查询的key-value键值对)
2、Spec: 期望的状态信息
用来设置期望的状态字段,调和器(controller)依据这些期望的属性更新集群内部或外部的对象
3、Status: 结果状态信息
用来设置结果属性的状态字段
构建代码如下:

// Bird
// +k8s:openapi-gen=true
// +resource:path=birds,strategy=BirdStrategy
type Bird struct {
    metav1.TypeMeta   `json:",inline"`
    metav1.ObjectMeta `json:"metadata,omitempty"`

    Spec   BirdSpec   `json:"spec,omitempty"`
    Status BirdStatus `json:"status,omitempty"`
}

// BirdSpec defines the desired state of Bird
type BirdSpec struct {
}

// BirdStatus defines the observed state of Bird
type BirdStatus struct {
}

资源存储

在资源存储操作执行期间,我们可以通过如下几种方式验证或者修改存储的对象。

创建操作

1.DefaultingFunction:可以设置默认值

// DefaultingFunction sets default Bird field values
func (BirdSchemeFns) DefaultingFunction(o interface{}) {
    obj := o.(*Bird)
    // set default field values here
    log.Printf("Defaulting fields for Bird %s\n", obj.Name)
}

2.PrepareForCreate:修改对象属性,设置初始化或者资源回收
如下面的代码展示了k8s中pod资源通过PrepareForCreate方法设置Pending状态

// PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (podStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
    pod := obj.(*api.Pod)
    pod.Status = api.PodStatus{
        Phase:    api.PodPending,
        QOSClass: qos.GetPodQOS(pod),
    }

    podutil.DropDisabledAlphaFields(&pod.Spec)
}

如果代码中没有显示的定义PrepareForCreate方法,会默认执行apiserver-builder的该方法

func (DefaultStorageStrategy) PrepareForCreate(ctx request.Context, obj runtime.Object) {
    switch t := obj.(type) {
    default:
    case HasObjectMetaSpecStatus:
        // Clear the status if the resource has a Status
        t.GetObjectMeta().Generation = 1
        t.SetStatus(t.NewStatus())
    }
}

3.Validate:验证值的合法性,如果不合法可以直接拒绝创建请求

// Validate checks that an instance of Bird is well formed
func (BirdStrategy) Validate(ctx request.Context, obj runtime.Object) field.ErrorList {
    o := obj.(*demo.Bird)
    log.Printf("Validating fields for Bird %s\n", o.Name)
    errors := field.ErrorList{}
    // perform validation here and add to errors using field.Invalid
    return errors
}

4.Canonicalize(规范化):对象存储前最后一道修改的入口,用于将存储对象进行规范化,如将一些无序的数据进行排列以利于分析预测。

func (DefaultStorageStrategy) Canonicalize(obj runtime.Object) {}

更新操作:

包括ValidateUpdate和PrepareForUpdate,分别用于更新是的验证和预处理

删除操作

Finalizers: 需要通过Controller手动回收下游资源.
如果在对象上指定了回收器(例如PrepareForCreate),则删除对象将使用宽限期设置对象上的DeletionTimestamp字段。 控制器接收到该对象已被删除信息后,对创建的资源进行清理。
OwnerReference:通过apiserver自动回收下游资源

资源调和(Reconciliation)


通过控制器,实现调和

控制器初始化

在controller.go中我们可以看到初始化方法


// Init initializes the controller and is called by the generated code
// Register watches for additional resource types here.
func (c *BirdControllerImpl) Init(arguments sharedinformers.ControllerInitArguments) {
    // Use the lister for indexing birds labels
    c.lister = arguments.GetSharedInformers().Factory.Demo().V1alpha1().Birds().Lister()
}

调和

通过watch资源变化,对资源进行调和操作

// Reconcile handles enqueued messages
func (c *BirdControllerImpl) Reconcile(u *v1alpha1.Bird) error {
    // Implement controller logic here
    log.Printf("Running reconcile Bird for %s\n", u.Name)
    return nil
}

调和操作包括:
1.更新对象属性metadata, spec or status
2.创建、更新、删除k8s中其它资源(如pod)
3.创建、更新、删除k8s外的其它资源(如cloudprovider提供的云盘存储等)

总结

apiserver-builder提供了一套标准的k8s API构建规范,构建的API可以通过k8s汇聚层实现对k8s API的扩展,另外我们可以看到通过apiserver-builder来构建出来的项目完全可以作为一个独立系统运行。试想一下当你可以自动构建一个拥有面向终态的设计模式加上支持k8s生态组件(kubectl等)的工程的时候,it's amazing!
在近期的项目中我们也是采用了apiserver-builder构建了一套独立的系统,为用户提供了k8s式的服务访问模式,有兴趣的欢迎一起交流。

参考

1、https://github.com/kubernetes-incubator/apiserver-builder/blob/master/docs/concepts/api_building_overview.md
2、https://kubernetes.io/docs/concepts/api-extension/custom-resources/
3、https://github.com/kubernetes-incubator/apiserver-builder/blob/master/docs/tools_user_guide.md

你可能感兴趣的:(【Sigma敏捷版系列文章】如何利用apiserver-builder自定义Kubernetes API)