【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注册资源某一外部版本数据结构࿰…...
TextBlob:简单高效的自然语言处理工具
TextBlob:简单高效的自然语言处理工具 TextBlob 是一个基于 NLTK 和 Pattern 的自然语言处理库,以简单易用著称。它提供了直观的 API,支持文本分析、情感分析、拼写纠正等常见任务,非常适合快速原型开发和学习。 为什么选择 Text…...
QT:将QTableWidget内容写入txt文件中
文章详请:最近在做手在眼上的标定,首先要采集机械臂数据和图像数据,我使用tablewidget进行机械臂数据的显示,最后的计算需要将机械臂位姿数据存储在txt文件中。 引用:Qt如何保存tableWidget数据?_qt table…...
每日十题八股-2024年12月2日
1.你知道有哪个框架用到NIO了吗? 2.有一个学生类,想按照分数排序,再按学号排序,应该怎么做? 3.Native方法解释一下 4.数组与集合区别,用过哪些? 5.说说Java中的集合? 6.Java中的线程…...
R语言森林生态系统结构、功能与稳定性分析与可视化实践高级应用
在生态学研究中,森林生态系统的结构、功能与稳定性是核心研究内容之一。这些方面不仅关系到森林动态变化和物种多样性,还直接影响森林提供的生态服务功能及其应对环境变化的能力。森林生态系统的结构主要包括物种组成、树种多样性、树木的空间分布与密度…...
RDMA驱动学习(三)- cq的创建
用户通过ibv_create_cq接口创建完成队列,函数原型和常见用法如下,本节以该用法为例看下cq的创建过程。 struct ibv_cq *ibv_create_cq(struct ibv_context *context, int cqe,void *cq_context,struct ibv_comp_channel *channel,int comp_vector); cq …...
Python-使用类和实例-Sun-Mon
9.2.1 Car类 class Car():"""概述车辆信息"""def __init__(self,make,model,year):"""初始化参数"""self.makemakeself.modelmodelself.yearyear //__init__方法会把依据Car类创建的实例传入的实参的值ÿ…...
【MIT-OS6.S081笔记0.5】xv6 gdb调试环境搭建
补充一下xv6 gdb调试环境的搭建,我这里装的是最新的15.2的gdb的版本。我下载的是下面的第二个xz后缀的文件: 配置最详细的步骤可以参考下面的文章: [MIT 6.S081] Lab 0: 实验配置, 调试及测试 这里记录一下踩过的一些报错: 文…...
vmware虚拟机移植
最近发现虚拟机的系统非常适合移植,接下来看一下具体的过程 复制vmdk 第一个重要的文件是保存vmdk,如果磁盘使用的是多个文件则最好进行合并一下(用着用着会发现vmdk文件特别大,这是正常的,不要想着能压缩了…...
最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组 是数组中的一个连续部分。 示例 1: 输入:nums [-2,1,-3,4,-1,2,1,-5,4] 输出ÿ…...
活着就好20241202
亲爱的朋友们,大家早上好!今天是2024年12月2日,第49周的第一天,也是十二月的第二天,农历甲辰[龙]年十月三十。在这个全新月份的开始、阳光初升的清晨,愿第一缕阳光悄悄探进你的房间,带给你满满的…...
Scala的练习题(成绩计算)
//1.迭代器,跳过第一个元素 //2.把字符串转成数字 //3.如何判断一个正整数是否可以被三整除? (123) % 3 0 import wyyyy.Studentimport scala.collection.mutable.ListBuffer import scala.io.Sourcecase class Student(name: St…...
Docker中配置Mysql主从备份
Mysql配置主从备份 一、Docker中实现跨服务器主从备份二、配置步骤1.配置主库2.配置从库3.遇到问题3.其它使用到的命令 一、Docker中实现跨服务器主从备份 在 Docker 中配置 MySQL 主从备份主要通过 MySQL 主从复制实现 二、配置步骤 1.配置主库 # 进入mysql主库容器 docke…...
分布式通用计算——MapReduce(重点在shuffle 阶段)
图片均来源于B站:哈喽鹏程 面向批处理的分布式计算框架——MapReduce 1、Mapreduce 起源2、适用场景3、MapReduce 词频统计原理 1、Mapreduce 起源 2、适用场景 3、MapReduce 词频统计原理 map 阶段到reduce阶段,通过hash取模来实现reduce 。比如&…...
VMware三种网络模式(桥接、NAT模式、仅主机)模式说明
VMware三种网络模式(桥接、NAT模式、仅主机)模式说明 VMware 提供了三种主要的网络连接模式:桥接模式(Bridged Mode)、NAT模式(Network Address Translation Mode)和仅主机模式(Hos…...
实习冲刺第三十八天
236.二叉树的最近公共祖先 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大࿰…...
[Linux] 信号(singal)详解(一)
标题:[Linux] 信号(singal)详解 水墨不写bug (图片来源于网络) 目录 一、认识信号 1、认识信号 2、信号特点 3、基本概念 二、信号的产生(5种方式) 三、信号的保存 正文开始: 一、认识信号 1、认识信…...
【设计模式系列】备忘录模式(十九)
目录 一、什么是备忘录模式 二、备忘录模式的角色 三、备忘录模式的典型应用场景 四、备忘录模式在Calendar中的应用 一、什么是备忘录模式 备忘录模式(Memento Pattern)是一种行为型设计模式,它允许在不暴露对象内部状态的情况下保存和恢…...
书生大模型实战营第4期——3.3 LMDeploy 量化部署实践
文章目录 1 基础任务2 配置LMDeploy环境2.1 环境搭建2.2 模型配置2.3 LMDeploy验证启动模型文件 3 LMDeploy与InternLM2.53.1 LMDeploy API部署InternLM2.53.1.1 启动API服务器3.1.2 以命令行形式连接API服务器3.1.3 以Gradio网页形式连接API服务器 3.2 LMDeploy Lite3.2.1 设置…...
11.28深度学习_bp算法
七、BP算法 多层神经网络的学习能力比单层网络强得多。想要训练多层网络,需要更强大的学习算法。误差反向传播算法(Back Propagation)是其中最杰出的代表,它是目前最成功的神经网络学习算法。现实任务使用神经网络时,…...
U盘文件夹变打不开的文件:深度解析、恢复策略与预防之道
一、U盘文件夹变打不开的文件现象解析 在日常使用U盘的过程中,我们时常会遇到这样的困扰:原本存储有序、可以轻松访问的文件夹,突然之间变成了无法打开的文件。这些文件通常以未知图标或乱码形式显示,双击或右键尝试打开时&#…...
软件工程中的需求分析流程详解
一、需求分析的定义 需求分析(Requirements Analysis)是指在软件开发过程中,通过与用户、相关人员的沟通与讨论,全面理解和确定软件需求的过程。需求分析的最终目标是清晰、准确地定义软件系统应具备的功能、性能、用户界面、约束…...
springboot369高校教师教研信息填报系统(论文+源码)_kaic
毕 业 设 计(论 文) 题目:高校教师教研信息填报系统的设计与实现 摘 要 如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,…...
Docker Buildx 与 CNB 多平台构建实践
一、Docker Buildx 功能介绍 docker buildx 是 Docker 提供的一个增强版构建工具,支持更强大的构建功能,特别是在构建多平台镜像和高效处理复杂 Docker 镜像方面。 1.1 主要功能 多平台构建支持 使用 docker buildx,可以在单台设备上构建…...
VBA字典与数组第二十一讲:文本转换为数组函数Split
《VBA数组与字典方案》教程(10144533)是我推出的第三套教程,目前已经是第二版修订了。这套教程定位于中级,字典是VBA的精华,我要求学员必学。7.1.3.9教程和手册掌握后,可以解决大多数工作中遇到的实际问题。…...
开源项目 - 人脸关键点检测 facial landmark 人脸关键点 (98个关键点)
开源项目 - 人脸关键点检测 facial landmark 人脸关键点 (98个关键点) 示例: 助力快速掌握数据集的信息和使用方式。 数据可以如此美好!...
【Postgres_Python】使用python脚本批量导出PG数据库
示例代码说明: 有多个数据库需要导出为.sql格式,数据库名与sql文件名一致,读取的数据库名需要根据文件名进行拼接 import psycopg2 import subprocess import os folder_path D:/HQ/chongqing_20241112 # 获取文件夹下所有文件和文件夹的名称 filename…...
嵌入式Linux(SOC带GPU树莓派)无窗口系统下搭建 OpenGL ES + Qt 开发环境,并绘制旋转金字塔
树莓派无窗口系统下搭建 OpenGL ES Qt 开发环境,并绘制旋转金字塔 1. 安装 OpenGL ES 开发环境 运行以下命令安装所需的 OpenGL ES 开发工具和库: sudo apt install cmake mesa-utils libegl1-mesa-dev libgles2-mesa-dev libdrm-dev libgbm-dev2. 安…...
MySQL事物
目录 何谓事物? 何谓数据库事务? 并发事务带来了哪些问题? 脏读(Dirty read) 丢失修改(Lostto modify) 不可重复读(Unrepeatable read) 幻读(Phantom read) 不可重复读和幻读有什么区别? 并发事务的控制方式有哪些? SQL 标准定义了哪些事务隔离级别?…...
在 CentOS 上安装 Docker:构建容器化环境全攻略
一、引言 在当今的软件开发与运维领域,Docker 无疑是一颗璀璨的明星。它以轻量级虚拟化的卓越特性,为应用程序的打包、分发和管理开辟了崭新的高效便捷之路。无论是开发环境的快速搭建,还是生产环境的稳定部署,Docker 都展现出了…...
基于Spring Boot的宠物咖啡馆平台的设计与实现
私信我获取源码和万字论文,制作不易,感谢点赞支持。 基于Spring Boot的宠物咖啡馆平台的设计与实现 摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了基于Spring Boot的宠物咖啡馆平台的设…...
JAVAWeb之javascript学习
1.js引入方式 1. 内嵌式:在head中,通过一对script标签引入JS代码;cript代码放置位置有一定的随意性,一般放在head标签中;2.引入外部js文件 在head中,通过一对script标签引入外部JS代码;注意&…...
电脑与优傲协作机器人(实体)的TCP通讯(操作记录)
目录 一、UR通信端口 二、电脑(客户端)连接协作机器人(服务端) 1.设置网络方法 2.检查设置 3.示教器切换远程控制(注) 4.客户端与协作机器人建立连接 5.连接测试 三、电脑(服务端&#…...
C++初阶——动态内存管理
目录 1、C/C内存区域划分 2、C动态内存管理:malloc/calloc/realloc/free 3、C动态内存管理:new/delete 3.1 new/delete内置类型 3.2 new/delete自定义类型 4、operator new与operator delete函数 5、new和delete的实现原理 5.1 内置类型 5.2 自定…...
Python 【图像分类】之 PyTorch 进行猫狗分类功能的实现(Swanlab训练可视化/ Gradio 实现猫狗分类 Demo)
Python 【图像分类】之 PyTorch 进行猫狗分类功能的实现(Swanlab训练可视化/ Gradio 实现猫狗分类 Demo) 目录 Python 【图像分类】之 PyTorch 进行猫狗分类功能的实现(Swanlab训练可视化/ Gradio 实现猫狗分类 Demo) 一、简单介绍 二、PyTorch 三、CNN 1、神经网络 2、卷…...
Attention显存统计与分析
Attention显存估计 简单的Attention函数 import torch import torch.nn as nn import einops class Attention(nn.Module):def __init__(self, dim, num_heads8, qkv_biasFalse, qk_scaleNone, attn_drop0., proj_drop0.):super().__init__()self.num_heads num_headshead_d…...
java反射
反射 Java 反射是 Java 提供的一种强大特性,它允许程序在运行时动态地获取类的信息,并操作类的属性和方法。这为编写灵活、可扩展的 Java 应用程序提供了强有力的支持 获取Class对象 package ref;public class Person {private String name ;private …...
Spring Boot入门
1、Spring Boot是什么 Spring Boot 帮我们简单、快速地创建一个独立的、生产级别的 Spring 应用(说明:Spring Boot底层是Spring) 大多数 Spring Boot 应用只需要编写少量配置即可快速整合 Spring 平台以及第三方技术 特性: 快速…...
Spring Web:深度解析与实战应用
概述 大家好,欢迎来到今天的技术分享。我是你们的老朋友,今天,我们要深入探讨的是Spring Web模块,这个模块为Java Web应用程序提供了全面的支持,不仅具备基本的面向Web的综合特性,还能与常见框架如Struts2无…...
学习日志019--初识PyQt
使用pyqt创建一个登录界面 from PyQt6.QtCore import Qt # 引入pyqt6包 from PyQt6.QtGui import QIcon, QMovie from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QLineEdit import sysclass MyWidget(QWidget):# 构造函数,继承父类的构造…...
Swift 宏(Macro)入门趣谈(五)
概述 苹果在去年 WWDC 23 中就为 Swift 语言新增了“其利断金”的重要小伙伴 Swift 宏(Swift Macro)。为此,苹果特地用 2 段视频(入门和进阶)颇为隆重的介绍了它。 那么到底 Swift 宏是什么?有什么用&…...
Linux 35.6 + JetPack v5.1.4@DeepStream安装
Linux 35.6 JetPack v5.1.4DeepStream安装 1. 源由2. 步骤Step 1 安装Jetpack 5.1.4 L4T 35.6Step 2 安装依赖组件Step 3 安装librdkafkaStep 4 安装 DeepStream SDKStep 5 测试 deepstream-appStep 6 运行 deepstream-app 3. 总结3.1 版本问题3.2 二进制help 4. 参考资料 1. …...
C++基础:list的底层实现
文章目录 1.基本结构2.迭代器的实现2.1 尾插的实现2.2 迭代器的实现 3.打印函数(模版复用实例化)4.任意位置的插入删除1. 插入2. 删除 5.析构与拷贝构造5.1 析构函数5.2 拷贝构造5.3 赋值重载 1.基本结构 与vector和string不同list需要: 一个类来放入数据和指针也就是节点 一…...
Spring中@Transactional注解与事务传播机制
文章目录 事务传播机制事务失效的场景 事务传播机制 事务的传播特性指的是 当一个事务方法调用另一个事务方法时,事务方法应该如何执行。 事务传播行为类型外部不存在事务外部存在事务使用方式REQUIRED(默认)开启新的事务融合到外部事务中Transactional(propagati…...
实验七 用 MATLAB 设计 FIR 数字滤波器
实验目的 加深对窗函数法设计 FIR 数字滤波器的基本原理的理解。 学习用 Matlab 语言的窗函数法编写设计 FIR 数字滤波器的程序。 了解 Matlab 语言有关窗函数法设计 FIR 数字滤波器的常用函数用法。 掌握 FIR 滤波器的快速卷积实现原理。 不同滤波器的设计方法具有不同的优…...
Linux - selinux
七、selinux 1、说明 SELinux是Security-Enhanced Linux的缩写,意思是安全强化的linux。 SELinux是对程序、文件等权限设置依据的一个内核模块。由于启动网络服务的也是程序,因此刚好也 是能够控制网络服务能否访问系统资源的一道关卡。 传统的文件权…...
【STL】C++ vector类模板
文章目录 基本概念vector的使用定义和初始化构造函数赋值操作容量和大小插入和删除数据存取 互换容器vector的迭代器vector储存自定义数据类型 基本概念 vector是类型相同的对象的容器,vector的大小可以变化,可以向数组中增加元素。因此,vec…...
物联网——WatchDog(监听器)
看门狗简介 独立看门狗框图 看门狗原理:定时器溢出,产生系统复位信号;若定时‘喂狗’则不产生系统复位信号 定时中断基本结构(对比) IWDG键寄存器 独立看门狗超时时间 WWDG(窗口看门狗) WWDG特性 WWDG超时时间 由于…...
从零开始写游戏之斗地主-网络通信
在确定了数据结构后,原本是打算直接开始写斗地主的游戏运行逻辑的。但是突然想到我本地写出来之后,也测试不了啊,所以还是先写通信模块了。 基本框架 在Java语言中搞网络通信,那么就得请出Netty这个老演员了。 主要分为两个端&…...
【智能控制】实验,基于MATLAB的模糊推理系统设计,模糊控制系统设计
关注作者了解更多 我的其他CSDN专栏 过程控制系统 工程测试技术 虚拟仪器技术 可编程控制器 工业现场总线 数字图像处理 智能控制 传感器技术 嵌入式系统 复变函数与积分变换 单片机原理 线性代数 大学物理 热工与工程流体力学 数字信号处理 光电融合集成电路…...