Kubernetes operator系列:kubebuilder 实战演练 之 开发多版本CronJob

news2024/10/6 10:33:06

云原生学习路线导航页(持续更新中)

  • 本文是 Kubernetes operator学习 系列文章,本节会在上一篇开发的Cronjob基础上,进行 多版本Operator 开发的实战
    • 本文的所有代码,都存储于github代码库:https://github.com/graham924/share-code-operator-study/tree/main/cronJob-operator
    • 希望各位大佬们,点点star,大家的鼓励是我更新的动力
  • Kubernetes operator学习系列 快捷链接
    • Kubernetes operator系列:client-go篇
    • Kubernetes operator系列:CRD篇
    • Kubernetes operator系列:code-generator 篇
    • Kubernetes operator系列:controller-tools 篇
    • Kubernetes operator系列:api 和 apimachinery 篇
    • Kubernetes operator系列:CRD控制器 开发实战篇
    • Kubernetes operator系列:kubebuilder 的安装及简单使用 篇
    • Kubernetes operator系列: kubebuilder 实战演练之deploy-image插件的使用
    • Kubernetes operator系列:kubebuilder 实战演练 之 自定义CronJob
    • Kubernetes operator系列:kubebuilder 实战演练 之 开发多版本CronJob
    • Kubernetes operator系列:零散知识篇

1.本项目开发的 多版本CronJob 介绍

1.1.什么情况下需要用到多版本CRD

  • 大多数项目都是从一个 alpha API 开始的,我们可以将其作为发布版本供用户使用。
  • 但增加一些重要特性后,大多数项目还是需要发布一个更稳定的 API版本。一旦 API 版本稳定,就不能对其进行重大更改。
  • 这就是 API 多版本发挥作用的地方。

1.2.本文基于前一篇开发的 CronJob:v1

  • 在 前一篇文章 中,我们制作了一个cronjob,版本为v1。
    • 建议先阅读这篇之后,再阅读本文
    • Kubernetes operator(九) kubebuilder 实战演练 之 自定义CronJob
  • 本文基于前一篇开发的 CronJob:v1,添加一个新的版本v2

1.3.两个版本的差异

  • 本文基于前一篇开发的 CronJob:v1,添加一个新的版本v2,版本的差异如下:
    • v1版本的CronJob,Spec中Schedule字段是string字符串,没有结构化
    • v2版本的CronJob,我们对Schedule字段,进行结构化,更便于使用
  • 本文仅仅是为了演示多版本的开发方法,所以v2中只对Spec进行结构化,其他的全部和v1一样

1.4.Kubernetes 版本 与 CRD 转换方法的关系

  • 多版本API,需要包含增多个版本之间能够互相转换,所以需要CRD转换能力
  • Kubernetes 1.13版本,将CRD 转换作为 alpha 特性引入,但默认未开启。
    • 如果你使用的是 Kubernetes 1.13-1.14,一定要启用功能,请自行探索
    • https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/
  • Kubernetes 1.15版本,将CRD转换升级为 beta,意味着默认开启。
  • 如果你使用更低版本的kubernetes,请参考官方文档https://kubernetes.io/zh-cn/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#webhook-conversion。

1.5.完整代码github仓库

  • 本文的所有代码,都存储于github代码库:https://github.com/graham924/share-code-operator-study/tree/main/cronJob-operator
  • 希望各位大佬们,点点star,大家的鼓励是我更新的动力

2.CronJob:v2 开发

2.1.创建新的API:v2

  • 接下来的操作,全部基于 Kubernetes operator(九) kubebuilder 实战演练 之 自定义CronJob 得到的项目
  • 执行命令
    kubebuilder create api --group batch --version v2 --kind CronJob
    # 询问中,创建Resource回答y,创建Controller回答n
    
  • 执行命令实践,结果如下
    # 执行创建API的命令
    [root@localhost cronJob-operator]# kubebuilder create api --group batch --version v2 --kind CronJob
    INFO Create Resource [y/n]
    y
    INFO Create Controller [y/n]
    n
    INFO Writing kustomize manifests for you to edit...
    INFO Writing scaffold for you to edit...
    INFO api/v2/cronjob_types.go
    INFO api/v2/groupversion_info.go
    INFO Update dependencies:
    $ go mod tidy
    go: downloading github.com/stretchr/testify v1.8.4
    go: downloading github.com/pmezard/go-difflib v1.0.0
    go: downloading go.uber.org/goleak v1.3.0
    go: downloading github.com/evanphx/json-patch v4.12.0+incompatible
    go: downloading gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
    go: downloading github.com/kr/pretty v0.3.1
    go: downloading github.com/rogpeppe/go-internal v1.10.0
    go: downloading github.com/kr/text v0.2.0
    INFO Running make:
    $ make generate
    /root/zgy/project/share-code-operator-study/cronJob-operator/bin/controller-gen-v0.14.0 object:headerFile="hack/boilerplate.go.txt" paths="./..."
    Next: implement your new API and generate the manifests (e.g. CRDs,CRs) with:
    $ make manifests
    
    # 执行命令后,得到的项目目录如下
    [root@localhost cronJob-operator]# tree
    .
    ├── api
    │   ├── v1
    │   │   ├── cronjob_types.go
    │   │   ├── cronjob_webhook.go
    │   │   ├── cronjob_webhook_test.go
    │   │   ├── groupversion_info.go
    │   │   ├── webhook_suite_test.go
    │   │   └── zz_generated.deepcopy.go
    │   └── v2
    │       ├── cronjob_types.go
    │       ├── groupversion_info.go
    │       └── zz_generated.deepcopy.go
    ├── bin
    │   ├── controller-gen-v0.14.0
    │   └── kustomize-v5.3.0
    ├── cmd
    │   └── main.go
    ├── config
    │   ├── certmanager
    │   │   ├── certificate.yaml
    │   │   ├── kustomization.yaml
    │   │   └── kustomizeconfig.yaml
    │   ├── crd
    │   │   ├── bases
    │   │   │   └── batch.graham924.com_cronjobs.yaml
    │   │   ├── kustomization.yaml
    │   │   ├── kustomizeconfig.yaml
    │   │   └── patches
    │   │       ├── cainjection_in_cronjobs.yaml
    │   │       └── webhook_in_cronjobs.yaml
    │   ├── default
    │   │   ├── kustomization.yaml
    │   │   ├── manager_auth_proxy_patch.yaml
    │   │   ├── manager_config_patch.yaml
    │   │   ├── manager_webhook_patch.yaml
    │   │   └── webhookcainjection_patch.yaml
    │   ├── manager
    │   │   ├── kustomization.yaml
    │   │   └── manager.yaml
    │   ├── prometheus
    │   │   ├── kustomization.yaml
    │   │   └── monitor.yaml
    │   ├── rbac
    │   │   ├── auth_proxy_client_clusterrole.yaml
    │   │   ├── auth_proxy_role_binding.yaml
    │   │   ├── auth_proxy_role.yaml
    │   │   ├── auth_proxy_service.yaml
    │   │   ├── cronjob_editor_role.yaml
    │   │   ├── cronjob_viewer_role.yaml
    │   │   ├── kustomization.yaml
    │   │   ├── leader_election_role_binding.yaml
    │   │   ├── leader_election_role.yaml
    │   │   ├── role_binding.yaml
    │   │   ├── role.yaml
    │   │   └── service_account.yaml
    │   ├── samples
    │   │   ├── batch_v1_cronjob.yaml
    │   │   ├── batch_v2_cronjob.yaml
    │   │   └── kustomization.yaml
    │   └── webhook
    │       ├── kustomization.yaml
    │       ├── kustomizeconfig.yaml
    │       ├── manifests.yaml
    │       └── service.yaml
    ├── Dockerfile
    ├── go.mod
    ├── go.sum
    ├── hack
    │   └── boilerplate.go.txt
    ├── internal
    │   └── controller
    │       ├── cronjob_controller.go
    │       ├── cronjob_controller_test.go
    │       └── suite_test.go
    ├── Makefile
    ├── PROJECT
    ├── README.md
    └── test
        ├── e2e
        │   ├── e2e_suite_test.go
        │   └── e2e_test.go
        └── utils
            └── utils.go
    
    22 directories, 61 files
    

2.2.修改 api/v2/cronjob_types.go

  • 从 2.1 输出的目录可以看到,创建完 v2 版本的 API,在api 目录下多出一个 v2 目录,v2 目录下是 新版本的CronJob实体类相关资源
  • 我们修改 api/v2/cronjob_types.go,CronJobSpec 的Schedule写成结构化,其他所有的内容都和 v1版本的CronJob 一样
  • 和 v1 版本的差异处
    // represents a Cron field specifier.
    type CronField string
    
    // describes a Cron schedule.
    type CronSchedule struct {
    	// specifies the minute during which the job executes.
    	// +optional
    	Minute *CronField `json:"minute,omitempty"`
    	// specifies the hour during which the job executes.
    	// +optional
    	Hour *CronField `json:"hour,omitempty"`
    	// specifies the day of the month during which the job executes.
    	// +optional
    	DayOfMonth *CronField `json:"dayOfMonth,omitempty"`
    	// specifies the month during which the job executes.
    	// +optional
    	Month *CronField `json:"month,omitempty"`
    	// specifies the day of the week during which the job executes.
    	// +optional
    	DayOfWeek *CronField `json:"dayOfWeek,omitempty"`
    }
    
    // CronJobSpec defines the desired state of CronJob
    type CronJobSpec struct {
    	// The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
    	Schedule CronSchedule `json:"schedule"`
    	......
    }
    
  • 完整的 api/v2/cronjob_types.go
    package v2
    
    import (
    	batchv1 "k8s.io/api/batch/v1"
    	corev1 "k8s.io/api/core/v1"
    	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    )
    
    // EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
    // NOTE: json tags are required.  Any new fields you add must have json tags for the fields to be serialized.
    
    // ConcurrencyPolicy describes how the job will be handled.
    // Only one of the following concurrent policies may be specified.
    // If none of the following policies is specified, the default one
    // is AllowConcurrent.
    // +kubebuilder:validation:Enum=Allow;Forbid;Replace
    type ConcurrencyPolicy string
    
    const (
    	// AllowConcurrent allows CronJobs to run concurrently.
    	AllowConcurrent ConcurrencyPolicy = "Allow"
    
    	// ForbidConcurrent forbids concurrent runs, skipping next run if previous
    	ForbidConcurrent ConcurrencyPolicy = "Forbid"
    
    	// ReplaceConcurrent cancels currently running job and replaces it with a new one.
    	ReplaceConcurrent ConcurrencyPolicy = "Replace"
    )
    
    // represents a Cron field specifier.
    type CronField string
    
    // describes a Cron schedule.
    type CronSchedule struct {
    	// specifies the minute during which the job executes.
    	// +optional
    	Minute *CronField `json:"minute,omitempty"`
    	// specifies the hour during which the job executes.
    	// +optional
    	Hour *CronField `json:"hour,omitempty"`
    	// specifies the day of the month during which the job executes.
    	// +optional
    	DayOfMonth *CronField `json:"dayOfMonth,omitempty"`
    	// specifies the month during which the job executes.
    	// +optional
    	Month *CronField `json:"month,omitempty"`
    	// specifies the day of the week during which the job executes.
    	// +optional
    	DayOfWeek *CronField `json:"dayOfWeek,omitempty"`
    }
    
    // CronJobSpec defines the desired state of CronJob
    type CronJobSpec struct {
    	// The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
    	Schedule CronSchedule `json:"schedule"`
    
    	// +kubebuilder:validation:Minimum=0
    
    	// Optional deadline in seconds for starting the job if it misses scheduled
    	// time for any reason.  Missed jobs executions will be counted as failed ones.
    	// +optional
    	StartingDeadlineSeconds *int64 `json:"startingDeadlineSeconds,omitempty"`
    
    	// Specifies how to treat concurrent executions of a Job.
    	// Valid values are:
    	// - "Allow" (default): allows CronJobs to run concurrently;
    	// - "Forbid": forbids concurrent runs, skipping next run if previous run hasn't finished yet;
    	// - "Replace": cancels currently running job and replaces it with a new one
    	// +optional
    	ConcurrencyPolicy ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"`
    
    	// This flag tells the controller to suspend subsequent executions, it does
    	// not apply to already started executions.  Defaults to false.
    	// +optional
    	Suspend *bool `json:"suspend,omitempty"`
    
    	// Specifies the job that will be created when executing a CronJob.
    	JobTemplate batchv1.JobTemplateSpec `json:"jobTemplate"`
    
    	// +kubebuilder:validation:Minimum=0
    
    	// The number of successful finished jobs to retain.
    	// This is a pointer to distinguish between explicit zero and not specified.
    	// +optional
    	SuccessfulJobsHistoryLimit *int32 `json:"successfulJobsHistoryLimit,omitempty"`
    
    	// +kubebuilder:validation:Minimum=0
    
    	// The number of failed finished jobs to retain.
    	// This is a pointer to distinguish between explicit zero and not specified.
    	// +optional
    	FailedJobsHistoryLimit *int32 `json:"failedJobsHistoryLimit,omitempty"`
    }
    
    // CronJobStatus defines the observed state of CronJob
    type CronJobStatus struct {
    	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
    	// Important: Run "make" to regenerate code after modifying this file
    
    	// A list of pointers to currently running jobs.
    	// +optional
    	Active []corev1.ObjectReference `json:"active,omitempty"`
    
    	// Information when was the last time the job was successfully scheduled.
    	// +optional
    	LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"`
    }
    
    //+kubebuilder:object:root=true
    //+kubebuilder:subresource:status
    
    // CronJob is the Schema for the cronjobs API
    type CronJob struct {
    	metav1.TypeMeta   `json:",inline"`
    	metav1.ObjectMeta `json:"metadata,omitempty"`
    
    	Spec   CronJobSpec   `json:"spec,omitempty"`
    	Status CronJobStatus `json:"status,omitempty"`
    }
    
    //+kubebuilder:object:root=true
    
    // CronJobList contains a list of CronJob
    type CronJobList struct {
    	metav1.TypeMeta `json:",inline"`
    	metav1.ListMeta `json:"metadata,omitempty"`
    	Items           []CronJob `json:"items"`
    }
    
    func init() {
    	SchemeBuilder.Register(&CronJob{}, &CronJobList{})
    }
    

2.3.设置etcd的存储版本

  • 当API有多个版本时,对于一个API资源,etcd不知道统一保存哪个版本的资源,需要我们指定一个 存储版本
  • 这样etcd会将该API资源,统一转成存储版本,加以存储
  • 我们决定将v1版本设置为存储版本,设置方法为:在v1版本的CronJob结构体上方,使用 +kubebuilder:storageversion 标记
  • api/v1/cronjob_types.go内容如下
    //+kubebuilder:object:root=true
    //+kubebuilder:subresource:status
    //+kubebuilder:storageversion
    
    // CronJob is the Schema for the cronjobs API
    type CronJob struct {
    	metav1.TypeMeta   `json:",inline"`
    	metav1.ObjectMeta `json:"metadata,omitempty"`
    
    	Spec   CronJobSpec   `json:"spec,omitempty"`
    	Status CronJobStatus `json:"status,omitempty"`
    }
    

2.5.编写版本间的转换方法

2.5.1.controller-runtime的Hubs、spokes概念

  • 存在多个版本的API,用户可以请求任何一个版本,所以必须定义一种可以在多个版本之间来回转换的方法

  • 版本转换 有两种解决方案

    • 两两版本间转换:每两个版本之间,就写一套转换方法
    • 中心轴条式转换(hub-spokes):定义一个中心版本,其他版本 只写 转成中心版本的方法,版本间相互转换通过中心版本做中转
      • 中心版本,称为Hub
      • 其他所有版本,称为Spokes

    在这里插入图片描述

  • 很明显,第二种 中心轴条式转换(hub-spokes) 更优异,不需要维护那么多转换方法,易扩展,controller-runtime 也是如此

2.5.2.controller-runtime 的 Hub 和 Convertible 接口

  • controller-runtimepkg/conversion 包下提供了两个接口:
    • Hub 接口
      • 具有多版本的API,要从中选择一个版本作为 中心版本
      • 中心版本需要实现 Hub 接口,相当于完成了标记
        type Hub interface {
        	runtime.Object
        	Hub()
        }
        
    • Convertible 接口
      • 具有多版本的API,每个Spokes版本,都需要 实现 Convertible 接口,实现 ConvertTo、ConvertFrom 方法,用于和Hub版本之间相互转换
        type Convertible interface {
        	runtime.Object
        	// 将 当前版本 转成 Hub中心版本
        	ConvertTo(dst Hub) error
        	// 将 Hub版本 转成 当前版本
        	ConvertFrom(src Hub) error
        }
        

2.5.3.将 CronJob:v1 版本作为Hub中心版本

  • api/v1 目录下创建一个 cronjob_conversion.go 文件,用于让 v1 版本的 CronJob 实现 Hub 接口
  • 实现 Hub 方法,空就行
  • api/v1/cronjob_conversion.go 内容
    package v1
    
    // Hub marks this type as a conversion hub.
    func (*CronJob) Hub() {}
    

2.5.4.将 CronJob:v2 版本作为Spoke轴条版本

  • api/v2 目录下创建一个 cronjob_conversion.go 文件,用于让 v2 版本的 CronJob 实现 Convertible 接口
  • 编写 ConvertTo 和 ConvertFrom 方法,用于和Hub版本之间相互转换
  • api/v2/cronjob_conversion.go 内容
    package v2
    
    import (
    	"fmt"
    	v1 "graham924.com/cronJob-operator/api/v1"
    	"sigs.k8s.io/controller-runtime/pkg/conversion"
    	"strings"
    )
    
    func (src *CronJob) ConvertTo(dstRaw conversion.Hub) error {
    	dst := dstRaw.(*v1.CronJob)
    
    	sched := src.Spec.Schedule
    	scheduleParts := []string{"*", "*", "*", "*", "*"}
    	if sched.Minute != nil {
    		scheduleParts[0] = string(*sched.Minute)
    	}
    	if sched.Hour != nil {
    		scheduleParts[1] = string(*sched.Hour)
    	}
    	if sched.DayOfMonth != nil {
    		scheduleParts[2] = string(*sched.DayOfMonth)
    	}
    	if sched.Month != nil {
    		scheduleParts[3] = string(*sched.Month)
    	}
    	if sched.DayOfWeek != nil {
    		scheduleParts[4] = string(*sched.DayOfWeek)
    	}
    	dst.Spec.Schedule = strings.Join(scheduleParts, " ")
    	/*
    		The rest of the conversion is pretty rote.
    	*/
    	// ObjectMeta
    	dst.ObjectMeta = src.ObjectMeta
    
    	// Spec
    	dst.Spec.StartingDeadlineSeconds = src.Spec.StartingDeadlineSeconds
    	dst.Spec.ConcurrencyPolicy = v1.ConcurrencyPolicy(src.Spec.ConcurrencyPolicy)
    	dst.Spec.Suspend = src.Spec.Suspend
    	dst.Spec.JobTemplate = src.Spec.JobTemplate
    	dst.Spec.SuccessfulJobsHistoryLimit = src.Spec.SuccessfulJobsHistoryLimit
    	dst.Spec.FailedJobsHistoryLimit = src.Spec.FailedJobsHistoryLimit
    
    	// Status
    	dst.Status.Active = src.Status.Active
    	dst.Status.LastScheduleTime = src.Status.LastScheduleTime
    
    	return nil
    }
    func (dst *CronJob) ConvertFrom(srcRaw conversion.Hub) error {
    	src := srcRaw.(*v1.CronJob)
    	schedParts := strings.Split(src.Spec.Schedule, " ")
    	if len(schedParts) != 5 {
    		return fmt.Errorf("invalid schedule: not a standard 5-field schedule")
    	}
    
    	partIfNeeded := func(raw string) *CronField {
    		if raw == "*" {
    			return nil
    		}
    		part := CronField(raw)
    		return &part
    	}
    
    	dst.Spec.Schedule = CronSchedule{
    		Minute:     partIfNeeded(schedParts[0]),
    		Hour:       partIfNeeded(schedParts[1]),
    		DayOfMonth: partIfNeeded(schedParts[2]),
    		Month:      partIfNeeded(schedParts[3]),
    		DayOfWeek:  partIfNeeded(schedParts[4]),
    	}
    
    	/*
    		The rest of the conversion is pretty rote.
    	*/
    	// ObjectMeta
    	dst.ObjectMeta = src.ObjectMeta
    
    	// Spec
    	dst.Spec.StartingDeadlineSeconds = src.Spec.StartingDeadlineSeconds
    	dst.Spec.ConcurrencyPolicy = ConcurrencyPolicy(src.Spec.ConcurrencyPolicy)
    	dst.Spec.Suspend = src.Spec.Suspend
    	dst.Spec.JobTemplate = src.Spec.JobTemplate
    	dst.Spec.SuccessfulJobsHistoryLimit = src.Spec.SuccessfulJobsHistoryLimit
    	dst.Spec.FailedJobsHistoryLimit = src.Spec.FailedJobsHistoryLimit
    
    	// Status
    	dst.Status.Active = src.Status.Active
    	dst.Status.LastScheduleTime = src.Status.LastScheduleTime
    
    	return nil
    }
    

2.6.多版本转换需要使用Webhook运行

2.6.1.CRD转换方法需要使用Webhook运行

  • Kubernetes CRD 的 conversion 方法通常需要使用 webhook 来实现。
  • Conversion webhook 是一种 Kubernetes API 扩展机制,它允许开发者在 CRD 对象在 API Server 中存储之前或之后对其进行版本升级、字段转换、默认值设置等处理

2.6.2.为 CronJob v2 创建webhook

  • 我们仅仅为了测试创建的是用于conversion的webhook,因此参数只使用 --conversion
    • 如果你想要为v2版本也进行 校验和设置默认值,那么就像 v1 版本那样,使用 --defaulting --programmatic-validation 参数
    • 我们这里就不测试 校验和设置默认值 了
  • 执行命令
    kubebuilder create webhook --group batch --version v2 --kind CronJob --conversion
    
  • 执行之后,项目目录如下:
    • 可以看到,api/v2 目录下生成了 cronjob_webhook.gocronjob_webhook_test.go 两个文件
    • 其中,cronjob_webhook.go 文件内容如下
    package v2
    
    import (
    	ctrl "sigs.k8s.io/controller-runtime"
    	logf "sigs.k8s.io/controller-runtime/pkg/log"
    )
    
    // log is for logging in this package.
    var cronjoblog = logf.Log.WithName("cronjob-resource")
    
    // SetupWebhookWithManager will setup the manager to manage the webhooks
    func (r *CronJob) SetupWebhookWithManager(mgr ctrl.Manager) error {
    	return ctrl.NewWebhookManagedBy(mgr).
    		For(r).
    		Complete()
    }
    
    // TODO(user): EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
    

2.6.3.查看main方法的变化

  • 为 CronJob v2 的创建完webhook之后,main方法有一些变化
  • 我们新创建了一个webhook,自然要在main方法中调用 SetupWebhookWithManager 方法,将 这个Webhook 启动起来
  • 查看main.go中,果然多了一段 batchv2.CronJob 的 Webhook 启动的逻辑
    ......
    if os.Getenv("ENABLE_WEBHOOKS") != "false" {
    	if err = (&batchv1.CronJob{}).SetupWebhookWithManager(mgr); err != nil {
    		setupLog.Error(err, "unable to create webhook", "webhook", "CronJob")
    		os.Exit(1)
    	}
    }
    if os.Getenv("ENABLE_WEBHOOKS") != "false" {
    	if err = (&batchv2.CronJob{}).SetupWebhookWithManager(mgr); err != nil {
    		setupLog.Error(err, "unable to create webhook", "webhook", "CronJob")
    		os.Exit(1)
    	}
    }
    ......
    

3.部署和验证

3.1.部署

  • 环境配置前提:安装cert-manager,修改配置文件,请看:Kubernetes operator(九) kubebuilder 实战演练 之 自定义CronJob 的 6.2
  • 确保webhook的证书可以正确提供,再执行命令
    make manifests
    make install
    export ENABLE_WEBHOOKS=true
    make docker-build docker-push IMG=gesang321/cronjob-operator:v3
    make deploy IMG=gesang321/cronjob-operator:v3
    
  • make deploy过程中可能遇到这个错误:
    • error: resource mapping not found for name: "cronjob-operator-controller-manager-metrics-monitor" namespace: "cronjob-operator-system" from "STDIN": no matches for kind "ServiceMonitor" in version "monitoring.coreos.com/v1" ensure CRDs are installed first ensure CRDs are installed first
    • github上有人遇到相同的错误,可以参考一下
      • https://github.com/kubernetes-sigs/kubebuilder/pull/3696
    • 看他的意思,好像是通过安装 kube-prometheus-stack 解决了问题,如果遇到可以试一下

3.2.验证

  • 编写测试 yaml

  • 修改 config/samples/batch_v2_cronjob.yaml

  • 内容如下

    apiVersion: batch.tutorial.kubebuilder.io/v2
    kind: CronJob
    metadata:
      labels:
        app.kubernetes.io/name: cronjob
        app.kubernetes.io/instance: cronjob-sample
        app.kubernetes.io/part-of: project
        app.kubernetes.io/managed-by: kustomize
        app.kubernetes.io/created-by: project
      name: cronjob-sample
    spec:
      schedule:
        minute: "*/1"
      startingDeadlineSeconds: 60
      concurrencyPolicy: Allow # explicitly specify, but Allow is also default.
      jobTemplate:
        spec:
          template:
            spec:
              containers:
              - name: hello
                image: busybox
                args:
                - /bin/sh
                - -c
                - date; echo Hello from the Kubernetes cluster
              restartPolicy: OnFailure
    
  • 创建 batch.tutorial.kubebuilder.io/v2 的 CronJob

    kubectl apply -f config/samples/batch_v2_cronjob.yaml
    
  • 验证转换逻辑可以生效

    • 下面两个命令,都能获取到我们的资源
    • 当获取v1版本资源的时候,apiserver会自动调用我们运行的 conversion webhook,进行 v2->Hub->v1 的转换,进而获取到 v1 版本的资源
    • 并且获取到的v1版本的Schedule,是 "*/1 * * * *",与我们编写的v2版本的yaml,是等价的
    kubectl get cronjobs.v2.batch.graham924.com
    kubectl get cronjobs.v1.batch.graham924.com
    

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

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

相关文章

three.js 元素周期表,可鼠标控制

一些文章里的元素周期表,能显示,但控制器却无法使用,周期表没法旋转 后来发现是three.js版本问题, 旧版本在调试状态下是可以旋转的。新版本只要在正常页面打开状态下就能鼠标控制 <!DOCTYPE html> <html> <head> <meta charset="utf-8"&…

网盘聚合工具:统筹管理所有网盘资源 | 开源日报 No.203

alist-org/alist Stars: 35.6k License: AGPL-3.0 alist 是一个支持多存储的文件列表/WebDAV 程序&#xff0c;使用 Gin 和 Solidjs。 该项目的主要功能、关键特性、核心优势包括&#xff1a; 支持多种存储方式易于部署和开箱即用文件预览&#xff08;PDF、markdown、代码等&…

Jmeter文件上传不成功问题

前言 最近好忙呀&#xff0c;项目上线然后紧接着又客户培训了&#xff0c;由于项目有个模块全是走配置的&#xff0c;所以导致问题不断&#xff0c;近期要培训为了保障培训时客户同时操作的情况&#xff0c;所以把我从功能端抽出来做压测了&#xff0c;之前安排了2个同事写压测…

微调大型语言模型进行命名实体识别

大型语言模型的目标是理解和生成与人类语言类似的文本。它们经过大规模的训练&#xff0c;能够对输入的文本进行分析&#xff0c;并生成符合语法和语境的回复。这种模型可以用于各种任务&#xff0c;包括问答系统、对话机器人、文本生成、翻译等。 命名实体识别&#xff08;Na…

基于深度学习的口罩人脸识别研究进展

MTCNN模型训练输入的所有图像都是正样本&#xff08;戴口罩的照片&#xff09;&#xff0c;没有负样本作为模型输入。在后续的识别任务模块中&#xff0c;导入MTCNN模型检测结果&#xff0c;对特征点进行编码比较进行识别。 基于MTCNN的口罩人脸识别框架可分为四个阶段&#xf…

Linux 时间系统调用

UNIX及LinuxQ的时间系统是由「新纪元时间」Epoch开始计算起。Epoch是指定为1970年1月1日凌晨零点零分零秒&#xff0c;格林威治时间。目前大部份的UNX系统都是用32位来记录时间&#xff0c;正值表示为1970以后&#xff0c;负值则表示1970年以前。 对于当前时间到Epoch 我们用两…

刷题日记——干碎那个BFS!(含国科大机试2021)

例题小引——迷宫问题 问题描述: 迷宫由n行m列的单元格组成(n&#xff0c;m都小于等于50&#xff09;&#xff0c;每个单元格要么是空地&#xff0c;要么是障碍物。 现请你找到一条从起点到终点的最短路径长度。 分析——&#xff08;迷宫问题BFS解法&#xff09; 使用BFS…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:GridRow)

栅格布局可以为布局提供规律性的结构&#xff0c;解决多尺寸多设备的动态布局问题&#xff0c;保证不同设备上各个模块的布局一致性。 栅格容器组件&#xff0c;仅可以和栅格子组件(GridCol)在栅格布局场景中使用。 说明&#xff1a; 该组件从API Version 9开始支持。后续版本…

实战!wsl 与主机网络通信,在 wsl 中搭建服务器。学了计算机网络,但只能刷刷面试题?那也太无聊了!这篇文章可以让你检测你的计网知识!

前言&#xff08;碎碎念&#xff09;&#xff1a;每次发布文章时&#xff0c;我都是一个纠结的过程。因为我给自己写笔记时&#xff0c;只需要记录自己不清晰或者易忘的知识点就可以了&#xff0c;但一旦想要作为文章发布&#xff0c;那么我就得考虑到很多人是纯新手&#xff0…

1TGE120011R1111变频器全国发货质保一年

1TGE120011R1111 ABB ABB的1TGE120011R1111是一个属于其TGE系列的小型断路器&#xff08;也称为微型断路器或MCB&#xff09;。这个系列的断路器主要用于低压配电系统&#xff0c;为电路提供过载和短路保护。以下是这款断路器的一些特点&#xff1a; 紧凑设计&#xff1a;TGE系…

【数据库】数据库基本知识

1.数据库的四个基本概念 1.1 数据&#xff1a;描述事务的符号记录 1.2 数据库&#xff1a;概括的说&#xff0c;数据库数据具有永久存储、有组织的、可共享的大量数据的集合&#xff0c;数据库中的数据按一定的数据模型组织、描述和储存&#xff0c;具有较小的冗余度、较高的…

2、鸿蒙学习-申请调试证书和调试Profile文件

申请发布证书 发布证书由AGC颁发的、为HarmonyOS应用配置签名信息的数字证书&#xff0c;可保障软件代码完整性和发布者身份真实性。证书格式为.cer&#xff0c;包含公钥、证书指纹等信息。 说明 请确保您的开发者帐号已实名认证。每个帐号最多申请1个发布证书。 1、登录AppGa…

AI美图设计室试用,可以生成PPT,以及模特试衣

文章目录 美图设计室试用 美图设计室试用 美图设计室是美图秀秀的公司推出的AI图像处理工具&#xff0c;其功能涵盖图片编辑、抠图、海报设计、文生图等常用的AI功能。尽管很多功能需要开通会员使用&#xff0c;但一些免费功能的表现也还不错&#xff0c;值得一用。 美图设计…

【解读】NIST网络安全框架CSF 2.0

2014年&#xff0c;NIST&#xff08;美国国家标准与技术研究所&#xff0c;类似于中国的工信部&#xff09;首次发布了网络安全框架CSF&#xff08;Cybersecurity Framework)&#xff0c;十年后&#xff0c;在2024年2月26日发布了重大更新&#xff08;CSF 2.0&#xff09;&…

【蓝屏分析】WHEA_UNCORRECTABLE_ERROR 问题分析与解决

背景信息 电脑名字电脑类型厂商使用时间magicbook14 2020款 R5 4500U笔记本电脑荣耀HONOR3年9个月 内存CPUGPU硬盘焊死在主板上焊死在主板上集显PCIe 4.0 NVMe M.2 固态 软硬件错误源确定 电脑莫名频繁随机蓝屏&#xff0c;由于在软件环境上无迹可寻推测是硬件问题 蓝屏画面…

Pretrain-finetune、Prompting、Instruct-tuning训练方法的区别

来自&#xff1a;【多模态】28、LLaVA 第一版 | Visual Instruction Tuning 多模态模型的指令微调_多模态指令跟随数据-CSDN博客 几种模型训练方法的区别&#xff1a; 1、Pretrain-finetune&#xff1a;先在大量数据集上做预训练&#xff0c;然后针对某个子任务做 finetune 2…

Python图像处理指南:PIL与OpenCV的比较【第136篇—PIL】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Python图像处理指南&#xff1a;PIL与OpenCV的比较 图像处理在计算机视觉和图像识别等领域…

实现悲观协议,除了锁还能咋办?

相对乐观和局部悲观是一体两面的关系&#xff0c;识别它的要点就在于是否有全局有效性验证&#xff0c;这也和分布式数据库的架构特点息息相关。但是关于悲观协议&#xff0c;还有很多内容没有提及&#xff0c;下面我们就来填补这一大块空白。 悲观协议的分类 要先跳出来&…

Word使用通配符替换

1.通配符替换 使用&#xff08;[通配替换文本]&#xff09; M-MM12-00([0123456789])-0([0123456789])([0123456789])-0([0123456789])([0123456789]) 2.根据自定义格式替换 根据格式、样式替换&#xff0c;如只替换标题的内容&#xff0c;不替换征文

ASP.NET 服务器控件

目录 一、使用的软件 1、下载 2、新建文件&#xff08;写一个简单的web网页&#xff09; 二、相关知识点 1、Web窗体网页的组件 &#xff08;1&#xff09;可视化组件 &#xff08;2&#xff09;用户接口逻辑 2、Web Form网页的代码模型 &#xff08;1&#xff09;单文件…