当前位置: 首页 > 工具软件 > code-gen > 使用案例 >

结合Kubebuilder与code-generator开发Operator

钱修雅
2023-12-01

结合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的event
  • lister-gen: 为get和list方法提供只读缓存层

​ code-generator整合了这些gen,使用脚本generate-groups.shgenerate-internal-groups.sh可以为自定义资源生产相关代码。

1.2 Kubebuilder

Kubebuilder是用于使用自定义资源定义(CRD)构建Kubernetes API的框架。

类似于Ruby on RailsSpringBoot之类的Web开发框架,Kubebuilder可以提高速度并降低开发人员管理的复杂性,以便在Go中快速构建和发布Kubernetes API。它建立在用于构建核心Kubernetes API的规范技术的基础之上,以提供减少样板和麻烦的简单抽象。

Resource + Controller = Operator,可以利用Kubebuilder编写自定义资源的Operator。

二、结合背景

Kubebuildercode-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 依赖组件

3.2 初始化项目

安装Kubebuilder:参考官方文档 https://cloudnative.to/kubebuilder/quick-start.html

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.2
go get k8s.io/client-go@$K8S_VERSION
go get k8s.io/apimachinery@$K8S_VERSION

3.3.2 安装code-generator

K8S_VERSIONgo.mod 中的 k8s.io/client-go 的版本保持一致即可。

**注意:**需要将依赖复制到vendor中

K8S_VERSION=v0.17.2
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,注意根据项目修改相应变量:

    • MODULEgo.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
    
    # 注意:
    # 1. kubebuilder2.3.2版本生成的api目录结构code-generator无法直接使用(将api由api/${VERSION}移动至api/${GROUP}/${VERSION}即可)
    
    # 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)}
    
    rm -rf ${OUTPUT_PKG}/{clientset,informers,listers}
    
    # 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,informer,lister" \
    bash "${CODEGEN_PKG}"/generate-groups.sh all \
      ${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}/../../.."
    

    注意:

    1. kubebuilder2.3.2版本生成的api目录结构code-generator无法直接使用(将api由 api/${VERSION} 移动至 api/${GROUP}/${VERSION} 即可)
  • 修改 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
├── generated
│   └── example
│       ├── clientset
│       ├── informers
│       └── listers

之后便可以通过clientset等包对自定义资源对象进行操作。

注意事项:

kubebuilder2.3.2版本生成的api目录结构为 api/v1,而code-generator需要的api目录结构为 api/example/v1,相比较增加了group这一层。

参考

https://github.com/kubernetes-sigs/kubebuilder

https://github.com/kubernetes/sample-controller/blob/master/hack/update-codegen.sh

https://cloud.tencent.com/developer/article/1656317

https://blog.csdn.net/sixinchao_1/article/details/109997736

 类似资料: