kubebuilder

kubebuilder 官方文档
https://book.kubebuilder.io/introduction

kubebuilder 可以视为一个软件

go install

装kubebuilder 首先要安装go
https://golang.google.cn/learn/

kubebuilder 应该是只能在linux 中运行的 所以Linux中go 安装好还要配环境变量 当然还要proxy

1
2
3
4
5
6
7
8
# go 环境变量 (go 默认是放在 /usr/local下面)
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH

# go proxy
$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct

kubebuilder install

安装 kubebuilder 软件

1
2
3
# download kubebuilder and install locally.
curl -L -o kubebuilder "https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)"
chmod +x kubebuilder && sudo mv kubebuilder /usr/local/bin/

kubebuilder guide

init

初始化项目

1
2
3
4
5
# --domain 
# k8s crd 资源所属的域名。这个域名用于唯一标识你的自定义资源,避免与其他项目的资源冲突。在后续的开发中,生成的代码中的资源对象会包含这个域名作为其完整名称的一部分。
# --repo
# 设置项目的代码仓库路径。这可以帮助 Kubebuilder 工具在正确的位置生成代码,并方便你进行版本控制和代码管理。通常这个路径应该指向一个有效的 Git 仓库地址或者本地文件系统路径
kubebuilder init --domain my.domain --repo my.domain/guestbook

create api

1
kubebuilder create api --group webapp --version v1 --kind Guestbook

K8s 为声明式 Api

声明式即 我只需要提交一个定义好的API对象来说明 “声明” (即yaml文件) 表示所期望的最终状态是什么样子就可以了 而如果提交的是一个个命令去指导怎么一步一步达到期望状态 这就是命令式”Api”

由上图可知 group + version + resource 可确定一组api资源 因此对于我们自己的创建的api 要指定 group version resource类型即kind

k8s 所定义的API对象都是 声明式API
声明式API 并不像命令式API 那样有着明显的执行逻辑 声明式一个所声明的状态 这就使得基于声明式API的业务功能实现 往往需要通过controller 来监控 API的对象的变化以此来决定实际要执行的工作

因此执行完 上述 kubebuilder create api 命令后 除了会在 api/v1 下生成 关于资源定义的文件 groupversion_info.go\ guestbook_types.go 后还会在 internal/controller 下生成 crd资源的 controller文件
在controller文件中就可以实现对于 crd资源的业务操作

controller 的执行逻辑如下 涉及K8s 具体的底层原理机制

核心是一个 reconcile 调谐的过程 即不断当前crd的资源 调整成和 期望状态

controller其实可以分为2部分来看:左边的 Informer,右面的 control loop,中间通过一个workqueue来进行协同。

workqueue

K8s workqueue 的workqueue 是一个fifo 队列 基于fifo 的基础上可以实现 延迟队列、限速队列 支持顺序、去重、并发等

Informer 主要用来与APIServer 进行数据同步。就是一个自带缓存和索引机制,可以触发 Handler 的客户端库。
这个本地缓存在 Kubernetes 中一般被称为 Store,索引一般被称为 Index。Informer 使用了 Reflector 包,它是一个可以通过 ListAndWatch 机制获取并监视 API 对象变化的客户端封装。Reflector 和 Informer 之间,用到了一个“增量先进先出队列”进行协同。


list-watch

https://zhuanlan.zhihu.com/p/59660536

list-watch 是一个异步消息系统 对于消息机制要实现 以下四点要求:

消息可靠性
消息实时性
消息顺序性
高性能

list-watch 机制核心是通过 resourceVersion实现 wath机制 采用http长链接(更具体的是 采用了 Chunked transfer encoding)

list和watch一起保证了消息的可靠性,避免因消息丢失而造成状态不一致场景。具体而言,list API可以查询当前的资源及其对应的状态(即期望的状态),客户端通过拿期望的状态和实际的状态进行对比,纠正状态不一致的资源。Watch API和apiserver保持一个长链接,接收资源的状态变更事件并做相应处理。如果仅调用watch API,若某个时间点连接中断,就有可能导致消息丢失,所以需要通过list API解决消息丢失的问题。从另一个角度出发,可以认为list API获取全量数据,watch API获取增量数据。虽然仅仅通过轮询list API,也能达到同步资源状态的效果,但是存在开销大,实时性不足的问题。

消息必须是实时的,list-watch机制下,每当apiserver的资源产生状态变更事件,都会将事件及时的推送给客户端,从而保证了消息的实时性。

消息的顺序性也是非常重要的,在并发的场景下,客户端在短时间内可能会收到同一个资源的多个事件,对于关注最终一致性的K8S来说,它需要知道哪个是最近发生的事件,并保证资源的最终状态如同最近事件所表述的状态一样。K8S在每个资源的事件中都带一个resourceVersion的标签,这个标签是递增的数字,所以当客户端并发处理同一个资源的事件时,它就可以对比resourceVersion来保证最终的状态和最新的事件所期望的状态保持一致。

List-watch还具有高性能的特点,虽然仅通过周期性调用list API也能达到资源最终一致性的效果,但是周期性频繁的轮询大大的增大了开销,增加apiserver的压力。而watch作为异步消息通知机制,复用一条长链接,保证实时性的同时也保证了性能。


control loop

control loop 是 while true 循环 , 从workqueue从那里 不断拿event
每一个循环周期执行的正是我们真正关心的业务逻辑,通过对比“期望状态”和“实际状态”的差异,不断进行Reconcile

kubebuilder project dir

生成了 api 整个 kubebuilder project的目录 大致如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
└── guestbook
├── api # crd 资源相关信息
│ └── v1
│ ├── groupversion_info.go # group 这个基本不用动 全部自动生成
│ └── guestbook_types.go # crd resource 资源属性 包括spec status
├── bin # 自动生成基本不用动
│ ├── controller-gen -> /home/rain/projects/guestbook/bin/controller-gen-v0.16.1
│ ├── controller-gen-v0.16.1
│ ├── kustomize -> /home/rain/projects/guestbook/bin/kustomize-v5.4.3
│ └── kustomize-v5.4.3
├── cmd # 启动程序 可以将一些配置 或者修改进入类 动的相对比较少
│ └── main.go
├── config # 一些配置根据需求可以进行修改
│ ├── crd
│ │ ├── bases
│ │ │ └── webapp.my.domain_guestbooks.yaml
│ │ ├── kustomization.yaml
│ │ └── kustomizeconfig.yaml
│ ├── default
│ │ ├── kustomization.yaml
│ │ ├── manager_metrics_patch.yaml
│ │ └── metrics_service.yaml
│ ├── manager
│ │ ├── kustomization.yaml
│ │ └── manager.yaml # 部署 核心文件 根据需求修改
│ ├── network-policy
│ │ ├── allow-metrics-traffic.yaml
│ │ └── kustomization.yaml
│ ├── prometheus
│ │ ├── kustomization.yaml
│ │ └── monitor.yaml
│ ├── rbac
│ │ ├── guestbook_editor_role.yaml
│ │ ├── guestbook_viewer_role.yaml
│ │ ├── kustomization.yaml
│ │ ├── leader_election_role_binding.yaml
│ │ ├── leader_election_role.yaml
│ │ ├── metrics_auth_role_binding.yaml
│ │ ├── metrics_auth_role.yaml
│ │ ├── metrics_reader_role.yaml
│ │ ├── role_binding.yaml
│ │ ├── role.yaml
│ │ └── service_account.yaml
│ └── samples
│ ├── kustomization.yaml
│ └── webapp_v1_guestbook.yaml
├── Dockerfile # 生成docker 镜像 基本是要改的 这个 manager 里面的镜像联动
├── go.mod
├── go.sum
├── hack
│ └── boilerplate.go.txt
├── internal
│ └── controller # 核心业务逻辑都在这里写 纯业务也基本都是这里面写的
│ ├── guestbook_controller.go # 最核心代码
│ ├── guestbook_controller_test.go # 测试代码
│ └── suite_test.go
├── Makefile
├── PROJECT
├── README.md
└── test
├── e2e
│ ├── e2e_suite_test.go
│ └── e2e_test.go
└── utils
└── utils.go

types.go

crd的核心属性 基本与 实际K8s中crd资源的属性是一一对应的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package v1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.


// GuestbookSpec defines the desired state of Guestbook
// Spec 规约 期望状态 即定义资源的属性
// 除了 属性外 还可以通过注释的形式 对资源语法进行校验
type GuestbookSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file

// Foo is an example field of Guestbook. Edit guestbook_types.go to remove/update
Foo string `json:"foo,omitempty"`
}

// GuestbookStatus defines the observed state of Guestbook
// Status 实际状态
// 对于K8s已有资源而言 status 表示对象的实际状态 该属性由K8s自己维护 K8s会通过一系列的controller对 对应对象进行管理 让对象尽可能的让实际状态与期望状态重合
// 而对于crd 而言 status 则是 reconcile 调谐的对象
type GuestbookStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status

// Guestbook is the Schema for the guestbooks API
type Guestbook struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec GuestbookSpec `json:"spec,omitempty"`
Status GuestbookStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

// GuestbookList contains a list of Guestbook
type GuestbookList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Guestbook `json:"items"`
}

func init() {
SchemeBuilder.Register(&Guestbook{}, &GuestbookList{})
}

每次 修改完 types中的属性 都要在make 一下
然后属性就会更新到 crd/base 下面的具体crd 文件里面 根据这个文件 即可 通过 Kubectl 命令 更新实际K8s集群中的文件