当前位置: 首页 > news >正文

创建K8s pod Webhook

目录

1.前提条件

2.开始创建核心组件Pod的Webhook

2.1.什么是Webhook

 2.2.在本地k8s集群安装cert-manager

2.3.创建一个空的文件夹

2.4. 生成工程框架

2.5. 生成核心组件Pod的API

2.6.生成Webhook

2.7.开始实现Webhook相关代码

2.7.1.修改相关配置

2.7.2.修改代码

2.7.3.按照最新配置更新yaml

2.7.4.集群运行测试

2.7.4.1.修改Makefile文件

2.7.4.2.将应用部署到k8s集群上

2.7.5.测试

2.7.5.1.准备测试Deployment的yaml配置

2.7.5.2.部署这个Pod demo

 2.7.5.3.停止测试

参考文章


1.前提条件

可以按之前的文章配置,配置好之后,我们会准备好以下内容:

  • 本地多节点集群
  • kubectl 客户端命令工具
  • Lens k8s dashboard 可视化客户端工具
  • golang开发语言
  • VScode Linux 版
  • kubebuilder

2.开始创建核心组件Pod的Webhook

        K8s的核心组件就那些几个:Deployment、Pod、Service、Ingress、ConfigMap、Secret、……

        在Kubebuilder的官方文档中,也有提到webhook的内容,但是比较简单,而且有些参考文章是基于CRD做的Webhook示例,此处我将演示核心组件Pod的Webhook。

图1 K8s核心组件

2.1.什么是Webhook

         从我的角度看,Webhook就是一个回调动作,举个例子方便理解:例如,我们希望在pod创建的时候,在annotation中增加一个标签,如author:geoff之类的,就可以利用这个webhook实现,当创建调用链完成之后,利用“回调”在这个创建动作之后丝滑地加入“增加annotation的额外动作”。可以结合Js的回调、java的AOP切片来理解。我们看看其中的原理:

图2 webhook基于k8s资源调用链中的admission 

        从图中可以看到,Webhook是在Mutating Admission或者Vaildating Admission阶段往Webhook发送一个“请求”并接受来自Webhook的“响应”。我们此时不妨大胆推测一下,实现这两点,要做些什么呢?我可以不负责任推测一下步骤:

  1. 在XXX Admission Controller配置一个请求,请求会往Webhook接受、处理并返回
  2. Webhook开发一个接收这个请求的Handler(类似Java的controller)

        事实也正如我们瞎想的哪样,确实核心就是这两个动作。这里我们看到,一共有两种Admission Controller,有点点区别:

  1. Mutating Admission Controller       # 可以修改资源
  2. Vaildating Admission Controller     # 验证资源,可以通过或者不通过

2.2.在本地k8s集群安装cert-manager

        这个算是提前准备了,需要注意的是,k8s集群的版本和cert-manage需要注意下,不然版本差别过多,可能会有些意想不到的问题

# 查看本地集群的版本
kind version# 在集群上安装cert-maneger,其中v1.7.1就是cert-manager版本。
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.7.1/cert-manager.yaml# 验证对应的资源实例是否安装成功
kubectl get all -n cert-manager
# 看对应的Pod是否被成功创建,可能一开始pod的replicas=0,因为集群节点是image container,下载image可能比较慢
kubectl get pods -n cert-manager
图3 检查cert-manager安装状况

        事实上,有时候,get pods可能会发现无资源,这可能是local cluster有问题,建议先删除,后新建:

# 先删除本地的集群
kind delete cluster --name k8s-local-dev 
# 再新建
## 创建集群配置文件,1个master node,1个worker node。
cat << EOF > kind-clusters-mutil-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
EOF## start to create cluster
kind create cluster --name k8s-local-dev --config ./kind-clusters-mutil-config.yaml## copy kubeconfig to Lens kubecofig
## 不然Lens在Kind集群更新后,配置文件没更新,会打不开
cp ~/.kube/config /mnt/c/Users/${current_user}/.kube/config

2.3.创建一个空的文件夹

# 创建一个空的文件节夹
mkdir create-pod-webhook-demo
# 进入空文件夹
cd create-pod-webhook-demo

2.4. 生成工程框架

# 生成工程框架
kubebuilder init --domain geoff.wh.demo --repo k8s-operator/kubebuilder-webhook-demo

         生成文件目录如下, 此处,我不赘述了,上一篇文章已经有比较清晰的介绍了。

➜  create-pod-webhook-demo tree
.
├── Dockerfile
├── Makefile
├── PROJECT
├── README.md
├── config
│   ├── default
│   │   ├── kustomization.yaml
│   │   ├── manager_auth_proxy_patch.yaml
│   │   └── manager_config_patch.yaml
│   ├── manager
│   │   ├── controller_manager_config.yaml
│   │   ├── kustomization.yaml
│   │   └── manager.yaml
│   ├── prometheus
│   │   ├── kustomization.yaml
│   │   └── monitor.yaml
│   └── rbac
│       ├── auth_proxy_client_clusterrole.yaml
│       ├── auth_proxy_role.yaml
│       ├── auth_proxy_role_binding.yaml
│       ├── auth_proxy_service.yaml
│       ├── kustomization.yaml
│       ├── leader_election_role.yaml
│       ├── leader_election_role_binding.yaml
│       ├── role_binding.yaml
│       └── service_account.yaml
├── go.mod
├── go.sum
├── hack
│   └── boilerplate.go.txt
└── main.go6 directories, 25 files

2.5. 生成核心组件Pod的API

        从这里之前,和创建Operator的步骤基本是一致的,从这里开始,要开始有点不一样了

# 定义API,此处要实现Pod的Webhook,因此直接按Pod的Api创建
kubebuilder create api --group core --version v1 --kind Pod
# 之后的option我们不创建Resource和Controller,因此都选择n

        之后的option我们不创建Resource和Controller,因此都选择n

图3 不创建Resource和Controller,选n

2.6.生成Webhook

# --Kind这里,可以指定现存的PAAS组件,也可以
kubebuilder create webhook --group core --version v1 --kind Pod --defaulting --webhook-version v1

        生成webhook的代码结构如下: 

➜  create-pod-webhook-demo tree
.
├── Dockerfile
├── Makefile
├── PROJECT
├── README.md
├── api
│   └── v1
│       ├── pod_webhook.go
│       └── webhook_suite_test.go
├── bin
│   └── controller-gen
├── config
│   ├── certmanager
│   │   ├── certificate.yaml
│   │   ├── kustomization.yaml
│   │   └── kustomizeconfig.yaml
│   ├── default
│   │   ├── kustomization.yaml
│   │   ├── manager_auth_proxy_patch.yaml
│   │   ├── manager_config_patch.yaml
│   │   ├── manager_webhook_patch.yaml
│   │   └── webhookcainjection_patch.yaml
│   ├── manager
│   │   ├── controller_manager_config.yaml
│   │   ├── kustomization.yaml
│   │   └── manager.yaml
│   ├── prometheus
│   │   ├── kustomization.yaml
│   │   └── monitor.yaml
│   ├── rbac
│   │   ├── auth_proxy_client_clusterrole.yaml
│   │   ├── auth_proxy_role.yaml
│   │   ├── auth_proxy_role_binding.yaml
│   │   ├── auth_proxy_service.yaml
│   │   ├── kustomization.yaml
│   │   ├── leader_election_role.yaml
│   │   ├── leader_election_role_binding.yaml
│   │   ├── role_binding.yaml
│   │   └── service_account.yaml
│   └── webhook
│       ├── kustomization.yaml
│       ├── kustomizeconfig.yaml
│       └── service.yaml
├── go.mod
├── go.sum
├── hack
│   └── boilerplate.go.txt
└── main.go11 directories, 36 files

到这一步之后,我们直接转转入VScode开发

# 当前工程目录打开VScode
code .
图3 转入Vscode进行开发

2.7.开始实现Webhook相关代码

2.7.1.修改相关配置

        修改config/default/kustomize.yaml:

  1. 注释-crd 相关内容,因为我们没有CRD,只是实现Pod webhook
  2. 打开webhook、cert-manager相关配置,如下图所示
  3. 但是要注意属性缩进对齐,不然会报错
图4 修改config/default/kustomize.yaml的配置

        修改config/rdbc/kustomize.yaml:

1.注释 -role,这个也是和CRD相关,因为我们没有,所以注释掉

图4 注释config/rdbc/kustomize.yaml的role配置

        修改config/default/webhookcainjection_patch.yaml:

1.我们为了不搞这么复杂,我们只关心MutatingAdmision,因此可以直接注释掉ValidateAdmission

图5 注释config/default/webhookcainjection_patch.yaml中ValidateAdmission配置

2.7.2.修改代码

        因为核心组件Pod的Webhook和一般的CRD的webhook不一样,此处生成的pod_webhook.go只有Default()这个function,因此,我们需要直接重写整个代码,最重要的是Handle()方法。

        修改api/v1/pod_webhook.go文件:

/*
Copyright 2023.Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/package v1import ("context""encoding/json""fmt""net/http"corev1 "k8s.io/api/core/v1""sigs.k8s.io/controller-runtime/pkg/client"logf "sigs.k8s.io/controller-runtime/pkg/log""sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)// log is for logging in this package.
var podlog = logf.Log.WithName("pod-resource")// 定义核心组件pod的webhook的主struct,类似于java的Class
type PodWebhookMutate struct {Client  client.Clientdecoder *admission.Decoder
}// +kubebuilder:webhook:path=/mutate-core-v1-pod,mutating=true,failurePolicy=fail,sideEffects=None,groups=core,resources=pods,verbs=create;update,versions=v1,name=mpod.kb.io,admissionReviewVersions=v1
func (a *PodWebhookMutate) Handle(ctx context.Context, req admission.Request) admission.Response {pod := &corev1.Pod{}err := a.decoder.Decode(req, pod)if err != nil {return admission.Errored(http.StatusBadRequest, err)}// TODO: 变量marshaledPod是一个Map,可以直接修改pod的一些属性marshaledPod, err := json.Marshal(pod)if err != nil {return admission.Errored(http.StatusInternalServerError, err)}// 打印fmt.Println("======================================================")fmt.Println(string(marshaledPod))return admission.PatchResponseFromRaw(req.Object.Raw, marshaledPod)
}func (a *PodWebhookMutate) InjectDecoder(d *admission.Decoder) error {a.decoder = dreturn nil
}// 注释掉一开始生成的内容
// func (r *Pod) SetupWebhookWithManager(mgr ctrl.Manager) error {
// 	return ctrl.NewWebhookManagedBy(mgr).
// 		For(r).
// 		Complete()
// }// // TODO(user): EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!// var _ webhook.Defaulter = &Pod{}// // Default implements webhook.Defaulter so a webhook will be registered for the type
// func (r *Pod) Default() {
// 	podlog.Info("default", "name", r.Name)// 	// TODO(user): fill in your defaulting logic.
// }

        修改api/v1/xxx_suite_test.go

        全部注释掉,替换一个简单的测试Function。这个测试也是有讲究的,有兴趣可以研究一下。

/*
Copyright 2023.Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/package v1import ("fmt""testing"
)func TestFunc(t *testing.T) {// 打印fmt.Println("this is test function......")
}

        修改main.go文件:

import (......# 增加一个依赖包 v1 "k8s-operator/kubebuilder-webhook-demo/api/v1"......
)
......// 注释掉
// if err = (&corev1.Pod{}).SetupWebhookWithManager(mgr); err != nil {
// 	setupLog.Error(err, "unable to create webhook", "webhook", "Pod")
// 	os.Exit(1)
// }// 增加webhook注册
mgr.GetWebhookServer().Register("/mutate-core-v1-pod", &webhook.Admission{Handler: &v1.PodWebhookMutate{Client: mgr.GetClient()}})......
图6 在main.go注册webhook endpoint

        修改Dockerfile

1.和上面一样,因为我们是创建Pod的Webhook,不需要自定义Controller,注释掉。

图7 注释Dockerfile中关于controller的内容

         修改Makefile文件

增加一个kind-load,方便将image上传到本地集群中

......
# local k8s cluster name
KUBE_CLUSTER = k8s-local-dev 
......
.PHONY: kind-load
kind-load: ## load the local image to the kind clusterkind load docker-image ${IMG} --name ${KUBE_CLUSTER}
......
图8 在Makefile增加kind-load命令

2.7.3.按照最新配置更新yaml

# 更新yaml,这里和之前main.go和xxx_webhook.go的注释有点关,生成的代码是依据注释实现的
make mainfests generate

        这一步比较重要,执行后会生成config/webhook/mainfests.yaml文件,切记执行

2.7.4.集群运行测试

2.7.4.1.修改Makefile文件

# 增加一个kind-load,方便将本地image上传到本地k8s集群中

......
# local k8s cluster name
KUBE_CLUSTER = k8s-local-dev 
......
.PHONY: kind-load
kind-load: ## load the local image to the kind cluster
    kind load docker-image ${IMG} --name ${KUBE_CLUSTER}
......

图9 增加kind-load,可以将image上传到kind创建的集群容器中

2.7.4.2.将应用部署到k8s集群上

以下是部署到集群的步骤:构建镜像、上传到集群容器中、部署

其中IMG是一个可以自行设置镜像名的变量,此处为:k8s-podwebhook-demo:1.0。

按如下命令执行后,即可在k8s集群中看到部署的CRD controller应用。

# 构建镜像(有时候会失败,可能是网络问题,多试几遍),IMG需要指定,不然后面部署还是有问题
make docker-build IMG=k8s-podwebhook-demo:1.0
# local镜像上传到Kind创建的k8s集群所在的所有node中(如果本地是)
make kind-load IMG=k8s-podwebhook-demo:1.0
# 部署controller
make deploy IMG=k8s-podwebhook-demo:1.0

        在这个过程中,bin/目录存在一些二进制的工具包,可以先删除,是之前make deploy时下载的,如果已存在,有可能会报错。

图10 bin目录应该要提前删除kustomize工具
图11 将webhook项目工程直接部署到本地k8s集群中
图12 在Lens上查看被部署到本地k8s集群的webhook应用
图13 在Lens上查看注册的MutatingAdmission实例

         这个MutatingAdmission的作用是注册一个endpoint,使得Pod在被创建后,往这个endpoint发送一个https请求(这也是为什么需要证书的原因)。事实上main.go中

// 增加webhook注册
mgr.GetWebhookServer().Register("/mutate-core-v1-pod", &webhook.Admission{Handler: &v1.PodWebhookMutate{Client: mgr.GetClient()}})

 这句就类似java controller,接受这个https的请求。

2.7.5.测试

2.7.5.1.准备测试Deployment的yaml配置

        这里需要说明,因为我们这里创建的是Pod Webhook,但是一般而言,我们不直接创建Pod,而是创建一个deployment,因为deployment最后也是会创建pod的。当然直接创建Pod也是可以的,这里创建一个pod-demo-ngnix.yaml文件去创建一个简单的pod,这个Pod只是一个基本的Ngnix镜像。

cat <<EOF >pod-demo-ngnix.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-demo-ngnixlabels:role: myrole
spec:containers:- name: webimage: nginxports:- name: webcontainerPort: 80protocol: TCP
EOF

2.7.5.2.部署这个Pod demo

# 部署这个pod demo实例
kubectl apply -f ./pod-demo-ngnix.yaml
图14 在创建pod的时候,webhook接受到了请求并在日志中体现

 2.7.5.3.停止测试

# 删除测试Pod
kubectl delete -f ./pod-demo-ngnix.yaml
# undeploy这个webhook
make undeploy

参考文章

8. kubebuilder 进阶: webhook - Mohuishou

使用kubebuilder开发kubernetes核心资源的webhook | 老 宋

调试运行中的 Pod | Kubernetes

相关文章:

创建K8s pod Webhook

目录 1.前提条件 2.开始创建核心组件Pod的Webhook 2.1.什么是Webhook 2.2.在本地k8s集群安装cert-manager 2.3.创建一个空的文件夹 2.4. 生成工程框架 2.5. 生成核心组件Pod的API 2.6.生成Webhook 2.7.开始实现Webhook相关代码 2.7.1.修改相关配置 2.7.2.修改代码 2…...

抓包-要抓取Spring Boot应用程序的请求

要抓取Spring Boot应用程序的请求&#xff0c;可以按照以下步骤进行操作&#xff1a; 1. 确保你已经按照之前提到的方法设置了Charles代理&#xff0c;并在Charles的SSL代理设置中添加了Spring Boot应用程序的域名。 2. 在Spring Boot应用程序的代码中&#xff0c;添加以下配…...

jmeter+nmon+crontab简单的执行接口定时压测

一、概述 临时接到任务要对系统的接口进行压测&#xff0c;上面的要求就是&#xff1a;压测&#xff0c;并发2000 在不熟悉系统的情况下&#xff0c;按目前的需求&#xff0c;需要做的步骤&#xff1a; 需要有接口脚本需要能监控系统性能需要能定时执行脚本 二、观察 >针…...

ZooKeeper基础命令和Java客户端操作

1、zkCli的常用命令操作 &#xff08;1&#xff09;Help &#xff08;2&#xff09;ls 使用 ls 命令来查看当前znode中所包含的内容 &#xff08;3&#xff09;ls2查看当前节点数据并能看到更新次数等数据 &#xff08;4&#xff09;stat查看节点状态 &#xff08;5&#xf…...

【数据分享】2000-2020年全球人类足迹数据(无需转发\免费获取)

人类足迹(Human Footprint)是生态过程和自然景观变化对生态环境造成的压力&#xff0c;是世界各国对生物多样性和生态保护的关注重点。那如何才能获取长时间跨度的人类足迹时空数据呢&#xff1f; 之前我们分享了来自于中国农业大学土地科学与技术学院的城市环境监测及建模&am…...

基于机器学习的fNIRS信号质量控制方法

摘要 尽管功能性近红外光谱(fNIRS)在神经系统研究中的应用越来越广泛&#xff0c;但fNIRS信号处理仍未标准化&#xff0c;并且受到经验和手动操作的高度影响。在任何信号处理过程的开始阶段&#xff0c;信号质量控制(SQC)对于防止错误和不可靠结果至关重要。在fNIRS分析中&…...

分布式锁的三种实现方式是什么?

分布式锁三种实现方式&#xff1a; 基于数据库实现分布式锁&#xff1b;基于缓存&#xff08;Redis等&#xff09;实现分布式锁&#xff1b;基于Zookeeper实现分布式锁&#xff1b; 一&#xff0c; 基于数据库实现分布式锁 悲观锁 利用select … where … for update 排他锁…...

华为云软件精英实战营——感受软件改变世界,享受Coding乐趣

机器人已经在诸多领域显现出巨大的商业价值&#xff0c;华为云计算致力于以云助端的方式为机器人产业带来全新机会 如果您是开发爱好者&#xff0c;想了解华为云&#xff0c;想和其他自由开发者交流经验&#xff1b; 如果您是学生&#xff0c;想和正在从事软件开发行业的大佬…...

贪心算法总结篇

文章转自代码随想录 贪心算法总结篇 我刚刚开始讲解贪心系列的时候就说了&#xff0c;贪心系列并不打算严格的从简单到困难这么个顺序来讲解。 因为贪心的简单题可能往往过于简单甚至感觉不到贪心&#xff0c;如果我连续几天讲解简单的贪心&#xff0c;估计录友们一定会不耐…...

ICCV 2023 | 港中文MMLab: 多帧光流估计模型VideoFlow,首次实现亚像素级别误差

本文提出了一个多帧光流估计模型 VideoFlow&#xff0c;旨在充分挖掘视频中的时序信息和运动规律&#xff0c;避免当前主流方法只以两帧图片作为输入而面临的信息瓶颈&#xff0c;显著提升了光流估计的性能。 在公开的 Sintel Bechmark 上&#xff0c;VideoFlow 在 Clean 和 Fi…...

【python爬虫】—图片爬取

图片爬取 需求分析Python实现 需求分析 从https://pic.netbian.com/4kfengjing/网站爬取图片&#xff0c;并保存 Python实现 获取待爬取网页 def get_htmls(pageslist(range(2, 5))):"""获取待爬取网页"""pages_list []for page in pages:u…...

自动化运维工具—Ansible

一、Ansible概述1.1 Ansible是什么1.2 Ansible的特性1.3 Ansible的特点1.4 Ansible数据流向 二、Ansible 环境安装部署三、Ansible 命令行模块&#xff08;1&#xff09;command 模块&#xff08;2&#xff09;shell 模块&#xff08;3&#xff09;cron 模块&#xff08;4&…...

uniapp 安卓平台签名证书(.keystore)生成

安装JRE环境 下载jre安装包&#xff1a;https://www.oracle.com/java/technologies/downloads/#java8安装jre安装包时&#xff0c;记录安装目录(例:C:\Program Files\Java\jdk-20)打开命令行&#xff08;cmd&#xff09;&#xff0c;将JRE安装路径添加到系统环境变量 d: se…...

缓存中间件Redis常考知识点

缓存中间件Redis常考知识点 1 什么是RDB和AOF2 Redis的过期键的删除策略3 简述Redis事务实现4 Redis 主从复制的核⼼原理5 Redis有哪些数据结构&#xff1f;分别有哪些典型的应⽤场景&#xff1f;6 Redis分布式锁底层是如何实现的&#xff1f;7 Redis主从复制的核⼼原理8 Redis…...

detour编译问题及导入visual studio

Detours是经过微软认证的一个开源Hook库&#xff0c;Detours在GitHub上&#xff0c;网址为 https://github.com/Microsoft/Detours 注意版本不一样的话也是会出问题的&#xff0c;因为我之前是vs2022的所以之前的detours.lib不能使用&#xff0c;必须用对应版本的x64 Native To…...

江西武功山旅游攻略(周末两日游)

一、 往返路线 1: 出发路线 周五晚上上海出发坐火车&#x1f684;到江西萍乡(11.5小时,卧铺550左右) 打车到江西武功山景区,120-150元左右,人均30元,1小时10分左右到达 或者 &#x1f697;到达萍乡北之后 出站后步行200米到长途汽车站&#xff0c;乘旅游巴士直达武功山游…...

Django静态文件媒体文件文件上传

文章目录 一、静态文件和媒体文件1.在django中使用静态文件实践2.在django中使用媒体文件 二、文件上传单文件上传实践多文件上传 一、静态文件和媒体文件 媒体文件: 用户上传的文件&#xff0c;叫做media 静态文件:存放在服务器的css,js,image,font等 叫做static1.在django中…...

mysql 分库分表实现思路

MySQL的分库分表是一种常用的数据库拆分方案&#xff0c;它可以提高数据库的性能和扩展性。下面是一般的实现步骤&#xff1a; 数据库设计&#xff1a;首先&#xff0c;需要对数据库进行良好的设计。确定要分库分表的实体和关系&#xff0c;并根据业务需求进行合理的拆分。 数…...

Android深思如何防止快速点击

前言 其实快速点击是个很好解决的问题&#xff0c;但是如何优雅的去解决确是一个难题&#xff0c;本文主要是记录一些本人通过解决快速点击的过程中脑海里浮现的一些对这个问题的深思。 作者&#xff1a;流浪汉kylin 链接&#xff1a;https://juejin.cn/post/7197337416096055…...

PHP自己的框架cookie()使用(完善篇七)

1、PHP自己的框架cookie() 2、cookie类&#xff08;CookieBase.php&#xff09; <?php class CookieBase {/*** 设置cookie*/public static function set($name, $value, $expire 3600, $path , $domain , $secure false, $httponly false) {setcookie($name, $valu…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...