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

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

  • 本文是 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.go22 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.// +optionalMinute *CronField `json:"minute,omitempty"`// specifies the hour during which the job executes.// +optionalHour *CronField `json:"hour,omitempty"`// specifies the day of the month during which the job executes.// +optionalDayOfMonth *CronField `json:"dayOfMonth,omitempty"`// specifies the month during which the job executes.// +optionalMonth *CronField `json:"month,omitempty"`// specifies the day of the week during which the job executes.// +optionalDayOfWeek *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 v2import (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 stringconst (// AllowConcurrent allows CronJobs to run concurrently.AllowConcurrent ConcurrencyPolicy = "Allow"// ForbidConcurrent forbids concurrent runs, skipping next run if previousForbidConcurrent 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.// +optionalMinute *CronField `json:"minute,omitempty"`// specifies the hour during which the job executes.// +optionalHour *CronField `json:"hour,omitempty"`// specifies the day of the month during which the job executes.// +optionalDayOfMonth *CronField `json:"dayOfMonth,omitempty"`// specifies the month during which the job executes.// +optionalMonth *CronField `json:"month,omitempty"`// specifies the day of the week during which the job executes.// +optionalDayOfWeek *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.// +optionalStartingDeadlineSeconds *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// +optionalConcurrencyPolicy ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"`// This flag tells the controller to suspend subsequent executions, it does// not apply to already started executions.  Defaults to false.// +optionalSuspend *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.// +optionalSuccessfulJobsHistoryLimit *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.// +optionalFailedJobsHistoryLimit *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.// +optionalActive []corev1.ObjectReference `json:"active,omitempty"`// Information when was the last time the job was successfully scheduled.// +optionalLastScheduleTime *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.ObjectHub()
        }
        
    • 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 v2import ("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.SchedulescheduleParts := []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.*/// ObjectMetadst.ObjectMeta = src.ObjectMeta// Specdst.Spec.StartingDeadlineSeconds = src.Spec.StartingDeadlineSecondsdst.Spec.ConcurrencyPolicy = v1.ConcurrencyPolicy(src.Spec.ConcurrencyPolicy)dst.Spec.Suspend = src.Spec.Suspenddst.Spec.JobTemplate = src.Spec.JobTemplatedst.Spec.SuccessfulJobsHistoryLimit = src.Spec.SuccessfulJobsHistoryLimitdst.Spec.FailedJobsHistoryLimit = src.Spec.FailedJobsHistoryLimit// Statusdst.Status.Active = src.Status.Activedst.Status.LastScheduleTime = src.Status.LastScheduleTimereturn 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.*/// ObjectMetadst.ObjectMeta = src.ObjectMeta// Specdst.Spec.StartingDeadlineSeconds = src.Spec.StartingDeadlineSecondsdst.Spec.ConcurrencyPolicy = ConcurrencyPolicy(src.Spec.ConcurrencyPolicy)dst.Spec.Suspend = src.Spec.Suspenddst.Spec.JobTemplate = src.Spec.JobTemplatedst.Spec.SuccessfulJobsHistoryLimit = src.Spec.SuccessfulJobsHistoryLimitdst.Spec.FailedJobsHistoryLimit = src.Spec.FailedJobsHistoryLimit// Statusdst.Status.Active = src.Status.Activedst.Status.LastScheduleTime = src.Status.LastScheduleTimereturn 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 v2import (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: cronjobapp.kubernetes.io/instance: cronjob-sampleapp.kubernetes.io/part-of: projectapp.kubernetes.io/managed-by: kustomizeapp.kubernetes.io/created-by: projectname: cronjob-sample
    spec:schedule:minute: "*/1"startingDeadlineSeconds: 60concurrencyPolicy: Allow # explicitly specify, but Allow is also default.jobTemplate:spec:template:spec:containers:- name: helloimage: busyboxargs:- /bin/sh- -c- date; echo Hello from the Kubernetes clusterrestartPolicy: 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.mzph.cn/news/751239.shtml

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

相关文章

【全面了解自然语言处理三大特征提取器】RNN(LSTM)、transformer(注意力机制)、CNN

目录 一 、RNN1.RNN单个cell的结构2.RNN工作原理3.RNN优缺点 二、LSTM1.LSTM单个cell的结构2. LSTM工作原理 三、transformer1 Encoder(1)position encoding(2)multi-head-attention(3)add&norm 残差链…

【C语言】指针基础知识(一)

计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中。 一,内存和地址 内存被分为一个个单元,一个内存单元的大小是一个字节。 内存单元的编号(可以理解为门…

2024年值得创作者关注的十大AI动画创新平台

别提找大型工作室制作动画了。如今,AI平台让我们就可以轻松制作动画。从简单的文本生动画功能到复杂的角色动作,这些平台为各种类型的创作者提供了不同的功能。 AI已经有了长足的发展,现在它可以理解复杂的人类动作和艺术意图,将简单的输入转化成丰富而详细的动画。 下面…

【前端Vue】Vue3+Pinia小兔鲜电商项目第1篇:认识Vue3,1. Vue3组合式API体验【附代码文档】

全套笔记资料代码移步&#xff1a; 前往gitee仓库查看 感兴趣的小伙伴可以自取哦&#xff0c;欢迎大家点赞转发~ 全套教程部分目录&#xff1a; 部分文件图片&#xff1a; 认识Vue3 1. Vue3组合式API体验 通过 Counter 案例 体验Vue3新引入的组合式API vue <script> ex…

AJAX学习(四)

版权声明 本文章来源于B站上的某马课程&#xff0c;由本人整理&#xff0c;仅供学习交流使用。如涉及侵权问题&#xff0c;请立即与本人联系&#xff0c;本人将积极配合删除相关内容。感谢理解和支持&#xff0c;本人致力于维护原创作品的权益&#xff0c;共同营造一个尊重知识…

C++内存分布与动态内存管理

文章目录 :dizzy: C/C内存分布:dizzy:C语言中动态内存管理方式  :sparkles:malloc   :sparkles:calloc  :sparkles:reallocfree :dizzy:C语言中动态内存管理方式  :sparkles:new和delete操作内置类型  :sparkles:new和delete操作自定义类型 :dizzy:operator new与ope…

数星星 刷题笔记 (树状数组)

依题意 要求每个点 x, y 的左下方有多少个星星 又因为 是按照y从小到大 给出的 所以 我们在计算个数的时候是按照y一层层变大来遍历的 因此我们在处理每一个点的时候 只需要看一下 当前的点有多少个点的x值比当前点小即可 树状数组的 操作模板 P3374 【模板】树…

动态规划题目集一(代码 注解)

目录 介绍&#xff1a; 题目一: 题目二&#xff1a; 题目三&#xff1a; 题目四&#xff1a; 题目五&#xff1a; 题目六&#xff1a; 题目七&#xff1a; 题目八&#xff1a; 题目九&#xff1a; 介绍&#xff1a; 动态规划是一种算法设计技术&#xff0c;用于解决具有重叠…

水泵房远程监控物联网系统

随着物联网技术的快速发展&#xff0c;越来越多的行业开始利用物联网技术实现设备的远程监控与管理。水泵房作为城市供水系统的重要组成部分&#xff0c;其运行状态的监控与管理至关重要。HiWoo Cloud作为专业的物联网云服务平台&#xff0c;为水泵房远程监控提供了高效、稳定、…

Java访问数据库(重点:SpringBoot整合Mybatis)

目录 一、通过JDBC访问数据库1、思路2、示例3、思考 二、通过ORM框架访问数据库&#xff08;主要是Mybatis&#xff09;1、示例1.1 配置1.2 SQL写在xxxMapper.xml中&#xff1a;mapper/UserMapper.xml1.3 xxxMapper.xml对应的xxxMapper接口&#xff08;Application通过该接口访…

磁盘未格式化,数据恢复有妙招

一、初遇磁盘未格式化&#xff0c;惊慌失措 在日常生活和工作中&#xff0c;我们经常会使用各种存储设备来保存重要的文件和数据。然而&#xff0c;有时当我们尝试访问这些存储设备时&#xff0c;却会突然遇到一个令人头痛的问题——磁盘未格式化。这个突如其来的提示让我们措…

[Python初阶]2255.统计是给定字符串前缀的字符串数目

目录 2255.统计是给定字符串前缀的字符串数目 ①.题目 ②.问题分析 ③.startswith()方法理解 与 说明 Ⅰ.定义和用法 Ⅱ.语法 ④.问题解决 ⑤总结 2255.统计是给定字符串前缀的字符串数目 ①.题目 ②.问题分析 需求:统计列表words中,是字符串s的前缀的字符串的数目. 解…

接口与多态

通过接口实现多态 接口中声明若干个 bstract方法&#xff1b; 方法体的内容细节由实现接口的类去完成&#xff0c;不同的类有 不同的实现方式 → 则接口变量在回调接口方法时具有多 种形态。 用接口进行程序设计的核心思想 使用接口回调技术&#xff1a;接口变量存放实现该接口…

(学习日记)2024.03.13:UCOSIII第十五节:基于时基列表的时延操作(持续更新)

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

《LeetCode热题100》笔记题解思路技巧优化_Part_3

《LeetCode热题100》笔记&题解&思路&技巧&优化_Part_3 &#x1f60d;&#x1f60d;&#x1f60d; 相知&#x1f64c;&#x1f64c;&#x1f64c; 相识&#x1f622;&#x1f622;&#x1f622; 开始刷题链表&#x1f7e2;1. 相交链表&#x1f7e2;2. 反转链表&…

大数据数据分析-scala、IDEA、jdk之间的搭配关系

Scala主要是一门面向对象编程语言和函数式编程语言。 一、大数据框架&#xff08;处理海量/流式数据&#xff09; - ---以HADOOP 2. x为系列的大数据生态系统处理框架 离线数据分析&#xff0c;分析的数据为N1天数据 -----MapReduce 并行计算框架&#xff0c;分而治之…

数据治理系统论-结合数据要素等

什么是数据治理&#xff1f; 数据治理是指组织内外部对数据进行管理和监控的全面框架。它涵盖了数据的安全、合规性、可用性和价值最大化等方面。通过有效的数据治理&#xff0c;组织能够更好地理解其数据资产&#xff0c;并确保数据被正确地管理和利用。 数据治理的重要性 在…

django实现api接口

&#xff08;前期准备&#xff09;第一步&#xff1a;虚拟环境 在windows上使用virtualenvwrapper。 pip install virtualenvwrapper-win 接着&#xff0c;添加环境变量。 echo %WORKON_HOME% 接下来就是创建虚拟环境&#xff0c;假如创建myenv mkvirtualenv myenv 进入…

sqllab第二十八关通关笔记(附带28a)

知识点&#xff1a; union select 整体过滤 union all select 替换where id(输入)空格 过滤了&#xff0c;使用%09代替 经过不断的测试&#xff0c;发现原始语句为 where id(输入) 构造payload:id1)and%091(1 成功回显出了相关的信息 好&#xff0c;尝试进行错误注入 构造…

java虚拟机的堆核心知识介绍

Java虚拟机&#xff08;JVM&#xff09;的堆&#xff08;Heap&#xff09;是Java内存模型中一个至关重要的部分。它是运行时数据区&#xff0c;用于存储Java对象实例。堆是垃圾收集器工作的地方&#xff0c;也是Java应用程序内存管理的关键区域。在本教程中&#xff0c;我们将深…