参考
- 【k8s基础篇】k8s scheme3 之序列化_基于schema进行序列化-CSDN博客
- 【k8s基础篇】k8s scheme4 之资源数据结构与资源注册_kubernetes 的scheam-CSDN博客
Scheme的字段总览
type Scheme struct {// gvkToType 允许通过给定的版本和名称来推断对象的 Go 类型。// map 键是 GroupVersionKind (GVK),值是 Go 类型(reflect.Type)。gvkToType map[schema.GroupVersionKind]reflect.Type// typeToGVK 允许通过 Go 类型查找对应的 GroupVersionKind(GVK)。// map 键是 Go 类型(reflect.Type),值是一个包含多个 GVK 的切片。// 注意,这里存储的是 Go 类型而非指针。typeToGVK map[reflect.Type][]schema.GroupVersionKind// unversionedTypes 用于转换时不做版本转换的类型。// 这些类型会在调用 ConvertToVersion 时直接使用原始的类型,不进行版本转换。unversionedTypes map[reflect.Type]schema.GroupVersionKind// unversionedKinds 是可以在任何组或版本下创建的 Kind 的名称。// 这些 Kind 没有特定的版本。// TODO: 需要进一步解决无版本类型的状态。unversionedKinds map[string]reflect.Type// fieldLabelConversionFuncs 是一个映射,记录了每个 GroupVersionKind 对应的字段标签转换函数。// 这些函数用于将资源字段标签从某个版本转换为内部版本的标签。fieldLabelConversionFuncs map[schema.GroupVersionKind]FieldLabelConversionFunc// defaulterFuncs 是一个映射,记录了每种 Go 类型对应的默认值设置函数。// 这些函数会在对象创建时为该对象提供默认值,函数接受指向对象的指针。defaulterFuncs map[reflect.Type]func(interface{})// converter 存储所有已注册的转换函数,也包含默认的转换行为。// 它负责处理版本间的转换和数据的默认转换。converter *conversion.Converter// versionPriority 是一个映射,记录了每个组内版本的优先级顺序。// 该顺序决定了在 Scheme 中注册的版本的默认优先级。versionPriority map[string][]string// observedVersions 记录了已注册版本的顺序。// 这个顺序用于帮助追踪各个版本的注册次序。observedVersions []schema.GroupVersion// schemeName 是 Scheme 的名称。如果没有指定名称,默认为调用 NewScheme 的栈信息。// 这个名称用于错误报告,以指示 Scheme 来源。schemeName string
}
gvkToType
:根据 GroupVersionKind
(GVK)来查找对应的 Go 类型,用于将资源的元数据(如版本和组)转换为 Go 类型。
typeToGVK
:将 Go 类型映射到多个 GroupVersionKind
(GVK)。这允许从 Go 类型反向查找其所属的版本和组。
unversionedTypes
:记录那些没有版本号的资源类型。在转换过程中,这些类型不会进行版本转换,通常用于特殊的资源类型。
unversionedKinds
:记录可在任何组或版本中创建的 Kind
,这些类型不依赖于版本。
fieldLabelConversionFuncs
:为特定版本的资源提供字段标签转换的函数,用于支持版本间的字段转换。
defaulterFuncs
:记录类型对应的默认值函数,当资源创建时,这些函数会为资源对象提供默认值。
converter
:管理资源版本之间的转换,执行实际的数据转换工作。
versionPriority
:按优先级顺序记录每个组内的版本,用于确定默认版本。优先级高的版本会排在前面。
observedVersions
:记录已注册版本的顺序,帮助确定资源类型的处理顺序。
schemeName
:为 Scheme
指定名称,帮助错误诊断和日志记录。
Q1 | 为什么需要 Scheme
为什么需要 Scheme?
1-序列化与反序列化
-
控制器与 API Server 的交互是通过标准的 RESTful API 完成的(如创建 Deployment,或 获取 Deployment),是将信息序列化为 protobuf 或 json 形式,然后包装为 body,通过 https 形式与 apiserver 进行交互(可以理解 apiserver 为普通的 https 服务端)—— 因此如何进行反序列化和序列化转换呢?
- 同理反序列化,就是客户端对 response body 内容的处理或服务端对 requests body 内容的处理,将 body 的 protobuf 或 json 转换为 go Struct,然后便于进行下一步业务逻辑处理
-
以反序列化举例 —— 获取 Deployment —— 通过向 Rest URL 发起请求获得 Deploy —— Rest URL 就是 GVR
-
通过 Rest URL 转换很容易获得 GVK —— 但是 GVK 对应的存储数据结构(也就是 go Struct) 在哪呢?—— 如何进行反序列化
-
答 —— 通过 Scheme 中记录
gvkToType
便可以查找到 GVK 对应的 go Struct —— 根据 go Struct 后面的 protobuf 或 json tag 便可以反序列化 —— 将 https 响应 body 中的内容转为 Deployment 的 go Struct,便于下一步处理 -
go Struct 后面的 protobuf 或 json tag —— 见下面的 Deployment 代码示例
-
-
所以可以理解 Scheme 的部分参数
gvkToType
:根据GroupVersionKind
(GVK)来查找对应的 Go 类型,用于将资源的元数据(如版本和组)转换为 Go 类型。typeToGVK
:将 Go 类型映射到多个GroupVersionKind
(GVK)。这允许从 Go 类型反向查找其所属的版本和组。
// 路径 /Users/dufengyang/go/pkg/mod/k8s.io/api@v0.29.0/apps/v1/types.go// Deployment enables declarative updates for Pods and ReplicaSets.
type Deployment struct {metav1.TypeMeta `json:",inline"`// Standard object's metadata.// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata// +optionalmetav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`// Specification of the desired behavior of the Deployment.// +optionalSpec DeploymentSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`// Most recently observed status of the Deployment.// +optionalStatus DeploymentStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}// DeploymentSpec is the specification of the desired behavior of the Deployment.
type DeploymentSpec struct {// Number of desired pods. This is a pointer to distinguish between explicit// zero and not specified. Defaults to 1.// +optionalReplicas *int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"`// Label selector for pods. Existing ReplicaSets whose pods are// selected by this will be the ones affected by this deployment.// It must match the pod template's labels.Selector *metav1.LabelSelector `json:"selector" protobuf:"bytes,2,opt,name=selector"`// Template describes the pods that will be created.// The only allowed template.spec.restartPolicy value is "Always".Template v1.PodTemplateSpec `json:"template" protobuf:"bytes,3,opt,name=template"`// The deployment strategy to use to replace existing pods with new ones.// +optional// +patchStrategy=retainKeysStrategy DeploymentStrategy `json:"strategy,omitempty" patchStrategy:"retainKeys" protobuf:"bytes,4,opt,name=strategy"`// Minimum number of seconds for which a newly created pod should be ready// without any of its container crashing, for it to be considered available.// Defaults to 0 (pod will be considered available as soon as it is ready)// +optionalMinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,5,opt,name=minReadySeconds"`// The number of old ReplicaSets to retain to allow rollback.// This is a pointer to distinguish between explicit zero and not specified.// Defaults to 10.// +optionalRevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty" protobuf:"varint,6,opt,name=revisionHistoryLimit"`// Indicates that the deployment is paused.// +optionalPaused bool `json:"paused,omitempty" protobuf:"varint,7,opt,name=paused"`// The maximum time in seconds for a deployment to make progress before it// is considered to be failed. The deployment controller will continue to// process failed deployments and a condition with a ProgressDeadlineExceeded// reason will be surfaced in the deployment status. Note that progress will// not be estimated during the time a deployment is paused. Defaults to 600s.ProgressDeadlineSeconds *int32 `json:"progressDeadlineSeconds,omitempty" protobuf:"varint,9,opt,name=progressDeadlineSeconds"`
}
2-版本转换
-
Deployment 也是不断迭代的,目前有 v1、v1betea1、v1beta2,每个版本的 go Struct 都会有变动(如新增字段)
-
同时为了便于转换和存储,存在一个 internal 版本,作为一个存储态或中间态(因为 版本两两建立映射关系(如 v1 对 v1beta1、v1对 v1betaN、v1beta1 对 v1betaN等),明显比都与 internal 映射复杂(v1–internal – v1beta1(v1betaN)))
-
所以采用 Scheme 中的 converter 参数
- **
converter
**管理资源版本之间的转换,执行实际的数据转换工作
- **
3-版本优先级
-
以 Deployment 为例,目前有三个对外版本,一个内部版本
- 三个对外版本,
k8s.io/api/apps
目录下的 v1、v1beta1、v1beta2 目录的 register.go 记录了各个组版本,types.go 分别记录了每个版本的结构定义var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"}
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta2"}
- 一个内部版本,
k8s.io/kubernetes/pkg/apis/apps/register.go
记录了内部版本__internal
,同目录的 register.go 记录了组版本,也有 types.go 文件记录__internal
版本资源的数据结构var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
k8s.io/kubernetes/pkg/apis/apps
也有 v1、v1beta1、v1beta2 目录,和对应的 register.go 文件,但是没有各个外部版本的 types.go 文件
- 版本间的优先级
k8s.io/kubernetes/pkg/apis/apps/install/install.go
utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1beta1.SchemeGroupVersion))
- 表示该 Group 中的 Kind 或 Resource ,优先使用 v1 版本,接下来才是 v1beta2,最后是 v1beta1
- 三个对外版本,
-
以 Deployment 为例,优先级配置代码所在位置
-
// 代码路径 k8s.io/kubernetes/pkg/apis/apps/install/install.gofunc init() {Install(legacyscheme.Scheme) }// Install registers the API group and adds types to a scheme func Install(scheme *runtime.Scheme) {// internal 内部版本utilruntime.Must(apps.AddToScheme(scheme))// v1beta1 版本utilruntime.Must(v1beta1.AddToScheme(scheme))// v1beta2 版本utilruntime.Must(v1beta2.AddToScheme(scheme))// v1 版本utilruntime.Must(v1.AddToScheme(scheme))// 优先级 v1 v1beta2 v1beta1utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1beta1.SchemeGroupVersion)) }
-
-
所以可以理解 Scheme 中的参数
versionPriority
:按优先级顺序记录每个组内的版本,用于确定默认版本。优先级高的版本会排在前面。- 作用:可以知道先使用哪个版本的资源,如 Scheme 中注册了 Deploy 的 v1、v1beta2、v1beta1 版本,那么会优先使用 v1 版本(指的就是优先使用此版本 Deployment 的 go Struct 数据结构,进行业务逻辑处理和序列化、反序列化等)
observedVersions
:记录已注册版本的顺序,帮助确定资源类型的处理顺序。- 作用:可以通过顺序,了解版本更新的顺序,如 v1beta1、v1beta2、 v1,知道先开发完成的是 v1beta1,最后完成的是 v1
4-Scheme其他参数
-
unversionedTypes
:记录那些没有版本号的资源类型。在转换过程中,这些类型不会进行版本转换,通常用于特殊的资源类型。- 记录 go Struct 和 gvk 的映射关系,类似 typeToGVK
- 不同点在于,unversionedTypes 记录无版本资源, typeToGVK 记录有版本资源
- 无版本资源 —— 可以理解为稳定,没有版本更新,类似 Status,对应的版本为(Group为空,v1,也就是核心组v1版本)
- 另外注意,Metadata、Object一般称之为“无版本资源”,但其实并不是【独立 API 资源类型】,就是真正的没有版本,其作为【结构体字段】存在各个资源结构体中,因此也称之为【元数据】
- 【元数据-无版本资源】一般都存储在
k8s.io/apimachinery
项目中,因此可以说此项目是【地基】 - 【独立资源-无版本资源】一般存储在
k8s.io/kubernetes
项目中,一般在k8s.io/kubernetes/pkg/apis
目录中,该目录中也包含有版本资源
- 有版本资源 —— 类似 Deployment,有版本更新,apps 组有 v1beta1、v1beta2、v1 等版本
- 下面 AddToGroupVersion 有详细介绍
-
unversionedKinds
:记录可在任何组或版本中创建的Kind
,这些类型不依赖于版本。- 记录 gvk 和 go Struct 的映射关系
- 下面 AddToGroupVersion 有详细介绍
-
fieldLabelConversionFuncs
:为特定版本的资源提供字段标签转换的函数,用于支持版本间的字段转换。- 此字段为 map 结构,存储对某些字段的转换函数
- 如 status.phase 字段存储个对应的函数,便可以通过 fieldLabelConversionFuncs["status.phase "] 调用该字段对应的转换函数,进行下一步处理
-
defaulterFuncs
:记录类型对应的默认值函数,当资源创建时,这些函数会为资源对象提供默认值。-
就是为一些资源类型填写默认值
-
gvk 和 默认填充函数的对应,可以理解为每个资源版本的 go struct 有对应的默认值填充函数
-
defaulterFuncs map[reflect.Type]func(interface{})
-
下面 AddToGroupVersion 有详细介绍
-
-
schemeName
:为Scheme
指定名称,帮助错误诊断和日志记录。
无版本资源的典型例子
无版本资源常常用于以下类型的对象:
Status
:Status
是用于表示 API 请求的执行结果或状态的对象,它通常不会随版本变化而变化。它用于在处理某些操作(如资源创建、删除)时提供反馈和错误信息。Status
通常是无版本的,因为它并不是一种具有长期持久性的资源,而是一个临时的反馈对象。
APIGroup
、APIGroupList
:- 这些对象表示 Kubernetes 中的 API 组及其版本信息,通常用于在 API 服务器启动时向客户端报告哪些 API 组和版本是可用的。
- 这些对象与 Kubernetes 的版本控制无关,因此也可以被视为无版本资源。
APIResourceList
:APIResourceList
是一种用于描述 API 资源列表的对象,通常用于支持 Kubernetes 资源的发现。- 这种类型也没有特定的版本,因为它只是列出当前可用的资源,而与具体版本无关。
无版本资源的应用场景
- API 资源的发现:当用户或客户端查询 Kubernetes API 服务器时,可能会通过无版本资源(如
APIGroupList
)查询集群中的所有支持的 API 组和版本。 - 状态返回:像
Status
这样的资源用于返回操作的状态,例如在资源创建、删除或更新时返回的成功或错误消息。 - 兼容性支持:一些资源可能没有版本化机制,因为它们是为了确保系统的兼容性和稳定性,或者它们只是用于基础性操作,不需要随 Kubernetes 的版本变化而变化。
Q2 | 为什么 types.go 文件内部版本和外部版本存在不同路径
2.1-资源数据结构存储路径
通过上面的介绍你也会注意到
- 资源的外部版本的数据结构,也就是 types.go 文件,放置在
k8s.io/api
项目中 - 资源的内部版本的数据结构,也就是 types.go 文件,放置在
k8s.io/kubernetes
项目 中
# 没有 vendor 目录,可以随便找个 资源进行跳转
# 若有,可以在 vendor 目录观察到,没有此目录,可以尝试用 go mod vendor
# 下面以 apps Group 为例,资源数据结构定义所在路径,如下# 外部 v1 版本
mod/k8s.io/api@v0.29.0/apps/v1/types.go
# 外部 v1beta1 版本
mod/k8s.io/api@v0.29.0/apps/v1beta1/types.go
# 外部 v1beta2 版本
mod/k8s.io/api@v0.29.0/apps/v1beta2/types.go# 内部 internal 版本,注意内部版本 没有 json 或 protobuf tag
mod/k8s.io/kubernetes@v1.29.0/pkg/apis/apps/types.go
# 版本优先级注册
mod/k8s.io/kubernetes@v1.29.0/pkg/apis/apps/install/install.go# 版本优先级顺序
func init() {Install(legacyscheme.Scheme)
}// Install registers the API group and adds types to a scheme
func Install(scheme *runtime.Scheme) {utilruntime.Must(apps.AddToScheme(scheme))utilruntime.Must(v1beta1.AddToScheme(scheme))utilruntime.Must(v1beta2.AddToScheme(scheme))utilruntime.Must(v1.AddToScheme(scheme))utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1beta1.SchemeGroupVersion))
}
2.2-资源存储路径不同带来的优势
Kubernetes 的 API 类型和实现是严格分离的,这种设计有以下好处:
- 简单理解
internal
版本主要供 k8s 项目使用,k8s.io/kubernetes
项目主要用于设计和实现 k8s controller 和 apiserver 等主要控制组件,因此采用密切使用的数据结构放在一起,也就是internal
版本- 外部版本不仅可以 k8s 项目使用,还可以被其他项目引用使用,所以外部版本独立放在
k8s.io/api
项目中,且没有将 Internal 版本放在该项目里面
特性 | Internal 类型 | 外部版本 |
---|---|---|
用途 | Kubernetes 内部使用,支撑核心逻辑 | 面向用户和客户端,用于操作资源 |
修改自由度 | 可自由修改,不需要向后兼容 | 必须遵循向后兼容性规则 |
版本管理 | 无版本概念,保持内部逻辑一致即可 | 需要严格区分版本,遵循语义版本(v1, v1beta1) |
依赖结构 | 位于 k8s.io/kubernetes ,与核心代码强绑定 | 位于 k8s.io/api ,与核心实现解耦 |
2.3-资源不同版本的转换
- 上面讲解了,资源的各版本转换(v1beata1、v1beta2、v1)都是通过(internal)进行中转
- 而相应的转换函数,是通过 conversion-gen 自动生成,然后注册到 Scheme 中
- 那么考虑,现在 v2 基于 v1 版本进行演化,新增了几个字段,那么 internal 内部版本需要进行变化吗?
- 假设 internal 版本字段不变,那么 v2 版本转为别的版本,internal 会自动忽略新增字段;
- 同样,别的版本通过 internal 转为 v2,v2的新增字段也为空;但这种情况可以通过 Scheme 中的
defaulterFuncs
避免,其可以在转换时为新增字段配置默认值
- 同样,别的版本通过 internal 转为 v2,v2的新增字段也为空;但这种情况可以通过 Scheme 中的
- 若想将 v2 新增字段同步到其他外部版本,那么需要再 internal 版本和其他版本都新增此字段,然后再利用 conversion-gen 生成自动转换函数
- 若只在 internal 版本新增字段,其他外部版本都不具有此字段,那么此字段属于内部字段,不对外暴露,不影响其他外部版本
- 假设 internal 版本字段不变,那么 v2 版本转为别的版本,internal 会自动忽略新增字段;
Q3 | k8s 三个重要的库都是什么?
- 其实前面已经大致讲解了,就是
k8s.io/apimachinery
、k8s.io/api
和k8s.io/kubernetes
- 可以简单理解为
k8s.io/apimachinery
—— 地基,包含 TypeMeta 和 ObjectMeta 等元数据字段,每个资源和CRD定义都需要引用k8s.io/api
—— 资源的 API 定义,主要包含各个 Group、Version、Kind,包含资源各个外部版本的数据结构定义k8s.io/kubernetes
—— k8s 的核心,包含 apiserver 和各类控制器;以及资源的 internal 内部版本(用于存储、版本转换等);还定义外部版本的优先级顺序
特性 | k8s.io/api | k8s.io/apimachinery | k8s.io/kubernetes |
---|---|---|---|
核心功能 | 定义 Kubernetes API 对象类型(就是 GVK 的定义) | 提供元信息、工具和通用组件(理解为 metada、ObjectMeta、TypeMeta 之类) | 实现 Kubernetes 的核心功能(理解为控制器、apiserver之类) |
模块类型 | 轻量级 API 类型模块 | 底层工具模块 | 核心实现模块 |
适用场景 | 客户端使用,用于定义和解析资源 | 提供对象元信息和类型转换工具 | Kubernetes 集群运行和核心功能 |
依赖关系 | 依赖 k8s.io/apimachinery | 独立存在 | 依赖 k8s.io/api 和 k8s.io/apimachinery |
使用场景 | 客户端工具或控制器开发 | 类型注册、元信息操作、深拷贝工具 | Kubernetes 核心开发 |
Q4 | Scheme 如何使用
- 以 apps Group 为例
4.1- k8s.io/api
中 v1 版本的 register.go 文件
- 可以理解为,基础的资源数据结构、元数据结构、基础结构转换函数、默认值函数等注册到 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 当做一个 list 队列,元素类型为函数,因此就是一个 存储函数的列表SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)localSchemeBuilder = &SchemeBuilder// 调用此函数,就相当于调用 localSchemeBuilder 中注册的所有函数AddToScheme = localSchemeBuilder.AddToScheme
)// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {// 相当于,更新传入参数 scheme 中的 gvkToType 和 typeToGVK// 并且记录 GroupVersion 到 scheme 中的 observedVersionsscheme.AddKnownTypes(SchemeGroupVersion,&Deployment{},&DeploymentList{},&StatefulSet{},&StatefulSetList{},&DaemonSet{},&DaemonSetList{},&ReplicaSet{},&ReplicaSetList{},&ControllerRevision{},&ControllerRevisionList{},)// 这个比较复杂// 1. 注册了无版本资源,更新 scheme 的 unversionedTypes 和 unversionedKinds 和 observedVersions// 2. 注册一些基础的转换函数,更新 scheme 的 converter// 3. 注册一些基础的默认值填充函数,更新 scheme 中的 defaulterFuncs// 注意此函数,internal 版本没有,因为 internal 没有版本的概念,下面会详细讲解metav1.AddToGroupVersion(scheme, SchemeGroupVersion)return nil
}
4.2-k8s.io/kubernetes
中 v1 版本的 register.go 文件
- 可以理解为,将扩展的默认值函数、版本转换函数等注册到 Scheme
- 没有注册资源的数据结构到 Scheme
// 路径 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
)// 相比于上面,多了默认值填充函数,更新到 scheme 中的 defaulterFuncs
// 但是转换函数在哪里呀?
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.// 跳转-1localSchemeBuilder.Register(addDefaultingFuncs)
}// 跳转-2
func addDefaultingFuncs(scheme *runtime.Scheme) error {return RegisterDefaults(scheme)
}
// 跳转-3
func RegisterDefaults(scheme *runtime.Scheme) error {scheme.AddTypeDefaultingFunc(&v1.DaemonSet{}, func(obj interface{}) { SetObjectDefaults_DaemonSet(obj.(*v1.DaemonSet)) })scheme.AddTypeDefaultingFunc(&v1.DaemonSetList{}, func(obj interface{}) { SetObjectDefaults_DaemonSetList(obj.(*v1.DaemonSetList)) })scheme.AddTypeDefaultingFunc(&v1.Deployment{}, func(obj interface{}) { SetObjectDefaults_Deployment(obj.(*v1.Deployment)) })scheme.AddTypeDefaultingFunc(&v1.DeploymentList{}, func(obj interface{}) { SetObjectDefaults_DeploymentList(obj.(*v1.DeploymentList)) })scheme.AddTypeDefaultingFunc(&v1.ReplicaSet{}, func(obj interface{}) { SetObjectDefaults_ReplicaSet(obj.(*v1.ReplicaSet)) })scheme.AddTypeDefaultingFunc(&v1.ReplicaSetList{}, func(obj interface{}) { SetObjectDefaults_ReplicaSetList(obj.(*v1.ReplicaSetList)) })scheme.AddTypeDefaultingFunc(&v1.StatefulSet{}, func(obj interface{}) { SetObjectDefaults_StatefulSet(obj.(*v1.StatefulSet)) })scheme.AddTypeDefaultingFunc(&v1.StatefulSetList{}, func(obj interface{}) { SetObjectDefaults_StatefulSetList(obj.(*v1.StatefulSetList)) })return nil
}
- 转换函数的位置,同层级下,也就是同包中的
zz_generated.conversion.go
,其通过 init 函数,注册到localSchemeBuilder
变量中 - 调用 AddToScheme 方法时,会自动调用
localSchemeBuilder
变量中的所有函数,完成了转换函数注册到 Scheme 的converter
变量中
// 路径 mod/k8s.io/kubernetes@v1.29.0/pkg/apis/apps/v1/zz_generated.conversion.gofunc init() {localSchemeBuilder.Register(RegisterConversions)
}// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
// 此函数,就是 apps Group 的 v1 版本和 internal 版本的所有转换函数
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}// 太多函数了,此处简单截取
}
4.3-k8s.io/kubernetes
中 internal 版本的 register.go 文件
- 可以看出,和
k8s.io/io
中的 v1 版本注册逻辑基本一致,但是多了几个资源字段,为什么呢?- 因为 internal 是所有版本的中转者,因此包含所有版本必须的信息,所以信息会更为丰富
// 路径 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{},// 相比于 v1 新增&DeploymentRollback{},// 相比于 v1 新增&autoscaling.Scale{},&StatefulSet{},&StatefulSetList{},&ControllerRevision{},&ControllerRevisionList{},&ReplicaSet{},&ReplicaSetList{},)return nil
}
4.4-编写控制器时,该采用哪个 AddToScheme
- 首先应该清楚为什么 Controller 会使用 Scheme 吧?
- 因为 Controller 需要对资源进行处理,那如何知道资源GVK和数据结构的关系呢(就是如何监测到 yaml 后形成的 CR 资源对象,并将其解码为正确的数据结构呢?)之后再进行下一步处理
- 答 —— Scheme,通过 Group 暴露的 AddToScheme 中,Scheme 便记录了 GVK 和 go struct 数据结构的转换关系(以及多版本转换等)
- 如控制器需要监控 Deployments 资源,那么就需要调用 apps 组的 AddToScheme,将 Deployment 和 go Struct 的映射关系注册到 Scheme 中,之后控制器通过该 Scheme 便能进行后续的所有操作
- (可以理解为当前 Scheme 就是个 apps 组的字典,想要啥就从中找啥)
- 当然也可以将别的 Group 注册到 Scheme 中,相当于扩大了 Scheme 的查找范围
项目 | 版本 | 用途 | 使用场景 |
---|---|---|---|
k8s.io/api | V1 | v1 版本的基础资源数据结构 | 适用于指定v1版本编写控制器 |
k8s.io/kubernetes | V1 | 默认值填充函数,v1和internal的转换函数 | 不适用于单独编写控制器 |
k8s.io/kubernetes | internal | 所有版本的中转者,记录的资源数据结构相比于单一版本更丰富 | 适用于核心组件控制器,可以适配多版本 |
- 使用
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 逻辑
}
4.5-为什么 internal
版本不需要调用metav1.AddToGroupVersion
在 Kubernetes 中,internal
和 api/v1
资源的注册逻辑有所不同,其中 internal
的 register.go
通常不调用 metav1.AddToGroupVersion
,而 api/v1
版本中会调用。这种差异主要来源于两者的用途和设计目标。以下是详细解释:
1. metav1.AddToGroupVersion
的作用
-
定义:
metav1.AddToGroupVersion
是一个辅助函数,用于在Scheme
中注册与 API 版本相关的元信息(ObjectMeta
和TypeMeta
)。 -
注册的内容:
- 该函数主要将
ObjectMeta
和TypeMeta
等元数据类型与指定的GroupVersion
绑定。 - 用于标识资源所属的 API 组和版本。
- 该函数主要将
-
典型调用: 在
api/v1
版本中,通常在addKnownTypes
函数内调用:func addKnownTypes(scheme *runtime.Scheme) error {scheme.AddKnownTypes(SchemeGroupVersion,&Deployment{},&DeploymentList{},)metav1.AddToGroupVersion(scheme, SchemeGroupVersion) // 注册元信息return nil }
2. 为什么 internal
版本不需要调用?
2.1 internal
版本没有特定的 GroupVersion
internal
类型是未版本化的资源类型,不绑定具体的 API 版本(GroupVersion
)。- 因此,
metav1.AddToGroupVersion
中的GroupVersion
绑定逻辑对internal
类型无意义。
2.2 internal
类型不会直接与 API Server 交互
internal
类型是 Kubernetes 内部使用的中间表示,仅在内存中操作。- 它们不会直接通过 API Server 进行序列化或反序列化,因此无需注册与
metav1
相关的元信息。
2.3 元信息已经通过其他方式处理
internal
类型资源的元信息(如ObjectMeta
和TypeMeta
)会自动继承其父资源的结构。- 在需要时,
internal
类型可以通过版本转换逻辑(conversion
)与外部版本(如api/v1
)关联。
3. 为什么 api/v1
需要调用?
3.1 明确绑定资源到 GroupVersion
api/v1
是版本化的外部资源类型,必须绑定到特定的GroupVersion
(如apps/v1
)。metav1.AddToGroupVersion
会将元信息类型(如ObjectMeta
)与GroupVersion
关联,用于:- 标识资源所属的 API 组和版本。
- 确保序列化和反序列化时能够正确处理这些元信息。
3.2 用于与 API Server 的交互
api/v1
类型通过 RESTful API 与 API Server 交互。- 在资源序列化为 JSON 或反序列化为 Go 结构体时,
TypeMeta
中的Kind
和APIVersion
字段依赖于GroupVersion
的绑定。
3.3 支持客户端和工具
- 许多 Kubernetes 客户端工具(如
kubectl
)依赖于metav1
中的元信息来识别资源的版本和组。 metav1.AddToGroupVersion
的注册为这些工具提供了必要的元数据支持。
4. 示例对比
internal 版本的 register.go
func addKnownTypes(scheme *runtime.Scheme) error {scheme.AddKnownTypes(SchemeGroupVersion,&Deployment{},&DeploymentList{},)return nil // 不需要调用 metav1.AddToGroupVersion
}
api/v1 版本的 register.go
func addKnownTypes(scheme *runtime.Scheme) error {scheme.AddKnownTypes(SchemeGroupVersion,&Deployment{},&DeploymentList{},)metav1.AddToGroupVersion(scheme, SchemeGroupVersion) // 绑定 metav1 信息return nil
}
5. 总结
特性 | internal 版本 | api/v1 版本 |
---|---|---|
是否有具体的 GroupVersion | 无,需要通过转换逻辑映射到外部版本 | 有,明确绑定到 GroupVersion |
是否直接与 API Server 交互 | 否,内部使用 | 是,资源会通过 RESTful API 交互 |
是否需要元信息注册 | 否,metav1.AddToGroupVersion 无意义 | 是,需要注册以支持元信息处理 |
用途 | Kubernetes 内部类型的中间表示(性能优化) | 对外的版本化资源,支持客户端和 API Server |
因此,internal
版本的 register.go
没有调用 metav1.AddToGroupVersion
是合理的,因为 internal
类型不需要绑定 GroupVersion
,也不需要元信息的版本化支持。
Q6 | AddToGroupVersion 会更新 Scheme 哪些字段
// 路径 k8s.io/apimachinery/pkg/apis/meta/v1/register.go// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}// Unversioned is group version for unversioned API objects
// TODO: this should be v1 probably
var Unversioned = schema.GroupVersion{Group: "", Version: "v1"}// WatchEventKind is name reserved for serializing watch events.
const WatchEventKind = "WatchEvent"// AddToGroupVersion registers common meta types into schemas.
func AddToGroupVersion(scheme *runtime.Scheme, groupVersion schema.GroupVersion) {scheme.AddKnownTypeWithName(groupVersion.WithKind(WatchEventKind), &WatchEvent{})scheme.AddKnownTypeWithName(schema.GroupVersion{Group: groupVersion.Group, Version: runtime.APIVersionInternal}.WithKind(WatchEventKind),&InternalEvent{},)// Supports legacy code paths, most callers should use metav1.ParameterCodec for nowscheme.AddKnownTypes(groupVersion, optionsTypes...)// Register Unversioned types under their own special groupscheme.AddUnversionedTypes(Unversioned,&Status{},&APIVersions{},&APIGroupList{},&APIGroup{},&APIResourceList{},)// register manually. This usually goes through the SchemeBuilder, which we cannot use here.utilruntime.Must(RegisterConversions(scheme))utilruntime.Must(RegisterDefaults(scheme))
}// 路径 k8s.io/apimachinery/pkg/runtime/scheme.go
func (s *Scheme) AddUnversionedTypes(version schema.GroupVersion, types ...Object) {s.addObservedVersion(version)s.AddKnownTypes(version, types...)for _, obj := range types {// 获取对应的 go structt := reflect.TypeOf(obj).Elem()// 获取 gvk,t.Name 是go struct 的名称gvk := version.WithKind(t.Name())s.unversionedTypes[t] = gvkif old, ok := s.unversionedKinds[gvk.Kind]; ok && t != old {panic(fmt.Sprintf("%v.%v has already been registered as unversioned kind %q - kind name must be unique in scheme %q", old.PkgPath(), old.Name(), gvk, s.schemeName))}s.unversionedKinds[gvk.Kind] = t}
}func (s *Scheme) addObservedVersion(version schema.GroupVersion) {if len(version.Version) == 0 || version.Version == APIVersionInternal {return}for _, observedVersion := range s.observedVersions {if observedVersion == version {return}}s.observedVersions = append(s.observedVersions, version)
}
在 AddToGroupVersion
函数中,以下操作会将内容注册到 Scheme
的相应字段中:
1. gvkToType
和 typeToGVK
相关代码:
scheme.AddKnownTypeWithName(groupVersion.WithKind(WatchEventKind), &WatchEvent{})
scheme.AddKnownTypeWithName(schema.GroupVersion{Group: groupVersion.Group, Version: runtime.APIVersionInternal}.WithKind(WatchEventKind),&InternalEvent{},
)
scheme.AddKnownTypes(groupVersion, optionsTypes...)
注册内容:
-
gvkToType
:
-
注册
GroupVersionKind
到对应 Go 类型的映射。 -
示例:
GVK: meta.k8s.io/v1, Kind=WatchEvent -> Type: *WatchEvent GVK: meta.k8s.io/internal, Kind=WatchEvent -> Type: *InternalEvent
-
-
typeToGVK
:
-
注册 Go 类型到对应
GroupVersionKind
的映射。 -
示例:
Type: *WatchEvent -> GVK: [meta.k8s.io/v1, Kind=WatchEvent] Type: *InternalEvent -> GVK: [meta.k8s.io/internal, Kind=WatchEvent]
-
2. unversionedTypes
相关代码:
scheme.AddUnversionedTypes(Unversioned,&Status{},&APIVersions{},&APIGroupList{},&APIGroup{},&APIResourceList{},
)
注册内容:
-
无版本类型
:
-
记录不进行版本转换的资源类型。
-
示例:
Type: *Status -> GVK: v1, Kind=Status Type: *APIVersions -> GVK: v1, Kind=APIVersions Type: *APIGroupList -> GVK: v1, Kind=APIGroupList
-
3. unversionedKinds
相关代码:
scheme.AddUnversionedTypes(Unversioned,&Status{},&APIVersions{},&APIGroupList{},&APIGroup{},&APIResourceList{},
)
注册内容:
-
无版本 Kind
:
-
注册不依赖特定版本的资源
Kind
。 -
示例:
Kind: Status -> Type: *Status Kind: APIVersions -> Type: *APIVersions Kind: APIGroupList -> Type: *APIGroupList
-
4. converter
相关代码:
utilruntime.Must(RegisterConversions(scheme))
注册内容:
-
转换函数
:
-
将
GroupVersionKind
的转换函数注册到converter
。 -
用于支持内部版本与外部版本的字段映射和数据转换。
-
示例:
Conversion: meta.k8s.io/v1/WatchEvent <-> meta.k8s.io/internal/WatchEvent
-
5. defaulterFuncs
相关代码:
utilruntime.Must(RegisterDefaults(scheme))
注册内容:
-
默认值函数
:
- 为资源注册默认值设置逻辑。
- 示例:
- 如果
WatchEvent
类型未设置某字段,注册的默认值函数会填充该字段。
- 如果
总结
字段 | 注册内容 |
---|---|
gvkToType | 将 GroupVersionKind 映射到对应的 Go 类型。 |
typeToGVK | 将 Go 类型映射到可能的多个 GroupVersionKind 。 |
unversionedTypes | 注册无版本资源类型(如 Status 、APIGroupList 等)。 |
unversionedKinds | 注册无版本资源的 Kind (如 Status )。 |
converter | 注册转换函数,用于不同版本之间的字段映射。 |
defaulterFuncs | 注册默认值设置函数,用于初始化未设置的字段值。 |
这些注册确保了 API 类型的序列化、反序列化、跨版本转换和默认值填充功能的正常运行。
Q7 | AddToGroupVersion 中注册 optionsTypes 的作用
// 上面AddToGroupVersion函数中存在这一条代码
// 简单来说,就是让 Scheme 知道该资源可以支持的一些操作
scheme.AddKnownTypes(groupVersion, optionsTypes...)
这段代码是 Kubernetes 中的 Scheme
注册过程的一部分,它将多种资源操作相关的 Options
类型注册到 Scheme
中。具体来说,它将与资源操作(如列出、获取、删除、创建、更新、部分更新)相关的选项类型添加到指定的 API 组和版本的 Scheme
中,以便 Kubernetes API 服务器能够在处理请求时识别和序列化这些类型。
代码解释
var optionsTypes = []runtime.Object{&ListOptions{},&GetOptions{},&DeleteOptions{},&CreateOptions{},&UpdateOptions{},&PatchOptions{},
}
optionsTypes
切片:- 这行代码定义了一个
optionsTypes
切片,包含了多个 Kubernetes 资源操作相关的结构体类型(例如:ListOptions
、GetOptions
等)。 - 每个类型都是 Kubernetes API 请求中的参数类型,用于描述特定的操作选项。
- 这些结构体通常包含一些控制资源操作的字段,如分页、筛选、排序等。
- 这行代码定义了一个
runtime.Object
:runtime.Object
是 Kubernetes 中的一个接口,表示所有 API 对象的通用类型。所有在 Kubernetes 中定义的 API 对象(包括这些Options
类型)都必须实现这个接口。- 将这些类型定义为
runtime.Object
是为了让它们能够被 Kubernetes 的 API 系统正确地识别、序列化、反序列化。
scheme.AddKnownTypes(groupVersion, optionsTypes...)
AddKnownTypes
方法:- 这个方法将类型注册到
Scheme
中。Scheme
是一个 Kubernetes 中用于管理 API 类型和版本的对象,它帮助 Kubernetes 确定如何处理不同版本的 API 类型。 groupVersion
是一个表示 API 组和版本的对象(如apps/v1
或core/v1
)。optionsTypes...
使用切片展开语法,意味着将optionsTypes
切片中的所有类型逐一传递给AddKnownTypes
方法进行注册。- 这行代码的作用是将
ListOptions
、GetOptions
、DeleteOptions
、CreateOptions
、UpdateOptions
、PatchOptions
这些类型注册到Scheme
中,使它们能够在指定的groupVersion
下使用。
- 这个方法将类型注册到
用途和示例
1. ListOptions
ListOptions
用于列出资源时的选项,通常包括分页、排序、字段选择等。例如,获取所有 Pods 时可以使用 ListOptions
来指定要获取的字段或分页信息。
示例:
listOptions := &ListOptions{Limit: 10, // 获取前10个 PodContinue: "abc123", // 分页标识符,获取分页后的 PodFieldSelector: "status.phase=Running", // 过滤条件,获取所有运行中的 Pod
}
2. GetOptions
GetOptions
用于获取资源时的附加选项。例如,获取某个 Pod 时,GetOptions
可以包含一些条件,如字段选择或某些特定的请求参数。
示例:
getOptions := &GetOptions{ResourceVersion: "12345", // 指定获取某个特定版本的资源
}
3. DeleteOptions
DeleteOptions
用于删除资源时的选项。例如,删除 Pod 时,DeleteOptions
可以包括是否强制删除、是否删除挂载的 PVC 等选项。
示例:
deleteOptions := &DeleteOptions{GracePeriodSeconds: 30, // 设置删除时的宽限期为30秒Preconditions: metav1.NewUIDPreconditions("12345678-1234-1234-1234-1234567890ab"), // 设置条件
}
4. CreateOptions
CreateOptions
用于创建资源时的附加选项。它通常包含创建时的策略,例如是否允许自定义字段或使用默认值。
示例:
createOptions := &CreateOptions{DryRun: []string{"All"}, // 创建时进行干运行,即模拟创建但不实际执行
}
5. UpdateOptions
UpdateOptions
用于更新资源时的选项,例如更新时的策略、版本控制等。
示例:
updateOptions := &UpdateOptions{FieldManager: "kubectl", // 使用的字段管理器
}
6. PatchOptions
PatchOptions
用于部分更新资源时的选项。它通常用于表示通过补丁操作更新资源时所使用的选项。
示例:
patchOptions := &PatchOptions{DryRun: []string{"All"}, // 补丁时进行干运行
}
为什么要将这些类型注册到 Scheme
?
- 序列化与反序列化:
AddKnownTypes
方法将这些类型注册到Scheme
中,使得 Kubernetes 在处理 API 请求时能够正确地序列化(转换成 JSON 或 Protobuf)和反序列化(从 JSON 或 Protobuf 转回对象)这些类型。 - 版本管理:这些类型会与特定的 API 版本关联,在 Kubernetes 不同版本之间进行转换时,
Scheme
会帮助正确地处理它们。 - 请求处理:在实际 API 请求处理中(如列出、创建、删除资源),Kubernetes 会根据客户端的请求和参数使用相应的
Options
类型来解析请求并执行相应的操作。
总结
这段代码的作用是将多种与资源操作相关的选项类型(如 ListOptions
、GetOptions
等)注册到 Scheme
中,以便 Kubernetes API 服务器在处理这些操作时能够识别和正确序列化/反序列化这些类型。这样可以确保 API 请求中的参数能够在不同版本的 API 之间正确处理,并且在资源操作(如列出、获取、创建、删除)时提供灵活的选项。
Q8 | AddToGroupVersion 中注册 optionsTypes 的作用(补充说明)
重点:
- optionsTypes 的注册,如
ListOptions
、GetOptions
、PatchOptions
等,是控制 API 操作行为( List 获取,Get 获取、Patch更新),其是类似 Restful API 的方法,表示一种动作- Options 中可以包含一些简单的控制逻辑(如分页、获取的数量相纸 limit、简单的元数据过滤 ResourceVersion 等)
- 其实对获取资源行为的控制,而不是对资源内容的控制
AddToGroupVersion
中的 scheme.AddKnownTypes(groupVersion, optionsTypes...)
的作用是将指定的类型注册到 Scheme
中,使其能够被识别并用于序列化和反序列化。这些类型主要是 API 操作选项类型,如 ListOptions
、GetOptions
等,它们不是用来直接操作资源(如 Deployment
的 spec
部分)的,而是用来控制 API 操作行为。以下是详细说明:
1. 是否只能对元数据或无版本资源操作?
不完全是,但这些类型主要与以下内容有关:
- 元数据相关操作:
- 例如通过
ListOptions
使用labelSelector
或fieldSelector
筛选资源,与元数据字段相关。 - 例如
DeleteOptions
定义删除行为的相关参数。
- 例如通过
- 无版本资源:
- 这些选项类型是 Kubernetes 中的通用资源,适用于所有 API 组和版本。
- 不直接操作资源内容:
- 例如
Deployment
的spec
部分,它是具体资源的定义和配置,与这些选项类型无直接关联。 - 它们不会直接改变资源的状态或配置。
- 例如
2. 为什么这些类型无法直接操作 spec
?
2.1 类型的职责
- 这些类型是 操作选项类型,仅用于控制 Kubernetes API 行为,例如筛选、分页、删除策略等。
- 它们不会参与具体资源的业务逻辑(如
Deployment
的创建、更新、状态变化等)。
2.2 不属于资源本身
-
例如,
Deployment
的
spec
是资源内容的一部分,描述了资源的具体配置:
spec:replicas: 3selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:1.14.2ports:- containerPort: 80
这些内容是
Deployment
定义的一部分,而不是通过
ListOptions
、
GetOptions
等选项类型直接修改的。
2.3 操作资源的 API 路径不同
- 操作
spec
通常通过特定的 API 路径,例如:GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}
获取Deployment
。PATCH /apis/apps/v1/namespaces/{namespace}/deployments/{name}
更新spec
。
- 而
ListOptions
等是通用的 API 行为控制类型,它们的用途是控制列表操作(如过滤或分页),并不直接接触spec
内容。
3. 这些类型的具体用途
3.1 用于通用 API 操作行为
- 它们通过选项参数控制操作行为,例如筛选、分页、删除传播策略等。
- 示例:
ListOptions
:控制资源列表的行为。DeleteOptions
:控制资源删除的行为。
3.2 适用于所有资源类型
- 这些选项类型是通用的,不限定于某个具体资源(如
Deployment
或Pod
),而是作用于所有支持的资源。
4. 示例:如何对 Deployment
的 spec
操作
如果要操作 Deployment
的 spec
部分,需要直接操作 Deployment
资源,而不是通过这些选项类型。以下是示例:
4.1 获取 Deployment
的 spec
deployment, err := clientset.AppsV1().Deployments("default").Get(context.TODO(), "nginx-deployment", metav1.GetOptions{})
if err != nil {panic(err)
}
fmt.Println("Replicas:", *deployment.Spec.Replicas)
4.2 更新 Deployment
的 spec
deployment, err := clientset.AppsV1().Deployments("default").Get(context.TODO(), "nginx-deployment", metav1.GetOptions{})
if err != nil {panic(err)
}// 修改 replicas
replicas := int32(5)
deployment.Spec.Replicas = &replicas// 更新 Deployment
updatedDeployment, err := clientset.AppsV1().Deployments("default").Update(context.TODO(), deployment, metav1.UpdateOptions{})
if err != nil {panic(err)
}
fmt.Println("Updated Replicas:", *updatedDeployment.Spec.Replicas)
5. 总结
AddToGroupVersion
中的类型(如ListOptions
等)主要用于控制 Kubernetes API 的操作行为,不直接操作资源内容。- 如果需要对具体资源(如
Deployment
)的spec
部分进行操作,需要通过对应的客户端 API(如Get
、Update
)处理,而不是通过这些选项类型完成。 - 选项类型适用于所有资源,而
spec
是资源定义中的特定字段,需要单独操作。
Q9 | Option 的使用(以 listOptions 为例)
要发起请求并利用 ListOptions
,你通常是在 Kubernetes API 客户端中执行列出资源的操作(如列出 Pods、Deployments 等)。ListOptions
是一种用于过滤、分页和定制列出操作的选项,它可以传递给 kubectl
或 Kubernetes Go 客户端库中的相关方法。
1. 使用 Kubernetes Go 客户端
在 Kubernetes Go 客户端中,你可以使用 ListOptions
来指定资源列出的行为,例如分页、字段选择、标签选择等。以下是如何通过 Kubernetes Go 客户端发起列出请求并使用 ListOptions
的示例:
示例:列出 Pods 并使用 ListOptions
package mainimport ("context""flag""fmt""log"metav1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/client-go/kubernetes""k8s.io/client-go/tools/clientcmd""k8s.io/client-go/util/homedir""path/filepath"
)func main() {// 获取 kubeconfig 文件路径kubeconfig := filepath.Join(homedir.HomeDir(), ".kube", "config")// 创建 Kubernetes 客户端配置config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)if err != nil {log.Fatalf("failed to build kubeconfig: %v", err)}// 创建 Kubernetes 客户端clientset, err := kubernetes.NewForConfig(config)if err != nil {log.Fatalf("failed to create Kubernetes client: %v", err)}// 使用 ListOptions 设置分页和标签选择listOptions := &metav1.ListOptions{LabelSelector: "app=myapp", // 通过标签选择器过滤Limit: 10, // 限制返回的结果为10个}// 获取 Pods 列表pods, err := clientset.CoreV1().Pods("default").List(context.TODO(), *listOptions)if err != nil {log.Fatalf("failed to list pods: %v", err)}// 打印结果fmt.Printf("Found %d pods:\n", len(pods.Items))for _, pod := range pods.Items {fmt.Printf("Pod Name: %s\n", pod.Name)}
}
2. 解释关键部分
- Kubernetes 客户端配置 (
clientcmd.BuildConfigFromFlags
):- 这里的
BuildConfigFromFlags
函数会根据 kubeconfig 文件(通常位于~/.kube/config
)创建一个客户端配置,连接到 Kubernetes 集群。
- 这里的
ListOptions
:LabelSelector
:用于筛选具有特定标签的资源,例如"app=myapp"
,这会返回所有具有app=myapp
标签的 Pods。Limit
:限制返回的资源数量,这里设为10
,表示最多返回 10 个 Pod。- 你还可以通过
FieldSelector
过滤资源,例如筛选处于某个状态的 Pod,或者指定其他选项,如Continue
(用于分页)和ResourceVersion
(用于版本控制)。
- 发起请求:
clientset.CoreV1().Pods("default").List(context.TODO(), *listOptions)
:该调用会向 Kubernetes 集群的 “default” 命名空间发起一个列出 Pods 的请求,并应用上面设置的ListOptions
。
- 处理响应:
- 返回的
pods.Items
是一个包含 Pod 对象的切片,你可以遍历它们并查看相关信息。
- 返回的
3. 使用 ListOptions
进行高级筛选
除了分页和标签选择外,ListOptions
还可以通过 FieldSelector
实现基于字段的筛选。例如,你可以筛选出所有状态为 Running
的 Pods,或者通过 resourceVersion
来获取某个版本的资源。
listOptions := &metav1.ListOptions{FieldSelector: "status.phase=Running", // 只返回状态为 Running 的 PodLimit: 10, // 限制返回的结果为 10 个
}
4. 其他 ListOptions
示例
- 分页:如果有很多资源,Kubernetes API 会返回一部分结果并包含一个
continue
字段,允许你继续获取更多结果。例如,你可以通过以下代码获取更多资源:
listOptions := &metav1.ListOptions{Limit: 10, // 每次返回最多 10 个资源Continue: "abc123", // 继续获取的分页标识符
}
- 资源版本:你可以通过
ResourceVersion
来指定列出资源时的版本,从而确保列出的资源与某个特定版本一致。
listOptions := &metav1.ListOptions{ResourceVersion: "12345", // 只列出版本为 12345 的资源
}
5. 总结
ListOptions
是 Kubernetes API 中列出资源时的参数,允许你控制返回的资源内容、过滤方式、分页和排序。- 在 Kubernetes Go 客户端中,通过
clientset.CoreV1().Pods("default").List
这样的 API 调用发起请求并传递ListOptions
。 ListOptions
可以包含多个筛选条件,如LabelSelector
、FieldSelector
、Limit
、Continue
和ResourceVersion
,使得列出操作更加灵活。
通过这种方式,你可以根据具体需求定制列出资源的行为,方便开发人员和运维人员高效管理 Kubernetes 中的资源。