登录  | 加入社区

黑狼游客您好!登录后享受更多精彩

只需一步,快速开始

新浪微博登陆

只需一步, 快速开始

查看: 479|回复: 0

KubernetesCRD开辟实践

[复制链接]

958

主题

958

帖子

0

现金

黑狼菜鸟

Rank: 1

积分
0
发表于 2020-12-24 03:43:37 | 显示全部楼层 |阅读模式 来自 中国

原标题:Kubernetes CRD开辟实践

gGHxBz3pZln091X9.jpg

配景

Kubernetes的最大亮点之一肯定是它的声明式API计划,所谓的声明式就是告诉Kubernetes你要什么,而不是告诉它怎么做下令。我们一样平常利用Kubernetes做编排工作的时间,常常会打仗Deployment、Service、Pod等资源对象,我们可以很机动地创建其界说设置,然后实行kubectl apply下令,Kubernetes总能为我们创建相干资源对象并完成资源的注册,进而实行资源所负责的功能。

但是我们有没想过,一样平常业务开辟过程中,固然通例的资源根本满意需求,但是这些通例的资源大多仅仅是代表相对底层、通用的概念的对象, 某些环境下我们总是想根据业务定制我们的资源范例,而且使用Kubernetes的声明式API,对资源的增编削查举行监听并作出详细的业务功能。随着Kubernetes生态体系的连续发展,越来越多高条理的对象将会不停涌现,比起现在利用的对象,新对象将更加专业化。

有了自界说资源界说API,开辟者将不必要逐一举行Deployment、Service、ConfigMap等步调,而是创建并关联一些用于表述整个应用步伐大概软件服务的对象。除此,我们能利用自界说的高阶对象,并在这些高阶对象的底子上创建底层对象。比方:我们想要一个Backup资源,我们创建它的对象时,就盼望通过spec的界说举行一样平常的备份操纵声明,当提交给Kubernetes集群的时间,相干的Deployment、Service资源会被主动创建,很大水平让业务扩展性加大。

在Kubernetes 1.7之前,要实现雷同的自界说资源,必要通过TPR(ThirdPartyResource ) 对象方式界说自界说资源,但由于这种方式非常复杂,一度并不被人器重。到了Kubernetes 1.8版本,TPR开始被停用,被官方保举的CRD(CustomResourceDefinitions)所代替。

CRD,称之为自界说资源界说,本质上,它的体现情势是一段声明,用于界说用户界说的资源对象罢了。单单通过它还不能产生任何收益,由于开辟者还要针对CRD界说提供关联的CRD对象CRD控制器(CRD Controller)。CRD控制器通常可以通过Golang举行开辟,只必要遵照Kubernetes的控制器开辟规范,并基于client-go举行调用,并实现Informer、ResourceEventHandler、Workqueue等组件逻辑即可。听起来感觉很复杂的样子,不外实在真正开辟的时间,并不困难,由于大部门繁琐的代码逻辑都能通过Kubernetes的code generator代码天生出来。关于怎样举行CRD控制器的开辟,下面我们会通过一个例子逐步地深入,盼望通过实践来明白CRD的原理。

睁开全文

CRD界说范例

与其他资源对象一样,对CRD的界说也利用YAML设置举行声明。下面先来看下本文的Demo例子的CRD界说。

本文的crddemo源码为:http://github.com/domac/crddemo

mydemo.yaml

apiVersion: apiextensions.k8s.io/v1beta1

kind: CustomResourceDefinition

name: mydemos.crddemo.k8s.io

group: crddemo.k8s.io

version: v1

names:

kind: Mydemo

plural: mydemos

scope: Namespaced

CRD界说中的关键字段如下:

bq8X89IKmM4bF6l5.jpg

  • group:设置API所属的组,将其映射为API URL中的“/apis/”下一级目次。它是逻辑上相干的Kinds聚集
  • scope:该API的见效范围,可选项为Namespaced和Cluster。
  • version:每个Group可以存在多个版本。比方,v1alpha1,然后升为v1beta1,末了稳固为v1版本。
  • names: CRD的名称,包罗单数、复数、kind、所属组等名称界说

在这个CRD中,我指定了group: crddemo.k8s.io,version: v1如许的API信息,也指定了这个CR的资源范例叫作Mydemo,复数(plural)是 mydemos。

我们先别发急利用kubectl create创建资源界说,我们接下来要做的是再基于这个CRD的界说创建相应的详细自界说对象。

example-mydemo.yaml

apiVersion: crddemo.k8s.io/v1

kind: Mydemo

name: example-mydemo

ip: "127.0.0.1"

port: 8080

这个资源对象跟界说Pod差不多,它的重要信息都是泉源上面的界说,Kind是Mydemo,版本是v1,资源组是crddemo.k8s.io

除了这些设置,还必要在spec端设置相应的参数,一样平常是开辟者自界说定制的,在这里,我定制了两个属性:ip和port。以是整个对象我要告诉Kubernetes,我等待监听处置惩罚一个叫example-mydemo的步伐,它的地点是127.0.0.1,端口是8080。固然,这里只是一个demo,没有什么严酷的属性束缚,开辟者照旧根据本身的业务必要自行界说吧。为了不影响本文的先容,姑且以为这两个属性黑白常紧张的。

到这里为止,相对轻松的工作已经完成,我们已经完成CRD的“计划图”工作,下面我们开始动手构建这个CRD控制器的编码工作了。

CRD控制器原理

在正式编码之前,我们先明白一下自界说控制器的工作原理,如下图:

xP2bvbaF6TAftMFV.jpg

CRD控制器的工作流,可分为监听、同步、触发三个步调:

一、Controller起首会通过Informer (所谓的Informer,就是一个自带缓存和索引机制),从Kubernetes的API Server中获取它所关心的对象,举个例子,也就是我编写的Controller获取的应该是Mydemo对象。值得留意的是Informer在构建之前,会利用我们天生的client(下面编码阶段会提到),再透过Reflector的ListAndWatch机制跟API Server创建毗连,不停地监听Mydemo对象实例的变革。在ListAndWatch 机制下,一旦 APIServer 端有新的 Mydemo 实例被创建、删除大概更新,Reflector都会收到“变乱关照”。该变乱及它对应的API对象会被放进一个Delta FIFO Queue中。

二、Local Store此时完成同步缓存操纵。

三、Informer根据这些变乱的范例,触发我们编写并注册号的ResourceEventHandler,完成业务动作的触发。

上面图中的Control Loop现实上可以通过code-generator天生,下面也会提到。总之Control Loop中我们只关心怎样拿到“现实状态”,并与“等待状态”对比,从而详细的差别处置惩罚逻辑,只必要开辟者自行编写即可。

CRD开辟过程

下面会通过一个简朴的例子,开始我们的CRD代码的编写, 完备代码:http://github.com/domac/crddemo

自界说资源代码编写

起首,Kubernetes涉及的代码天生对项目目次布局是有要求的,以是我们先创建一个布局如下的项目。

├── controller.go

├── crd

│ └── mydemo.yaml

├── example

│ └── example-mydemo.yaml

├── main.go

└── pkg

└── apis

└── crddemo

├── register.go

└── v1

├── doc.go

├── register.go

├── types.go

可见关键部门的pkg目次就是API组的URL布局,如下图:

T2SHeVYeX4NfdYZx.jpg

v1下面的types.go文件里,则界说了Mydemo对象的完备形貌。

1、我们起首开看pkg/apis/crddemo/register.go,这个文件重要用来存放全局变量,如下:

package crddemo

const (

GroupName = "crddemo.k8s.io"

Version = "v1"

)

2、pkg/apis/crddemo/v1下的doc.go 也是比力简朴的:

// +k8s:deepcopy-gen=package

// +groupName=crddemo.k8s.io

package v1

在这个文件中,你会看到+k8s:deepcopy-gen=package和+groupName=crddemo.k8s.io,这就是Kubernetes举行代码天生要用的Annotation风格的解释。

  • +k8s:deepcopy-gen=package意思是,请为整个v1包里的全部范例界说主动天生DeepCopy方法;
  • +groupName=crddemo.k8s.io,则界说了这个包对应的crddemo API组的名字。

可以看到,这些界说在doc.go文件的解释,起到的是全局的代码天生控制的作用,以是也被称为Global Tags。

3、pkg/apis/crddemo/types.go的作用就是界说一个Mydemo范例到底有哪些字段(好比,spec字段里的内容)。这个文件的重要内容如下所示:

package v1

import (

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

)

// +genclient

// +genclient:noStatus

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

// Mydemo 形貌一个Mydemo的资源字段

typeMydemo struct {

metav1.TypeMeta `json: ",inline"`

metav1.ObjectMeta `json: "metadata,omitempty"`

Spec MydemoSpec `json: "spec"`

}

//MydemoSpec 为Mydemo的资源的spec属性的字段

typeMydemoSpec struct {

Ip string `json: "ip"`

Port int `json: "port"`

}

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

//复数情势

typeMydemoList struct {

metav1.TypeMeta `json: ",inline"`

metav1.ListMeta `json: "metadata"`

Items []Mydemo `json: "items"`

}

上面的代码,可以开的我们的Mydemo可续界说方法跟Kubernetes对象一样,都包罗了TypeMeta和ObjectMeta字段,而此中比力紧张的是 Spec 字段,就是必要我们本身界说的部门:界说了IP和Port两个字段。

别的,除了界说Mydemo范例,你还必要界说一个MydemoList范例,用来形貌一组Mydemo对象应该包罗哪些字段。之以是必要如许一个范例,是由于在Kubernetes中,获取全部某对象的List方法,返回值都是List范例,而不是某范例的数组。以是代码上肯定要做区分。

关于上面代码的几个紧张注解,下面阐明一下:

  • +genclient这段注解的意思是:请为下面资源范例天生对应的Client代码。
  • +genclient:noStatus的意思是:这个API资源范例界说里,没有Status字段,由于Mydemo才是主范例,以是+genclient要写在Mydemo之上,不消写在MydemoList之上,这时要仔细留意的。
  • +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object的意思是,请在天生DeepCopy的时间,实现Kubernetes提供的runtime.Object接口。否则,在某些版本的Kubernetes里,你的这个范例界说会出现编译错误。

4、pkg/apis/crddemo/register.go作用就是注册一个范例(Type)给APIServer。

package v1

import (

"github.com/domac/crddemo/pkg/apis/crddemo"

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

"k8s.io/apimachinery/pkg/runtime"

"k8s.io/apimachinery/pkg/runtime/schema"

)

var SchemeGroupVersion = schema.GroupVersion{

Group: crddemo.GroupName,

Version: crddemo.Version,

}

var (

SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)

AddToScheme = SchemeBuilder.AddToScheme

)

func Resource(resource string) schema.GroupResource {

returnSchemeGroupVersion.WithResource(resource).GroupResource

}

func Kind(kind string) schema.GroupKind {

returnSchemeGroupVersion.WithKind(kind).GroupKind

}

//Mydemo资源范例在服务器端的注册的工作,APIServer会主动帮我们完成

//但与之对应的,我们还必要让客户端也能知道Mydemo资源范例的界说

func addKnownTypes(scheme *runtime.Scheme) error {

scheme.AddKnownTypes(

SchemeGroupVersion,

&Mydemo{},

&MydemoList{},

)

// register the typeinthe scheme

metav1.AddToGroupVersion(scheme, SchemeGroupVersion)

returnnil

}

有了addKnownTypes这个方法,Kubernetes就可以或许在背面天生客户端的时间,知道Mydemo以及MydemoList范例的界说了。

好了,到这里为止,我们有关界说的代码已经写好了,正如controller原理图所示,接下来我们必要通过Kubernetes提供的代码天生工具,为上面的Mydemo资源范例天生clientset、informer和lister。

关于怎样利用代码天生,这里我已经编写了一个脚步,只需只需本脚步即可。

代码天生

详细可以调用我提供的shll脚本:code-gen.sh

#!/bin/bash

set-x

ROOT_PACKAGE= "github.com/domac/crddemo"

CUSTOM_RESOURCE_NAME= "crddemo"

CUSTOM_RESOURCE_VERSION= "v1"

GO111MODULE=off

# 安装k8s.io/code-generator

[[ -d $GOPATH/src/k8s.io/code-generator ]] || go get -u k8s.io/code-generator/...

# 实行代码主动天生,此中pkg/client是天生目的目次,pkg/apis是范例界说目次

cd$GOPATH/src/k8s.io/code-generator && ./generate-groups.sh all " $ROOT_PACKAGE/pkg/client" " $ROOT_PACKAGE/pkg/apis" " $CUSTOM_RESOURCE_NAME: $CUSTOM_RESOURCE_VERSION"

当只需代码天生脚步后,可以发现我们的代码工作目次也发送了变革,多出了一个client目次。

client

├── clientset

│ └── versioned

│ ├── clientset.go

│ ├── doc.go

│ ├── fake

│ │ ├── clientset_generated.go

│ │ ├── doc.go

│ │ └── register.go

│ ├── scheme

│ │ ├── doc.go

│ │ └── register.go

│ └── typed

│ └── crddemo

│ └── v1

│ ├── crddemo_client.go

│ ├── doc.go

│ ├── fake

│ │ ├── doc.go

│ │ ├── fake_crddemo_client.go

│ │ └── fake_mydemo.go

│ ├── generated_expansion.go

│ └── mydemo.go

├── informers

│ └── externalversions

│ ├── crddemo

│ │ ├── interface.go

│ │ └── v1

│ │ ├── interface.go

│ │ └── mydemo.go

│ ├── factory.go

│ ├── generic.go

│ └── internalinterfaces

│ └── factory_interfaces.go

└── listers

└── crddemo

└── v1

├── expansion_generated.go

└── mydemo.go

此中,pkg/apis/crddemo/v1下面的zz_generated.deepcopy.go文件,就是主动天生的DeepCopy代码文件。下面的三个包(clientset、informers、 listers),都是Kubernetes为Mydemo范例天生的client库,这些库会在背面编写自界说控制器的时间用到。

自界说控制器代码编写

Kubernetes的声明式API并不像“下令式API”那样有着显着的实行逻辑。这就使得基于声明式API的业务功能实现,每每必要通过控制器模式来“监督”API 对象的变革,然后以此来决定现实要实行的详细工作。

main.go

package main

import (

"flag"

"os"

"os/signal"

"syscall"

"time"

"github.com/golang/glog"

"k8s.io/client-go/kubernetes"

"k8s.io/client-go/tools/clientcmd"

clientset "github.com/domac/crddemo/pkg/client/clientset/versioned"

informers "github.com/domac/crddemo/pkg/client/informers/externalversions"

)

//步伐启动参数

var (

flagSet = flag.NewFlagSet( "crddemo", flag.Exit)

master = flag.String( "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")

kubeconfig = flag.String( "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")

onlyOneSignalHandler = make(chan struct{})

shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM}

)

//设置信号处置惩罚

func setupSignalHandler (stopCh <-chan struct{}) {

close(onlyOneSignalHandler)

stop := make(chan struct{})

c := make(chan os.Signal, 2)

signal.Notify(c, shutdownSignals...)

go func{

<-c

close(stop)

<-c

os.Exit(1)

}

returnstop

}

func main{

flag.Parse

//设置一个信号处置惩罚,应用于优雅关闭

stopCh := setupSignalHandler

cfg, err := clientcmd.BuildConfigFromFlags(*master, *kubeconfig)

iferr != nil {

glog.Fatalf( "Error building kubeconfig: %s", err.Error)

}

kubeClient, err := kubernetes.NewForConfig(cfg)

iferr != nil {

glog.Fatalf( "Error building kubernetes clientset: %s", err.Error)

}

mydemoClient, err := clientset.NewForConfig(cfg)

iferr != nil {

glog.Fatalf( "Error building example clientset: %s", err.Error)

}

//informerFactory工厂类, 这里注入我们通过代码天生的client

//clent重要用于和API Server举行通讯,实现ListAndWatch

mydemoInformerFactory := informers.NewSharedInformerFactory(mydemoClient, time.Second*30)

//天生一个crddemo组的Mydemo对象通报给自界说控制器

controller := NewController(kubeClient, mydemoClient,

mydemoInformerFactory.V1.Mydemos)

go mydemoInformerFactory(stopCh)

iferr = controller.Run(2, stopCh); err != nil {

glog.Fatalf( "Error running controller: %s", err.Error)

}

}

接下来,我们来看跟业务最精密的控制器Controller的编写。

controller.go部门紧张代码:

… …

func NewController(

kubeclientset kubernetes.Interface,

mydemoslientset clientset.Interface,

mydemoInformer informers.MydemoInformer) *Controller {

// Create event broadcaster

// Add sample-controller types to the default Kubernetes Scheme so Events can be

// logged forsample-controller types.

utilruntime.Must(mydemoscheme.AddToScheme(scheme.Scheme))

glog.V(4).Info( "Creating event broadcaster")

eventBroadcaster := record.NewBroadcaster

eventBroadcaster.StartLogging(glog.Infof)

eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1.Events( "")})

recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName})

//利用client和前面创建的Informer,初始化了自界说控制器

controller := &Controller{

kubeclientset: kubeclientset,

mydemoslientset: mydemoslientset,

demoInformer: mydemoInformer.Lister,

mydemosSynced: mydemoInformer.Informer.HasSynced,

workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter, "Mydemos”), //WorkQueue的实现,负责同步Informer和控制循环之间的数据

recorder: recorder,

}

glog.Info(" Setting up mydemo event handlers”)

//mydemoInformer注册了三个Handler(AddFunc、UpdateFunc和DeleteFunc)

// 分别对应API对象的“添加”“更新”和“删除”变乱。而详细的处置惩罚操纵,都是将该变乱对应的API对象参加到工作队列中

mydemoInformer.Informer.AddEventHandler(cache.ResourceEventHandlerFuncs{

AddFunc: controller.enqueueMydemo,

UpdateFunc: func(old, new interface{}) {

oldMydemo := old.(*samplecrdv1.Mydemo)

newMydemo := new.(*samplecrdv1.Mydemo)

ifoldMydemo.ResourceVersion == newMydemo.ResourceVersion {

return

}

controller.enqueueMydemo(new)

},

DeleteFunc: controller.enqueueMydemoForDelete,

})

returncontroller

}

… …

通过上面Controller的代码实现,我们根本实现了控制器ListAndWatch的变乱注册逻辑:通过APIServer的LIST API获取全部最新版本的API对象;然后,再通过WATCH-API来监听全部这些API对象的变革。通过监听到的变乱变革,Informer就可以及时地更新当地缓存,而且调用这些变乱对应的EventHandler了。

下面,我们再来看原理图中的Control Loop的部门。

func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {

defer runtime.HandleCrash

defer c.workqueue.ShutDown

// 记载开始日记

glog.Info( "Starting Mydemo control loop")

glog.Info( "Waiting for informer caches to sync")

ifok := cache.WaitForCacheSync(stopCh, c.mydemosSynced); !ok {

returnfmt.Errorf( "failed to wait for caches to sync")

}

glog.Info( "Starting workers")

fori := 0; i < threadiness; i++ {

go wait.Until(c.runWorker, time.Second, stopCh)

}

glog.Info( "Started workers")

<-stopCh

glog.Info( "Shutting down workers")

returnnil

}

可以看到,启动控制循环的逻辑非常简朴,就是同步+循环监听使命。而这个循环监听使命就是我们真正的业务实现部门了。

//runWorker是一个不停运行的方法,而且不停会调用c.processNextWorkItem从workqueue读取和读取消息

func (c *Controller) runWorker{

forc. processNextWorkItem{

}

}

//从workqueue读取和读取消息

func (c *Controller) processNextWorkItem bool {

obj, shutdown := c.workqueue.Get

ifshutdown {

returnfalse

}

err := func(obj interface{}) error {

defer c.workqueue.Done(obj)

var key string

var ok bool

ifkey, ok = obj.(string); !ok {

c.workqueue.Forget(obj)

runtime.HandleError(fmt.Errorf( "expected string in workqueue but got %#v", obj))

returnnil

}

iferr := c.syncHandler(key); err != nil {

returnfmt.Errorf( "error syncing '%s': %s", key, err.Error)

}

c.workqueue.Forget(obj)

glog.Infof( "Successfully synced '%s'", key)

returnnil

}(obj)

iferr != nil {

runtime.HandleError(err)

returntrue

}

returntrue

}

//实验从Informer维护的缓存中拿到了它所对应的Mydemo对象

func (c *Controller) syncHandler(key string) error {

namespace, name, err := cache.SplitMetaNamespaceKey(key)

iferr != nil {

runtime.HandleError(fmt.Errorf( "invalid resource key: %s", key))

returnnil

}

mydemo, err := c.demoInformer.Mydemos(namespace).Get(name)

//从缓存中拿不到这个对象,那就意味着这个Mydemo对象的Key是通过前面的“删除”变乱添加进工作队列的。

iferr != nil {

iferrors.IsNotFound(err) {

//对应的Mydemo对象已经被删除了

glog.Warningf( "DemoCRD: %s/%s does not exist in local cache, will delete it from Mydemo ...",

namespace, name)

glog.Infof( "[DemoCRD] Deleting mydemo: %s/%s ...", namespace, name)

returnnil

}

runtime.HandleError(fmt.Errorf( "failed to list mydemo by: %s/%s", namespace, name))

returnerr

}

glog.Infof( "[DemoCRD] Try to process mydemo: %#v ...", mydemo)

c.recorder.Event(mydemo, corev1.EventTypeNormal, SuccessSynced, MessageResourceSynced)

returnnil

}

代码中的demoInformer,从namespace中通过key获取Mydemo对象这个操纵,实在就是在访问当地缓存的索引,现实上,在Kubernetes的源码中,你会常常看到控制器从各种Lister里获取对象,好比:podLister、nodeLister等等,它们利用的都是Informer和缓存机制。

而假如控制循环从缓存中拿不到这个对象(demoInformer返回了IsNotFound错误),那就意味着这个Mydemo对象的Key是通过前面的“删除”变乱添加进工作队列的。以是,只管队列里有这个Key,但是对应的Mydemo对象已经被删除了。而假如可以或许获取到对应的Mydemo对象,就可以实行控制器模式里的对比“盼望状态(用户通过YAML文件提交到APIServer里的信息)”和“现实状态(我们的控制循环必要通过查询现实的Mydemo资源环境”的功能逻辑了。不外在本例子中,就不做过多的业务假设了。

至此,一个完备的自界说API对象和它所对应的自界说控制器,就编写完毕了。

摆设测试

代码编码后,我们预备开始代码的发布,可以利用提供Makefile举行编译。

$ make

... …

gofmt -w .

go test-v .

? github.com/domac/crddemo [no testfiles]

mkdir -p releases

GOOS=linux GOARCH=amd64 go build -mod=vendor -ldflags "-s -w"-v -o releases/crddemo *.go

github.com/golang/groupcache/lru

k8s.io/apimachinery/third_party/forked/golang/json

k8s.io/apimachinery/pkg/util/mergepatch

k8s.io/kube-openapi/pkg/util/proto

k8s.io/client-go/tools/record/util

k8s.io/apimachinery/pkg/util/strategicpatch

k8s.io/client-go/tools/record

command-line-arguments

go clean -i

... ...

编译完成后,会天生crddemo的二进制文件,我们要做把crddemo放到Kubernetes集群中,大概当地也行,只要能访问到apiserver和具备kubeconfig。

可以看到,步伐运行的时间,一开始会报错。这是由于,此时Mydemo对象的CRD还没有被创建出来,以是Informer去APIServer里获取Mydemos对象时,并不能找到Mydemo这个API资源范例的界说。

接下来,我们实行我们自界说资源的界说文件:

$ kubectl apply -f crd/mydemo.yaml

customresourcedefinition.apiextensions.k8s.io/mydemos.crddemo.k8s.io created

此时,观察crddemo的日记输出,可以看到Controller的日记规复了正常,控制循环启动乐成。

然后,我们可以对我们的Mydemo对象举行增编削查操纵了。

提交我们的自界说资源对象:

$ kubectl apply -f example-mydemo.yaml

mydemo.crddemo.k8s.io/example-mydemo created

创建乐成够,看Kubernetes集群是否乐成存储起来:

$ kubectl get Mydemo

NAME AGE

example-mydemo 2s

这时间,检察一下控制器的输出:

可以看到,我们上面创建example-mydemo.yaml的操纵,触发了EventHandler的添加变乱,从而被放进了工作队列。紧接着,控制循环就从队列里拿到了这个对象,而且打印出了正在处置惩罚这个Mydemo对象的日记。

我们这时间,实验修改资源,对对应的port属性举行修改。

apiVersion: crddemo.k8s.io/v1

kind: Mydemo

name: example-mydemo

ip: "127.0.0.1"

port: 9090

手动实行修改:

$ kubectl apply -f example-mydemo.yaml

此时,crddemo新增出来的日记如下:

可以看到,这一次,Informer 注册的更新变乱被触发,更新后的Mydemo对象的Key被添加到了工作队列之中。

以是,接下来控制循环从工作队列里拿到的Mydemo对象,与前一个对象是差别的:它的ResourceVersion的值从10818363酿成了10818457;而Spec里的Port字段,则酿成了9080。末了,我再把这个对象删撤除:

$ kubectl delete -f example-mydemo.yaml

mydemo.crddemo.k8s.io "example-mydemo"deleted

这一次,在控制器的输出里,我们就可以看到,Informer注册的“删除”变乱被触发,输出如下:

然后,Kubernetes集群的资源也被扫除了:

$ kubectl get Mydemo

No resources found indefault namespace.

以上就是利用自界说控制器的根本开辟流程。

原文链接:http://lihaoquan.me/2020/3/8/k8s-crd-develop.html 返回搜狐,检察更多

责任编辑:





上一篇:安卓12最新谍报:提供单手模式、多使命改进,引入双App分屏 ...
下一篇:最年轻上市公司女CEO演绎“复仇爽剧”:打造爆红的“女性优先”约会App,吊 ...
您需要登录后才可以回帖 登录 | 加入社区

本版积分规则

 

QQ|申请友链|小黑屋|手机版|Hlshell Inc. ( 豫ICP备16002110号-5 )

GMT+8, 2024-5-13 22:48 , Processed in 0.170129 second(s), 47 queries .

HLShell有权修改版权声明内容,如有任何爭議,HLShell將保留最終決定權!

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表