kubernetes之kube-ApiServer代碼分析


一、概述:

  kube-ApiServer相當於是k8集群的一個入口,不論通過kubectl還是使用remote api 直接控制,都要經過apiserver。apiserver說白了就是一個server負責監聽指定的端口(http/https協議),之后處理不同的請求,只不過加上的很多控制;apiserver是k8s系統中所有對象的增刪查改盯的http/restful式服務端,其中盯是指watch操作【watch是apiserver中的重要操作之一】。數據最終存儲在分布式一致的etcd存儲內,apiserver本身是無狀態的,提供了這些數據訪問的認證鑒權、緩存、api版本適配轉換等一系列的功能。

二、restful基礎

  對於http服務和使用go語言實現方式,可以看go-restful的文檔例子

  k8s存放在etcd內的存儲對象是api.Pod對象(無版本),從不同版本的請求路徑標示來操作,例如api/v1,最后獲取到的是不同版本,例如v1.Pod的json文本。這里就經歷了幾個過程,包括:

    1、http client訪問/api/v1/pod/xyz, 想要獲取到這個Pod的數據

    2、從etcd獲取到api.Pod對象

    3、api.Pod對象轉換為v1.Pod對象

    4、v1.Pod對象序列化為json或yaml文本

    5、文本通過http的response體,返回給http client

  其中用於處理業務數據的關鍵數據結構是APIGroupVersion:

 1 // APIGroupVersion is a helper for exposing rest.Storage objects as http.Handlers via go-restful
 2 // It handles URLs of the form:
 3 // /${storage_key}[/${object_name}]
 4 // Where 'storage_key' points to a rest.Storage object stored in storage.
 5 // This object should contain all parameterization necessary for running a particular API version
 6 //重點數據結構
 7 type APIGroupVersion struct {
 8     //最重要的數據結構,該map的key是用於對,value是rest.Storage結構,用於對接etcd存儲,
 9     //在初始化注冊時,會把這個map化開,化為真正的rest服務到存儲的一條龍服務
10     Storage map[string]rest.Storage
11 
12     Root string
13 
14     // GroupVersion is the external group version
15     // 包含api/v1這樣的string,用於標示這個實例
16     GroupVersion unversioned.GroupVersion
17 
18     // RequestInfoResolver is used to parse URLs for the legacy proxy handler.  Don't use this for anything else
19     // TODO: refactor proxy handler to use sub resources
20     RequestInfoResolver *RequestInfoResolver
21 
22     // OptionsExternalVersion controls the Kubernetes APIVersion used for common objects in the apiserver
23     // schema like api.Status, api.DeleteOptions, and api.ListOptions. Other implementors may
24     // define a version "v1beta1" but want to use the Kubernetes "v1" internal objects. If
25     // empty, defaults to GroupVersion.
26     OptionsExternalVersion *unversioned.GroupVersion
27 
28     Mapper meta.RESTMapper
29 
30     // Serializer is used to determine how to convert responses from API methods into bytes to send over
31     // the wire.
32     //對象序列化和反序列化器
33     Serializer     runtime.NegotiatedSerializer
34     ParameterCodec runtime.ParameterCodec
35 
36     Typer   runtime.ObjectTyper
37     Creater runtime.ObjectCreater
38     //可以轉換任意一種對象到另一種,只要你事先注入了相應的轉換函數
39     HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
40     Convertor runtime.ObjectConvertor
41     Copier    runtime.ObjectCopier
42     Linker    runtime.SelfLinker
43 
44     Admit   admission.Interface
45     Context api.RequestContextMapper
46 
47     MinRequestTimeout time.Duration
48 
49     // SubresourceGroupVersionKind contains the GroupVersionKind overrides for each subresource that is
50     // accessible from this API group version. The GroupVersionKind is that of the external version of
51     // the subresource. The key of this map should be the path of the subresource. The keys here should
52     // match the keys in the Storage map above for subresources.
53     SubresourceGroupVersionKind map[string]unversioned.GroupVersionKind
54 }

三、API分組、多版本的初始化注冊(Rest)

  

  k8s采用ApiGroup來管理所有的api分組和版本升級,目前的API分組包括:

    1、核心組,REST路徑在/api/v1,但這個路徑不是固定的,v1是當前的版本。與之相對應的代碼里面的apiVersion字段的值為v1.

    2、擴展組,REST路徑在/apis/extensions/$version, 相應的代碼里面的apiversion:extensions/$VERSION(eg:apiVersion:extensions/v1beta1),這里的API對象可能會被重新分組;

    3、"componentconfig" 和 "metrics"這些組

  在這個文檔里面講述了實現ApiGroup的幾個目標,包括api分組演化,對舊版API的向后兼容(Backwards compatibility),包括用戶可以自定義自己的api等。接下來我們看看他么是怎么初始化注冊的,這里都是縮減版代碼,去掉了其他部分。

  1. api注冊入口:
    kubernets/pkg/master/master.go 
    func New(c *Config)(*Master, error) {
          m.InstallAPIs(c)  
    }

  2.根據Config往APIGroupsInfo內增加組信息,然后通過InstallAPIGroups進行注冊

1 func (m *Master) InstallAPIs(c *Config) {
2     if err := m.InstallAPIGroups(apiGroupsInfo); err != nil {
3         glog.Fatalf("Error in registering group versions:%v", err)
4     }
5 }

  3.轉換為APIGroupVersion這個關鍵數據結構,然后進行注冊

func (s *GenericAPIServer) installAPIGroup(apiGroupInfo *APIGroupInfo) error {
    apiGroupVersion, err := s.getAPIGroupVersion(apiGroupInfo, groupVersion, apiPrefix)
    if err := apiGroupVersion.InstallREST(s.HandlerContainer); err != nil {
        return fmt.Errorf("Unable to setup API %v: %v", apiGroupInfo, err)
    }  
}

  4.APIGroupVersion 關鍵數據結構

kubernetes/pkg/apiserver/apiserver.go 
type APIGroupVersion struct {
     Storage  map[string]rest.Storage 
     Root  string
     //GroupVersion is the external group version 
    GroupVersion unversioned.GroupVersion
}

  5.實際注冊的Storage的map如下:

kubernetes/pkg/master/master.go
    m.v1ResourcesStorage = 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,

  那么,這里的map[string]rest.Storage最后是怎么變成一個具體的API來提供服務的呢?例如這么一個URL:

    GET /api/v1/namespaces/{namespace}/pods/{name}
 
k8s使用的一個第三方庫github.com/emicklei/go-restful,里面提供了一組核心的對象,看例子
數據結構 功能 在k8s內的位置
restful.Container 代表一個http rest服務對象,包括一組restful.WebService genericapiserver.go - GenericAPIServer.HandlerContainer
restful.WebService 由多個restful.Route組成,處理這些路徑下所有的特殊的MIME類型等 api_installer.go - NewWebService()
restful.Route 路徑——處理函數映射map api_installer.go - registerResourceHandlers
  • 實際注冊過程
    kubernetes/pkg/apiserver/api_installer.go 
    func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService, proxyHandler http.Handler) (*unversioned.APIResource, error) { }
    最終的API注冊過程是在這個函數中完成的,把一個rest.Storage對象轉換為實際的getter, lister等處理函數,並和實際的url關聯起來。

4.etcd存儲的操作(ORM)

  上面已經基本厘清了從http請求 -> restful.Route -> rest.Storage這條線路,那rest.Storage僅僅是一個接口,有何德何能,可以真正的操作etcd呢?

  這段也是牽涉到多個文件,但還比較清晰,首先,所有的對象都有增刪改查這些操作,如果為Pod單獨搞一套,Controller單獨搞一套,那代碼會非常重復,不可復用,所以存儲的關鍵目錄是在這里:

  kubernetes/pkg/registry/generic/etcd/etcd.go 

  這個文件定義了所有的對etcd對象的操作,get,list,create等,但具體的對象是啥,這個文件不關心;etcd客戶端地址,這個文件也不關心。這些信息都是在具體的PodStorage對象創建的時候注入的。以Pod為例子,文件在:

   kubernetes/pkg/registry/pod/etcd/etcd.go 

  這里的NewStorage方法,把上述的信息注入了etcd里面去,生成了PodStorage這個對象。

  // REST implements a RESTStorage for pods against etcd   type REST struct {   *etcdgeneric.Etcd   proxyTransport http.RoundTripper   } 

  由於PodStorage.Pod是一個REST類型,而REST類型采用了Go語言的struct匿名內部成員,天然就擁有Get, List等方法。

    kubernetes/pkg/apiserver/api_installer.go 

  最后在這里把PodStorage轉換成了Getter對象,並最終注冊到ApiGroup里面去。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM