混合kubebuilder与code generator编写CRD
概览
kubebuilder和k8s.io/code-generator类似,是一个代码生成工具,用于为你的CRD生成kubernetes-style API实现。区别在于:
- Kubebuilder不会生成informers、listers、clientsets,而code-generator会。
- Kubebuilder会生成Controller、Admission Webhooks,而code-generator不会。
- Kubebuilder会生成manifests yaml,而code-generator不会。
- Kubebuilder还带有一些其他便利性设施。
Resource + Controller = Operator,因此你可以利用Kubebuilder编写你自己的Operator。
总结:如果你不想做Operator,如果你不会直接or间接生成Pod,只是想存取CRD(把K8S当作数据库使用)。那你可以使用Kubebuilder生成CRD和manifests yaml,再使用code-generator生成informers、listers、clientsets。
本文讲的就是这个方法。
第一步:创建项目
创建一个目录,然后在里面运行 kubebuilder init
命令,初始化一个新项目。示例如下。
mkdir $GOPATH/src/foo-controller cd $GOPATH/src/foo-controller # 我们将使用zsy.com域, # 所以所有的 API 组将是<group>.zsy.com. kubebuilder init --domain zsy.com kubebuilder edit --multigroup=true
注意:1、确保你已经安装 Kubebuilder,如果你的项目目录不在 $GOPATH 中,你需要运行 go mod init <modulename> 来告诉 kubebuilder 和 Go module 的基本导入路径。
2、执行kubebuilder edit --multigroup=true命令,之后通过Kubebuilder创建API生成的Resource将放到项目apis路径下,不执行此条命令默认放到api路径下。
控制台输出类似以下内容:
apple@appledeMacBook-Pro foo-controller$ kubebuilder init --domain zsy.com Writing scaffold for you to edit... Get controller runtime: $ go get sigs.k8s.io/controller-runtime@v0.5.0 Update go.mod: $ go mod tidy Running make: $ make /usr/local/go/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..." go fmt ./... go vet ./... go build -o bin/manager main.go Next: define a resource with: $ kubebuilder create api
会生成以下文件:
. ├── Dockerfile ├── Makefile ├── PROJECT ├── bin │ └── manager ├── config │ ├── certmanager │ ├── default │ ├── manager │ ├── prometheus │ ├── rbac │ └── webhook ├── hack │ └── boilerplate.go.txt └── main.go
第二步:生成Resource和manifests
kubebuilder create api --group webapp --version v1 --kind Guestbook Create Resource [y/n] y Create Controller [y/n] n
会生成以下文件go代码和manifests文件:
. ├── apis │ └── webapp │ └── v1 │ ├── groupversion_info.go │ ├── guestbook_types.go │ └── zz_generated.deepcopy.go └── config ├── crd │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ └── patches │ ├── cainjection_in_guestbooks.yaml │ └── webhook_in_guestbooks.yaml ├── rbac │ ├── guestbook_editor_role.yaml │ ├── guestbook_viewer_role.yaml └── samples └── webapp_v1_guestbook.yaml
添加文件apis/webapp/v1/rbac.go
,这个文件用生成RBAC manifests:
// +kubebuilder:rbac:groups=webapp.example.com,resources=guestbooks,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=webapp.example.com,resources=guestbooks/status,verbs=get;update;patch package v1
然后生成CRD manifests:
make manifests
得到:
config ├── crd │ └── bases │ └── webapp.example.com_guestbooks.yaml └── rbac └── role.yaml
注意:
如果你修改了guestbook_types.go
的结构,你需要执行以下命令来更新代码和manifests:
make && make manifests
第三步:使用code-generator
1)准备脚本
在hack目录下准备以下文件:
. └── hack ├── tools.go ├── update-codegen.sh └── verify-codegen.sh
新建hack/tools.go
文件:
// +build tools package tools
新建hack/update-codegen.sh
,注意修改几个变量:
MODULE
和go.mod
保持一致API_PKG=apis
,和apis
目录保持一致OUTPUT_PKG=generated/webapp
,生成Resource时指定的group一样GROUP_VERSION=webapp:v1
和生成Resource时指定的group version对应
#!/usr/bin/env bash set -o errexit set -o nounset set -o pipefail # corresponding to go mod init <module> MODULE=foo-controller # api package APIS_PKG=apis # generated output package OUTPUT_PKG=generated/webapp # group-version such as foo:v1alpha1 GROUP_VERSION=webapp:v1 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)} # 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. bash "${CODEGEN_PKG}"/generate-groups.sh "client,lister,informer" \ ${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}/../../.." \
新建hack/verify-codegen.sh
#!/usr/bin/env bash set -o errexit set -o nounset set -o pipefail OUTPUT_PKG=generated/webapp MODULE=zsy.com/foo-controller SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. DIFFROOT="${SCRIPT_ROOT}/${OUTPUT_PKG}" TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/${OUTPUT_PKG}" _tmp="${SCRIPT_ROOT}/_tmp" cleanup() { rm -rf "${_tmp}" } trap "cleanup" EXIT SIGINT cleanup mkdir -p "${TMP_DIFFROOT}" cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}" "${SCRIPT_ROOT}/hack/update-codegen.sh" echo "copying generated ${SCRIPT_ROOT}/${MODULE}/${OUTPUT_PKG} to ${DIFFROOT}" cp -r "${SCRIPT_ROOT}/${MODULE}/${OUTPUT_PKG}"/* "${DIFFROOT}" echo "diffing ${DIFFROOT} against freshly generated codegen" ret=0 diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$? cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}" if [[ $ret -eq 0 ]] then echo "${DIFFROOT} up to date." else echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh" exit 1 fi
2)下载code-generator
先把code-generator下载下来,注意这里的K8S版本号,得和go.mod
里的k8s.io/client-go
的版本一致:
K8S_VERSION=v0.18.6 go get k8s.io/code-generator@$K8S_VERSION go mod vendor
然后给generate-groups.sh
添加可执行权限:
chmod +x vendor/k8s.io/code-generator/generate-groups.sh
3)更新依赖版本
因为code-generator用的是v0.18.6,因此要把其他的k8s库也更新到这个版本,修改项目go.mod文件:
module foo-controller go 1.13 require ( github.com/go-logr/logr v0.1.0 github.com/onsi/ginkgo v1.11.0 github.com/onsi/gomega v1.8.1 k8s.io/apimachinery v0.18.6 k8s.io/client-go v0.18.6 k8s.io/code-generator v0.18.6 sigs.k8s.io/controller-runtime v0.6.0 )
执行下面命令更新vendor目录
go mod vendor
4)生成代码
你需要修改guestbook_types.go
文件,添加上tag // +genclient
:
// +genclient // +kubebuilder:object:root=true // Guestbook is the Schema for the guestbooks API type Guestbook struct {
新建apis/webapp/v1/doc.go
,注意// +groupName=webapp.zsy.com
:
// +groupName=webapp.zsy.com package v1
新建apis/webapp/v1/register.go
,code generator生成的代码需要用到它:
package v1 import ( "k8s.io/apimachinery/pkg/runtime/schema" ) // SchemeGroupVersion is group version used to register these objects. var SchemeGroupVersion = GroupVersion func Resource(resource string) schema.GroupResource { return SchemeGroupVersion.WithResource(resource).GroupResource() }
执行hack/update-codegen.sh:
./hack/update-codegen.sh
会得到zsy.com/foo-controller
目录:
foo-controller └── generated └── webapp ├── clientset ├── informers └── listers
移动文件:
foo-controller/generated
直接移出来,放到项目根下面generated
例子程序
先apply manifests yaml:
kubectl apply -f config/crd/bases/webapp.example.com_guestbooks.yaml kubectl apply -f config/samples/webapp_v1_guestbook.yaml
然后执行项目的main.go。
参考:https://segmentfault.com/a/1190000023097945
https://github.com/chanjarster/kubebuilder-mix-codegen-how-to
https://chanjarster.github.io/post/k8s/use-code-generator/
https://cloudnative.to/kubebuilder/cronjob-tutorial/cronjob-tutorial.html