摘要:我们先将上面的接口解析放放,先看下是如何初始化的路径定义了,再看路径定义空的创建,用于不同版本对象转换增加一些转换函数上面就创建了一个空的。其实就是向添加了转换函数,比如将转换为,将转换为。
源码版本
Kubernetes v1.5.0
简介k8s里面有各种资源,如Pod、Service、RC、namespaces等资源,用户操作的其实也就是这一大堆资源。但这些资源并不是杂乱无章的,使用了GroupVersion的方式组织在一起。每一种资源都属于一个Group,而资源还有版本之分,如v1、v1beta1等。
k8s目前正在使用的API groups:
"core" group:它的REST path是api/v1
"extensions" group:它的REST path是/apis/extensions/v1beta1
"autoscaling", "abac" ...
服务器的URL的格式:/prefix/group/version/... (例如:/apis/extensions/v1beta1)
重要结构体APIGroupVersion:对API资源的组织,里面包含了Storage、GroupVersion、Mapper、Serializer、Convertor等成员。Storage是etcd的接口,这是一个map类型,每一种资源都会与etcd建立一个连接;GroupVersion表示该APIGroupVersion属于哪个Group、哪个version;Serializer用于序列化,反序列化;Convertor提供各个不同版本进行转化的接口;Mapper实现了RESTMapper接口。
type APIGroupVersion struct { // key存在对象的url,value是一个rest.Storage,用于对接etcd存储 Storage map[string]rest.Storage // 该group的prefix,例如核心组的Root是"/api" Root string // 包含类似"api/v1"这样的string,用于标识这个实例 GroupVersion unversioned.GroupVersion // OptionsExternalVersion controls the Kubernetes APIVersion used for common objects in the apiserver // schema like api.Status, api.DeleteOptions, and api.ListOptions. Other implementors may // define a version "v1beta1" but want to use the Kubernetes "v1" internal objects. If // empty, defaults to GroupVersion. OptionsExternalVersion *unversioned.GroupVersion // 关键性成员 Mapper meta.RESTMapper // 对象序列化和反序列化器 Serializer runtime.NegotiatedSerializer ParameterCodec runtime.ParameterCodec // 以下4个都是被赋值为Scheme结构 Typer runtime.ObjectTyper Creater runtime.ObjectCreater // 相互转换任意api版本的对象,需要事先注册转换函数 Convertor runtime.ObjectConvertor Copier runtime.ObjectCopier Linker runtime.SelfLinker // 用于访问许可控制 Admit admission.Interface Context api.RequestContextMapper MinRequestTimeout time.Duration // SubresourceGroupVersionKind contains the GroupVersionKind overrides for each subresource that is // accessible from this API group version. The GroupVersionKind is that of the external version of // the subresource. The key of this map should be the path of the subresource. The keys here should // match the keys in the Storage map above for subresources. SubresourceGroupVersionKind map[string]unversioned.GroupVersionKind // ResourceLister is an interface that knows how to list resources // for this API Group. ResourceLister APIResourceLister }
APIGroupVersion的创建接口是pkg/genericapiserver/genericapiserver.go中的newAPIGroupVersion()接口,在接口在创建APIGroupVersion还用到了好几个别的结构:APIGroupInfo、Scheme、GroupMeta。下面一个一个介绍:
APIGroupInfo:
type APIGroupInfo struct { // 该Group的元信息 GroupMeta apimachinery.GroupMeta // 不同版本的所有的Storage VersionedResourcesStorageMap map[string]map[string]rest.Storage // OptionsExternalVersion controls the APIVersion used for common objects in the // schema like api.Status, api.DeleteOptions, and api.ListOptions. Other implementors may // define a version "v1beta1" but want to use the Kubernetes "v1" internal objects. // If nil, defaults to groupMeta.GroupVersion. // TODO: Remove this when https://github.com/kubernetes/kubernetes/issues/19018 is fixed. OptionsExternalVersion *unversioned.GroupVersion // core group的话,对应的就是api.Scheme Scheme *runtime.Scheme // NegotiatedSerializer controls how this group encodes and decodes data NegotiatedSerializer runtime.NegotiatedSerializer // ParameterCodec performs conversions for query parameters passed to API calls ParameterCodec runtime.ParameterCodec // 所有resources信息,key就是resource的path // 比如:key为"replicationcontrollers/scale",GroupVersionKind: autoscaling, v1, Scale SubresourceGroupVersionKind map[string]unversioned.GroupVersionKind }
Scheme: 用于API资源之间的序列化、反序列化、版本转换。Scheme里面还有好几个map,前面的结构体存储的都是unversioned.GroupVersionKind、unversioned.GroupVersion这些东西,这些东西本质上只是表示资源的字符串标识,Scheme存储了对应着标志的具体的API资源的结构体,即relect.Type
type Scheme struct { // versionMap allows one to figure out the go type of an object with // the given version and name. gvkToType map[unversioned.GroupVersionKind]reflect.Type // typeToGroupVersion allows one to find metadata for a given go object. // The reflect.Type we index by should *not* be a pointer. typeToGVK map[reflect.Type][]unversioned.GroupVersionKind // unversionedTypes are transformed without conversion in ConvertToVersion. unversionedTypes map[reflect.Type]unversioned.GroupVersionKind // unversionedKinds are the names of kinds that can be created in the context of any group // or version // TODO: resolve the status of unversioned types. 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[string]map[string]FieldLabelConversionFunc // defaulterFuncs is an array of interfaces to be called with an object to provide defaulting // the provided object must be a pointer. defaulterFuncs map[reflect.Type]func(interface{}) // converter stores all registered conversion functions. It also has // default coverting behavior. converter *conversion.Converter // cloner stores all registered copy functions. It also has default // deep copy behavior. cloner *conversion.Cloner }
GroupMeta: 主要包括Group的元信息,里面的成员RESTMapper,与APIGroupVersion一样,其实APIGroupVersion的RESTMapper直接取之于GroupMeta的RESTMapper.一个Group可能包含多个版本,存储在GroupVersion中,而GroupVersion是默认存储在etcd中的版本。
type GroupMeta struct { // 默认版本 GroupVersion unversioned.GroupVersion // 该Group中可能会有多个版本,该字段就包含了所有的versions GroupVersions []unversioned.GroupVersion // 用于编解码 Codec runtime.Codec // SelfLinker can set or get the SelfLink field of all API types. // TODO: when versioning changes, make this part of each API definition. // TODO(lavalamp): Combine SelfLinker & ResourceVersioner interfaces, force all uses // to go through the InterfacesFor method below. SelfLinker runtime.SelfLinker // 用于类型,对象之间的转换 RESTMapper meta.RESTMapper // InterfacesFor returns the default Codec and ResourceVersioner for a given version // string, or an error if the version is not known. // TODO: make this stop being a func pointer and always use the default // function provided below once every place that populates this field has been changed. InterfacesFor func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) // InterfacesByVersion stores the per-version interfaces. InterfacesByVersion map[unversioned.GroupVersion]*meta.VersionInterfaces }
RESTMapper: 用于管理所有对象的信息。外部要获取的话,直接通过version,group获取到RESTMapper,然后通过kind类型可以获取到对应的信息。该RESTMapper其实是实现了一个DefaultRESTMapper结构。
type DefaultRESTMapper struct { defaultGroupVersions []unversioned.GroupVersion resourceToKind map[unversioned.GroupVersionResource]unversioned.GroupVersionKind kindToPluralResource map[unversioned.GroupVersionKind]unversioned.GroupVersionResource kindToScope map[unversioned.GroupVersionKind]RESTScope singularToPlural map[unversioned.GroupVersionResource]unversioned.GroupVersionResource pluralToSingular map[unversioned.GroupVersionResource]unversioned.GroupVersionResource interfacesFunc VersionInterfacesFunc // aliasToResource is used for mapping aliases to resources aliasToResource map[string][]string }
APIRegistrationManager:这个结构体主要提供了已经"registered"的概念,将所有已经注册的,已经激活的,第三方的的GroupVersions进行了汇总,还包括了各个GroupVersion的GroupMeta(元数据)。
type APIRegistrationManager struct { // 所以已经registered的GroupVersions registeredVersions map[unversioned.GroupVersion]struct{} // 第三方注册的GroupVersions,这些都向apiServer动态注册的 thirdPartyGroupVersions []unversioned.GroupVersion // 所有已经enable的GroupVersions,可以通过EnableVersions()将要enable的GroupVersion加入进来。 // 只有enable了,才能使用对应的GroupVersion enabledVersions map[unversioned.GroupVersion]struct{} // 所有groups的GroupMeta groupMetaMap map[string]*apimachinery.GroupMeta // 跟环境变量"KUBE_API_VERSIONS"有关 envRequestedVersions []unversioned.GroupVersion }APIRegistrationManager初始化
该结构的路径:pkg/apimachinery/registered/registered.go
在该文件中我们能看到初始化了一个DefaultAPIRegistrationManager对象:
var ( DefaultAPIRegistrationManager = NewOrDie(os.Getenv("KUBE_API_VERSIONS")) )
进入NewOrDie()接口看下:
func NewOrDie(kubeAPIVersions string) *APIRegistrationManager { m, err := NewAPIRegistrationManager(kubeAPIVersions) if err != nil { glog.Fatalf("Could not construct version manager: %v (KUBE_API_VERSIONS=%q)", err, kubeAPIVersions) } return m } func NewAPIRegistrationManager(kubeAPIVersions string) (*APIRegistrationManager, error) { m := &APIRegistrationManager{ registeredVersions: map[unversioned.GroupVersion]struct{}{}, thirdPartyGroupVersions: []unversioned.GroupVersion{}, enabledVersions: map[unversioned.GroupVersion]struct{}{}, groupMetaMap: map[string]*apimachinery.GroupMeta{}, envRequestedVersions: []unversioned.GroupVersion{}, } // 如果环境变量KUBE_API_VERSIONS进行了设置的话,进行遍历 if len(kubeAPIVersions) != 0 { // 通过逗号进行分隔 for _, version := range strings.Split(kubeAPIVersions, ",") { // 解析version并转换成GroupVersion格式 // 一般这里的version是group/version格式,比如"/api/v1" gv, err := unversioned.ParseGroupVersion(version) if err != nil { return nil, fmt.Errorf("invalid api version: %s in KUBE_API_VERSIONS: %s.", version, kubeAPIVersions) } // 然后将该gv放入envRequestedVersions m.envRequestedVersions = append(m.envRequestedVersions, gv) } } // 否则返回一个空的APIRegistrationManager return m, nil }
瞅了下我们正在使用的环境,没有配置KUBE_API_VERSIONS,即返回了一个空的结构,还提供了好多方法。
var ( ValidateEnvRequestedVersions = DefaultAPIRegistrationManager.ValidateEnvRequestedVersions AllPreferredGroupVersions = DefaultAPIRegistrationManager.AllPreferredGroupVersions RESTMapper = DefaultAPIRegistrationManager.RESTMapper GroupOrDie = DefaultAPIRegistrationManager.GroupOrDie AddThirdPartyAPIGroupVersions = DefaultAPIRegistrationManager.AddThirdPartyAPIGroupVersions IsThirdPartyAPIGroupVersion = DefaultAPIRegistrationManager.IsThirdPartyAPIGroupVersion RegisteredGroupVersions = DefaultAPIRegistrationManager.RegisteredGroupVersions IsRegisteredVersion = DefaultAPIRegistrationManager.IsRegisteredVersion IsRegistered = DefaultAPIRegistrationManager.IsRegistered Group = DefaultAPIRegistrationManager.Group EnabledVersionsForGroup = DefaultAPIRegistrationManager.EnabledVersionsForGroup EnabledVersions = DefaultAPIRegistrationManager.EnabledVersions IsEnabledVersion = DefaultAPIRegistrationManager.IsEnabledVersion IsAllowedVersion = DefaultAPIRegistrationManager.IsAllowedVersion EnableVersions = DefaultAPIRegistrationManager.EnableVersions RegisterGroup = DefaultAPIRegistrationManager.RegisterGroup RegisterVersions = DefaultAPIRegistrationManager.RegisterVersions InterfacesFor = DefaultAPIRegistrationManager.InterfacesFor )
在分析apiServer的启动流程的时候,你会发现初始化ServerRunOptions对象时,用到了好多上面的变量,比如:
路径:pkg/genericapiserver/options/server_run_options.go
func NewServerRunOptions() *ServerRunOptions { return &ServerRunOptions{ AdmissionControl: "AlwaysAdmit", 。。。 // 这里就使用了AllPreferredGroupVersions接口 DefaultStorageVersions: registered.AllPreferredGroupVersions(), 。。。 StorageVersions: registered.AllPreferredGroupVersions(), } }
上面就使用到了registered.AllPreferredGroupVersions()接口,顺便看下接口具体实现:
func (m *APIRegistrationManager) AllPreferredGroupVersions() string { // 如果没有注册groupMeta的话,这里就==0 // 不过不可能没有注册,至于在哪里进行注册就得看下后面介绍的GroupMeta初始化了 if len(m.groupMetaMap) == 0 { return "" } var defaults []string for _, groupMeta := range m.groupMetaMap { defaults = append(defaults, groupMeta.GroupVersion.String()) } sort.Strings(defaults) return strings.Join(defaults, ",") }
该接口比较简单,就是从m.groupMetaMap中取出所有的groupMeta,然后通过逗号拼接成"group1/version1,group2/version2,..."的字符串。
这里可以想一下,既然有list,那总得有groupMeta啊。而我们看APIRegistrationManager的初始化,如果没有设置KUBE_API_VERSIONS环境变量的话,根本就没有groupMeta。
既然不可能没有groupMeta,那肯定得从别的地方进行register & enable。我们可以从APIRegistrationManager提供的RegisterGroup方法入手:
func (m *APIRegistrationManager) RegisterGroup(groupMeta apimachinery.GroupMeta) error { groupName := groupMeta.GroupVersion.Group if _, found := m.groupMetaMap[groupName]; found { return fmt.Errorf("group %v is already registered", m.groupMetaMap) } m.groupMetaMap[groupName] = &groupMeta return nil }
该RegisterGroup接口的入参就是GroupMeta,所以我们得继续查看该结构的初始化了。
GroupMeta初始化k8s现阶段,API一共分为13个Group:Core、apps、authentication、authorization、autoscaling、batch、certificates、componentconfig、extensions、imagepolicy、policy、rbac、storage。其中Core的Group Name为空,它包含的API是最核心的API,如Pod、Service等,它的代码位于pkg/api下面,其它12个Group代码位于pkg/apis。每个目录下都有一个install目录,里面有一个install.go文件,接着通过init()负责初始化。这些程序都是通过下列文件进行import:
路径: pkg/master/import_known_versions.go
package master // These imports are the API groups the API server will support. import ( "fmt" _ "k8s.io/kubernetes/pkg/api/install" "k8s.io/kubernetes/pkg/apimachinery/registered" _ "k8s.io/kubernetes/pkg/apis/apps/install" _ "k8s.io/kubernetes/pkg/apis/authentication/install" _ "k8s.io/kubernetes/pkg/apis/authorization/install" _ "k8s.io/kubernetes/pkg/apis/autoscaling/install" _ "k8s.io/kubernetes/pkg/apis/batch/install" _ "k8s.io/kubernetes/pkg/apis/certificates/install" _ "k8s.io/kubernetes/pkg/apis/componentconfig/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install" _ "k8s.io/kubernetes/pkg/apis/imagepolicy/install" _ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/rbac/install" _ "k8s.io/kubernetes/pkg/apis/storage/install" )
一共import了13个group。其中"k8s.io/kubernetes/pkg/api/install"就是Core Group,我们就以它为例,查看下对应的install.go文件。
路径: pkg/api/install/install.go
var availableVersions = []unversioned.GroupVersion{v1.SchemeGroupVersion} func init() { // 进行Versions注册,其实就是存入APIRegistrationManager.registeredVersions中 registered.RegisterVersions(availableVersions) externalVersions := []unversioned.GroupVersion{} for _, v := range availableVersions { // 判断下是否已经注册,并追加成一个切片 if registered.IsAllowedVersion(v) { externalVersions = append(externalVersions, v) } } if len(externalVersions) == 0 { glog.V(4).Infof("No version is registered for group %v", api.GroupName) return } // 再进行enable,其实就是存入APIRegistrationManager.enabledVersions if err := registered.EnableVersions(externalVersions...); err != nil { glog.V(4).Infof("%v", err) return } // 该接口比较关键,进行多带带介绍 if err := enableVersions(externalVersions); err != nil { glog.V(4).Infof("%v", err) return } }
首先定义了一个切片availableVersions,里面只有一个元素v1.SchemeGroupVersion:
const GroupName = "" var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1"}
根据该元素的定义,可以看出availableVersions就定义了一个GroupName为空,Version是"v1"的GroupVersion。接着把该GroupVersion放入APIRegistrationManager的registeredVersions和enabledVersions中。
registered的几个接口实现比较简单不进行介绍了,但是执行的enableVersions()是重头戏,我们继续深入:
func enableVersions(externalVersions []unversioned.GroupVersion) error { // 字面意思:将所有的Versions添加到Scheme // 又牵扯到Scheme,后面会介绍Scheme的初始化 // 越深入看牵扯出的概念越多,该接口也很重要,需要耐心层层挖掘 addVersionsToScheme(externalVersions...) // 将一个GroupVersion作为默认的,即"/api/v1" preferredExternalVersion := externalVersions[0] // 就是这里! 进行了GroupMeta的初始化。这就是我们这小节要看的关键 groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, // RESTMapper也是关键所在,下面也会单做一节进行介绍 RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, } // 前面都是register和enable了versions,这里才是进行了Group的register // 该接口其实就是以第一个GroupVersion的groupName为key,groupMeta为value // 对APIRegistrationManager的groupMetaMap,进行了赋值 if err := registered.RegisterGroup(groupMeta); err != nil { return err } return nil }
到这步,我们再结合之前APIRegistrationManager的初始化,就能知道groupMetaMap中应该有了好几组groupMeta。那在ServerRunOptions对象初始化中调用的registered.AllPreferredGroupVersions()接口,能返回好几个DefaultStorageVersions,至少肯定有"/api/v1"。至于别的groupMeta,需要再看下别的install.go,大同小异就不展开一个一个讲了。
groupMeta的初始化虽然结束了,但是这里又引出一个关键Scheme,那么继续下一小节吧。。
Scheme初始化在上一节介绍enableVersions()函数时,第一行便是调用了addVersionsToScheme(externalVersions...),将GroupVersions加到Scheme。我们就来看下该接口:
func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) { // add the internal version to Scheme if err := api.AddToScheme(api.Scheme); err != nil { // Programmer error, detect immediately panic(err) } // add the enabled external versions to Scheme for _, v := range externalVersions { if !registered.IsEnabledVersion(v) { glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v) continue } switch v { case v1.SchemeGroupVersion: if err := v1.AddToScheme(api.Scheme); err != nil { // Programmer error, detect immediately panic(err) } } } }
接口中我们可以看到AddToScheme(api.Scheme)都是将GroupVersion加入到api.Scheme。我们先将上面的接口解析放放,先看下api.Scheme是如何初始化的:
路径:pkg/api/register.go
var Scheme = runtime.NewScheme()
定义了Scheme,再看NewScheme():
路径:pkg/runtime/scheme.go
func NewScheme() *Scheme { // 定义空的Scheme s := &Scheme{ gvkToType: map[unversioned.GroupVersionKind]reflect.Type{}, typeToGVK: map[reflect.Type][]unversioned.GroupVersionKind{}, unversionedTypes: map[reflect.Type]unversioned.GroupVersionKind{}, unversionedKinds: map[string]reflect.Type{}, cloner: conversion.NewCloner(), fieldLabelConversionFuncs: map[string]map[string]FieldLabelConversionFunc{}, defaulterFuncs: map[reflect.Type]func(interface{}){}, } // 创建converter,用于不同版本对象转换 s.converter = conversion.NewConverter(s.nameFunc) // 增加一些转换函数 s.AddConversionFuncs(DefaultEmbeddedConversions()...) // Enable map[string][]string conversions by default if err := s.AddConversionFuncs(DefaultStringConversions...); err != nil { panic(err) } if err := s.RegisterInputDefaults(&map[string][]string{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil { panic(err) } if err := s.RegisterInputDefaults(&url.Values{}, JSONKeyMapper, conversion.AllowDifferentFieldTypeNames|conversion.IgnoreMissingFields); err != nil { panic(err) } return s }
上面就创建了一个空的Scheme。
知道哪里创建Scheme后,我们继续回到上面的addVersionsToScheme()函数。
其实主要就是看两个接口: api.AddToScheme()和v1.AddToScheme()。
先看第一个:
var ( SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs) AddToScheme = SchemeBuilder.AddToScheme )
通过runtime.NewSchemeBuilder()接口传入两个函数,然后创建了SchemeBuilder:
type SchemeBuilder []func(*Scheme) error func (sb *SchemeBuilder) Register(funcs ...func(*Scheme) error) { for _, f := range funcs { *sb = append(*sb, f) } } func NewSchemeBuilder(funcs ...func(*Scheme) error) SchemeBuilder { var sb SchemeBuilder sb.Register(funcs...) return sb }
根据上面的定义和函数可以看出,SchemeBuilder就是一个接口切片,包含了addKnownTypes, addDefaultingFuncs两个接口。
SchemeBuilder定义好了之后,继续看AddToScheme:
func (sb *SchemeBuilder) AddToScheme(s *Scheme) error { for _, f := range *sb { if err := f(s); err != nil { return err } } return nil }
该函数就是调用了addKnownTypes, addDefaultingFuncs两个接口,我们一个一个看:
func addKnownTypes(scheme *runtime.Scheme) error { if err := scheme.AddIgnoredConversionType(&unversioned.TypeMeta{}, &unversioned.TypeMeta{}); err != nil { return err } // 把下列对象加入到Scheme中 // 该SchemeGroupVersion的GroupName为空,Version是"__internal" // 所以该接口其实是把k8s内置的version添加到Scheme,而且每个group都有该步 scheme.AddKnownTypes(SchemeGroupVersion, &Pod{}, &PodList{}, &PodStatusResult{}, &PodTemplate{}, &PodTemplateList{}, &ReplicationControllerList{}, &ReplicationController{}, &ServiceList{}, &Service{}, &ServiceProxyOptions{}, &NodeList{}, &Node{}, &NodeProxyOptions{}, &Endpoints{}, &EndpointsList{}, &Binding{}, &Event{}, &EventList{}, &List{}, &LimitRange{}, &LimitRangeList{}, &ResourceQuota{}, &ResourceQuotaList{}, &Namespace{}, &NamespaceList{}, &ServiceAccount{}, &ServiceAccountList{}, &Secret{}, &SecretList{}, &PersistentVolume{}, &PersistentVolumeList{}, &PersistentVolumeClaim{}, &PersistentVolumeClaimList{}, &DeleteOptions{}, &ListOptions{}, &PodAttachOptions{}, &PodLogOptions{}, &PodExecOptions{}, &PodProxyOptions{}, &ComponentStatus{}, &ComponentStatusList{}, &SerializedReference{}, &RangeAllocation{}, &ConfigMap{}, &ConfigMapList{}, ) // 在GroupName为空,Version为"v1"的groupVersion中,添加这些对象到Scheme scheme.AddUnversionedTypes(Unversioned, &unversioned.ExportOptions{}, &unversioned.Status{}, &unversioned.APIVersions{}, &unversioned.APIGroupList{}, &unversioned.APIGroup{}, &unversioned.APIResourceList{}, ) return nil }
查看AddKnownTypes()接口:
func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) { if len(gv.Version) == 0 { panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0])) } for _, obj := range types { t := reflect.TypeOf(obj) if t.Kind() != reflect.Ptr { panic("All types must be pointers to structs.") } t = t.Elem() if t.Kind() != reflect.Struct { panic("All types must be pointers to structs.") } gvk := gv.WithKind(t.Name()) s.gvkToType[gvk] = t s.typeToGVK[t] = append(s.typeToGVK[t], gvk) } }
该接口主要操作了s.gvkToType和s.typeToGVK,用于转换的目的。
综上得出,是将internal version添加到Scheme中。
为什么会有一个internal version呢? 其实每一个Group都有一个internal version。而apiserver操作的也都是internal version.
举个例子:假如有一个创建Pod的请求来了,apiserver首先会将请求给反序列化,用户发过来的Pod请求往往是有版本的,比如v1,因此会反序列化为一个v1.Pod。apiserver会立即将这个v1.Pod利用convertor转换成internal.Pod,然后进行一些操作,最后要把它存到etcd里面去,etcd里面的Pod信息是有版本的,因此会先发生一次转换,将其转换为v1.Pod,然后序列化存入etcd。
这样看上去好像多此一举?其实这就是k8s对api多版本的支持,这样用户可以以一个v1beta1创建一个Pod,然后存入etcd的是一个相对稳定的版本,比如v1版本。
internal version添加完成后,继续回到最开始的addVersionsToScheme()函数,还要继续执行v1.AddToScheme(api.Scheme)函数.其实就是把v1版本的api添加到Scheme中,和添加internal版本一样。
我们看看v1.AddToScheme。
路径:pkg/api/v1/register.go
var ( SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs, addConversionFuncs, addFastPathConversionFuncs) AddToScheme = SchemeBuilder.AddToScheme )
这里可以看到v1相比较internal版本,还多了好几个函数addConversionFuncs, addFastPathConversionFuncs。
这些函数在执行AddToScheme()时其实都会要遍历执行,可以深入看下。其实就是向Scheme添加了转换函数,比如将v1.Pod转换为internal.Pod,将internal.Pod转换为v1.Pod。如果同时有v1,v2,v3会如何进行转换?其实也还是先统一转换成internal,然后再转换为相应的版本(v1,v2,v3).所以internal相当于转换的桥梁,更好的支持了不同版本的api。
到这里Scheme的初始化基本结束了。 上面讲GroupMeta初始化时还引出了关键性的RESTMapper,所以继续进行介绍。
RESTMapper初始化该部分的初始化就直接看GroupMeta初始化时调用的接口newRESTMapper():
路径: pkg/api/install/install.go
func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper { // 这些是API最顶层的对象,可以理解为没有namespace的对象 // 根据有无namespace,对象分为两类:RESTScopeNamespace和RESTScopeRoot rootScoped := sets.NewString( "Node", "Namespace", "PersistentVolume", "ComponentStatus", ) // 需要忽略Scheme中如下的kinds ignoredKinds := sets.NewString( "ListOptions", "DeleteOptions", "Status", "PodLogOptions", "PodExecOptions", "PodAttachOptions", "PodProxyOptions", "NodeProxyOptions", "ServiceProxyOptions", "ThirdPartyResource", "ThirdPartyResourceData", "ThirdPartyResourceList") mapper := api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped) return mapper }
其实所有的api资源可以分为两类:一类是有namespace,另一类是没有namespace。比如该接口中的Node、Namespace、PersistentVolume、ComponentStatus不属于任何namespace。ignoredKinds是下面接口需要用到的参数,表示遍历Scheme时忽略这些kinds。
然后调用api.NewDefaultRESTMapper(),importPrefix参数为:"k8s.io/kubernetes/pkg/api",
interfacesFor是一个接口。
路径:pkg/api/mapper.go
func NewDefaultRESTMapper(defaultGroupVersions []unversioned.GroupVersion, interfacesFunc meta.VersionInterfacesFunc, importPathPrefix string, ignoredKinds, rootScoped sets.String) *meta.DefaultRESTMapper { // 加入Scheme,并继续调用下面的接口 return NewDefaultRESTMapperFromScheme(defaultGroupVersions, interfacesFunc, importPathPrefix, ignoredKinds, rootScoped, Scheme) } func NewDefaultRESTMapperFromScheme(defaultGroupVersions []unversioned.GroupVersion, interfacesFunc meta.VersionInterfacesFunc, importPathPrefix string, ignoredKinds, rootScoped sets.String, scheme *runtime.Scheme) *meta.DefaultRESTMapper { // 初始化了一个DefaultRESTMapper对象 mapper := meta.NewDefaultRESTMapper(defaultGroupVersions, interfacesFunc) // 根据输入的defaultGroupVersions,比如"/api/v1",从Scheme中遍历所有的kinds // 然后进行Add for _, gv := range defaultGroupVersions { for kind, oType := range scheme.KnownTypes(gv) { gvk := gv.WithKind(kind) // 过滤掉不属于"k8s.io/kubernetes/pkg/api"路径下的api,和ignoredKinds if !strings.Contains(oType.PkgPath(), importPathPrefix) || ignoredKinds.Has(kind) { continue } // 判断该kind是否有namespace属性 scope := meta.RESTScopeNamespace if rootScoped.Has(kind) { scope = meta.RESTScopeRoot } // 然后将该gvk加入到对应的组中 mapper.Add(gvk, scope) } } return mapper }
再看看该接口,先创建了一个空的DefaultRESTMapper,然后根据"/api/v1"的groupVersion,遍历Scheme中所有的kinds,接着再调用mapper.Add(gvk, scope)去填充这个mapper,最后返回该mapper。
看下mapper.Add()的实现:
func (m *DefaultRESTMapper) Add(kind unversioned.GroupVersionKind, scope RESTScope) { // resource还分为单数和复数 plural, singular := KindToResource(kind) // 单数,复数相互转换 m.singularToPlural[singular] = plural m.pluralToSingular[plural] = singular // 根据单复数的resource找到对应的kind m.resourceToKind[singular] = kind m.resourceToKind[plural] = kind // 根据kind找到对应的单复数resource m.kindToPluralResource[kind] = plural // kind到scope的转换 m.kindToScope[kind] = scope }
RESTMapper其实包含的是一种转换关系,resource到kind,kind到resource,kind到scope的转换。resource还分单数和复数。
kind和resource有什么区别呢?二者都是字符串,kind是通过Kind=reflector.TypeOf(&Pod{}).Elem().Name()进行取值,去的就是Pod这个结构体的名字。resource是通过plural, singular := KindToResource(kind)取值。singular是将Kind转换为小写字母,而plural是变为复数。
示例:以Pod为例,Kind是{Group:"", Version: "v1", Kind: "Pod"},那么singular是{Group:"", Version: "v1", Kind: "pod"},plural则是{Group:"", Version:"v1", Resource:"pods"}。
resource要区分单复数,是为了获取Pods信息。比如可以kubectl get pod,也可以kubectl get pods.
到这里RESTMapper也基本初始化完了,综合上面所有的初始化可以看到,其实主要用internal version和external versions填充Scheme,用external versions去填充GroupMeta以及其成员RESTMapper。
GroupMeta有啥作用呢?主要用于初始化APIGroupVersion。
之前所有的初始化都是为了这步做铺垫,上面还有一个APIGroupInfo和APIGroupVersion都没有进行介绍,这一节都会出现。
当API资源初始化完成以后,需要将这些API资源注册为restful api,用来接收用户的请求。
kube-apiServer使用了go-restful这套框架,里面主要包括三种对象:
Container: 一个Container包含多个WebService
WebService: 一个WebService包含多条route
Route: 一条route包含一个method(GET、POST、DELETE等),一条具体的path(URL)以及一个响应的handler function。
API注册的入口函数有两个: m.InstallAPIs 和 m.InstallLegacyAPI。
文件路径:pkg/master/master.go
这两个函数分别用于注册"/api"和"/apis"的API,这里先拿InstallLegacyAPI进行介绍。
这些接口都是在config.Complete().New()函数中被调用:
restOptionsFactory := restOptionsFactory{ deleteCollectionWorkers: c.DeleteCollectionWorkers, enableGarbageCollection: c.GenericConfig.EnableGarbageCollection, storageFactory: c.StorageFactory, } // 判断是否使能了用于Watch的Cache // 有无cache赋值的是不同的接口实现 // restOptionsFactory.storageDecorator:是一个各个资源的REST interface(CRUD)装饰者 // 后面调用NewStorage()时会用到该接口,并输出对应的CRUD接口及销毁接口。 // 可以参考pkg/registry/core/pod/etcd/etcd.go中的NewStorage() // 其实这里有无cache的接口差异就在于:有cache的话,就提供操作cache的接口;无cache的话,就提供直接操作etcd的接口 if c.EnableWatchCache { restOptionsFactory.storageDecorator = registry.StorageWithCacher } else { restOptionsFactory.storageDecorator = generic.UndecoratedStorage } // 判断/api/v1的group是否已经注册并enable,是的话再进行install if c.GenericConfig.APIResourceConfigSource.AnyResourcesForVersionEnabled(apiv1.SchemeGroupVersion) { // 该对象主要提供了一个NewLegacyRESTStorage()的接口 legacyRESTStorageProvider := corerest.LegacyRESTStorageProvider{ StorageFactory: c.StorageFactory, ProxyTransport: c.ProxyTransport, KubeletClientConfig: c.KubeletClientConfig, EventTTL: c.EventTTL, ServiceIPRange: c.ServiceIPRange, ServiceNodePortRange: c.ServiceNodePortRange, LoopbackClientConfig: c.GenericConfig.LoopbackClientConfig, } // 进行"/api/v1"的API安装 m.InstallLegacyAPI(c.Config, restOptionsFactory.NewFor, legacyRESTStorageProvider) }
继续查看m.InstallLegacyAPI():
func (m *Master) InstallLegacyAPI(c *Config, restOptionsGetter genericapiserver.RESTOptionsGetter, legacyRESTStorageProvider corerest.LegacyRESTStorageProvider) { // 该对象前面介绍过了,比较关键,需要深入查看 // 返回了RESTStorage和apiGroupInfo,都是重量级的成员 // 这些初始化也就在这个接口中 legacyRESTStorage, apiGroupInfo, err := legacyRESTStorageProvider.NewLegacyRESTStorage(restOptionsGetter) if err != nil { glog.Fatalf("Error building core storage: %v", err) } // 判断是否enable了controller,默认是true,这里跟主题关系不大,暂不深入 if c.EnableCoreControllers { serviceClient := coreclient.NewForConfigOrDie(c.GenericConfig.LoopbackClientConfig) bootstrapController := c.NewBootstrapController(legacyRESTStorage, serviceClient) if err := m.GenericAPIServer.AddPostStartHook("bootstrap-controller", bootstrapController.PostStartHook); err != nil { glog.Fatalf("Error registering PostStartHook %q: %v", "bootstrap-controller", err) } } // install core Group"s API if err := m.GenericAPIServer.InstallLegacyAPIGroup(genericapiserver.DefaultLegacyAPIPrefix, &apiGroupInfo); err != nil { glog.Fatalf("Error in registering group versions: %v", err) } }
先看下创建APIGroupVersion和RESTStorage对象的接口NewLegacyRESTStorage().
路径:pkg/registry/core/rest/storage_core.go
func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(restOptionsGetter genericapiserver.RESTOptionsGetter) (LegacyRESTStorage, genericapiserver.APIGroupInfo, error) { // 初始化创建一个APIGroupVersion apiGroupInfo := genericapiserver.APIGroupInfo{ // 该GroupMeta是从APIRegistrationManager初始化后的结构体获取 GroupMeta: *registered.GroupOrDie(api.GroupName), VersionedResourcesStorageMap: map[string]map[string]rest.Storage{}, // 这个api.Scheme之前已经介绍过其初始化了 Scheme: api.Scheme, ParameterCodec: api.ParameterCodec, NegotiatedSerializer: api.Codecs, SubresourceGroupVersionKind: map[string]unversioned.GroupVersionKind{}, } // 判断下autoscaling是否已经注册并使能,是的话加入到apiGroupInfo.SubresourceGroupVersionKind // key是该资源的path if autoscalingGroupVersion := (unversioned.GroupVersion{Group: "autoscaling", Version: "v1"}); registered.IsEnabledVersion(autoscalingGroupVersion) { apiGroupInfo.SubresourceGroupVersionKind["replicationcontrollers/scale"] = autoscalingGroupVersion.WithKind("Scale") } var podDisruptionClient policyclient.PodDisruptionBudgetsGetter if policyGroupVersion := (unversioned.GroupVersion{Group: "policy", Version: "v1beta1"}); registered.IsEnabledVersion(policyGroupVersion) { apiGroupInfo.SubresourceGroupVersionKind["pods/eviction"] = policyGroupVersion.WithKind("Eviction") var err error podDisruptionClient, err = policyclient.NewForConfig(c.LoopbackClientConfig) if err != nil { return LegacyRESTStorage{}, genericapiserver.APIGroupInfo{}, err } } // 初始化一个LegacyRESTStorage对象 // 下面会进行各个接口的初始化,会有Node注册,IP申请,NodePort申请等等 restStorage := LegacyRESTStorage{} // 创建各类Storage podTemplateStorage := podtemplateetcd.NewREST(restOptionsGetter(api.Resource("podTemplates"))) eventStorage := eventetcd.NewREST(restOptionsGetter(api.Resource("events")), uint64(c.EventTTL.Seconds())) limitRangeStorage := limitrangeetcd.NewREST(restOptionsGetter(api.Resource("limitRanges"))) resourceQuotaStorage, resourceQuotaStatusStorage := resourcequotaetcd.NewREST(restOptionsGetter(api.Resource("resourceQuotas"))) secretStorage := secretetcd.NewREST(restOptionsGetter(api.Resource("secrets"))) serviceAccountStorage := serviceaccountetcd.NewREST(restOptionsGetter(api.Resource("serviceAccounts"))) persistentVolumeStorage, persistentVolumeStatusStorage := pvetcd.NewREST(restOptionsGetter(api.Resource("persistentVolumes"))) persistentVolumeClaimStorage, persistentVolumeClaimStatusStorage := pvcetcd.NewREST(restOptionsGetter(api.Resource("persistentVolumeClaims"))) configMapStorage := configmapetcd.NewREST(restOptionsGetter(api.Resource("configMaps"))) namespaceStorage, namespaceStatusStorage, namespaceFinalizeStorage := namespaceetcd.NewREST(restOptionsGetter(api.Resource("namespaces"))) restStorage.NamespaceRegistry = namespace.NewRegistry(namespaceStorage) endpointsStorage := endpointsetcd.NewREST(restOptionsGetter(api.Resource("endpoints"))) restStorage.EndpointRegistry = endpoint.NewRegistry(endpointsStorage) nodeStorage, err := nodeetcd.NewStorage(restOptionsGetter(api.Resource("nodes")), c.KubeletClientConfig, c.ProxyTransport) if err != nil { return LegacyRESTStorage{}, genericapiserver.APIGroupInfo{}, err } restStorage.NodeRegistry = node.NewRegistry(nodeStorage.Node) // 创建PodStorage // api.Resource("pods")是合成了一个GroupResource的结构 podStorage := podetcd.NewStorage( restOptionsGetter(api.Resource("pods")), nodeStorage.KubeletConnectionInfo, c.ProxyTransport, podDisruptionClient, ) serviceRESTStorage, serviceStatusStorage := serviceetcd.NewREST(restOptionsGetter(api.Resource("services"))) restStorage.ServiceRegistry = service.NewRegistry(serviceRESTStorage) var serviceClusterIPRegistry rangeallocation.RangeRegistry serviceClusterIPRange := c.ServiceIPRange if serviceClusterIPRange.IP == nil { return LegacyRESTStorage{}, genericapiserver.APIGroupInfo{}, fmt.Errorf("service clusterIPRange is missing") } serviceStorageConfig, err := c.StorageFactory.NewConfig(api.Resource("services")) if err != nil { return LegacyRESTStorage{}, genericapiserver.APIGroupInfo{}, err } ServiceClusterIPAllocator := ipallocator.NewAllocatorCIDRRange(&serviceClusterIPRange, func(max int, rangeSpec string) allocator.Interface { mem := allocator.NewAllocationMap(max, rangeSpec) // TODO etcdallocator package to return a storage interface via the storageFactory etcd := etcdallocator.NewEtcd(mem, "/ranges/serviceips", api.Resource("serviceipallocations"), serviceStorageConfig) serviceClusterIPRegistry = etcd return etcd }) restStorage.ServiceClusterIPAllocator = serviceClusterIPRegistry var serviceNodePortRegistry rangeallocation.RangeRegistry ServiceNodePortAllocator := portallocator.NewPortAllocatorCustom(c.ServiceNodePortRange, func(max int, rangeSpec string) allocator.Interface { mem := allocator.NewAllocationMap(max, rangeSpec) // TODO etcdallocator package to return a storage interface via the storageFactory etcd := etcdallocator.NewEtcd(mem, "/ranges/servicenodeports", api.Resource("servicenodeportallocations"), serviceStorageConfig) serviceNodePortRegistry = etcd return etcd }) restStorage.ServiceNodePortAllocator = serviceNodePortRegistry controllerStorage := controlleretcd.NewStorage(restOptionsGetter(api.Resource("replicationControllers"))) serviceRest := service.NewStorage(restStorage.ServiceRegistry, restStorage.EndpointRegistry, ServiceClusterIPAllocator, ServiceNodePortAllocator, c.ProxyTransport) // 初始化了一个restStorage的map,然后赋值给APIGroupInfo.VersionedResourcesStorageMap["v1"] restStorageMap := map[string]rest.Storage{ "pods": podStorage.Pod, "pods/attach": podStorage.Attach, "pods/status": podStorage.Status, "pods/log": podStorage.Log, "pods/exec": podStorage.Exec, "pods/portforward": podStorage.PortForward, "pods/proxy": podStorage.Proxy, "pods/binding": podStorage.Binding, "bindings": podStorage.Binding, "podTemplates": podTemplateStorage, "replicationControllers": controllerStorage.Controller, "replicationControllers/status": controllerStorage.Status, "services": serviceRest.Service, "services/proxy": serviceRest.Proxy, "services/status": serviceStatusStorage, "endpoints": endpointsStorage, "nodes": nodeStorage.Node, "nodes/status": nodeStorage.Status, "nodes/proxy": nodeStorage.Proxy, "events": eventStorage, "limitRanges": limitRangeStorage, "resourceQuotas": resourceQuotaStorage, "resourceQuotas/status": resourceQuotaStatusStorage, "namespaces": namespaceStorage, "namespaces/status": namespaceStatusStorage, "namespaces/finalize": namespaceFinalizeStorage, "secrets": secretStorage, "serviceAccounts": serviceAccountStorage, "persistentVolumes": persistentVolumeStorage, "persistentVolumes/status": persistentVolumeStatusStorage, "persistentVolumeClaims": persistentVolumeClaimStorage, "persistentVolumeClaims/status": persistentVolumeClaimStatusStorage, "configMaps": configMapStorage, "componentStatuses": componentstatus.NewStorage(componentStatusStorage{c.StorageFactory}.serversToValidate), } if registered.IsEnabledVersion(unversioned.GroupVersion{Group: "autoscaling", Version: "v1"}) { restStorageMap["replicationControllers/scale"] = controllerStorage.Scale } if registered.IsEnabledVersion(unversioned.GroupVersion{Group: "policy", Version: "v1beta1"}) { restStorageMap["pods/eviction"] = podStorage.Eviction } // 将上面的restStorageMap赋值给v1 apiGroupInfo.VersionedResourcesStorageMap["v1"] = restStorageMap return restStorage, apiGroupInfo, nil }
看完这个接口后,我们继续回到前面,看下m.GenericAPIServer.InstallLegacyAPIGroup()接口:
路径:pkg/genericapiserver/genericapiserver.go
func (s *GenericAPIServer) InstallLegacyAPIGroup(apiPrefix string, apiGroupInfo *APIGroupInfo) error { // 判断前缀参数是否正确 if !s.legacyAPIGroupPrefixes.Has(apiPrefix) { return fmt.Errorf("%q is not in the allowed legacy API prefixes: %v", apiPrefix, s.legacyAPIGroupPrefixes.List()) } // 关键接口,真正install API if err := s.installAPIResources(apiPrefix, apiGroupInfo); err != nil { return err } // 获取了该Group下所有的version信息 // 应该用于发现当前的所有版本信息 apiVersions := []string{} for _, groupVersion := range apiGroupInfo.GroupMeta.GroupVersions { apiVersions = append(apiVersions, groupVersion.Version) } // Install the version handler. // Add a handler at /to enumerate the supported api versions. apiserver.AddApiWebService(s.Serializer, s.HandlerContainer.Container, apiPrefix, func(req *restful.Request) *unversioned.APIVersions { clientIP := utilnet.GetClientIP(req.Request) apiVersionsForDiscovery := unversioned.APIVersions{ ServerAddressByClientCIDRs: s.discoveryAddresses.ServerAddressByClientCIDRs(clientIP), Versions: apiVersions, } return &apiVersionsForDiscovery }) return nil }
那我们继续进入关键接口s.installAPIResources(apiPrefix, apiGroupInfo):
func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *APIGroupInfo) error { // 遍历该Group下的所有GroupVersons for _, groupVersion := range apiGroupInfo.GroupMeta.GroupVersions { // 创建APIGroupVersion apiGroupVersion, err := s.getAPIGroupVersion(apiGroupInfo, groupVersion, apiPrefix) if err != nil { return err } if apiGroupInfo.OptionsExternalVersion != nil { apiGroupVersion.OptionsExternalVersion = apiGroupInfo.OptionsExternalVersion } // 根据之前创建的APIGroupVersion,然后安装restful API // 该s.HandlerContainer.Container就是go-restful的Container if err := apiGroupVersion.InstallREST(s.HandlerContainer.Container); err != nil { return fmt.Errorf("Unable to setup API %v: %v", apiGroupInfo, err) } } return nil } func (s *GenericAPIServer) getAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupVersion unversioned.GroupVersion, apiPrefix string) (*apiserver.APIGroupVersion, error) { storage := make(map[string]rest.Storage) // 如果是核心组的话,Version为"v1",该VersionedResourcesStorageMap的初始化要看 // 之前的NewLegacyRESTStorage()接口,在该接口中进行的初始化 // 遍历所有的ResourcesStorage,并赋值给storage for k, v := range apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version] { storage[strings.ToLower(k)] = v } // 创建APIGroupVersion version, err := s.newAPIGroupVersion(apiGroupInfo, groupVersion) // 设置Prefix, 核心组的话是"/api" version.Root = apiPrefix version.Storage = storage return version, err }
到这里从API资源到restful API,就已经注册完成了。
至于apiGroupVersion.InstallREST()接口,我们这里先简单介绍,后面会另起一篇文章结合go-restful进行介绍。
InstallREST()接口路径:pkg/apiserver/apiserver.go
func (g *APIGroupVersion) InstallREST(container *restful.Container) error { installer := g.newInstaller() ws := installer.NewWebService() apiResources, registrationErrors := installer.Install(ws) lister := g.ResourceLister if lister == nil { lister = staticLister{apiResources} } AddSupportedResourcesWebService(g.Serializer, ws, g.GroupVersion, lister) container.Add(ws) return utilerrors.NewAggregate(registrationErrors) } func (a *APIInstaller) Install(ws *restful.WebService) (apiResources []unversioned.APIResource, errors []error) { errors = make([]error, 0) proxyHandler := (&ProxyHandler{ prefix: a.prefix + "/proxy/", storage: a.group.Storage, serializer: a.group.Serializer, mapper: a.group.Context, }) // Register the paths in a deterministic (sorted) order to get a deterministic swagger spec. paths := make([]string, len(a.group.Storage)) var i int = 0 for path := range a.group.Storage { paths[i] = path i++ } sort.Strings(paths) for _, path := range paths { // 该接口是关键,最终将一个rest.Storage对象转换成实际的restful api,比如getter、lister等处理函数,并将实际的URL关联起来 apiResource, err := a.registerResourceHandlers(path, a.group.Storage[path], ws, proxyHandler) if err != nil { errors = append(errors, fmt.Errorf("error in registering resource: %s, %v", path, err)) } if apiResource != nil { apiResources = append(apiResources, *apiResource) } } return apiResources, errors }
在这个注册的过程中,InstallREST最终调用了registerResourceHandlers()接口,该接口最终会把一个rest.Storage对象转换成实际的getter、lister等处理函数,并和实际的URL关联起来。
用户参数配置runtime-config: 用于enable/disable extensions group。默认的情况下DaemonSets、Deployments、HorizontalPodAutoscalers、Ingress、Jobs和ReplicaSets是使能的,还有v1下的默认都是使能的。另外的功能就可以通过该配置进行设置. 例如:disable deployments: --runtime-config=extensions/v1beta1/deployments=false.
参考资料1.api-group.md: https://github.com/kubernetes...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/32557.html
摘要:它包括一组和一个对象,使用进行请求派发。流程基本就是这样,接着我们直接进入接口看实现拼装然后填充并返回一个对象创建一个这个是关键,会对各种进行注册增加一个的将该加入到前两个调用函数比较简单,这里不进行介绍了。 源码版本 Kubernetes v1.5.0 go-restful 简介 go-restful是用于构建REST-style web服务的golang包。它是出现时因为一个jav...
摘要:为所有对外提供服务的资源实现了一套通用的符合要求的操作接口,每个服务接口负责处理一类资源对象。该接口最终返回了的和清除操作资源的接口。 源码版本 Kubernetes v1.5.0 简介 k8s的各个组件与apiServer交互操作各种资源对象,最终都会落入到etcd中。k8s为所有对外提供服务的Restful资源实现了一套通用的符合Restful要求的etcd操作接口,每个服务接口负...
摘要:源码版本简介是最重要的组成部分,不论是命令操作还是通过进行控制,实际都需要经过。仅用于长时间执行的请求最小请求处理超时时间,默认仅用于该文件内设置鉴权机构一组用于运行时的配置信息。在最后会启动服务。 源码版本 Kubernetes v1.5.0 简介 apiserver是K8S最重要的组成部分,不论是命令操作还是通过remote API进行控制,实际都需要经过apiserver。api...
摘要:用于获取元数据及根据的来匹配该会使用到的接口如下用于根据反推根据获取元数据提供了接口用于获取指定下管理的所有通过的数据变更,比如,来操作该。 k8s version: v1.11.0author: lbl167612@alibaba-inc.com 源码流程图 showImg(https://segmentfault.com/img/remote/1460000016496285?w...
摘要:源码版本简介在急群众,在每个节点上都会启动一个服务进程。该进程用于处理节点下发到本节点的任务,管理及中的容器。每个进程会在上注册节点自身信息,定期向节点汇报节点资源的使用情况,并通过监控容器和节点资源。最后运行健康检测服务。 源码版本 kubernetes version: v1.3.0 简介 在Kubernetes急群众,在每个Node节点上都会启动一个kubelet服务进程。该进程...
阅读 3218·2021-09-08 09:45
阅读 1212·2019-08-30 15:53
阅读 1470·2019-08-30 14:12
阅读 897·2019-08-29 17:01
阅读 2535·2019-08-29 15:35
阅读 362·2019-08-29 13:09
阅读 1903·2019-08-29 12:32
阅读 3061·2019-08-26 18:37