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,请参考官方文档。

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运行

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

豆瓣书影音存入Notion

使用Python将图书和影视数据存放入Notion中。 🖼️介绍 环境 Python 3.10 (建议 3.11 及以上)Pycharm / Vs Code / Vs Code Studio 项目结构 │ .env │ main.py - 主函数、执行程序 │ new_book.txt - 上一次更新书籍 │ new_video.…

13-Vue基础之路由

个人名片: 😊作者简介:一名大二在校生 🤡 个人主页:坠入暮云间x 🐼座右铭:懒惰受到的惩罚不仅仅是自己的失败,还有别人的成功。 🎅**学习目标: 坚持每一次的学习打卡 文章…

26-1 SQL 注入攻击 - delete注入

环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 一、注入原理: 对于后台来说,delete操作通常是将对应的id传递到后台,然后后台会删除该id对应的数据。 如果后台没有对接收到的 id 参数进行充分的验证和过滤,恶意用户可能会…

一、NLP中的文本分类

目录 1.0 文本分类的应用场景 1.1 文本分类流程 ​编辑 1.2 判别式模型 1.3 生成式模型 1.4 评估 1.5 参考文献 NLP学习笔记系列,欢迎收藏交流: 零、自然语言处理开篇-CSDN博客 一、NLP中的文本分类-CSDN博客 二、NLP中的序列标注(分…

scrcpy远程投屏控制Android

下载 下载后解压压缩包scrcpy-win64-v2.4.zip scrcpy连接手机 1. 有线连接 - 手机开启开发者选项,并开启USB调试,连接电脑,华为手机示例解压scrcpy,在scrcpy目录下打开终端,(或添加scrcpy路径为环境变…

NVIDIA vGPU三种授权方式(个人玩家版)

NVIDIA vGPU三种授权方式(个人玩家版) 旧版本的License Server搭建(比较推荐)说明搭建所需文件创建一个Linux虚拟机(我创建的是Ubuntu 18.04.06)修改虚拟机的MAC地址关闭虚拟机的时间同步及修改系统时间安装java安装Apache Tomcat安装许可证服务器软件上传授权文件新版本…

智慧城管:街面秩序沿街商铺视频可视化AI智能监管方案

一、背景分析 随着城市化的加速和商业活动的日益繁荣,沿街商铺的管理面临着越来越多的挑战。沿街商户的乱堆乱放、占道经营、违章停车等违法行为,一直以来都是城市管理中的难题。这不仅存在交通安全隐患,也造成了市容秩序混乱,严…

【CSS3】CSS3 3D 转换示例 - 3D 旋转木马 ( @keyframes 规则 定义动画 | 为 盒子模型 应用动画 | 开启透视视图 | 设置 3D 呈现样式 )

文章目录 一、3D 导航栏示例 - 核心要点1、需求分析2、HTML 结构section 标签 3、CSS 样式keyframes 规则 定义动画为 盒子模型 应用动画开启透视视图设置 3D 呈现样式鼠标移动到控件上方效果设置 6 个子盒子模型的效果 二、完整代码示例1、代码示例2、展示效果 一、3D 导航栏示…

访问者模式(Visitor Pattern)

访问者模式 说明 访问者模式(Visitor Pattern)属于行为型模式,表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。 该模式是将数据结构与数据操作分离的设计模式,是…

实现微服务:匹配系统

HTTP与WebSocket协议 1. HTTP协议是无状态的,每次请求都是独立的,服务器不会保存客户端的状态信息。而WebSocket协议是有状态的,一旦建立连接后,服务器和客户端可以进行双向通信,并且可以保持连接状态,服务…

“遥感+”多技术融合:碳排放监测的创新路径“

在全球环境问题日益严重的今天,以全球变暖为主要特征的气候变化成为了人类面临的巨大挑战。它威胁着地球的生态平衡,对全球可持续发展构成了严峻的挑战。为了应对这一挑战,各国纷纷采取行动,致力于实现碳达峰和碳中和的目标。 在…

Window11安装达梦数据库

由于现在流行国产化,很多公司的数据库产品都使用了国产数据库,所以,今天给大家讲解一下,达梦数据库的安装和试用,这样学完以后,就可以直接在公司里面用了。 首先,需要先注册账号,然…

怎么在家里远程控制公司电脑?

在家远程控制公司办公电脑需求渐增 在家工作也被称为远程办公,可以节省通勤时间,而且也为老板提供了对应的工作成果,是一个一举两得的好方法。 如果您想要在家远程控制公司电脑,先需要在公司的电脑上安装并运行相应的远程工具&a…

css设置选中文字和选中图片字的颜色

要改变页面中选中文字的颜色,可以使用 CSS 的 ::selection 伪元素来实现 *::selection {/* 改变选中文字的背景色 */background-color: #c42121;/* 改变选中文字的文本颜色 */color: #fff; } 用通配符选择器给所有元素都加上了 ::selection伪元素,用于…

CrossOver24软件免费电脑虚拟机,快速在Mac和Linux上运行Windows软件

当然,除了之前提到的核心技术、兼容性和性能优化外,CrossOver2024还具有其他一些值得关注的性能特点: CrossOver Mac-安装包下载如下:https://wm.makeding.com/iclk/?zoneid50028 CrossOver linux-安装包下载如下:ht…

工业界真实的推荐系统(小红书)-离散特征处理、矩阵补充模型、双塔模型

课程特点:系统、清晰、实用,原理和落地经验兼具 b站:https://www.bilibili.com/video/BV1HZ421U77y/?spm_id_from333.337.search-card.all.click&vd_sourceb60d8ab7e659b10ea6ea743ede0c5b48 讲义:https://github.com/wangsh…

linux系统创建私有容器仓库和docker容器的资源限制

私有仓库创建和资源限制 创建私有仓库docker资源限制系统压力测试工具stresscpu资源限制限制CPU Share限制CPU核数CPU绑定 mem资源限制限制IO 创建私有仓库 上传harbor压缩包 解压 下载docker-compose 进入解压后的目录 修改配置文件 mv harbor.yml.tmpl harbor.yml vim harb…

Unity3d Shader篇(十四)— 卡通着色

文章目录 前言一、什么是卡通着色?1. 卡通着色原理2. 卡通着色优缺点优点:缺点: 二、使用步骤1. Shader 属性定义2. SubShader 设置3. 卡通轮廓 Pass4. 卡通主 Pass 三、效果四、总结 前言 卡通着色是一种常见的图形渲染效果,它将…

大语言模型:Query Rewriting for Retrieval-Augmented Large Language Models

总体思路 作者首先指出大语言模型虽然取得了很好的效果,但是仍然存在幻觉和时间顺序混乱的问题,因此需要额外知识库和LLM内部知识库相结合,来修正;因此优化传统的retriever-reader的方案成为需要;目前的研究方案当中使…

Nginx的日志怎么看,在哪看,access.log日志内容详解

Nginx 的日志文件通常位于服务器的文件系统中,具体位置可能因配置而异。以下是查看 Nginx 日志的几种方法: 1、查看访问日志:在默认配置下,Nginx 的访问日志文件路径为 /var/log/nginx/access.log。您可以通过命令 sudo cat /var…