结合Kubebuilder与code-generator开发Operator
一、概念简介
1.1 code-generator
k8s.io/client-go for talking to a kubernetes cluster.
k8s.io/client-go
提供了对k8s原生资源的informer和clientset等等,但对于自定义资源的操作则相对低效,需要使用 rest api 和 dynamic client 来操作,并自己实现反序列化等功能。
code-generator 提供了以下工具用于为k8s中的资源生成相关代码,可以更加方便的操作自定义资源:
deepcopy-gen
: 生成深度拷贝对象方法client-gen
: 为资源生成标准的操作方法(get;list;watch;create;update;patch;delete)informer-gen
: 生成informer,提供事件机制(AddFunc,UpdateFunc,DeleteFunc)来响应kubernetes的eventlister-gen
: 为get和list方法提供只读缓存层
code-generator整合了这些gen,使用脚本generate-groups.sh和generate-internal-groups.sh可以为自定义资源生产相关代码。
1.2 Kubebuilder
Kubebuilder是用于使用自定义资源定义(CRD)构建Kubernetes API的框架。类似于Ruby on Rails和SpringBoot之类的Web开发框架,Kubebuilder可以提高速度并降低开发人员管理的复杂性,以便在Go中快速构建和发布Kubernetes API。它建立在用于构建核心Kubernetes API的规范技术的基础之上,以提供减少样板和麻烦的简单抽象。
Resource + Controller = Operator,可以利用Kubebuilder编写自定义资源的Operator。
二、结合背景
Kubebuilder
与 code-generator
都可以为CRD生成Kubernetes API相关代码,从代码生成层面来讲, 两者的区别在于:
- Kubebuilder不会生成informers、listers、clientsets,而code-generator会。
- Kubebuilder会生成Controller、Admission Webhooks,而code-generator不会。
- Kubebuilder会生成manifests yaml,而code-generator不会。
- Kubebuilder还带有一些其他便利性设施。
使用Kubebuilder可以快捷生成CRD以及相关的控制器框架,然而由于Kubebuilder不会生成clientset等包,当别的服务想要操作CRD时将会很麻烦。
两者结合后可以使用Kubebuilder生成CRD和一整套控制器架构,再使用code-generator生成informers、listers、clientsets等。
三、操作步骤
3.1 依赖组件
- go version v1.15+,v1.15.2
- kustomize v3.1.0+,v3.8.8
- kubebuilder v2.3.0+,v2.3.2
- controller-gen,v0.2.5
3.2 初始化项目
安装Kubebuilder:参考官方文档 https://cloudnative.to/kubebu...
1、创建一个项目
mkdir -p $GOPATH/src/my.domain/example
cd $GOPATH/src/my.domain/example
kubebuilder init --domain my.domain
tree -CL 2
.
├── bin
│ └── manager
├── config
│ ├── certmanager
│ ├── default
│ ├── manager
│ ├── prometheus
│ ├── rbac
│ └── webhook
├── Dockerfile
├── go.mod
├── go.sum
├── hack
│ └── boilerplate.go.txt
├── main.go
├── Makefile
└── PROJECT
9 directories, 8 files
2、创建一个 API
kubebuilder create api --group example --version v1 --kind Guestbook
Create Resource [y/n]
y
Create Controller [y/n]
n
tree -CL 3
.
├── api
│ └── v1
│ ├── groupversion_info.go
│ ├── guestbook_types.go
│ └── zz_generated.deepcopy.go
......
注意:
如果修改了 api/v1/guestbook_types.go
,需要执行以下命令来更新代码和manifests:
make && make manifests
3.3 使用code-generator
3.3.1 更新依赖版本
初始化项目后的go.mod:
cat go.mod
module my.domain/example
go 1.13
require (
k8s.io/apimachinery v0.17.2
k8s.io/client-go v0.17.2
sigs.k8s.io/controller-runtime v0.5.0
)
需要将初始化的k8s库更新到要使用的版本,如:
K8S_VERSION=v0.17.3
go get k8s.io/client-go@$K8S_VERSION
go get k8s.io/apimachinery@$K8S_VERSION
3.3.2 安装code-generator
k8s的版本号与 go.mod
中的 k8s.io/client-go
的版本保持一致即可。
注意:需要将依赖复制到vendor中
K8S_VERSION=v0.17.3
go get k8s.io/code-generator@$K8S_VERSION
go mod vendor
3.3.3 创建&修改所需文件
需要在api目录下创建code-generator所需的文件,并添加相关注释。
新增
api/v1/doc.go
注意:修改groupName,package与api的version保持一致。
// +k8s:deepcopy-gen=package // Package v1 is the v1alpha1 version of the API. // +groupName=example.my.domain package v1
新增
api/v1/register.go
注意:package与api的version保持一致。
package v1 import ( "k8s.io/apimachinery/pkg/runtime/schema" ) // SchemeGroupVersion is group version used to register these objects. var SchemeGroupVersion = GroupVersion // Resource takes an unqualified resource and returns a Group qualified GroupResource func Resource(resource string) schema.GroupResource { return SchemeGroupVersion.WithResource(resource).GroupResource() }
修改
api/v1/{crd}_types.go
文件,添加注释// +genclient
// +genclient // +kubebuilder:object:root=true // Guestbook is the Schema for the guestbooks API type Guestbook struct {
3.3.4 准备脚本
在项目 hack
目录下准备以下文件:
新建
hack/tools.go
文件// +build tools package tools import _ "k8s.io/code-generator"
新建
hack/update-codegen.sh
,注意根据项目修改相应变量:MODULE
和go.mod
保持一致API_PKG=api
,和api
目录保持一致OUTPUT_PKG=generated/example
,与生成Resource时指定的group保持一致GROUP=example
, 和生成Resource时指定的group 保持一致VERSION=v1
, 和生成Resource时指定的version保持一致
#!/usr/bin/env bash set -o errexit set -o nounset set -o pipefail # corresponding to go mod init <module> MODULE=my.domain/example # api package APIS_PKG=api # generated output package OUTPUT_PKG=generated/example # group-version such as foo:v1alpha1 GROUP=example VERSION=v1 GROUP_VERSION=${GROUP}:${VERSION} SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)} # kubebuilder2.3.2版本生成的api目录结构code-generator无法直接使用 rm -rf "${APIS_PKG}/${GROUP}" && mkdir -p "${APIS_PKG}/${GROUP}" && cp -r "${APIS_PKG}/${VERSION}/" "${APIS_PKG}/${GROUP}" # generate the code with: # --output-base because this script should also be able to run inside the vendor dir of # k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir # instead of the $GOPATH directly. For normal projects this can be dropped. #client,informer,lister(注意: code-generator 生成的deepcopy不适配 kubebuilder 所生成的api) bash "${CODEGEN_PKG}"/generate-groups.sh "client,informer,lister" \ ${MODULE}/${OUTPUT_PKG} ${MODULE}/${APIS_PKG} \ ${GROUP_VERSION} \ --go-header-file "${SCRIPT_ROOT}"/hack/boilerplate.go.txt # --output-base "${SCRIPT_ROOT}" # --output-base "${SCRIPT_ROOT}/../../.."
注意:
- kubebuilder2.3.2版本生成的api目录结构code-generator无法直接使用,需要在sh脚本中进行处理。
- 修改脚本执行参数可以选择生成的代码,如:"client,informer,lister"。注意无需再次生成deepcopy:code-generator 生成的deepcopy不适配 kubebuilder 所生成的api。
修改
Makefile
,添加生成命令update-codegen: chmod +x ./hack/update-codegen.sh ./hack/update-codegen.sh
3.3.5 生成代码
项目根目录下执行make update-codegen
即可,将生成如下代码结构:
.
├── api
│ ├── example
│ │ └── v1
│ │ ├── doc.go
│ │ ├── groupversion_info.go
│ │ ├── guestbook_types.go
│ │ ├── register.go
│ │ └── zz_generated.deepcopy.go
│ └── v1
│ ├── doc.go
│ ├── groupversion_info.go
│ ├── guestbook_types.go
│ ├── register.go
│ └── zz_generated.deepcopy.go
├── generated
│ └── example
│ ├── clientset
│ ├── informers
│ └── listers
之后便可以通过clientset等包对自定义资源对象进行操作。
注意事项:
kubebuilder2.3.2版本生成的api目录结构为 api/v1
,而code-generator需要的api目录结构为 api/example/v1
,相比较增加了group这一层。
hack/update-codegen.sh
脚本会自动根据kubebuilder的api生成code-generator所需目录结构。- code-generator 生成的deepcopy不适配 kubebuilder 所生成的api。
- code-generator 生成代码后再次使用make操作时可能由于生成的代码影响命令正常执行,例如:
make manifests
在生成CRD模版时,需先删除api以及generated目录中为code-generator生成的代码,才可正常生成CRD模版。 - 使用时kubebuilder正常使用
api/v1
中的types,而code-generator生成的clientset等则需要使用api/example/v1
中的types。
参考
https://github.com/kubernetes...
https://github.com/kubernetes...
https://cloud.tencent.com/dev...
https://blog.csdn.net/sixinch...
有疑问加站长微信联系(非本文作者)