Kubernetes Visitor設(shè)計模式及發(fā)送pod創(chuàng)建請求解析
確立目標
- 理解kubectl的核心實現(xiàn)之一:
Visitor Design Pattern
訪問者模式 - 理解發(fā)送pod創(chuàng)建請求的細節(jié)
visitor design pattern
在設(shè)計模式中,訪問者模式的定義為:
允許一個或者多個操作應(yīng)用到對象上,解耦操作和對象本身
那么,對一個程序來說,具體的表現(xiàn)就是:
- 表面:某個對象執(zhí)行了一個方法
- 內(nèi)部:對象內(nèi)部調(diào)用了多個方法,最后統(tǒng)一返回結(jié)果
舉個例子,
- 表面:調(diào)用一個查詢訂單的接口
- 內(nèi)部:先從緩存中查詢,沒查到再去熱點數(shù)據(jù)庫查詢,還沒查到則去歸檔數(shù)據(jù)庫里查詢
Visitor
我們來看看kubeadm中的訪問者模式的定義:
// Visitor 即為訪問者這個對象 type Visitor interface { Visit(VisitorFunc) error } // VisitorFunc對應(yīng)這個對象的方法,也就是定義中的“操作” type VisitorFunc func(*Info, error) error
基本的數(shù)據(jù)結(jié)構(gòu)很簡單,但從當前的數(shù)據(jù)結(jié)構(gòu)來看,有兩個問題:
- 單個操作 可以直接調(diào)用
Visit
方法,那多個操作如何實現(xiàn)呢? - 在應(yīng)用多個操作時,如果出現(xiàn)了error,該退出還是繼續(xù)應(yīng)用下一個操作呢?
Chained
以下內(nèi)容在staging/src/k8s.io/cli-runtime/pkg/resource
VisitorList和EagerVisitorList是將多個對象聚合為一個對象
DecoratedVisitor和ContinueOnErrorVisitor是將多個方法聚合為一個方法
FlattenListVisitor和FilteredVisitor是將對象抽象為多個底層對象,逐個調(diào)用方法
VisitorList
封裝多個Visitor為一個,出現(xiàn)錯誤就立刻中止并返回
// VisitorList定義為[]Visitor,又實現(xiàn)了Visit方法,也就是將多個[]Visitor封裝為一個Visitor type VisitorList []Visitor // 發(fā)生error就立刻返回,不繼續(xù)遍歷 func (l VisitorList) Visit(fn VisitorFunc) error { for i := range l { if err := l[i].Visit(fn); err != nil { return err } } return nil }
EagerVisitorList
封裝多個Visitor為一個,出現(xiàn)錯誤暫存下來,全部遍歷完再聚合所有的錯誤并返回
// EagerVisitorList 也是將多個[]Visitor封裝為一個Visitor type EagerVisitorList []Visitor // 返回的錯誤暫存到[]error中,統(tǒng)一聚合 func (l EagerVisitorList) Visit(fn VisitorFunc) error { errs := []error(nil) for i := range l { if err := l[i].Visit(func(info *Info, err error) error { if err != nil { errs = append(errs, err) return nil } if err := fn(info, nil); err != nil { errs = append(errs, err) } return nil }); err != nil { errs = append(errs, err) } } return utilerrors.NewAggregate(errs) }
DecoratedVisitor
這里借鑒了裝飾器的設(shè)計模式,將一個Visitor調(diào)用多個VisitorFunc方法,封裝為調(diào)用一個VisitorFunc
// 裝飾器Visitor type DecoratedVisitor struct { visitor Visitor decorators []VisitorFunc } // visitor遍歷調(diào)用decorators中所有函數(shù),有失敗立即返回 func (v DecoratedVisitor) Visit(fn VisitorFunc) error { return v.visitor.Visit(func(info *Info, err error) error { if err != nil { return err } for i := range v.decorators { if err := v.decorators[i](info, nil); err != nil { return err } } return fn(info, nil) }) }
ContinueOnErrorVisitor
// 報錯依舊繼續(xù) type ContinueOnErrorVisitor struct { Visitor } // 報錯不立即返回,聚合所有錯誤后返回 func (v ContinueOnErrorVisitor) Visit(fn VisitorFunc) error { errs := []error{} err := v.Visitor.Visit(func(info *Info, err error) error { if err != nil { errs = append(errs, err) return nil } if err := fn(info, nil); err != nil { errs = append(errs, err) } return nil }) if err != nil { errs = append(errs, err) } if len(errs) == 1 { return errs[0] } return utilerrors.NewAggregate(errs) }
FlattenListVisitor
將runtime.ObjectTyper解析成多個runtime.Object,再轉(zhuǎn)換為多個Info,逐個調(diào)用VisitorFunc
type FlattenListVisitor struct { visitor Visitor typer runtime.ObjectTyper mapper *mapper }
FilteredVisitor
對Info資源的檢驗
// 過濾的Info type FilteredVisitor struct { visitor Visitor filters []FilterFunc } func (v FilteredVisitor) Visit(fn VisitorFunc) error { return v.visitor.Visit(func(info *Info, err error) error { if err != nil { return err } for _, filter := range v.filters { // 檢驗Info是否滿足條件,出錯則退出 ok, err := filter(info, nil) if err != nil { return err } if !ok { return nil } } return fn(info, nil) }) }
Implements
StreamVisitor
最基礎(chǔ)的Visitor
type StreamVisitor struct { // 讀取信息的來源,實現(xiàn)了Read這個接口,這個"流式"的概念,包括了常見的HTTP、文件、標準輸入等各類輸入 io.Reader *mapper Source string Schema ContentValidator }
FileVisitor
文件的訪問,包括標準輸入,底層調(diào)用StreamVisitor來訪問
type FileVisitor struct { // 表示文件路徑或者STDIN Path string *StreamVisitor }
URLVisitor
HTTP用GET方法獲取數(shù)據(jù),底層也是復用StreamVisitor
type URLVisitor struct { URL *url.URL *StreamVisitor // 提供錯誤重試次數(shù) HttpAttemptCount int }
KustomizeVisitor
自定義的Visitor,針對自定義的文件系統(tǒng),Customize 定制,是將C轉(zhuǎn)成了K
type KustomizeVisitor struct { Path string *StreamVisitor }
發(fā)送創(chuàng)建Pod請求的實現(xiàn)細節(jié)
kubectl是怎么向kube-apiserver發(fā)送請求的呢?
send request
// 在RunCreate函數(shù)中,關(guān)鍵的發(fā)送函數(shù) obj, err := resource. NewHelper(info.Client, info.Mapping). DryRun(o.DryRunStrategy == cmdutil.DryRunServer). WithFieldManager(o.fieldManager). Create(info.Namespace, true, info.Object) // 進入create函數(shù),查看到 m.createResource(m.RESTClient, m.Resource, namespace, obj, options) // 對應(yīng)的實現(xiàn)為 func (m *Helper) createResource(c RESTClient, resource, namespace string, obj runtime.Object, options *metav1.CreateOptions) (runtime.Object, error) { return c.Post(). NamespaceIfScoped(namespace, m.NamespaceScoped). Resource(resource). VersionedParams(options, metav1.ParameterCodec). Body(obj). Do(context.TODO()). Get() } /* 到這里,我們發(fā)現(xiàn)了2個關(guān)鍵性的定義: 1. RESTClient 與kube-apiserver交互的RESTful風格的客戶端 這個RESTClient是來自于Builder時的傳入,生成的Result,底層是一個NewClientWithOptions生成的 2. runtime.Object 資源對象的抽象,包括Pod/Deployment/Service等各類資源 3. 我們是傳入的文件,是FileVisitor來執(zhí)行的,底層Builder.mapper調(diào)用Decode來生成obj(Unstructured()) */
RESTful Client
我們先來看看,與kube-apiserver交互的Client是怎么創(chuàng)建的
// 從傳入?yún)?shù)來看,數(shù)據(jù)來源于Info這個結(jié)構(gòu) r.Visit(func(info *resource.Info, err error) error{}) // 而info來源于前面的Builder,前面部分都是將Builder參數(shù)化,核心的生成為Do函數(shù) r := f.NewBuilder(). Unstructured(). Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, &o.FilenameOptions). LabelSelectorParam(o.Selector). Flatten(). Do() // 大致看一下這些函數(shù),我們可以在Unstructured()中看到getClient函數(shù),其實這就是我們要找的函數(shù) func (b *Builder) getClient(gv schema.GroupVersion) (RESTClient, error) // 從返回值來看,client包括默認的REST client和配置選項 NewClientWithOptions(client, b.requestTransforms...) // 這個Client會在kubernetes項目中大量出現(xiàn),它是與kube-apiserver交互的核心組件,以后再深入。
Object
Object
這個對象是怎么獲取到的呢?因為我們的數(shù)據(jù)源是來自文件的,那么我們最直觀的想法就是FileVisitor
func (v *FileVisitor) Visit(fn VisitorFunc) error { // 省略讀取這塊的代碼,底層調(diào)用的是StreamVisitor的邏輯 return v.StreamVisitor.Visit(fn) } func (v *StreamVisitor) Visit(fn VisitorFunc) error { d := yaml.NewYAMLOrJSONDecoder(v.Reader, 4096) for { // 這里就是返回info的地方 info, err := v.infoForData(ext.Raw, v.Source) } } // 再往下一層看,來到mapper層,也就是kubernetes的資源對象映射關(guān)系 func (m *mapper) infoForData(data []byte, source string) (*Info, error){ // 這里就是我們返回Object的地方,其中GVK是Group/Version/Kind的縮寫,后續(xù)我們會涉及 obj, gvk, err := m.decoder.Decode(data, nil, nil) }
這時,我們想回頭去看,這個mapper是在什么時候被定義的?
// 在Builder初始化中,我們就找到了 func (b *Builder) Unstructured() *Builder { b.mapper = &mapper{ localFn: b.isLocal, restMapperFn: b.restMapperFn, clientFn: b.getClient, // 我們查找資源用到的是這個decoder decoder: &metadataValidatingDecoder{unstructured.UnstructuredJSONScheme}, } return b } // 逐層往下找,對應(yīng)的Decode方法的實現(xiàn),就是對應(yīng)的數(shù)據(jù)解析成data: func (s unstructuredJSONScheme) decode(data []byte) (runtime.Object, error) { // 細節(jié)暫時忽略 }
Post
了解了REST Client
和Object
的大致產(chǎn)生邏輯后,我們再回過頭來看發(fā)送的方法
// RESTful接口風格中,POST請求對應(yīng)的就是CREATE方法 c.Post(). NamespaceIfScoped(namespace, m.NamespaceScoped). Resource(resource). VersionedParams(options, metav1.ParameterCodec). Body(obj). Do(context.TODO()). Get() // Do方法,發(fā)送請求 err := r.request(ctx, func(req *http.Request, resp *http.Response) { result = r.transformResponse(resp, req) }) // Get方法,獲取請求的返回結(jié)果,用來打印狀態(tài) switch t := out.(type) { case *metav1.Status: if t.Status != metav1.StatusSuccess { return nil, errors.FromObject(t) } }
站在前人的肩膀上,向前輩致敬,Respect!
Summary
通過Visitor的設(shè)計模式,從傳入的參數(shù)中解析出內(nèi)容,然后在Factory進行NewBuilder的時候進行配置實現(xiàn)RESTClient,mapper,obj的生成,Do()拿到Result,組裝好POST請求發(fā)送到ApiServer。
到這里我們對kubectl的功能有了初步的了解,以下是關(guān)鍵內(nèi)容所在:
命令行采用了cobra
庫,主要支持7個大類的命令;
掌握Visitor設(shè)計模式,這個是kubectl實現(xiàn)各類資源對象的解析和校驗的核心;
初步了解RESTClient
和Object
這兩個對象,它們是貫穿kubernetes的核心概念;
調(diào)用邏輯
- cobra匹配子命令
- 用Visitor模式構(gòu)建Builder
- 用RESTClient將Object發(fā)送到kube-apiserver
以上就是Kubernetes Visitor設(shè)計模式及發(fā)送pod創(chuàng)建請求解析的詳細內(nèi)容,更多關(guān)于Kubernetes Visitor發(fā)送pod請求的資料請關(guān)注腳本之家其它相關(guān)文章!
- kubernetes數(shù)據(jù)持久化PV?PVC深入分析詳解
- kubernetes數(shù)據(jù)持久化StorageClass動態(tài)供給實現(xiàn)詳解
- Kubernetes?controller?manager運行機制源碼解析
- Kubernetes Informer數(shù)據(jù)存儲Index與Pod分配流程解析
- Kubernetes scheduler啟動監(jiān)控資源變化解析
- Kubernetes ApiServer三大server權(quán)限與數(shù)據(jù)存儲解析
- Kubernetes kubectl中Pod創(chuàng)建流程源碼解析
- 一文詳解基于Kubescape進行Kubernetes安全加固
相關(guān)文章
Kubernetes(K8S)入門基礎(chǔ)內(nèi)容介紹
這篇文章介紹了Kubernetes(K8S)的入門基礎(chǔ)內(nèi)容,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-03-03KubeSphere接入外部Elasticsearch實戰(zhàn)示例
這篇文章主要為大家介紹了KubeSphere接入外部Elasticsearch實戰(zhàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12Kubernetes關(guān)鍵組件與結(jié)構(gòu)組成介紹
這篇文章介紹了Kubernetes的關(guān)鍵組件與結(jié)構(gòu)組成,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-03-03Kubernetes scheduler啟動監(jiān)控資源變化解析
這篇文章主要為大家介紹了Kubernetes scheduler啟動監(jiān)控資源變化解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11Kubernetes(K8S)容器集群管理環(huán)境完整部署詳細教程-中篇
本系列文章主要介紹了Kubernetes(K8S)容器集群管理環(huán)境完整部署的詳細教程,分為上中下三篇文章,此為中篇,需要的朋友可以參考下2022-01-01kubernetes數(shù)據(jù)持久化StorageClass動態(tài)供給實現(xiàn)詳解
這篇文章主要為大家介紹了kubernetes數(shù)據(jù)持久化StorageClass動態(tài)供給實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11Kubernetes Visitor設(shè)計模式及發(fā)送pod創(chuàng)建請求解析
這篇文章主要為大家介紹了Kubernetes Visitor設(shè)計模式及發(fā)送pod創(chuàng)建請求解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11