网站建设公司开发,wordpress有什么功能,临淄信息港招聘,管理平台登录云原生学习路线导航页#xff08;持续更新中#xff09; 本文是 Kubernetes operator学习 系列第五篇#xff0c;主要对 k8s.io/api 和 k8s.io/apimachinery 两个项目 进行学习基于 kubernetes v1.24.0 代码分析Kubernetes operator学习系列 快捷链接 Kubernetes operator持续更新中 本文是 Kubernetes operator学习 系列第五篇主要对 k8s.io/api 和 k8s.io/apimachinery 两个项目 进行学习基于 kubernetes v1.24.0 代码分析Kubernetes operator学习系列 快捷链接 Kubernetes operator一client-go篇Kubernetes operator二CRD篇Kubernetes operator三code-generator 篇Kubernetes operator四controller-tools 篇Kubernetes operator五api 和 apimachinery 篇 1.k8s.io/api 项目
1.1.k8s.io/api 项目是什么
一开始kubernetes的 内建资源 还不太多内建资源的 结构定义都是放在项目里维护的。后来为了方便资源的管理和扩展将 所有内建资源 的结构定义文件、scheme注册文件、deepcopy等文件放入了 staging/src 目录下作为一个单独的项目维护。该项目的名称就是 k8s.io/api因此简单来说k8s.io/api 项目维护着 Kubernetes 所有内建资源 的 struct定义。
1.2.k8s.io/api 的源码分析
下图中的每一个目录都代表一个group 一个 Group 下可能会存在多个 Version 每个version下都会包含三个文件doc.go、register.go、types.go。 doc.go声明了按照 package 维度为所有 structs 提供生成的声明types.go编写资源的详细结构一般包括资源、资源List、资源Spec、资源Status 的详细定义register.go提供注册到 runtime.Scheme 的函数 以 apps/v1/types.go 为例查看其内容发现包含 GroupVersionapps/v1 下的所有Resource结构定义。 下图只截出了一部分没有截出全部。 因此我们操作内建资源的时候所有 GVK 内建资源的结构都是由 k8s.io/api 这个项目提供的。
2.k8s.io/apimachinery 项目
2.1.k8s.io/apimachinery 项目是什么
k8s.io/apimachinery 项目是一个关于Kubernetes API资源的工具集为 k8s.io/api 项目所有的资源提供下列能力。 ObjectMeta与TypeMetaSchemeRESTMapper编码与解码版本转换… 有了 k8s.io/apimachinery就可以很方便的操作 kubernetes API。
2.2.k8s.io/apimachinery 提供 TypeMeta 与 ObjectMeta
TypeMeta 与 ObjectMeta 是特别常用的两个数据结构。kubernetes 的每一个资源都会包含一个 TypeMeta、一个ObjectMeta。 TypeMeta是内嵌的转json的时候不会有嵌套结构ObjectMetajson标签就是 metadata type Pod struct {metav1.TypeMeta json:,inlinemetav1.ObjectMeta json:metadata,omitempty protobuf:bytes,1,opt,namemetadataSpec PodSpec json:spec,omitempty protobuf:bytes,2,opt,namespecStatus PodStatus json:status,omitempty protobuf:bytes,3,opt,namestatus
}TypeMeta位于 apimachinery/pkg/runtime/types.gotype TypeMeta struct {// optionalAPIVersion string json:apiVersion,omitempty yaml:apiVersion,omitempty protobuf:bytes,1,opt,nameapiVersion// optionalKind string json:kind,omitempty yaml:kind,omitempty protobuf:bytes,2,opt,namekind
}ObjectMeta位于 apimachinery/pkg/apis/meta/v1/types.gotype ObjectMeta struct {Name string json:name,omitempty protobuf:bytes,1,opt,namenameGenerateName string json:generateName,omitempty protobuf:bytes,2,opt,namegenerateNameNamespace string json:namespace,omitempty protobuf:bytes,3,opt,namenamespaceSelfLink string json:selfLink,omitempty protobuf:bytes,4,opt,nameselfLinkUID types.UID json:uid,omitempty protobuf:bytes,5,opt,nameuid,casttypek8s.io/kubernetes/pkg/types.UIDResourceVersion string json:resourceVersion,omitempty protobuf:bytes,6,opt,nameresourceVersionGeneration int64 json:generation,omitempty protobuf:varint,7,opt,namegenerationCreationTimestamp Time json:creationTimestamp,omitempty protobuf:bytes,8,opt,namecreationTimestampDeletionTimestamp *Time json:deletionTimestamp,omitempty protobuf:bytes,9,opt,namedeletionTimestampDeletionGracePeriodSeconds *int64 json:deletionGracePeriodSeconds,omitempty protobuf:varint,10,opt,namedeletionGracePeriodSecondsLabels map[string]string json:labels,omitempty protobuf:bytes,11,rep,namelabelsAnnotations map[string]string json:annotations,omitempty protobuf:bytes,12,rep,nameannotationsOwnerReferences []OwnerReference json:ownerReferences,omitempty patchStrategy:merge patchMergeKey:uid protobuf:bytes,13,rep,nameownerReferencesFinalizers []string json:finalizers,omitempty patchStrategy:merge protobuf:bytes,14,rep,namefinalizersZZZ_DeprecatedClusterName string json:clusterName,omitempty protobuf:bytes,15,opt,nameclusterNameManagedFields []ManagedFieldsEntry json:managedFields,omitempty protobuf:bytes,17,rep,namemanagedFields
}2.3.k8s.io/apimachinery 的rumetime/schema包提供 GVRK 各种数据结构
在kubernetes中为了方便描述资源或描述REST 的URL提出了5个概念 GVGroupVersionGRGroupResourceGVRGroupVersionResourceGKGroupKindGVKGroupVersionKind 其中GR、GVR都是用来描述 RESTFUL API 的GK、GVK都是用来描述资源类型的这 5种数据结构的 struct 定义都是写在 k8s.io/apimachinery/pkg/runtime/schema/group_version.go 文件中该文件中还提供了这5种数据结构相互转换的方法 其中APIVersion Kind就是我们平时写yaml看到的apiVersionGroup/Version Kind 各结构的转换方法如下 源码如下
2.4.k8s.io/apimachinery 提供 scheme 数据结构
2.4.1.资源的internal版本、external版本
2.4.1.1.资源的internal版本、external版本是什么
kubernetes的资源并非一下就确定好的是有一个发展过程的因此一个资源Kind可能在多个 GroupVersion 下同时存在。比如 Deployment在apps/v1下存在在apps/v1beta1下也存在。那么在kubernetes的开发者想要处理Deployment的时候到底应该按照哪个版本写程序呢 按理说每一种GVK都要有相应的处理方法。但是这样实在是太繁琐了维护起来不方便还会有大量重复代码 因此为每一种GK维护了一个internal版本作为中转节点。 apps/v1/Deployment 和 apps/v1beta1/Deployment 的相互转换均是先转成internal的Deployment再转成 对外的版本kubernetes的作者们只需要对 internal版本 的资源编写逻辑就可以处理所有version的资源 这种设计方式将GVK之间的 拓扑结构变成了星型结构非常巧妙。
2.4.1.2.internal版本 和 external版本 相互转换的源码位置
kubernetes/pkg/apis 中每个目录都是一个group每个group都有一个 internal 的 资源 types.go 文件 external 资源 -- internal 资源 的方法
2.4.2.scheme的作用
kubernetes的资源版本太多了没有谁专门有时间去维护还是让资源自己来注册比较方便。scheme就是为资源注册信息设计的一个数据结构每个GVK将自己的信息封装成一个scheme对象并将这个scheme对象交给APIServer统一管理API Server就能够认识这种 GVK 了在k8s.io/api 项目中每一个GV下都有一个register.go文件里面就是将当前GV下的所有Kind注册到 APIServer 的统一scheme中去。比如 staging/src/k8s.io/api/apps/v1/register.go 文件package v1import (metav1 k8s.io/apimachinery/pkg/apis/meta/v1k8s.io/apimachinery/pkg/runtimek8s.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 SchemeBuilderAddToScheme 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
}2.4.3.k8s.io/apimachinery 提供 scheme 数据结构
根据前面的描述我们知道scheme是一个数据结构它的struct其实就是 k8s.io/apimachinery 提供的在 staging/src/k8s.io/apimachinery/pkg/runtime/scheme.go 文件中有 Scheme 结构type Scheme struct {// map记录 gvk--type。其中type是通过反射的方式记录的gvkToType map[schema.GroupVersionKind]reflect.Type// map记录 type--gvktypeToGVK map[reflect.Type][]schema.GroupVersionKind// map记录 type--gvk。像pod这种只有一个version的就记录在这里。unversionedTypes map[reflect.Type]schema.GroupVersionKind// map记录 gvk--type。像pod这种只有一个version的就记录在这里。unversionedKinds map[string]reflect.Type// Map from version and resource to the corresponding func to convert// resource field labels in that version to internal version.fieldLabelConversionFuncs map[schema.GroupVersionKind]FieldLabelConversionFunc// map记录默认方法。为某一个具体的type设置默认值defaulterFuncs map[reflect.Type]func(interface{})// 转换器converter *conversion.Converter// 记录version的优先级。当没有选择version的时候优先使用谁versionPriority map[string][]string// observedVersions keeps track of the order weve seen versions during type registrationobservedVersions []schema.GroupVersion// schemeName is the name of this scheme. If you dont specify a name, the stack of the NewScheme caller will be used.// This is useful for error reporting to indicate the origin of the scheme.schemeName string
}Scheme结构中所有的字段首字母都是小写的即非导出的外界无法访问。为此staging/src/k8s.io/apimachinery/pkg/runtime/scheme.go 文件中还提供了一个方法 NewScheme()用于初始化一个空的Scheme对象func NewScheme() *Scheme {s : Scheme{gvkToType: map[schema.GroupVersionKind]reflect.Type{},typeToGVK: map[reflect.Type][]schema.GroupVersionKind{},unversionedTypes: map[reflect.Type]schema.GroupVersionKind{},unversionedKinds: map[string]reflect.Type{},fieldLabelConversionFuncs: map[schema.GroupVersionKind]FieldLabelConversionFunc{},defaulterFuncs: map[reflect.Type]func(interface{}){},versionPriority: map[string][]string{},schemeName: naming.GetNameFromCallsite(internalPackages...),}s.converter conversion.NewConverter(nil)// Enable couple default conversions by default.utilruntime.Must(RegisterEmbeddedConversions(s))utilruntime.Must(RegisterStringConversions(s))return s
}此外staging/src/k8s.io/apimachinery/pkg/runtime/scheme.go 文件还提供了很多方法用于将GVK注册到Scheme对象中。用的比较多的是AddKnownTypes()方法func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {s.addObservedVersion(gv)for _, obj : range types {t : reflect.TypeOf(obj)if t.Kind() ! reflect.Ptr {panic(All types must be pointers to structs.)}t t.Elem()s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)}
}2.5.3.Scheme结构提供的常用方法
2.5.3.1.AddKnownTypes
方法签名func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object)方法功能向 scheme 中注册GVK参数1 gv 表示 GroupVersion参数2 types 是具体的 Kind 类型举例staging/src/k8s.io/api/apps/v1/register.go 文件中使用 AddKnownTypes 方法将apps/v1下的所有Kind都注册到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
}2.5.3.2.KnownTypes
方法签名func (s *Scheme) KnownTypes(gv schema.GroupVersion) map[string]reflect.Type方法功能获取指定GV下所有Kind的Type类型举例types : Scheme.KnownTypes(schema.GroupVersion{Group: apps,Version: v1,
})2.5.3.3.VersionsForGroupKind
方法签名func (s *Scheme) VersionsForGroupKind(gk schema.GroupKind) []schema.GroupVersion方法功能获取指定GK的所有Versions并以GV列表的形式返回举例groupVersions : Scheme.VersionsForGroupKind(schema.GroupKind{Group: apps,Kind: Deployment,
})
// 输出
[apps/v1 apps/v1beta1 apps/v1beta2]2.5.3.4.ObjectKinds
方法签名func (s *Scheme) ObjectKinds(obj Object) ([]schema.GroupVersionKind, bool, error)方法功能获取指定object 的 所有可能的 group、version、kind 值并以 GVK 列表的形式返回举例gvks, notVersioned, err : Scheme.ObjectKinds(appsv1.Deployment{})
// 输出
[apps/v1 Deployment]2.5.3.5.New
方法签名func (s *Scheme) New(kind schema.GroupVersionKind) (Object, error)方法功能根据指定的GVK创建该资源的一个对象举例deployment, err : Scheme.New(schema.GroupVersionKind{Group: apps,Version: v1,Kind: Deployment,
})2.5.3.6.AddConversionFunc
方法源码func (s *Scheme) AddConversionFunc(a, b interface{}, fn conversion.ConversionFunc) error {return s.converter.RegisterUntypedConversionFunc(a, b, fn)
}该方法用于向scheme中注册 不同资源 的自定义转换器。
2.5.k8s.io/apimachinery 提供 RESTMapper 结构
2.5.1.理解GVR和GVK的用途
在 上面2.3 中提到k8s.io/apimachinery 提供了 GR/GVR、GK/GVK 等数据结构。GR和GVR 负责对接 RESTful 风格的url路径GK和GVK 负责确定一个具体的kubernetes资源GVR举例 用户想要获取 apps组下、v1版本的 deployments如何编写url地址– GET /apis/apps/v1/deployments这个url中就可以使用 GVR 描述group为appsversion为v1Resource为deployments GVK举例 当kubernetes的代码中想要操作一个资源的时候如何找到资源的struct 结构通过GVK去找比如 apps/v1/Deployment就可以确定 group为appsversion为v1kind为Deployment就可以找到这个资源的struct
2.5.2.RESTMapper是什么
当用户使用 REST风格 的 url 访问资源时kubernetes如何确定需要操作哪一个GVK呢REST风格 的 url可以从中得到 GVR只需要完成 GVR 到 GVK 的转换就可以了因此apimachinery维护了一个数据结构 RESTMapper记录 GVR 和 GVK 的映射关系type RESTMapping struct {// Resource is the GroupVersionResource (location) for this endpointResource schema.GroupVersionResource// GroupVersionKind is the GroupVersionKind (data format) to submit to this endpointGroupVersionKind schema.GroupVersionKind// Scope contains the information needed to deal with REST Resources that are in a resource hierarchyScope RESTScope
}另外apimachinery还提供了一个接口 RESTMapper接口中提供了 将 GVR 转成 GVK 的方法 其中KindFor 和 KindsFor 就是将 GVR 转成 GVK 的方法 type RESTMapper interface {// KindFor takes a partial resource and returns the single match. Returns an error if there are multiple matchesKindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error)// KindsFor takes a partial resource and returns the list of potential kinds in priority orderKindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error)// ResourceFor takes a partial resource and returns the single match. Returns an error if there are multiple matchesResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error)// ResourcesFor takes a partial resource and returns the list of potential resource in priority orderResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error)// RESTMapping identifies a preferred resource mapping for the provided group kind.RESTMapping(gk schema.GroupKind, versions ...string) (*RESTMapping, error)// RESTMappings returns all resource mappings for the provided group kind if no// version search is provided. Otherwise identifies a preferred resource mapping for// the provided version(s).RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error)ResourceSingularizer(resource string) (singular string, err error)
}2.5.3.DefaultRESTMapper
RESTMapper接口有一个默认的实现 DefaultRESTMapper 位于 staging/src/k8s.io/apimachinery/pkg/api/meta/restmapper.go type DefaultRESTMapper struct {defaultGroupVersions []schema.GroupVersionresourceToKind map[schema.GroupVersionResource]schema.GroupVersionKindkindToPluralResource map[schema.GroupVersionKind]schema.GroupVersionResourcekindToScope map[schema.GroupVersionKind]RESTScopesingularToPlural map[schema.GroupVersionResource]schema.GroupVersionResourcepluralToSingular map[schema.GroupVersionResource]schema.GroupVersionResource
}staging/src/k8s.io/apimachinery/pkg/api/meta/restmapper.go 中还提供了一个 NewDefaultRESTMapper 方法用于新建一个DefaultRESTMapper
2.6.k8s.io/apimachinery 提供序列化、编解码能力
2.6.1.k8s.io/apimachinery 的 runtime.serializer 包
k8s.io/apimachinery 中关于 序列化 和 编解码 的代码大都在 staging/src/k8s.io/apimachinery/pkg/runtime/serializer 包下 json、protobuf、yaml包分别提供了对应格式的序列化器共3种序列化器
2.6.2.k8s.io/apimachinery 提供了序列化的通用接口
staging/src/k8s.io/apimachinery/pkg/runtime/interfaces.go 文件中提供了序列化的通用接口 Serializer。Serializer接口提供了编解码能力。type Serializer interface {EncoderDecoder
}Encoder是编码器接口还是在 staging/src/k8s.io/apimachinery/pkg/runtime/interfaces.go 文件中type Encoder interface {Encode(obj Object, w io.Writer) errorIdentifier() Identifier
}Decoder是解码器接口还是在 staging/src/k8s.io/apimachinery/pkg/runtime/interfaces.go 文件中type Decoder interface {Decode(data []byte, defaults *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error)
}2.6.3.json 序列化器
staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json.go 文件中提供了json序列化器type Serializer struct {meta MetaFactoryoptions SerializerOptionscreater runtime.ObjectCreatertyper runtime.ObjectTyperidentifier runtime.Identifier
}json序列化器 实现了 runtime.interface.go 中的Serializer接口实现了 Encode、Decode方法创建一个json序列化器有多个方法 NewSerializer、NewSerializerWithOptions
2.6.4.yaml 序列化器
staging/src/k8s.io/apimachinery/pkg/runtime/serializer/yaml/yaml.go 文件中提供了yaml序列化器type yamlSerializer struct {// the nested serializerruntime.Serializer
}yaml序列化器 实现了 runtime.interface.go 中的Serializer接口
2.6.5.protobuf 序列化器
staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go 文件中提供了protobuf序列化器type Serializer struct {prefix []bytecreater runtime.ObjectCreatertyper runtime.ObjectTyper
}protobuf序列化器 实现了 runtime.interface.go 中的Serializer接口
2.7.k8s.io/apimachinery 提供 不同资源 相互转换能力
scheme提供了AddConversionFunc方法用于向scheme中注册 不同资源 的自定义转换器。举例创建了一个Scheme对象名为scheme。我们就可以通过下面的方法注册 appsv1.Deployment 与 appsv1beta1.Deployment 的相互转换方法scheme.AddConversionFunc((*appsv1.Deployment)(nil),(*appsv1beta1.Deployment)(nil),func(a, b interface{}, scope conversion.Scope) error{v1deploy : a.(*appsv1.Deployment)v1beta1deploy : b.(*appsv1beta1.Deployment)// make conversion herereturn nil
})3.参考博客
Kubernetes学习笔记[第5章]API Machineryruntime/Serializer源码分析