Kubernetes code-generator使用

Cylon發表於2022-06-20

Overview

Kubernetes中提供了多種自定義控制器的方式:

Controller 作為CRD的核心,這裡將解釋如何使用 code-generator 來建立自定義的控制器,作為文章的案例,將完成一個 Firewalld Port 規則的控制器作為描述,通過 Kubernetes 規則來生成對應節點上的 iptables規則。

Prerequisites

CRD

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: ports.firewalld.fedoraproject.org
spec:
  group: firewalld.fedoraproject.org
  scope: Namespaced
  names:
    plural: ports
    singular: port
    kind: PortRule
    shortNames: 
    - fp
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              name:
                type: string
              port:
                type: integer
              host:
                type: string
              isPermanent:
                type: boolean

code-generator

需要預先下載 code-generator 。因為這個工具不是必需要求的。

注意,下載完成後需要將程式碼庫的的分支更改為你目前使用的版本,版本的選擇與client-go類似,如果使用master分支,會與當前的 Kubernetes 叢集不相容。

git clone https://github.com/kubernetes/code-generator
cd code-generator; git checkout {version}  # ex. v0.18.0

編寫程式碼模板

要想使用 code-generator 生成控制器,必須準備三個檔案 doc.go , register.go , types.go

  • doc.go 中宣告瞭這個包全域性內,要使用生成器的tag
  • register.go 類似於kubernetes API,是將宣告的型別註冊到schema中
  • type.go 是需要具體宣告物件型別

code-generator Tag說明

在使用 code-generator 時,就需要對 code-generator 的tag進行了解。code-generator 的tag是根據幾個固定格式進行定義的,tag是 +k8s: + conversion 的組合,在倉庫中 cmd 中的 *-gen* 資料夾就代表了 conversion 的替換位置。

注:最終準備完成的檔案( doc.go , register.go , types.go)應該為:apis/example.com/v1 這種型別的

需要遵循的是,將這些檔案放在 <version> 目錄中,例如 v1 。這裡 v1, v1alpha1, 根據自己需求定義。

開始填寫檔案內容

type.go

package v1

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

// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type Port struct {
	metav1.TypeMeta `json:",inline"`
	// Standard object metadata.
	// +optional
	metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

	// Specification of the desired behavior of the Deployment.
	// +optional
	Spec PortSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
}

// +k8s:deepcopy-gen=false
type PortSpec struct {
	Name        string `json:"name"`
	Host        string `json:"host"`
	Port        int    `json:"port"`
	IsPermanent bool   `json:"isPermanent"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type PortList struct {
	metav1.TypeMeta `json:",inline"`
	// +optional
	metav1.ListMeta `json:"metadata,omitempty"`

	Items []Port `json:"items"`
}

doc.go

// +k8s:deepcopy-gen=package
// +k8s:protobuf-gen=package
// +k8s:openapi-gen=true

// +groupName=firewalld.fedoraproject.org

package v1 // import "k8s.io/api/firewalld/v1"

register.go

這裡是從 k8s.io/api 裡任意一個複製的,例如 k8s.io/api/core/v1/register.go

package v1

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

// GroupName is the group name use in this package
const GroupName = "firewalld.fedoraproject.org"

// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}

// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
	return SchemeGroupVersion.WithResource(resource).GroupResource()
}

var (
	// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
	// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
	SchemeBuilder      = runtime.NewSchemeBuilder(addKnownTypes)
	localSchemeBuilder = &SchemeBuilder
	AddToScheme        = localSchemeBuilder.AddToScheme
)

// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
	scheme.AddKnownTypes(SchemeGroupVersion,
		&Port{},
		&PortList{},
	)
	metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
	return nil
}

生成所需檔案

使用 code-generator 時,實際上就是使用這個庫中的指令碼 generate-groups.sh ,該指令碼又四個引數

  • 第一個引數:使用那些生成器,就是 *.gen,用逗號分割,all表示使用全部
  • 第二個引數:client(client-go中informer, lister等)生成的檔案存放到哪裡
  • 第三個引數:api(api結構,k8s.io/api/) 生成的檔案存放到哪裡,可以和定義的檔案為一個目錄
  • 第四個引數:定義group:version
  • -output-base:輸出包存放的根目錄
  • -go-header-file:生成檔案的頭註釋資訊,這個是必要引數,除非生成失敗

注:對於引數二,三,與-output-base,指定的路徑,這裡可以使用相對路徑也可以使用go.mod中的定義的包名,對於使用相對路徑而言,生成的檔案中的import也將會為 "../../" 的格式

一個完整的示例

../code-generator/generate-groups.sh all \
	../code-controller/client \
	../code-controller/apis  \
	firewalld:v1 \
	--output-base ../code-controller/ \
	--go-header-file ../code-generator/hack/boilerplate.go.txt

Reference

CRD Programming

相關文章