【k8s深入理解之 Scheme 补充-2】理解 register.go 暴露的 AddToScheme 函数
AddToScheme 函数
- AddToScheme 就是为了对外暴露,方便别人调用,将当前Group组的信息注册到其 Scheme 中,以便了解该 Group 组的数据结构,用于后续处理
项目 | 版本 | 用途 | 使用场景 |
---|---|---|---|
k8s.io/api | V1 | 注册资源某一外部版本数据结构 (只包含v1 版本的基础资源数据结构,不包含版本转换函数) | 适用于指定v1版本编写控制器 |
k8s.io/kubernetes | V1 | 注册默认值填充函数和内外版本转换函数 (默认值填充函数,v1和internal的转换函数 zz_generated.conversion.go) | 不适用于单独编写控制器 |
k8s.io/kubernetes | internal | 注册资源内部版本数据结构 (所有版本的中转者,记录的资源数据结构相比于单一版本更丰富,不包含版本转换函数) | 适用于核心组件控制器,可以适配多版本 |
api 项目中 v1 版本的 AddToScheme 函数
register.go
中的AddToScheme
是专门设计为供外部调用的接口,用于将当前 API 组的所有资源类型和相关逻辑(如默认值和转换函数)注册到全局 Scheme 中。它通过封装复杂的注册逻辑,提供了一个统一、简单且易于使用的接口,大大降低了集成的复杂性。- 详细了解,见下面附录
// mod/k8s.io/api@v0.29.0/apps/v1/register.go
package v1import (metav1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/apimachinery/pkg/runtime""k8s.io/apimachinery/pkg/runtime/schema"
)// GroupName is the group name use in this package
const GroupName = "apps"// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {return SchemeGroupVersion.WithResource(resource).GroupResource()
}var (// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)localSchemeBuilder = &SchemeBuilder// 就是此函数,之后编写别的控制器需要此资源时,可以用过调用该 package-name.AddToScheme 就能了解到该 apps/v1 组的所有资源和对应的数据结构,用于逻辑处理AddToScheme = localSchemeBuilder.AddToScheme
)// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {scheme.AddKnownTypes(SchemeGroupVersion,&Deployment{},&DeploymentList{},&StatefulSet{},&StatefulSetList{},&DaemonSet{},&DaemonSetList{},&ReplicaSet{},&ReplicaSetList{},&ControllerRevision{},&ControllerRevisionList{},)metav1.AddToGroupVersion(scheme, SchemeGroupVersion)return nil
}
Kubernetes 项目中的 v1 版本的 AddToScheme 函数
// 路径 mod/k8s.io/kubernetes@v1.29.0/pkg/apis/apps/v1/register.go
package v1import (appsv1 "k8s.io/api/apps/v1""k8s.io/apimachinery/pkg/runtime/schema"
)// GroupName is the group name use in this package
const GroupName = "apps"// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {return SchemeGroupVersion.WithResource(resource).GroupResource()
}var (localSchemeBuilder = &appsv1.SchemeBuilderAddToScheme = localSchemeBuilder.AddToScheme
)func init() {// We only register manually written functions here. The registration of the// generated functions takes place in the generated files. The separation// makes the code compile even when the generated files are missing.localSchemeBuilder.Register(addDefaultingFuncs)
}
- 相比于 api 项目,同级目录还存在个
zz_generated.conversion.go
函数
// 路径 mod/k8s.io/kubernetes@v1.29.0/pkg/apis/apps/v1/zz_generated.conversion.go
func init() {localSchemeBuilder.Register(RegisterConversions)
}// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {if err := s.AddGeneratedConversionFunc((*v1.ControllerRevision)(nil), (*apps.ControllerRevision)(nil), func(a, b interface{}, scope conversion.Scope) error {return Convert_v1_ControllerRevision_To_apps_ControllerRevision(a.(*v1.ControllerRevision), b.(*apps.ControllerRevision), scope)}); err != nil {return err}if err := s.AddGeneratedConversionFunc((*apps.ControllerRevision)(nil), (*v1.ControllerRevision)(nil), func(a, b interface{}, scope conversion.Scope) error {return Convert_apps_ControllerRevision_To_v1_ControllerRevision(a.(*apps.ControllerRevision), b.(*v1.ControllerRevision), scope)}); err != nil {return err}// ... 省略若干 v1版本和内部版本的转换函数
}
内部版本的 AddToScheme 函数
// 路径 mod/k8s.io/kubernetes@v1.29.0/pkg/apis/apps/register.go
package appsimport ("k8s.io/apimachinery/pkg/runtime""k8s.io/apimachinery/pkg/runtime/schema""k8s.io/kubernetes/pkg/apis/autoscaling"
)var (// SchemeBuilder stores functions to add things to a scheme.SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)// AddToScheme applies all stored functions t oa scheme.AddToScheme = SchemeBuilder.AddToScheme
)// GroupName is the group name use in this package
const GroupName = "apps"// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {return SchemeGroupVersion.WithKind(kind).GroupKind()
}// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {return SchemeGroupVersion.WithResource(resource).GroupResource()
}// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {// TODO this will get cleaned up with the scheme types are fixedscheme.AddKnownTypes(SchemeGroupVersion,&DaemonSet{},&DaemonSetList{},&Deployment{},&DeploymentList{},&DeploymentRollback{},&autoscaling.Scale{},&StatefulSet{},&StatefulSetList{},&ControllerRevision{},&ControllerRevisionList{},&ReplicaSet{},&ReplicaSetList{},)return nil
}
附录1 | 理解 AddToScheme
是的,register.go
中的 AddToScheme
是专门供外部调用的接口。它的主要作用是将当前 API 组的所有资源类型、元数据、版本等注册到全局的 runtime.Scheme
,从而支持这些类型的序列化、反序列化、默认值设置和版本转换等操作。
AddToScheme
的特点
- 封装性:
- 它封装了资源类型注册的细节,调用者只需要知道
AddToScheme
能将特定 API 组的资源类型注册到Scheme
中,而无需了解具体的注册过程。
- 它封装了资源类型注册的细节,调用者只需要知道
- 便于外部调用:
- 通过导出
AddToScheme
,外部模块可以轻松将该 API 组集成到自己的项目中。
- 通过导出
- 统一注册点:
- 将所有相关类型(如
Deployment
、StatefulSet
)集中在一个函数中注册,方便维护和使用。
- 将所有相关类型(如
调用场景
在 Kubernetes 源码中
AddToScheme
通常会被 Kubernetes 的核心组件或工具(如 kubectl
、API server)调用,用于支持资源的处理:
import ("example.com/project/pkg/apis/apps/v1""k8s.io/apimachinery/pkg/runtime"
)func main() {scheme := runtime.NewScheme()v1.AddToScheme(scheme) // 注册 apps/v1 的资源类型
}
在 Controller 开发中
在开发 Kubernetes Controller 时,必须将自定义资源类型注册到控制器的 Scheme 中:
import (appsv1 "example.com/project/pkg/apis/apps/v1"ctrl "sigs.k8s.io/controller-runtime"
)func main() {scheme := ctrl.GetScheme()appsv1.AddToScheme(scheme) // 注册 apps/v1 类型到 controller-runtime 的 Scheme
}
在测试代码中
测试代码中需要模拟某些资源的序列化或反序列化操作时,也需要调用 AddToScheme
:
import ("example.com/project/pkg/apis/apps/v1""k8s.io/apimachinery/pkg/runtime"
)func TestSomething(t *testing.T) {scheme := runtime.NewScheme()_ = v1.AddToScheme(scheme) // 注册类型,确保测试环境中可以处理这些资源
}
设计目的
- 抽象和复用:
- 将复杂的注册过程(如
AddKnownTypes
和AddToGroupVersion
的调用)封装为一个简单的函数供外部使用。
- 将复杂的注册过程(如
- 统一接口:
- Kubernetes 中不同 API 组都会导出类似的
AddToScheme
函数,外部调用者只需要知道这个约定即可注册所需的类型。
- Kubernetes 中不同 API 组都会导出类似的
- 灵活性:
- 通过
SchemeBuilder
的机制,支持动态添加注册逻辑,便于扩展。
- 通过
总结
register.go
中的 AddToScheme
是专门设计为供外部调用的接口,用于将当前 API 组的所有资源类型和相关逻辑(如默认值和转换函数)注册到全局 Scheme 中。它通过封装复杂的注册逻辑,提供了一个统一、简单且易于使用的接口,大大降低了集成的复杂性。
附录2 | api 和 kubernetes 项目同版本的 register.go 文件有什么不同
Kubernetes 的 api
项目和 kubernetes
项目中的 register.go
文件不一致,主要是因为两者承担的职责不同,它们分属不同的模块和依赖层级。以下是原因和区别的详细分析:
1. 两者的职责和用途
k8s.io/api
- 功能:
- 是一个独立的 API 定义项目,主要用于定义 Kubernetes 的资源结构和常量。
- 提供资源类型的核心定义,如
Deployment
、StatefulSet
。 - 这些定义是“纯粹的”,不包含与 Kubernetes 内部运行时相关的逻辑。
- 特点:
- 仅包含资源的结构定义和自动生成的代码(如
deepcopy
、openapi
)。 - 用于支持外部项目,如客户端工具(
client-go
)或自定义控制器。
- 仅包含资源的结构定义和自动生成的代码(如
kubernetes
- 功能:
- 是 Kubernetes 主项目,包含所有 Kubernetes 的核心逻辑,包括控制器、调度器等。
- 除了使用
k8s.io/api
提供的资源定义外,还需要注册这些资源到runtime.Scheme
中。
- 特点:
- 包含具体的 Scheme 注册逻辑和运行时依赖。
- 需要将资源绑定到 Kubernetes 的控制器和调度器逻辑。
2. 两者 register.go
的区别
k8s.io/api
中的 register.go
- 内容:
- 提供
GroupName
和SchemeGroupVersion
等常量,用于定义 API 组和版本。 - 仅导出
SchemeBuilder
和AddToScheme
,用于资源类型注册。 - 不包含
runtime.Scheme
的直接引用,也不执行实际的资源注册。
- 提供
- 目的:
- 定义一个通用的接口,允许其他模块或项目将这些资源类型注册到自己的
runtime.Scheme
中。 - 不直接注册到运行时,保持模块的轻量级和独立性。
- 定义一个通用的接口,允许其他模块或项目将这些资源类型注册到自己的
var (SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)AddToScheme = SchemeBuilder.AddToScheme
)
kubernetes
中的 register.go
- 内容:
- 从
k8s.io/api
导入资源类型和SchemeBuilder
。 - 在主项目中,结合 Kubernetes 内部逻辑,扩展资源注册的功能。
- 增加了
localSchemeBuilder
和其他运行时相关逻辑。
- 从
- 目的:
- 扩展 API 定义,并将其绑定到 Kubernetes 主项目的运行时环境。
- 实现默认值填充(
addDefaultingFuncs
)、字段标签转换等额外功能。
localSchemeBuilder.Register(addDefaultingFuncs)
3. 为什么有分工?
- 模块化设计:
- Kubernetes 将
k8s.io/api
和kubernetes
主项目分开,是为了实现模块化和解耦。 - 资源定义是基础层,而运行时逻辑是上层依赖。
- Kubernetes 将
- 减少依赖复杂度:
k8s.io/api
是许多外部项目(如client-go
)的直接依赖,如果包含运行时逻辑会导致循环依赖。- 通过分离,
k8s.io/api
只关注定义,避免了复杂的依赖链。
- 适配不同场景:
k8s.io/api
可以被外部项目独立使用,而不需要整个 Kubernetes 主项目。kubernetes
主项目需要额外的运行时逻辑,处理更复杂的功能。
4. 实际工作流
在 k8s.io/api
中
- 提供了
SchemeBuilder
和AddToScheme
,供外部调用。 - 示例:
import (appsv1 "k8s.io/api/apps/v1""k8s.io/apimachinery/pkg/runtime"
)var scheme = runtime.NewScheme()func main() {appsv1.AddToScheme(scheme) // 注册 API 定义到 Scheme
}
在 kubernetes
中
- 除了注册类型,还会添加其他自定义逻辑,如默认值和字段标签转换。
- 示例:
import ("k8s.io/kubernetes/pkg/apis/apps/v1"
)func main() {v1.AddToScheme(scheme) // 注册到 Kubernetes 主项目的 Scheme
}
5. 总结
特性 | k8s.io/api | kubernetes |
---|---|---|
主要职责 | 定义 API 类型和常量 | 运行时绑定和扩展逻辑 |
是否包含运行时逻辑 | 否 | 是 |
导出内容 | SchemeBuilder 、AddToScheme | AddToScheme 、localSchemeBuilder 、扩展功能 |
是否直接注册 Scheme | 否 | 是 |
使用场景 | 提供轻量级定义供外部依赖 | Kubernetes 主项目内部使用 |
两者的设计目标不同,k8s.io/api
是独立的资源定义库,而 kubernetes
则是完整的实现与运行时逻辑结合。
附录3 | 以 apps 组说明同版本两个 register.go 的 AddToScheme 调用关系
以 apps
组为例,Kubernetes 的 Scheme
是通过逐层调用 AddToScheme
函数来注册 API 类型的。这两个 AddToScheme
函数分别来自:
k8s.io/api/apps/v1
:定义 API 类型(如Deployment
,StatefulSet
等)并提供注册方法。kubernetes/pkg/apis/apps/v1
:扩展 API 定义,并添加运行时相关功能(如默认值填充、字段标签转换等)。
以下是详细的调用流程和逻辑分析:
1. 两个 AddToScheme
的来源
k8s.io/api/apps/v1
- 定义
SchemeBuilder
和AddToScheme
,注册资源的基本定义到Scheme
中。 - 核心功能是调用
runtime.Scheme.AddKnownTypes
注册类型。
kubernetes/pkg/apis/apps/v1
-
主要在 Kubernetes 主项目中使用,扩展了
AddToScheme
,增加了:- 默认值函数(defaulter)。
- 字段标签转换(field label conversion functions)。
- 其他与运行时相关的功能。
2. 调用的层次和流程
在 Kubernetes 主项目中,Scheme
的初始化流程会递归调用所有注册函数,包括两个 AddToScheme
函数。以下是以 apps
组为例的调用流程:
第一步:调用 k8s.io/api/apps/v1
的 AddToScheme
Kubernetes 主项目会先调用 k8s.io/api/apps/v1
提供的 AddToScheme
:
import (appsv1 "k8s.io/api/apps/v1"
)var scheme = runtime.NewScheme()func main() {// 注册 apps 组的资源类型appsv1.AddToScheme(scheme)
}
此时的逻辑在 k8s.io/api/apps/v1/register.go
中:
var SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
var AddToScheme = SchemeBuilder.AddToSchemefunc addKnownTypes(scheme *runtime.Scheme) error {scheme.AddKnownTypes(SchemeGroupVersion,&Deployment{},&DeploymentList{},&StatefulSet{},&StatefulSetList{},&DaemonSet{},&DaemonSetList{},&ReplicaSet{},&ReplicaSetList{},&ControllerRevision{},&ControllerRevisionList{},)metav1.AddToGroupVersion(scheme, SchemeGroupVersion) // 注册元数据return nil
}
注册的内容主要是:
- 资源类型:如
Deployment
,StatefulSet
。 - 元数据:通过
metav1.AddToGroupVersion
注册通用的元信息类型。
第二步:调用 kubernetes/pkg/apis/apps/v1
的 AddToScheme
在 Kubernetes 主项目中,还需要进一步扩展 apps
的资源定义。这时会调用 kubernetes/pkg/apis/apps/v1
中的 AddToScheme
:
import (appsinternalv1 "k8s.io/kubernetes/pkg/apis/apps/v1"
)func main() {// 扩展 apps 组的功能appsinternalv1.AddToScheme(scheme)
}
此时的逻辑在 kubernetes/pkg/apis/apps/v1/register.go
中:
var localSchemeBuilder = &appsv1.SchemeBuilder // 直接复用 k8s.io/api/apps/v1 的 SchemeBuilder
var AddToScheme = localSchemeBuilder.AddToSchemefunc init() {// 注册运行时相关的函数localSchemeBuilder.Register(addDefaultingFuncs)
}
这里做了以下扩展:
- 注册默认值函数(
addDefaultingFuncs
)。 - 为
apps
资源添加额外的运行时功能。
3. 总结:两者如何组合
在 Kubernetes 主项目中,Scheme
会分别调用 k8s.io/api/apps/v1
和 kubernetes/pkg/apis/apps/v1
的 AddToScheme
,实现如下目标:
-
基础类型注册:
k8s.io/api/apps/v1
提供的AddToScheme
确保apps
组的资源类型被正确注册到Scheme
中。- 这是所有调用的基础。
-
运行时扩展:
kubernetes/pkg/apis/apps/v1
提供的AddToScheme
在基础类型的注册之上,添加运行时功能,如:- 默认值填充(Defaulting)。
- 字段标签转换(Field Label Conversion)。
调用顺序如下:
- 首先调用
k8s.io/api/apps/v1
的AddToScheme
。 - 其次调用
kubernetes/pkg/apis/apps/v1
的AddToScheme
扩展功能。
4. 实际调用示例
完整流程可能如下:
import (appsv1 "k8s.io/api/apps/v1"appsinternalv1 "k8s.io/kubernetes/pkg/apis/apps/v1""k8s.io/apimachinery/pkg/runtime"
)func main() {scheme := runtime.NewScheme()// 注册基础类型if err := appsv1.AddToScheme(scheme); err != nil {panic(err)}// 扩展运行时功能if err := appsinternalv1.AddToScheme(scheme); err != nil {panic(err)}// 验证是否注册成功gvk := schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}obj, err := scheme.New(gvk)if err != nil {panic(err)}fmt.Printf("Registered object: %T\n", obj)
}
5. 注册的依赖关系
步骤 | 来源 | 功能 |
---|---|---|
appsv1.AddToScheme | k8s.io/api/apps/v1 | 注册基础资源类型和元信息 |
appsinternalv1.AddToScheme | kubernetes/pkg/apis/apps/v1 | 扩展运行时功能,添加默认值、字段转换等 |
通过这种分层调用,Kubernetes 可以灵活地管理 API 类型的定义和运行时行为。
附录4 | 编写控制器时采用 api 项目的 AddToScheme 还是 kubernetes 项目 internal 版本的 AddToScheme
在编写 Kubernetes 控制器时,通常需要根据具体的需求选择调用 api
项目中的 AddToScheme
或 kubernetes
项目中的 internal
版本的 AddToScheme
。以下是两者的区别和使用场景分析:
1. 两种 AddToScheme 的区别
api
项目中的 AddToScheme
-
来源:
k8s.io/api
(例如k8s.io/api/apps/v1
)。 -
内容
- 提供公开的 API 类型定义。
- 注册外部(版本化)资源类型(如
apps/v1.Deployment
)。
-
特点
- 独立于 Kubernetes 核心实现,可被任何第三方控制器、客户端使用。
- 稳定性高,版本化清晰。
-
使用场景
- 第三方控制器开发时优先使用。
- 通过 Kubernetes API 与集群交互的场景。
kubernetes
项目中的 AddToScheme
-
来源:
k8s.io/kubernetes/pkg/apis
(例如k8s.io/kubernetes/pkg/apis/apps/v1
)。 -
内容
- 包含内部资源类型(internal type)。
- 提供额外的运行时功能,如默认值设置(defaulting)、版本转换(conversion)等。
-
特点
- 依赖 Kubernetes 核心实现,不能单独使用。
- 包含内部类型(
internal
),需要版本转换才能与外部 API 交互。
-
使用场景
- Kubernetes 核心组件(如控制器管理器、调度器)中使用。
- 需要直接操作内部类型的场景。
2. 控制器开发中如何选择
一般情况:使用 k8s.io/api
-
推荐做法:在控制器开发中,优先使用
k8s.io/api
中的AddToScheme
,因为:- 提供的是外部版本的资源类型,直接与 Kubernetes API 交互。
- 更加通用,易于维护,适用于所有 Kubernetes 集群。
-
示例:
import ("k8s.io/api/apps/v1""k8s.io/apimachinery/pkg/runtime" )var scheme = runtime.NewScheme()func init() {// 注册外部版本的 apps 组资源if err := v1.AddToScheme(scheme); err != nil {panic(err)} }
特殊情况:使用 kubernetes
项目中的 internal 版本
-
适用场景:
- 需要内部类型的性能优化:如果控制器需要直接操作 Kubernetes 内部类型(internal types),例如在同一个进程内执行大量的内部操作,可以使用
internal
版本。 - 与 Kubernetes 核心组件的深度集成:如自定义
ControllerManager
,在直接调用内部组件时可能需要使用internal
版本的AddToScheme
。
- 需要内部类型的性能优化:如果控制器需要直接操作 Kubernetes 内部类型(internal types),例如在同一个进程内执行大量的内部操作,可以使用
-
注意事项:
- 使用
internal
类型时,需要手动处理版本转换,将其转换为外部类型与 API Server 交互。 kubernetes/pkg/apis
依赖于 Kubernetes 主项目,控制器无法独立运行。
- 使用
-
示例:
import ("k8s.io/kubernetes/pkg/apis/apps""k8s.io/apimachinery/pkg/runtime" )var scheme = runtime.NewScheme()func init() {// 注册内部版本的 apps 组资源if err := apps.AddToScheme(scheme); err != nil {panic(err)} }
3. 推荐做法
优先使用 k8s.io/api
-
理由
- 更加通用,独立性高,不依赖 Kubernetes 源码。
- 版本稳定,减少与 Kubernetes 主项目版本变化的耦合。
- 适用于第三方控制器开发和部署。
仅在必要时使用 kubernetes/pkg/apis
-
理由
- 使用
internal
版本需要处理更多的兼容性问题(版本转换等)。 - 仅在需要直接操作 Kubernetes 内部类型,或开发 Kubernetes 核心组件时才考虑使用。
- 使用
4. 典型场景举例
使用 k8s.io/api
(推荐)
开发一个管理 Deployment
的控制器,直接与 Kubernetes API Server 交互:
import (appsv1 "k8s.io/api/apps/v1""k8s.io/apimachinery/pkg/runtime""k8s.io/client-go/kubernetes/scheme"
)func main() {// 初始化 SchemelocalScheme := runtime.NewScheme()appsv1.AddToScheme(localScheme)// 与 API Server 交互// 例如:监听 Deployment 资源的变化
}
使用 kubernetes/pkg/apis
(仅在核心组件开发中)
扩展 Kubernetes 核心控制器管理器,自定义默认值或内部优化:
import (appsinternal "k8s.io/kubernetes/pkg/apis/apps""k8s.io/apimachinery/pkg/runtime"
)func main() {// 注册内部版本资源scheme := runtime.NewScheme()appsinternal.AddToScheme(scheme)// 使用内部版本操作internalDeployment := &appsinternal.Deployment{}// 操作 internalDeployment 逻辑
}
5. 总结
场景 | 使用的 AddToScheme 来源 | 理由 |
---|---|---|
第三方控制器开发 | k8s.io/api | 独立性高,直接操作外部版本资源,易于与 API Server 交互。 |
Kubernetes 核心组件扩展 | kubernetes/pkg/apis | 内部类型性能更高,适合深度定制和运行时扩展功能。 |
操作需要运行时默认值或内部结构的资源 | kubernetes/pkg/apis (需转换外部类型) | 提供默认值填充、版本转换等运行时功能,但需要处理额外的兼容性问题。 |
在实际开发中,优先使用外部版本(k8s.io/api
),仅在必要时才使用 internal
类型(kubernetes/pkg/apis
)。
相关文章:
【k8s深入理解之 Scheme 补充-2】理解 register.go 暴露的 AddToScheme 函数
AddToScheme 函数 AddToScheme 就是为了对外暴露,方便别人调用,将当前Group组的信息注册到其 Scheme 中,以便了解该 Group 组的数据结构,用于后续处理 项目版本用途使用场景k8s.io/apiV1注册资源某一外部版本数据结构࿰…...
uni-app写的微信小程序每次换账号登录时出现缓存上一个账号数据的问题
uni-app写的微信小程序每次更换另外账号登录时出现缓存上一个账号数据的问题? 清除缓存数据:在 onShow 钩子中,我们将 powerStations、list 和 responseRoles 的值重置为初始状态,以清除之前的缓存数据。重新获取数据:…...
数据分析流程中的Lambda架构,以及数据湖基于Hadoop、Spark的实现
文章目录 一、Lambda架构1、Lambda的三层架构2、简单解释:3、Lambda架构的优缺点 二、数据湖基于Hadoop、Spark的实现1、架构2、数据管理(存储层的辅助功能) 一、Lambda架构 1、Lambda的三层架构 Batch View(批处理视图层&#…...
Android 原生解析 Json 字符串
Android 原生解析 JSON 字符串 1. JSON 基础2. Android 原生 JSON 解析方法2.1 解析 JSON 字符串到 JSONObject关键方法 2.2 解析 JSON 数组到 JSONArray关键方法 2.3 解析嵌套的 JSON 对象 3. 处理异常4. 总结 在 Android 开发中,我们经常需要从服务器获取 JSON 格…...
Windsurf可以上传图片开发UI了
背景 曾经羡慕Cursor的“画图”开发功能,这不Windsurf安排上了。 Upload Images to Cascade Cascade now supports uploading images on premium models Ask Cascade to build or tweak UI from on image upload New keybindings Keybindings to navigate betwe…...
Qt UI设计 菜单栏无法输入名字
在UI界面“在这里输入”,直接双击填写名称,无论是中文还是英文都没有反应。解决方案 2个: 1.双击“在这里输入之后”,在可编辑状态下,空格→enter键,然后在右下角属性框的title中直接填写中文或英文名&…...
blender 视频背景
准备视频文件 首先,确保你有想要用作背景的视频文件。视频格式最好是 Blender 能够很好兼容的,如 MP4 等常见格式。 创建一个新的 Blender 场景或打开现有场景 打开 Blender 软件后,你可以新建一个场景(通过点击 “文件” - “新建…...
【python】OpenCV—Tracking(10.5)—dlib
文章目录 1、功能描述2、代码实现3、效果展示4、完整代码5、涉及到的库函数dlib.correlation_tracker() 6、参考 1、功能描述 基于 dlib 库,实现指定类别的目标检测和单目标跟踪 2、代码实现 caffe 模型 https://github.com/MediosZ/MobileNet-SSD/tree/master/…...
音视频入门基础:MPEG2-TS专题(9)——FFmpeg源码中,解码TS Header的实现
一、引言 FFmpeg源码对MPEG2-TS传输流/TS文件解复用时,在通过read_packet函数读取出一个transport packet后,会调用handle_packet函数来处理该transport packet: static int handle_packets(MpegTSContext *ts, int64_t nb_packets) { //..…...
解决“磁盘已插上,但Windows系统无法识别“问题
电脑上有2块硬盘,一块是500GB的固态硬盘,另一块是1000GB的机械硬盘,按下开机键,发现500G的固态硬盘识别了,但1000GB的机械硬盘却无法识别。后面为了描述方便,将"500GB的固态硬盘"称为X盘…...
论文笔记-WWW2024-ClickPrompt
论文笔记-WWW2024-ClickPrompt: CTR Models are Strong Prompt Generators for Adapting Language Models to CTR Prediction ClickPrompt: CTR模型是大模型适配CTR预测任务的强大提示生成器摘要1.引言2.预备知识2.1传统CTR预测2.2基于PLM的CTR预测 3.方法3.1概述3.2模态转换3.…...
53 基于单片机的8路抢答器加记分
目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 首先有三个按键 分别为开始 暂停 复位,然后八个选手按键,开机显示四条杠,然后按一号选手按键,数码管显示30,这…...
【java数据结构】二叉树OJ题
【java数据结构】二叉树OJ题 一、检查两颗树是否相同二、另一颗树的子树三、翻转二叉树四、对称二叉树五、判断一颗二叉树是否是平衡二叉树六、给定一个二叉树, 找到该树中两个指定节点的最近公共祖先七、根据一棵树的前序遍历与中序遍历构造二叉树练习:八、二叉树前…...
IIC和SPI的时序图
SCL的变化快慢决定了通信速率,当SCL为低电平的时候,无论SDA是1还是0都不识别: ACK应答:当从设备为低电平的时候识别为从设备有应答: 谁接收,谁应答: 起始位和停止位: IIC的时序图&am…...
MySQL数据库表的操作
1、总述 今天我跟大家分享MySQL数据库中表的创建,查看,修改,删除。 2、创建表 create table table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎; 说明࿱…...
.net core 创建linux服务,并实现服务的自我更新
目录 创建服务创建另一个服务,用于执行更新操作给你的用户配置一些systemctl命令权限 创建服务 /etc/systemd/system下新建服务配置文件:yourapp.service,内容如下: [Unit] Descriptionyourapp Afternetwork.target[Service] Ty…...
springboot338it职业生涯规划系统--论文pf(论文+源码)_kaic
毕 业 设 计(论 文) 题目:it职业生涯规划系统的设计与实现 摘 要 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以…...
oracle将select作为字段查询
在Oracle中,如果你想将一个SELECT语句作为字段的值,你可以使用子查询或者使用WITH子句(也称为公用表表达式CTE)。以下是两种方法的示例: 方法1:使用子查询 语法如下: SELECTcolumn1,(SELECT …...
Java数据结构和算法相关面试题
天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…...
网络安全风险评估
项目背景 随着信息化技术的快速发展,特别是面向社会、政府机构、企业等业务系统的投入使用,各组织机构对网络和信息系统安全防护都提出了新的要求。为满足安全需求,需对组织机构的网络和信息系统的安全进行一次系统全面的评估,以…...
ADAM优化算法与学习率调度器:深度学习中的关键工具
深度学习模型的训练效果离不开优化算法和学习率的选择。ADAM(Adaptive Moment Estimation)作为深度学习领域中广泛应用的优化算法之一,以其高效性和鲁棒性成为许多任务的默认选择。而学习率调度器则是优化算法的“助推器”,帮助训…...
岛屿数量C++11新特性
每日一题 200. 岛屿数量 class Solution {//使用深度的优先搜索来搜索岛屿图//遍历整个图片 当char数组的值为1时开始从这个点开始往外扩散搜索//注意处理边界 图不是正方形 public:int ans;int d[4][2] {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};int N;int M;void dfs(vector<…...
Git 快速入门:全面了解与安装步骤
Git 快速入门:全面了解与安装步骤 一、关于Git 1.1 简介 Git 是一个开源的分布式版本控制系统,由 Linus Torvalds 于 2005 年创建,最初是为了更好地管理 Linux 内核开发而设计。 Git用于跟踪计算机文件的变化,特别是源代码文件…...
基于域自适应的双光融合
目录 引言DAF-Net编码器-解码器分支编码器部分融合层解码器部分 域自适应层概述多核最大均值差异(MK-MMD)第一阶段:编码器-解码器分支训练训练过程损失函数 第二阶段:融合层训练训练过程损失函数 实验与结果总结 文章声明…...
迭代器模式 (Iterator Pattern)
文章目录 迭代器模式 (Iterator Pattern)原理优点缺点示例代码场景描述1. 定义迭代器接口2. 定义集合接口3. 实现具体集合类4. 客户端代码输出结果 UML 类图使用场景优化与扩展小结 迭代器模式 (Iterator Pattern) 迭代器模式是一种 行为型设计模式,用于顺序访问集…...
039集——渐变色之:CAD中画彩虹()(CAD—C#二次开发入门)
(来左边儿 跟我一起画个龙,在你右边儿 画一道彩虹 ~~~~~~~~~~~ ) 效果如下: namespace AcTools {public class Class1{public Wform.Timer timer;//定时器需建在类下面public static DateTime startTime;[CommandM…...
如何将 GitHub 私有仓库(private)转换为公共仓库(public)
文章目录 如何将 GitHub 私有仓库转换为公共仓库步骤 1: 登录 GitHub步骤 2: 导航到目标仓库步骤 3: 访问仓库设置步骤 4: 更改仓库可见性步骤 5: 确认更改步骤 6: 验证更改注意事项 如何将 GitHub 私有仓库转换为公共仓库 在软件开发领域,GitHub 是一个广受欢迎的…...
C++11 右值引用
目录 左值 右值 左值引用与右值引用比较 左值引用总结: 右值引用总结: 左值引用的使用场景: 引用传参和做返回值都可以提高效率(减少拷贝) 左值引用的短板: 右值引用和移动语义解决上述问题: 下面就是有移动…...
WPS表格学习计划与策略
一、学习目标 掌握WPS表格的基本操作:包括新建、打开、保存工作簿,单元格的编辑与格式化,数据的输入与验证等。熟练运用WPS表格的数据处理功能:包括数据排序、筛选、分类汇总,以及使用公式和函数进行计算和分析。学会制作图表与数据可视化:掌握不同类型图表(如柱状图、折…...
Android 引入 proto 项目及使用方法
Proto(Protocol Buffers)是Google开发的一种语言无关、平台无关的序列化结构数据的方法,它类似于JSON和XML,但相对于XML而言更小,相对于JSON而言解析更快,支持多语言。以下是将Proto引入Android项目的方法及…...
网站建设设计服务公司/seo的作用主要有
JAVA2平台包括:J2SE(Java2 Standard Edtion)标准版、J2EE(Java2 Enterprise Edition)企业版、J2ME(Java2 Micro Edition)微缩版。 J2SE:包括构成Java语言核心的类。比如数据库连接、…...
怎么做棋牌网站/做互联网项目怎么推广
js 获取网络时间: 由于new Date获取的是客户本地时间,有些情况下我们需要获取网络时间,避免用户更改本地时间而产生的某些功能冲突,则我们可以通过如下方法从浏览器获取我们服务器上的时间! function getServerTime(){let xmlHt…...
为什么企业需要建设网站/个人怎么做推广
文章目录InnoDB体系架构图后台线程1、Master Thread2、IO Thread3、Purge Thread4、Page Cleaner Thread内存1、缓冲池(缓存)2.LRU List、Free List 和 Flush List3.重做日志缓冲4.额外内存池InnoDB体系架构图 后台线程 InnoDB是多线程模型,因…...
memcached wordpress 慢 卡/软文代发
javac是用来编译.java文件的,将.java文件转化成.class文件(二进制字节码文件);dx工具是用于编译.Class文件的,把它转化成.dex文件(Dalvik虚拟机可执行文件)。 Dalvik是用于Android平台的虚拟机,它可以支持已…...
建站公司分析/福州网站建设
一般前端代码崔主要是为了在浏览器环境运行,在有服务端渲染的需求的时候, 也会兼容一下代码的加载,比如同一个 React 组件, 同样可以用于服务端渲染,而其中涉及到浏览器 API 的代码, 可以选择不执行, 经典的: if (typeof window ! undefined) {// do something } 如果是 Clojur…...
兰州网站怎么建设/兰蔻搜索引擎营销案例
假如有一个包含产品目录的数据库表,其中每种类别的物品占一行。对于每种物品要存储的信息包含产品描述和价格,以及生产该产品的供应商信息。 现在,假如有由同一供应商生产的多种物品,那么在何处存储供应商信息(如供应…...