k8s编程operator——(3) 自定义资源CRD.md

news2025/7/13 12:32:27

文章目录

    • 1、自定义资源的使用
      • 1.1 注册自定义资源
      • 1.2 使用自定义资源:
      • 1.3 Finalizers
      • 1.4 合法性验证
    • 2、如何操作自定义资源
      • 2.1 使用RestClient和DynamicClient来操作自定义资源对象
      • 2.2 使用sharedIndexInformer
      • 2.3 code-generator
        • 2.3.1 下载安装
        • 2.3.2 code-generator使用
      • 2.4 controller-tools
        • 2.4.1 下载安装
        • 2.4.1 controller-tools使用

在K8S系统扩展中,开发者可以通过CRD(CustomResourceDefinition)来扩展K8S API,其功能主要由APIExtensionServer负责。使用CRD扩展资源分为三步:

  • 注册自定义资源:开发者需要通过K8S提供的方式注册自定义资源,即通过CRD进行注册,注册之后,K8S就知道我们自定义资源的存在了,然后我们就可以像使用K8S内置资源一样使用自定义资源(CR)
  • 使用自定义资源:像内置资源比如Pod一样声明资源,使用CR声明我们的资源
  • 删除自定义资源:当我们不需要时,可以删除自定义资源

1、自定义资源的使用

1.1 注册自定义资源

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # 名字必须与下面的spec字段匹配,并且格式为: <名称的复数形式>.<组名>
  name: demos.example.com
spec:
  # 组名,用于 REST API: /apis/<组>/<版本>
  group: example.com
  names:
    # 名称的复数形式,用于URL: /apis/<组>/<版本>/<名称的复数形式>
    plural: demos
    # 名称的单数形式,作为命令行使用时和显示时的别名
    singular: demo
    # kind通常是单数形式的帕斯卡编码形式。你的资源清单会使用这一形式
    kind: Demo
    # shortNames 允许你在命令行使用较短的字符串来匹配资源
    shortNames:
    - dm
  # 可以是Namespaced 或 Cluster  
  scope: Namespaced
  # 列举此CRD所支持的版本
  versions:
  - name: v1
    # 每个版本都可以通过served标准来独立启用或禁止
    served: true
    # 其中一个且只有一个版本必须被标记为存储版本
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              name:
                type: string

执行下面的命令来注册我们的CRD:

# 将上面内容复制到一个crd-demo.yaml文件中

# 注册我们的CRD
[root@master demo-test]# kubectl create -f crd-demo.yaml
customresourcedefinition.apiextensions.k8s.io/demos.example.com created

# 查看我们注册的CRD
[root@master demo-test]# kubectl get crd
NAME                                        CREATED AT
demos.example.com                           2022-11-24T07:16:38Z

# 查看自定义资源CR,目前还没有,因为我们还没有创建
[root@master demo-test]# kubectl get demos
No resources found in default namespace.

 

1.2 使用自定义资源:

待CRD创建完成后,我们就可以使用它来创建我们的自定义资源了,其创建方式跟内置的资源如Pod这些是一样的,只需要将kind、apiVersion指定为我们CRD中声明的值,比如使用上面的例子中的CRD定义资源:

apiVersion: "example.com/v1"
kind: Demo
metadata:
  name: crd-demo
spec:
  name: test

创建Demo:

# 将上面yaml内容复制到demo.yaml中

# 创建demo
[root@master demo-test]# kubectl create -f demo.yaml
demo.example.com/crd-demo created

# 查看demos
[root@master demo-test]# kubectl get dm
NAME       AGE
crd-demo   5s

虽然我们注册了CRD并且创建了一个CR,但是此时是没有任何效果的。要实现效果的话,就需要我们来实现一个controller来监听我们的CR。

 

1.3 Finalizers

Finalizers能够让控制器实现异步的删除前(Pre-delete)回调。与内置对象类似,定制对象也支持Finalizer

给我们的CR添加Finalizer:

apiVersion: "example.com/v1"
kind: Demo
metadata:
  name: demo-finalizer
  finalizers:
  - example.com/finalizer

自定义 Finalizer 的标识符包含一个域名、一个正向斜线和 finalizer 的名称。 任何控制器都可以在任何对象的 finalizer 列表中添加新的 finalizer。

对带有 Finalizer 的对象的第一个删除请求会为其 metadata.deletionTimestamp 设置一个值,但不会真的删除对象。一旦此值被设置,finalizers 列表中的表项只能被移除。 在列表中仍然包含 finalizer 时,无法强制删除对应的对象。

metadata.deletionTimestamp 字段被设置时,监视该对象的各个控制器会执行它们所能处理的 finalizer,并在完成处理之后将其从列表中移除。 每个控制器负责将其 finalizer 从列表中删除。

metadata.deletionGracePeriodSeconds 的取值控制对更新的轮询周期。

一旦 finalizers 列表为空时,就意味着所有 finalizer 都被执行过, Kubernetes 会最终删除该资源

 

下面进行一个测试:

创建CR:

# 将上面yaml内容复制到cr-finalizer.yaml

# 创建cr
[root@master demo-test]# kubectl create -f cr-finalizer.yaml
demo.example.com/demo-finalizer created

# 查看cr
[root@master demo-test]# kubectl get demos
NAME             AGE
crd-demo         9m48s
demo-finalizer   19s

# 删除cr
kubectl delete demo demo-finalizer

当我们删除时,可以看到会在命令执行后一直卡住,等待我们的controller来完成资源清理:

在这里插入图片描述

下面我们来模拟一下清理资源:

# 启动另一个终端

# 编辑我们的CR
kubectl edit demo demo-finalizer

将下面图片中红框中的内容删除

在这里插入图片描述

保存退出后,可以看到另一个终端已经OK了

在这里插入图片描述

1.4 合法性验证

在CRD中定义了我们的CR的一些字段,我们可以对字段进行合法性校验,比如我们使用正则表达式来限制name必须为test开头:

在这里插入图片描述

# 1.在我们的crd-demo.yaml中添加上图的pattern

# 2.将之前的cr删除
kubectl delete dm crd-demo

# 3. 更新我们的crd
[root@master demo-test]# kubectl apply -f crd-demo.yaml
customresourcedefinition.apiextensions.k8s.io/demos.example.com configured

将我们的demo.yaml中的spec.name修改

在这里插入图片描述

创建cr,可以看到name不合法,创建失败了。如果将name改为test开头的字符串就可以创建成功了。

在这里插入图片描述

 

2、如何操作自定义资源

client-go为每种K8S内置资源提供对应的clientsetinformer。那么如果我们要监听和操作自定义资源对象,应该如何操作呢?这里有两种方式:

  • 方式一:使用client-go提供的dynamicClient来操作自定义资源对象,当然由于dynamicClient是基于RESTClient实现的,所以我们也可以使用RESTClient来达到同样目的。
  • 方式二:使用code-generator来帮助我们生成我们需要的代码,这样我们就可以像使用client-go为K8S内置资源提供的方式监听和操作自定义资源了。

我们主要使用code-generator来编写我们的控制器。

 

2.1 使用RestClient和DynamicClient来操作自定义资源对象

下面将使用RestClient和DynamicClient来操作我们的自定义资源demo

使用RestClient操作自定义资源对象:

使用restClient时,需要我们在config中指定GV以及解码器同时还要配置APIPath

package main

import (
	"context"
	"encoding/json"
	"fmt"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/klog/v2"
)

func main() {
    // 获取配置 将/root/.kube/config拷贝到项目的conf目录下
	config, err := clientcmd.BuildConfigFromFlags("", "./conf/config")
	if err != nil {
		panic(err)
	}

    // 指定GV
	config.GroupVersion = &schema.GroupVersion{
		Group:   "example.com",
		Version: "v1",
	}
    // 指定解码器
	config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
    // 指定APIPath APIPath为通过http访问的路径前缀
	config.APIPath = "/apis/"
	
    // 创建restClient
	restClient, err := rest.RESTClientFor(config)
	if err != nil {
		panic(err)
	}
	
    // 将获取的数据保存到Unstructured类型的对象中
	obj := unstructured.Unstructured{}
    // 获取资源为demos,deafult命名空间下,名称为crd-demo的资源对象
	err = restClient.Get().Resource("demos").Name("crd-demo").
		Namespace(v1.NamespaceDefault).Do(context.Background()).Into(&obj)
	if err != nil {
		klog.Errorf("get demo error:%v", err)
		return
	}
	
    // 序列化为json后打印,看的更清晰
	bytes, err := json.Marshal(obj.Object)
	if err != nil {
		klog.Errorf("json marshal error:%v", err)
		return
	}

	fmt.Println(string(bytes))
}

对于K8S内建的资源对象例如Pod、Deployment来说,有对应的golang 结构体类型,我们可以直接使用。但是我们自定义的资源是没有的,所以数据的接收需要使用unstructured.Unstructured{}类型:

这个类型中就是一个map

type Unstructured struct {
	// Object is a JSON compatible map with string, float, int, bool, []interface{}, or
	// map[string]interface{}
	// children.
	Object map[string]interface{}
}

运行并使用json_pp格式化后的结果如下:

在这里插入图片描述

 

使用DynamicClient操作自定义资源对象:

在使用dynamicClient操作自定义资源对象时,需要传入自定义资源的GVR,然后就可以像使用内置资源对象一样来操作了。

package main

import (
	"context"
	"encoding/json"
	"fmt"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/klog/v2"
)

func main() {
	config, err := clientcmd.BuildConfigFromFlags("", "./conf/config")
	if err != nil {
		panic(err)
	}

	client, err := dynamic.NewForConfig(config)
	gvr := schema.GroupVersionResource{
		Group:    "example.com",
		Version:  "v1",
		Resource: "demos",
	}

	resourceInterface := client.Resource(gvr)
	
	obj, err := resourceInterface.Namespace(v1.NamespaceDefault).Get(context.Background(), "crd-demo", v1.GetOptions{})
	if err != nil {
		klog.Errorf("get error:%v", err)
		return
	}

	bytes, err := json.Marshal(obj.Object)
	if err != nil {
		klog.Errorf("json marshal error:%v", err)
		return
	}
	fmt.Println(string(bytes))

}

运行并使用json_pp格式化后的运行结果如下:

在这里插入图片描述

 

2.2 使用sharedIndexInformer

        K8S的内建资源都有对应的informer的实现,比如PodInformerDeploymentInformer。对于我们的自定义资源来说,并没有这样的informer,但是我们可以使用shredIndexInformer。在下一节的代码生成中,可以使用代码生成器来生成特定的informer,比如DemoInfomer和DemoLister等工具。

这节主要将sharedIndexInformer的使用,代码如下:

package main

import (
	"context"
	"encoding/json"
	"fmt"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/klog/v2"
	"time"
)

func main() {
    // 1、构建config
	config, err := clientcmd.BuildConfigFromFlags("", "./conf/config")
	if err != nil {
		panic(err)
	}
	// 2、创建dynamicClient,也可以使用restClient
	client, err := dynamic.NewForConfig(config)
	gvr := schema.GroupVersionResource{
		Group:    "example.com",
		Version:  "v1",
		Resource: "demos",
	}

	resourceInterface := client.Resource(gvr)
	
    // 使用sharedIndexInformer需要一个ListWatch对象,该对象可以从apiServer获取数据
	listwatch := cache.ListWatch{
		ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
			return resourceInterface.List(context.Background(), options)
		},
		WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
			return resourceInterface.Watch(context.Background(), options)
		},
		DisableChunking: false,
	}
    // 示例对象,unstructured.Unstructured实现了runtime.Object接口
	obj := unstructured.Unstructured{}
    // 3、创建sharedIndexInformer,使用Namespace索引器
	informer := cache.NewSharedIndexInformer(&listwatch, &obj, time.Minute, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
	
    // 4、添加资源事件处理方法
	informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: PrintObj,
		UpdateFunc: func(oldObj, newObj interface{}) {
			PrintObj(newObj)
		},
		DeleteFunc: PrintObj,
	})

	stopCh := make(chan struct{})
    // 5、启动informer
	informer.Run(stopCh)

	<-stopCh
}

func PrintObj(obj interface{}) {
	demo := obj.(*unstructured.Unstructured)
	bytes, err := json.Marshal(demo.Object)
	if err != nil {
		klog.Errorf("json marshal error:%v", err)
		return
	}
	fmt.Println(string(bytes))
}

        在使用sharedIndexInformer时需要我们传入ListWatch示例对象索引器。ListWatch用于用apiServer获取数据;由于我们没有自定义资源的go类型,因此只能使用unstructured.Unstructured类型。

 

2.3 code-generator

        code-generator是K8S官网提供的一组代码生成工具。当我们为CRD编写自定义controller时,可以使用它来生成我们需要的versioned clientinformerlister以及其它工具方法。

2.3.1 下载安装

github地址:https://github.com/kubernetes/code-generator

# 将code-generator克隆到$GOPATH/pkg中
cd $GOPATH/pkg

git clone https://github.com/kubernetes/code-generator

# 安装需要的组件
# 进入code-generator目录中
cd code-generator

$ go install ./cmd/{client-gen,deepcopy-gen,informer-gen,lister-gen}

# 这些组件被安装到了$GOPATH/bin目录下, 我们可以将它们添加到PATH中,这样就可以在任意地方使用了

        如果一个个使用这些组件也是很麻烦的,我们可以使用code-generator目录下的generate-groups.sh脚本文件来生成我们的代码。

2.3.2 code-generator使用

        接下来我们自定义一个CRD,然后使用code-generator来生成代码来实现对自定义资源的操作。在https://github.com/kubernetes/sample-controller中有一个样例,我们就根据这个样例来。

1、创建一个工程文件,然后使用我们的ide打开,我用的是Goland:

mkdir -p github.com/operator-crd
cd github.com/operator-crd
touch main.go
go mod init github.com/operator-crd

2、根据样例中的目录结构来创建出我们的目录结构

目录结构:pkg/apis/<Group>/<Version>

在这里插入图片描述

创建出如下的目录:

在这里插入图片描述

3、在样例的v1alpha1中有四个文件,其中doc.go types.go 以及 register.go都是需要我们自己写的,然后其余的代码根据这三个文件来生成。

在这里插入图片描述

创建出这些文件

  • types.go:在这个文件中需要定义我们的自定义资源的go结构体类型
  • register.go:用来注册我们的类型
  • doc.go:在其中添加全局的标记

我们需要在这些文件中添加标记,然后代码生成器就可以根据这些标记来生成代码,比如在doc.go中添加下面两个标记,// +k8s:deepcopy-gen=package用来告诉生成器来生成我们自定义资源类型的deepcopy方法+groupName=samplecontroller.k8s.io是指定我们的group名称

在这里插入图片描述

(1)我们需要在我们的doc.go中添加标记,内容如下:

doc.go

// +k8s:deepcopy-gen=package
// +groupName=crd.example.com
package v1

(2)然后在types.go中声明类型:

types.go

package v1

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

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// Foo is a specification for a Foo resource
type Foo struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   FooSpec   `json:"spec"`
	Status FooStatus `json:"status"`
}

// FooSpec is the spec for a Foo resource
type FooSpec struct {
	DeploymentName string `json:"deploymentName"`
	Replicas       *int32 `json:"replicas"`
}

// FooStatus is the status for a Foo resource
type FooStatus struct {
	AvailableReplicas int32 `json:"availableReplicas"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// FooList is a list of Foo resources
type FooList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata"`

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

我们声明的Foo类型跟K8S内建的资源类型是差不多的,都包含了TypeMetaObjectMeta以及Spec等。

下面两个标记分别用来告诉代码生成器生成自定义资源的clientset和Foo类型要实现的deepcopy的interface

在这里插入图片描述

(3)在register.go中注册我们的类型

register.go

package v1

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

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

// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
	return SchemeGroupVersion.WithKind(kind).GroupKind()
}

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

var (
	// SchemeBuilder initializes a scheme builder
	SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
	// AddToScheme is a global function that registers this API group & version to a scheme
	AddToScheme = SchemeBuilder.AddToScheme
)

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

上面三个步骤完成后,在下面会报红,是因为我们还没有为其生成相应的GetObjectKind和DeepCopyObject方法

在这里插入图片描述

4、生成其余代码

我们可以使用code-generator中的generate-groups.sh来生成代码

我们可以直接执行来查看它的使用方法,Examples中的第一个用来使用所有的组件,第二个我们可以单独使用组件:

在这里插入图片描述

生成的命令如下:

$GOPATH/pkg/code-generator/generate-groups.sh all github.com/operator-crd/pkg/generated github.com/operator-crd/pkg/apis crd.example.com:v1 --go-header-file=$GOPATH/pkg/code-generator/hack/boilerplate.go.txt --output-base ../../
  • github.com/operator-crd/pkg/generated:是我们生成的代码的位置
  • github.com/operator-crd/pkg/apis:我们的代码的位置,要根据我们的三个代码文件来生成其它代码
  • crd.example.com:v1:组名和版本

注意:在windows的gitbash上使用generate-groups.sh不成功,还没有找到解决办法,建议在linux中生成,其实这个code-generator不会也无所谓,后面有更好用的工具,主要看生成的步骤即可。可以根据上面的步骤在linux中安装code-generator来生成代码

最终在linux上生成了对应的代码:

在这里插入图片描述

最终生成了我们的deepcopy、clientset、informer以及listers

在这里插入图片描述

然后我们就可以像使用K8S的内建资源一样来操作我们的自定义资源了。

5、创建自定义资源

crd.yaml

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: foos.crd.example.com
spec:
  group: crd.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        # schema used for validation
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                deploymentName:
                  type: string
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10
            status:
              type: object
              properties:
                availableReplicas:
                  type: integer
  names:
    kind: Foo
    plural: foos
  scope: Namespaced

注册CRD:

# 将上面的内容黏贴到crd.yaml中

# 注册crd
[root@master manifests]# kubectl create -f crd.yaml
customresourcedefinition.apiextensions.k8s.io/foos.crd.example.com created

创建一个CR

example-foo.yaml

apiVersion: crd.example.com/v1
kind: Foo
metadata:
  name: example-foo
spec:
  deploymentName: example-foo
  replicas: 1
# 创建cr
[root@master manifests]# kubectl create -f example-foo.yaml
foo.crd.example.com/example-foo created

# 查看cr
[root@master manifests]# kubectl get foos
NAME          AGE
example-foo   27s

6、在代码中操作我们的自定义资源

main.go

package main

import (
	"fmt"
	v1 "github.com/operator-crd/pkg/apis/crd.example.com/v1"
	clientset "github.com/operator-crd/pkg/generated/clientset/versioned"
	"github.com/operator-crd/pkg/generated/informers/externalversions"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/klog/v2"
)

func main() {
	// 1.创建配置
	config, err := clientcmd.BuildConfigFromFlags("", "./conf/config")
	if err != nil {
		panic(config)
	}

	// 2.创建clientset
	clientset, err := clientset.NewForConfig(config)
	if err != nil {
		panic(err)
	}

	// 3.创建informerFactory
	factory := externalversions.NewSharedInformerFactory(clientset, 0)
	// 4.获取FooInformer
	fooInformer := factory.Crd().V1().Foos()
	// 获取SharedIndexInformer
	informer := fooInformer.Informer()
	// 获取lister
	lister := fooInformer.Lister()

	// 注册资源事件处理其
	informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			foo := obj.(*v1.Foo)
			fmt.Printf("[Add Event] %s\n", foo.Name)
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			foo := newObj.(*v1.Foo)
			fmt.Printf("[Add Event] %s\n", foo.Name)
		},
		DeleteFunc: func(obj interface{}) {
			foo := obj.(*v1.Foo)
			fmt.Printf("[Add Event] %s\n", foo.Name)
		},
	})

	stopCh := make(chan struct{})
	factory.Start(stopCh)
	factory.WaitForCacheSync(stopCh)

	// 使用lister查询foo
	foo, err := lister.Foos("default").Get("example-foo")
	if err != nil {
		klog.Errorf("get foo error:%v", err)
	} else {
		fmt.Println("foo name:", foo.Name)
	}

	<-stopCh
}

运行结果如下:

在这里插入图片描述

 

2.4 controller-tools

在上节中,使用code-generate可以帮助我们生成types文件以及informer、lister等工具方法。但是它不能为我们生成types文件以及CRD。但是使用controller-tools中的工具就可以来生成types文件以及CRD、RBAC等文件。

2.4.1 下载安装

github地址:https://github.com/kubernetes-sigs/controller-tools

# 将代码克隆到下来
git clone https://github.com/kubernetes-sigs/controller-tools

# 安装
cd controller-tools
go install ./cmd/{controller-gen,type-scaffold}

安装后,两个程序就被安装到了$GOPATH/bin目录下。

 

2.4.1 controller-tools使用

type-scaffold可以为我们生成自定义资源的go类型。使用时需要指定Kind以及resource(也可以不指定resource,会根据kind来生成),使用如下:

type-scaffold.exe --kind Foo --resource Foos

但是直接这样使用并不会为我们生成文件,而是直接打印出了生成的代码,因此我们可以使用重定向来生成文件:

type-scaffold.exe --kind Foo --resource Foos > pkg/apis/crd.example.com/v1/types.go

然后我们可以在FooSpec中添加需要的字段:

在这里插入图片描述

controller-gen可以生成deepcopy方法实现、CRD、RBAC等:

在这里插入图片描述

生成这些文件也要依赖于注释标记,比如在生成CRD时候,我们可以在types文件中添加标记来设置数据的校验:

在这里插入图片描述

生成deepcopy方法:

controller-gen object paths=pkg/apis/crd.example.com/v1/types.go

生成CRD

资源定义好了,我们需要将其注册到client-go中。在v1目录下创建register.go,在package上面添加groupName的标记,然后定义GV:`

// register.go
// +groupName=example.com
package v1

import (
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
)

var (
	Scheme       = runtime.NewScheme()
	GroupVersion = schema.GroupVersion{
		Group:   "example.com",
		Version: "v1",
	}

	Codec = serializer.NewCodecFactory(Scheme)
)

在types.go文件中调用Scheme.AddKnownTypes的方法来注册我们的类型:

// types.go
package v1

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

// FooSpec defines the desired state of Foo
type FooSpec struct {
	// INSERT ADDITIONAL SPEC FIELDS -- desired state of cluster
}

// FooStatus defines the observed state of Foo.
// It should always be reconstructable from the state of the cluster and/or outside world.
type FooStatus struct {
	// INSERT ADDITIONAL STATUS FIELDS -- observed state of cluster
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// Foo is the Schema for the foos API
// +k8s:openapi-gen=true
type Foo struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   FooSpec   `json:"spec,omitempty"`
	Status FooStatus `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

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

func init() {
	Scheme.AddKnownTypes(GroupVersion, &Foo{}, &FooList{})
}
# 生成CRD
controller-gen  crd paths=./... output:crd:dir=config/crd

但是不知为何,在这生成之后,没有任何文件,半天找不到原因。先不管了,这些东西知道一个流程就行了,反正后面也不会用。后面有kubebuilder脚手架,使用起来非常简单方便。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/38293.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ajax、Fetch、Axios三者的区别

1.Ajax&#xff08;Asynchronous JavaScript And XML&#xff09; Ajax 是一个技术统称&#xff0c;它很重要的特性之一就是让页面实现局部刷新。 特点&#xff1a; 局部刷新页面&#xff0c;无需重载整个页面。 简单来说&#xff0c;Ajax 是一种思想&#xff0c;XMLHttpReq…

毕业设计-基于机器学习的图片处理图片倾斜校正

前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科同学来说是充满挑战。为帮助大家顺利通过和节省时间与精力投…

如何简单理解大数据

如何简单理解大数据 HDFS-存储 海量的数据存储 hadoop 只是一套工具的总称&#xff0c;它包含三部分&#xff1a;HDFS&#xff0c;Yarn&#xff0c;MapReduce&#xff0c;功能分别是分布式文件存储、资源调度和计算。 按理来说&#xff0c;这就足够了&#xff0c;就可以完成大…

matlab实现MCMC的马尔可夫转换MS- ARMA - GARCH模型估计

状态转换模型&#xff0c;尤其是马尔可夫转换&#xff08;MS&#xff09;模型&#xff0c;被认为是识别时间序列非线性的不错的方法。 估计非线性时间序列的方法是将MS模型与自回归移动平均 - 广义自回归条件异方差&#xff08;ARMA - GARCH&#xff09;模型相结合&#xff0c;…

Ubuntu22.04+Nvidia驱动+Cuda11.8+cudnn8.6

Ubuntu22.04Nvidia驱动Cuda11.8 一、准备环境 ubuntu 22.04nvidia显卡 这里使用的是RTX3060已安装Python3.10 二、安装pip3 # 安装 sudo apt install python3-pip # 升级 sudo pip3 install --upgrade pip # 如果要卸载&#xff0c;使用命令&#xff1a; sudo apt-get remov…

MySQL创建和管理表

基础知识 一条数据存储的过程 存储数据是处理数据的第一步 。只有正确地把数据存储起来&#xff0c;我们才能进行有效的处理和分析。否则&#xff0c;只能是一团乱麻&#xff0c;无从下手。 那么&#xff0c;怎样才能把用户各种经营相关的、纷繁复杂的数据&#xff0c;有序、…

ES6解析赋值

ES6中新增了一种数据处理方式&#xff0c;可以将数组和对象的值提取出来对变量进行赋值&#xff0c;这个过程时将一个数据结构分解成更小的部分&#xff0c;称之为解析。 1.对象解析赋值: 在ES5中&#xff0c;要将一个对象的属性提取出来&#xff0c;需要经过一下几个过程。 …

aws sdk 学习和使用aws-sdk-go

https://www.go-on-aws.com/infrastructure-as-go/cdk-go/sdk example&#xff0c;https://github.com/awsdocs/aws-doc-sdk-examples golang的安装&#xff0c;使用1.15之后默认开启GO15VENDOREXPERIMENT的版本 yum install golang -y go env -w GOPROXYhttps://goproxy.cn,…

智慧教室解决方案-最新全套文件

智慧教室解决方案-最新全套文件一、建设背景1、教育信息化2.0行动计划2、中国教育现代化20353、加快推进教育现代化实施方案二、建设思路互联网时代教学环境定义三、建设方案四、获取 - 智慧教室全套最新解决方案合集一、建设背景 1、教育信息化2.0行动计划 “网络学习空间覆…

【直播】-DRM和TZC400的介绍-2022/11/26

直播背景和内容 最近有两位SOC大佬再和我探讨TZC的设计&#xff0c;以及使用场景。也有几位软件工程师&#xff0c;在深入得学习安全技术&#xff0c;也问到了TZC相关的技术。 然后就搞了本次的直播&#xff0c;共计17人报名。 上线12位同学。(有ASIC大佬、有软件大佬、芯片严…

shell脚本的条件判断3:探究[[]]和[]的区别

前言 实例中除非特别标注&#xff0c;否则都不是在centos中测试的。 一 简述 多数情况下[]和[[]]是可以通用的&#xff0c;两者的主要差异是&#xff1a;test或[]是符合POSIX标准的测试语句&#xff0c;兼容性更强&#xff0c;几乎可以运行在所有Shell解释器中&#xff0c;相…

驱动保护进程 句柄降权 杀软自保 游戏破图标技术原理和实现

文章目录实现效果实现原理代码实现实现效果句柄降权对抗(实现破游戏图标和关闭杀软自保)降权对抗延伸游戏降权对抗杀软自保对抗隐藏Object钩子回调完整代码实现效果 效果如图所示&#xff1a; 无法结束进程&#xff1a; CE无图标&#xff1a; 内存无法读取 可以看到被保护的进…

STC 51单片机41——汇编 串口连续发送数据

; 仿真时&#xff0c;单步运行&#xff0c;记得设置虚拟串口数据 ORG 0000H MOV TMOD ,#20H ;定时器1&#xff0c;工作方式2&#xff0c;8位重装载 MOV TH1,#0FDH ; 波特率9600 MOV TL1,#0FDH SETB TR1 ; 启动T1 MOV SCON ,#40H ; 串口工作方式1 …

[激光原理与应用-20]:《激光原理与技术》-6- 谐振腔的结构、作用、工作原理

目录 第1章 谐振腔简介 1.1 什么是谐振腔 1.2 什么是光学谐振腔 1.3 谐振腔的作用 1.4 什么是镜片镀膜 第2章 谐振腔的结构与工作原理 2.1 谐振腔的结构 2.2 谐振腔的分类 2.3 激活介质与谐振腔的工作原理 第1章 谐振腔简介 1.1 什么是谐振腔 谐振腔&#xff0c;是…

[Java反序列化]—CommonsCollections4

0x01&#xff1a; 这条链子 前半段跟CC3 一样 &#xff0c;都是动态加载字节码的过程&#xff0c;后边的构造用到了两个类&#xff0c;PriorityQueue和TransformingComparator Gadget chain:ObjectInputStream.readObject()PriorityQueue.readObject()...TransformingComparat…

如何Cisco的学员 摆脱游客登录

Cisco Packet Tracer 是一款强大的网络模拟工具&#xff0c;可用于在虚拟实验环境中练习网络、物联网和网络安全技能。您不需要任何硬件&#xff0c;即可获得实操经验&#xff01; 相信很多小伙伴在使用cisco packet tracer这个思科数据包跟踪器 - 网络仿真工具时在登录的时候…

云原生k8s的前世今生--Docker

不可修改(immutability)和幂等性(idempotent)是一致依赖困扰软件发布与运维的两个难题。以nodejs等解析执行的应用而言&#xff0c;如何控制发布后的软件不被修改运行&#xff0c;发布后&#xff0c;经常发现因为运行环境差异导致各种水土不服&#xff1a;比如应用程序对底层操…

【C++数据结构】渐近记法

渐近记法 3.1 引言 考察程序的操作计数和执行步数有两个重要的原因&#xff1a; 1)预测程序运行时间如何随着实例特征的变化而变化&#xff1b; 2)对两个功能相同的程序&#xff0c;比较它们的时间复杂度。 存在问题&#xff1a; 在使用操作计数时&#xff0c;我们关注的…

Vue生命周期--Vue实例创建过程详解

每个Vue实例在被创建时都要经过一系列的初始化过程--例如&#xff0c;需要设置数据监听、编译模板、将实例挂载到DOM并在数据变化时更新DOM等。同时在这个过程中也会运行一些叫做 生命周期钩子的函数&#xff0c;这给了用户在不同阶段添加自己的代码的机会。 生命周期图示&…

Linux入侵排查

Linux入侵排查 实验内容 当系统被黑客入侵、需短时间查找入侵来源&#xff0c;还原入侵事故过程&#xff0c;同时给出解决方案与防范措施。以下给出一些入侵排查思路 netstat -antpl 查看当前端口状态是LISTEN的端口 针对对外开放的端口&#xff0c;查看日志 先查看ssh爆破的…