摘要:我們先將上面的接口解析放放,先看下是如何初始化的路徑定義了,再看路徑定義空的創(chuàng)建,用于不同版本對(duì)象轉(zhuǎn)換增加一些轉(zhuǎn)換函數(shù)上面就創(chuàng)建了一個(gè)空的。其實(shí)就是向添加了轉(zhuǎn)換函數(shù),比如將轉(zhuǎn)換為,將轉(zhuǎn)換為。
源碼版本
Kubernetes v1.5.0
簡介k8s里面有各種資源,如Pod、Service、RC、namespaces等資源,用戶操作的其實(shí)也就是這一大堆資源。但這些資源并不是雜亂無章的,使用了GroupVersion的方式組織在一起。每一種資源都屬于一個(gè)Group,而資源還有版本之分,如v1、v1beta1等。
k8s目前正在使用的API groups:
"core" group:它的REST path是api/v1
"extensions" group:它的REST path是/apis/extensions/v1beta1
"autoscaling", "abac" ...
服務(wù)器的URL的格式:/prefix/group/version/... (例如:/apis/extensions/v1beta1)
重要結(jié)構(gòu)體APIGroupVersion:對(duì)API資源的組織,里面包含了Storage、GroupVersion、Mapper、Serializer、Convertor等成員。Storage是etcd的接口,這是一個(gè)map類型,每一種資源都會(huì)與etcd建立一個(gè)連接;GroupVersion表示該APIGroupVersion屬于哪個(gè)Group、哪個(gè)version;Serializer用于序列化,反序列化;Convertor提供各個(gè)不同版本進(jìn)行轉(zhuǎn)化的接口;Mapper實(shí)現(xiàn)了RESTMapper接口。
type APIGroupVersion struct { // key存在對(duì)象的url,value是一個(gè)rest.Storage,用于對(duì)接etcd存儲(chǔ) Storage map[string]rest.Storage // 該group的prefix,例如核心組的Root是"/api" Root string // 包含類似"api/v1"這樣的string,用于標(biāo)識(shí)這個(gè)實(shí)例 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 // 關(guān)鍵性成員 Mapper meta.RESTMapper // 對(duì)象序列化和反序列化器 Serializer runtime.NegotiatedSerializer ParameterCodec runtime.ParameterCodec // 以下4個(gè)都是被賦值為Scheme結(jié)構(gòu) Typer runtime.ObjectTyper Creater runtime.ObjectCreater // 相互轉(zhuǎn)換任意api版本的對(duì)象,需要事先注冊轉(zhuǎn)換函數(shù) 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的創(chuàng)建接口是pkg/genericapiserver/genericapiserver.go中的newAPIGroupVersion()接口,在接口在創(chuàng)建APIGroupVersion還用到了好幾個(gè)別的結(jié)構(gòu):APIGroupInfo、Scheme、GroupMeta。下面一個(gè)一個(gè)介紹:
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的話,對(duì)應(yīng)的就是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資源之間的序列化、反序列化、版本轉(zhuǎn)換。Scheme里面還有好幾個(gè)map,前面的結(jié)構(gòu)體存儲(chǔ)的都是unversioned.GroupVersionKind、unversioned.GroupVersion這些東西,這些東西本質(zhì)上只是表示資源的字符串標(biāo)識(shí),Scheme存儲(chǔ)了對(duì)應(yīng)著標(biāo)志的具體的API資源的結(jié)構(gòu)體,即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一樣,其實(shí)APIGroupVersion的RESTMapper直接取之于GroupMeta的RESTMapper.一個(gè)Group可能包含多個(gè)版本,存儲(chǔ)在GroupVersion中,而GroupVersion是默認(rèn)存儲(chǔ)在etcd中的版本。
type GroupMeta struct { // 默認(rèn)版本 GroupVersion unversioned.GroupVersion // 該Group中可能會(huì)有多個(gè)版本,該字段就包含了所有的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 // 用于類型,對(duì)象之間的轉(zhuǎn)換 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: 用于管理所有對(duì)象的信息。外部要獲取的話,直接通過version,group獲取到RESTMapper,然后通過kind類型可以獲取到對(duì)應(yīng)的信息。該RESTMapper其實(shí)是實(shí)現(xiàn)了一個(gè)DefaultRESTMapper結(jié)構(gòu)。
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:這個(gè)結(jié)構(gòu)體主要提供了已經(jīng)"registered"的概念,將所有已經(jīng)注冊的,已經(jīng)激活的,第三方的的GroupVersions進(jìn)行了匯總,還包括了各個(gè)GroupVersion的GroupMeta(元數(shù)據(jù))。
type APIRegistrationManager struct { // 所以已經(jīng)registered的GroupVersions registeredVersions map[unversioned.GroupVersion]struct{} // 第三方注冊的GroupVersions,這些都向apiServer動(dòng)態(tài)注冊的 thirdPartyGroupVersions []unversioned.GroupVersion // 所有已經(jīng)enable的GroupVersions,可以通過EnableVersions()將要enable的GroupVersion加入進(jìn)來。 // 只有enable了,才能使用對(duì)應(yīng)的GroupVersion enabledVersions map[unversioned.GroupVersion]struct{} // 所有g(shù)roups的GroupMeta groupMetaMap map[string]*apimachinery.GroupMeta // 跟環(huán)境變量"KUBE_API_VERSIONS"有關(guān) envRequestedVersions []unversioned.GroupVersion }APIRegistrationManager初始化
該結(jié)構(gòu)的路徑:pkg/apimachinery/registered/registered.go
在該文件中我們能看到初始化了一個(gè)DefaultAPIRegistrationManager對(duì)象:
var ( DefaultAPIRegistrationManager = NewOrDie(os.Getenv("KUBE_API_VERSIONS")) )
進(jìn)入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{}, } // 如果環(huán)境變量KUBE_API_VERSIONS進(jìn)行了設(shè)置的話,進(jìn)行遍歷 if len(kubeAPIVersions) != 0 { // 通過逗號(hào)進(jìn)行分隔 for _, version := range strings.Split(kubeAPIVersions, ",") { // 解析version并轉(zhuǎn)換成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) } } // 否則返回一個(gè)空的APIRegistrationManager return m, nil }
瞅了下我們正在使用的環(huán)境,沒有配置KUBE_API_VERSIONS,即返回了一個(gè)空的結(jié)構(gòu),還提供了好多方法。
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的啟動(dòng)流程的時(shí)候,你會(huì)發(fā)現(xiàn)初始化ServerRunOptions對(duì)象時(shí),用到了好多上面的變量,比如:
路徑:pkg/genericapiserver/options/server_run_options.go
func NewServerRunOptions() *ServerRunOptions { return &ServerRunOptions{ AdmissionControl: "AlwaysAdmit", 。。。 // 這里就使用了AllPreferredGroupVersions接口 DefaultStorageVersions: registered.AllPreferredGroupVersions(), 。。。 StorageVersions: registered.AllPreferredGroupVersions(), } }
上面就使用到了registered.AllPreferredGroupVersions()接口,順便看下接口具體實(shí)現(xiàn):
func (m *APIRegistrationManager) AllPreferredGroupVersions() string { // 如果沒有注冊groupMeta的話,這里就==0 // 不過不可能沒有注冊,至于在哪里進(jìn)行注冊就得看下后面介紹的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,然后通過逗號(hào)拼接成"group1/version1,group2/version2,..."的字符串。
這里可以想一下,既然有l(wèi)ist,那總得有g(shù)roupMeta啊。而我們看APIRegistrationManager的初始化,如果沒有設(shè)置KUBE_API_VERSIONS環(huán)境變量的話,根本就沒有g(shù)roupMeta。
既然不可能沒有g(shù)roupMeta,那肯定得從別的地方進(jìn)行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接口的入?yún)⒕褪荊roupMeta,所以我們得繼續(xù)查看該結(jié)構(gòu)的初始化了。
GroupMeta初始化k8s現(xiàn)階段,API一共分為13個(gè)Group:Core、apps、authentication、authorization、autoscaling、batch、certificates、componentconfig、extensions、imagepolicy、policy、rbac、storage。其中Core的Group Name為空,它包含的API是最核心的API,如Pod、Service等,它的代碼位于pkg/api下面,其它12個(gè)Group代碼位于pkg/apis。每個(gè)目錄下都有一個(gè)install目錄,里面有一個(gè)install.go文件,接著通過init()負(fù)責(zé)初始化。這些程序都是通過下列文件進(jìn)行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個(gè)group。其中"k8s.io/kubernetes/pkg/api/install"就是Core Group,我們就以它為例,查看下對(duì)應(yīng)的install.go文件。
路徑: pkg/api/install/install.go
var availableVersions = []unversioned.GroupVersion{v1.SchemeGroupVersion} func init() { // 進(jìn)行Versions注冊,其實(shí)就是存入APIRegistrationManager.registeredVersions中 registered.RegisterVersions(availableVersions) externalVersions := []unversioned.GroupVersion{} for _, v := range availableVersions { // 判斷下是否已經(jīng)注冊,并追加成一個(gè)切片 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 } // 再進(jìn)行enable,其實(shí)就是存入APIRegistrationManager.enabledVersions if err := registered.EnableVersions(externalVersions...); err != nil { glog.V(4).Infof("%v", err) return } // 該接口比較關(guān)鍵,進(jìn)行多帶帶介紹 if err := enableVersions(externalVersions); err != nil { glog.V(4).Infof("%v", err) return } }
首先定義了一個(gè)切片availableVersions,里面只有一個(gè)元素v1.SchemeGroupVersion:
const GroupName = "" var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1"}
根據(jù)該元素的定義,可以看出availableVersions就定義了一個(gè)GroupName為空,Version是"v1"的GroupVersion。接著把該GroupVersion放入APIRegistrationManager的registeredVersions和enabledVersions中。
registered的幾個(gè)接口實(shí)現(xiàn)比較簡單不進(jìn)行介紹了,但是執(zhí)行的enableVersions()是重頭戲,我們繼續(xù)深入:
func enableVersions(externalVersions []unversioned.GroupVersion) error { // 字面意思:將所有的Versions添加到Scheme // 又牽扯到Scheme,后面會(huì)介紹Scheme的初始化 // 越深入看牽扯出的概念越多,該接口也很重要,需要耐心層層挖掘 addVersionsToScheme(externalVersions...) // 將一個(gè)GroupVersion作為默認(rèn)的,即"/api/v1" preferredExternalVersion := externalVersions[0] // 就是這里! 進(jìn)行了GroupMeta的初始化。這就是我們這小節(jié)要看的關(guān)鍵 groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, // RESTMapper也是關(guān)鍵所在,下面也會(huì)單做一節(jié)進(jìn)行介紹 RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, } // 前面都是register和enable了versions,這里才是進(jìn)行了Group的register // 該接口其實(shí)就是以第一個(gè)GroupVersion的groupName為key,groupMeta為value // 對(duì)APIRegistrationManager的groupMetaMap,進(jìn)行了賦值 if err := registered.RegisterGroup(groupMeta); err != nil { return err } return nil }
到這步,我們再結(jié)合之前APIRegistrationManager的初始化,就能知道groupMetaMap中應(yīng)該有了好幾組groupMeta。那在ServerRunOptions對(duì)象初始化中調(diào)用的registered.AllPreferredGroupVersions()接口,能返回好幾個(gè)DefaultStorageVersions,至少肯定有"/api/v1"。至于別的groupMeta,需要再看下別的install.go,大同小異就不展開一個(gè)一個(gè)講了。
groupMeta的初始化雖然結(jié)束了,但是這里又引出一個(gè)關(guān)鍵Scheme,那么繼續(xù)下一小節(jié)吧。。
Scheme初始化在上一節(jié)介紹enableVersions()函數(shù)時(shí),第一行便是調(diào)用了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{}){}, } // 創(chuàng)建converter,用于不同版本對(duì)象轉(zhuǎn)換 s.converter = conversion.NewConverter(s.nameFunc) // 增加一些轉(zhuǎn)換函數(shù) 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 }
上面就創(chuàng)建了一個(gè)空的Scheme。
知道哪里創(chuàng)建Scheme后,我們繼續(xù)回到上面的addVersionsToScheme()函數(shù)。
其實(shí)主要就是看兩個(gè)接口: api.AddToScheme()和v1.AddToScheme()。
先看第一個(gè):
var ( SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs) AddToScheme = SchemeBuilder.AddToScheme )
通過runtime.NewSchemeBuilder()接口傳入兩個(gè)函數(shù),然后創(chuàng)建了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 }
根據(jù)上面的定義和函數(shù)可以看出,SchemeBuilder就是一個(gè)接口切片,包含了addKnownTypes, addDefaultingFuncs兩個(gè)接口。
SchemeBuilder定義好了之后,繼續(xù)看AddToScheme:
func (sb *SchemeBuilder) AddToScheme(s *Scheme) error { for _, f := range *sb { if err := f(s); err != nil { return err } } return nil }
該函數(shù)就是調(diào)用了addKnownTypes, addDefaultingFuncs兩個(gè)接口,我們一個(gè)一個(gè)看:
func addKnownTypes(scheme *runtime.Scheme) error { if err := scheme.AddIgnoredConversionType(&unversioned.TypeMeta{}, &unversioned.TypeMeta{}); err != nil { return err } // 把下列對(duì)象加入到Scheme中 // 該SchemeGroupVersion的GroupName為空,Version是"__internal" // 所以該接口其實(shí)是把k8s內(nèi)置的version添加到Scheme,而且每個(gè)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中,添加這些對(duì)象到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,用于轉(zhuǎn)換的目的。
綜上得出,是將internal version添加到Scheme中。
為什么會(huì)有一個(gè)internal version呢? 其實(shí)每一個(gè)Group都有一個(gè)internal version。而apiserver操作的也都是internal version.
舉個(gè)例子:假如有一個(gè)創(chuàng)建Pod的請(qǐng)求來了,apiserver首先會(huì)將請(qǐng)求給反序列化,用戶發(fā)過來的Pod請(qǐng)求往往是有版本的,比如v1,因此會(huì)反序列化為一個(gè)v1.Pod。apiserver會(huì)立即將這個(gè)v1.Pod利用convertor轉(zhuǎn)換成internal.Pod,然后進(jìn)行一些操作,最后要把它存到etcd里面去,etcd里面的Pod信息是有版本的,因此會(huì)先發(fā)生一次轉(zhuǎn)換,將其轉(zhuǎn)換為v1.Pod,然后序列化存入etcd。
這樣看上去好像多此一舉?其實(shí)這就是k8s對(duì)api多版本的支持,這樣用戶可以以一個(gè)v1beta1創(chuàng)建一個(gè)Pod,然后存入etcd的是一個(gè)相對(duì)穩(wěn)定的版本,比如v1版本。
internal version添加完成后,繼續(xù)回到最開始的addVersionsToScheme()函數(shù),還要繼續(xù)執(zhí)行v1.AddToScheme(api.Scheme)函數(shù).其實(shí)就是把v1版本的api添加到Scheme中,和添加internal版本一樣。
我們看看v1.AddToScheme。
路徑:pkg/api/v1/register.go
var ( SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs, addConversionFuncs, addFastPathConversionFuncs) AddToScheme = SchemeBuilder.AddToScheme )
這里可以看到v1相比較internal版本,還多了好幾個(gè)函數(shù)addConversionFuncs, addFastPathConversionFuncs。
這些函數(shù)在執(zhí)行AddToScheme()時(shí)其實(shí)都會(huì)要遍歷執(zhí)行,可以深入看下。其實(shí)就是向Scheme添加了轉(zhuǎn)換函數(shù),比如將v1.Pod轉(zhuǎn)換為internal.Pod,將internal.Pod轉(zhuǎn)換為v1.Pod。如果同時(shí)有v1,v2,v3會(huì)如何進(jìn)行轉(zhuǎn)換?其實(shí)也還是先統(tǒng)一轉(zhuǎn)換成internal,然后再轉(zhuǎn)換為相應(yīng)的版本(v1,v2,v3).所以internal相當(dāng)于轉(zhuǎn)換的橋梁,更好的支持了不同版本的api。
到這里Scheme的初始化基本結(jié)束了。 上面講GroupMeta初始化時(shí)還引出了關(guān)鍵性的RESTMapper,所以繼續(xù)進(jìn)行介紹。
RESTMapper初始化該部分的初始化就直接看GroupMeta初始化時(shí)調(diào)用的接口newRESTMapper():
路徑: pkg/api/install/install.go
func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper { // 這些是API最頂層的對(duì)象,可以理解為沒有namespace的對(duì)象 // 根據(jù)有無namespace,對(duì)象分為兩類: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 }
其實(shí)所有的api資源可以分為兩類:一類是有namespace,另一類是沒有namespace。比如該接口中的Node、Namespace、PersistentVolume、ComponentStatus不屬于任何namespace。ignoredKinds是下面接口需要用到的參數(shù),表示遍歷Scheme時(shí)忽略這些kinds。
然后調(diào)用api.NewDefaultRESTMapper(),importPrefix參數(shù)為:"k8s.io/kubernetes/pkg/api",
interfacesFor是一個(gè)接口。
路徑:pkg/api/mapper.go
func NewDefaultRESTMapper(defaultGroupVersions []unversioned.GroupVersion, interfacesFunc meta.VersionInterfacesFunc, importPathPrefix string, ignoredKinds, rootScoped sets.String) *meta.DefaultRESTMapper { // 加入Scheme,并繼續(xù)調(diào)用下面的接口 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 { // 初始化了一個(gè)DefaultRESTMapper對(duì)象 mapper := meta.NewDefaultRESTMapper(defaultGroupVersions, interfacesFunc) // 根據(jù)輸入的defaultGroupVersions,比如"/api/v1",從Scheme中遍歷所有的kinds // 然后進(jìn)行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加入到對(duì)應(yīng)的組中 mapper.Add(gvk, scope) } } return mapper }
再看看該接口,先創(chuàng)建了一個(gè)空的DefaultRESTMapper,然后根據(jù)"/api/v1"的groupVersion,遍歷Scheme中所有的kinds,接著再調(diào)用mapper.Add(gvk, scope)去填充這個(gè)mapper,最后返回該mapper。
看下mapper.Add()的實(shí)現(xiàn):
func (m *DefaultRESTMapper) Add(kind unversioned.GroupVersionKind, scope RESTScope) { // resource還分為單數(shù)和復(fù)數(shù) plural, singular := KindToResource(kind) // 單數(shù),復(fù)數(shù)相互轉(zhuǎn)換 m.singularToPlural[singular] = plural m.pluralToSingular[plural] = singular // 根據(jù)單復(fù)數(shù)的resource找到對(duì)應(yīng)的kind m.resourceToKind[singular] = kind m.resourceToKind[plural] = kind // 根據(jù)kind找到對(duì)應(yīng)的單復(fù)數(shù)resource m.kindToPluralResource[kind] = plural // kind到scope的轉(zhuǎn)換 m.kindToScope[kind] = scope }
RESTMapper其實(shí)包含的是一種轉(zhuǎn)換關(guān)系,resource到kind,kind到resource,kind到scope的轉(zhuǎn)換。resource還分單數(shù)和復(fù)數(shù)。
kind和resource有什么區(qū)別呢?二者都是字符串,kind是通過Kind=reflector.TypeOf(&Pod{}).Elem().Name()進(jìn)行取值,去的就是Pod這個(gè)結(jié)構(gòu)體的名字。resource是通過plural, singular := KindToResource(kind)取值。singular是將Kind轉(zhuǎn)換為小寫字母,而plural是變?yōu)閺?fù)數(shù)。
示例:以Pod為例,Kind是{Group:"", Version: "v1", Kind: "Pod"},那么singular是{Group:"", Version: "v1", Kind: "pod"},plural則是{Group:"", Version:"v1", Resource:"pods"}。
resource要區(qū)分單復(fù)數(shù),是為了獲取Pods信息。比如可以kubectl get pod,也可以kubectl get pods.
到這里RESTMapper也基本初始化完了,綜合上面所有的初始化可以看到,其實(shí)主要用internal version和external versions填充Scheme,用external versions去填充GroupMeta以及其成員RESTMapper。
GroupMeta有啥作用呢?主要用于初始化APIGroupVersion。
之前所有的初始化都是為了這步做鋪墊,上面還有一個(gè)APIGroupInfo和APIGroupVersion都沒有進(jìn)行介紹,這一節(jié)都會(huì)出現(xiàn)。
當(dāng)API資源初始化完成以后,需要將這些API資源注冊為restful api,用來接收用戶的請(qǐng)求。
kube-apiServer使用了go-restful這套框架,里面主要包括三種對(duì)象:
Container: 一個(gè)Container包含多個(gè)WebService
WebService: 一個(gè)WebService包含多條route
Route: 一條route包含一個(gè)method(GET、POST、DELETE等),一條具體的path(URL)以及一個(gè)響應(yīng)的handler function。
API注冊的入口函數(shù)有兩個(gè): m.InstallAPIs 和 m.InstallLegacyAPI。
文件路徑:pkg/master/master.go
這兩個(gè)函數(shù)分別用于注冊"/api"和"/apis"的API,這里先拿InstallLegacyAPI進(jìn)行介紹。
這些接口都是在config.Complete().New()函數(shù)中被調(diào)用:
restOptionsFactory := restOptionsFactory{ deleteCollectionWorkers: c.DeleteCollectionWorkers, enableGarbageCollection: c.GenericConfig.EnableGarbageCollection, storageFactory: c.StorageFactory, } // 判斷是否使能了用于Watch的Cache // 有無cache賦值的是不同的接口實(shí)現(xiàn) // restOptionsFactory.storageDecorator:是一個(gè)各個(gè)資源的REST interface(CRUD)裝飾者 // 后面調(diào)用NewStorage()時(shí)會(huì)用到該接口,并輸出對(duì)應(yīng)的CRUD接口及銷毀接口。 // 可以參考pkg/registry/core/pod/etcd/etcd.go中的NewStorage() // 其實(shí)這里有無cache的接口差異就在于:有cache的話,就提供操作cache的接口;無cache的話,就提供直接操作etcd的接口 if c.EnableWatchCache { restOptionsFactory.storageDecorator = registry.StorageWithCacher } else { restOptionsFactory.storageDecorator = generic.UndecoratedStorage } // 判斷/api/v1的group是否已經(jīng)注冊并enable,是的話再進(jìn)行install if c.GenericConfig.APIResourceConfigSource.AnyResourcesForVersionEnabled(apiv1.SchemeGroupVersion) { // 該對(duì)象主要提供了一個(gè)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, } // 進(jìn)行"/api/v1"的API安裝 m.InstallLegacyAPI(c.Config, restOptionsFactory.NewFor, legacyRESTStorageProvider) }
繼續(xù)查看m.InstallLegacyAPI():
func (m *Master) InstallLegacyAPI(c *Config, restOptionsGetter genericapiserver.RESTOptionsGetter, legacyRESTStorageProvider corerest.LegacyRESTStorageProvider) { // 該對(duì)象前面介紹過了,比較關(guān)鍵,需要深入查看 // 返回了RESTStorage和apiGroupInfo,都是重量級(jí)的成員 // 這些初始化也就在這個(gè)接口中 legacyRESTStorage, apiGroupInfo, err := legacyRESTStorageProvider.NewLegacyRESTStorage(restOptionsGetter) if err != nil { glog.Fatalf("Error building core storage: %v", err) } // 判斷是否enable了controller,默認(rèn)是true,這里跟主題關(guān)系不大,暫不深入 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) } }
先看下創(chuàng)建APIGroupVersion和RESTStorage對(duì)象的接口NewLegacyRESTStorage().
路徑:pkg/registry/core/rest/storage_core.go
func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(restOptionsGetter genericapiserver.RESTOptionsGetter) (LegacyRESTStorage, genericapiserver.APIGroupInfo, error) { // 初始化創(chuàng)建一個(gè)APIGroupVersion apiGroupInfo := genericapiserver.APIGroupInfo{ // 該GroupMeta是從APIRegistrationManager初始化后的結(jié)構(gòu)體獲取 GroupMeta: *registered.GroupOrDie(api.GroupName), VersionedResourcesStorageMap: map[string]map[string]rest.Storage{}, // 這個(gè)api.Scheme之前已經(jīng)介紹過其初始化了 Scheme: api.Scheme, ParameterCodec: api.ParameterCodec, NegotiatedSerializer: api.Codecs, SubresourceGroupVersionKind: map[string]unversioned.GroupVersionKind{}, } // 判斷下autoscaling是否已經(jīng)注冊并使能,是的話加入到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 } } // 初始化一個(gè)LegacyRESTStorage對(duì)象 // 下面會(huì)進(jìn)行各個(gè)接口的初始化,會(huì)有Node注冊,IP申請(qǐng),NodePort申請(qǐng)等等 restStorage := LegacyRESTStorage{} // 創(chuàng)建各類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) // 創(chuàng)建PodStorage // api.Resource("pods")是合成了一個(gè)GroupResource的結(jié)構(gòu) 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) // 初始化了一個(gè)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 }
看完這個(gè)接口后,我們繼續(xù)回到前面,看下m.GenericAPIServer.InstallLegacyAPIGroup()接口:
路徑:pkg/genericapiserver/genericapiserver.go
func (s *GenericAPIServer) InstallLegacyAPIGroup(apiPrefix string, apiGroupInfo *APIGroupInfo) error { // 判斷前綴參數(shù)是否正確 if !s.legacyAPIGroupPrefixes.Has(apiPrefix) { return fmt.Errorf("%q is not in the allowed legacy API prefixes: %v", apiPrefix, s.legacyAPIGroupPrefixes.List()) } // 關(guān)鍵接口,真正install API if err := s.installAPIResources(apiPrefix, apiGroupInfo); err != nil { return err } // 獲取了該Group下所有的version信息 // 應(yīng)該用于發(fā)現(xiàn)當(dāng)前的所有版本信息 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 }
那我們繼續(xù)進(jìn)入關(guān)鍵接口s.installAPIResources(apiPrefix, apiGroupInfo):
func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *APIGroupInfo) error { // 遍歷該Group下的所有GroupVersons for _, groupVersion := range apiGroupInfo.GroupMeta.GroupVersions { // 創(chuàng)建APIGroupVersion apiGroupVersion, err := s.getAPIGroupVersion(apiGroupInfo, groupVersion, apiPrefix) if err != nil { return err } if apiGroupInfo.OptionsExternalVersion != nil { apiGroupVersion.OptionsExternalVersion = apiGroupInfo.OptionsExternalVersion } // 根據(jù)之前創(chuàng)建的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()接口,在該接口中進(jìn)行的初始化 // 遍歷所有的ResourcesStorage,并賦值給storage for k, v := range apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version] { storage[strings.ToLower(k)] = v } // 創(chuàng)建APIGroupVersion version, err := s.newAPIGroupVersion(apiGroupInfo, groupVersion) // 設(shè)置Prefix, 核心組的話是"/api" version.Root = apiPrefix version.Storage = storage return version, err }
到這里從API資源到restful API,就已經(jīng)注冊完成了。
至于apiGroupVersion.InstallREST()接口,我們這里先簡單介紹,后面會(huì)另起一篇文章結(jié)合go-restful進(jìn)行介紹。
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 { // 該接口是關(guān)鍵,最終將一個(gè)rest.Storage對(duì)象轉(zhuǎn)換成實(shí)際的restful api,比如getter、lister等處理函數(shù),并將實(shí)際的URL關(guān)聯(lián)起來 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 }
在這個(gè)注冊的過程中,InstallREST最終調(diào)用了registerResourceHandlers()接口,該接口最終會(huì)把一個(gè)rest.Storage對(duì)象轉(zhuǎn)換成實(shí)際的getter、lister等處理函數(shù),并和實(shí)際的URL關(guān)聯(lián)起來。
用戶參數(shù)配置runtime-config: 用于enable/disable extensions group。默認(rèn)的情況下DaemonSets、Deployments、HorizontalPodAutoscalers、Ingress、Jobs和ReplicaSets是使能的,還有v1下的默認(rèn)都是使能的。另外的功能就可以通過該配置進(jìn)行設(shè)置. 例如:disable deployments: --runtime-config=extensions/v1beta1/deployments=false.
參考資料1.api-group.md: https://github.com/kubernetes...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/32557.html
摘要:它包括一組和一個(gè)對(duì)象,使用進(jìn)行請(qǐng)求派發(fā)。流程基本就是這樣,接著我們直接進(jìn)入接口看實(shí)現(xiàn)拼裝然后填充并返回一個(gè)對(duì)象創(chuàng)建一個(gè)這個(gè)是關(guān)鍵,會(huì)對(duì)各種進(jìn)行注冊增加一個(gè)的將該加入到前兩個(gè)調(diào)用函數(shù)比較簡單,這里不進(jìn)行介紹了。 源碼版本 Kubernetes v1.5.0 go-restful 簡介 go-restful是用于構(gòu)建REST-style web服務(wù)的golang包。它是出現(xiàn)時(shí)因?yàn)橐粋€(gè)jav...
摘要:為所有對(duì)外提供服務(wù)的資源實(shí)現(xiàn)了一套通用的符合要求的操作接口,每個(gè)服務(wù)接口負(fù)責(zé)處理一類資源對(duì)象。該接口最終返回了的和清除操作資源的接口。 源碼版本 Kubernetes v1.5.0 簡介 k8s的各個(gè)組件與apiServer交互操作各種資源對(duì)象,最終都會(huì)落入到etcd中。k8s為所有對(duì)外提供服務(wù)的Restful資源實(shí)現(xiàn)了一套通用的符合Restful要求的etcd操作接口,每個(gè)服務(wù)接口負(fù)...
摘要:源碼版本簡介是最重要的組成部分,不論是命令操作還是通過進(jìn)行控制,實(shí)際都需要經(jīng)過。僅用于長時(shí)間執(zhí)行的請(qǐng)求最小請(qǐng)求處理超時(shí)時(shí)間,默認(rèn)僅用于該文件內(nèi)設(shè)置鑒權(quán)機(jī)構(gòu)一組用于運(yùn)行時(shí)的配置信息。在最后會(huì)啟動(dòng)服務(wù)。 源碼版本 Kubernetes v1.5.0 簡介 apiserver是K8S最重要的組成部分,不論是命令操作還是通過remote API進(jìn)行控制,實(shí)際都需要經(jīng)過apiserver。api...
摘要:用于獲取元數(shù)據(jù)及根據(jù)的來匹配該會(huì)使用到的接口如下用于根據(jù)反推根據(jù)獲取元數(shù)據(jù)提供了接口用于獲取指定下管理的所有通過的數(shù)據(jù)變更,比如,來操作該。 k8s version: v1.11.0author: lbl167612@alibaba-inc.com 源碼流程圖 showImg(https://segmentfault.com/img/remote/1460000016496285?w...
摘要:源碼版本簡介在急群眾,在每個(gè)節(jié)點(diǎn)上都會(huì)啟動(dòng)一個(gè)服務(wù)進(jìn)程。該進(jìn)程用于處理節(jié)點(diǎn)下發(fā)到本節(jié)點(diǎn)的任務(wù),管理及中的容器。每個(gè)進(jìn)程會(huì)在上注冊節(jié)點(diǎn)自身信息,定期向節(jié)點(diǎn)匯報(bào)節(jié)點(diǎn)資源的使用情況,并通過監(jiān)控容器和節(jié)點(diǎn)資源。最后運(yùn)行健康檢測服務(wù)。 源碼版本 kubernetes version: v1.3.0 簡介 在Kubernetes急群眾,在每個(gè)Node節(jié)點(diǎn)上都會(huì)啟動(dòng)一個(gè)kubelet服務(wù)進(jìn)程。該進(jìn)程...
閱讀 3309·2021-09-08 09:45
閱讀 1260·2019-08-30 15:53
閱讀 1532·2019-08-30 14:12
閱讀 987·2019-08-29 17:01
閱讀 2578·2019-08-29 15:35
閱讀 401·2019-08-29 13:09
閱讀 1978·2019-08-29 12:32
閱讀 3089·2019-08-26 18:37