Kubernetes Visitor設(shè)計模式及發(fā)送pod創(chuàng)建請求解析
確立目標(biāo)
- 理解kubectl的核心實現(xiàn)之一:
Visitor Design Pattern訪問者模式 - 理解發(fā)送pod創(chuàng)建請求的細(xì)節(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)很簡單,但從當(dāng)前的數(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、文件、標(biāo)準(zhǔn)輸入等各類輸入
io.Reader
*mapper
Source string
Schema ContentValidator
}
FileVisitor
文件的訪問,包括標(biāo)準(zhǔn)輸入,底層調(diào)用StreamVisitor來訪問
type FileVisitor struct {
// 表示文件路徑或者STDIN
Path string
*StreamVisitor
}
URLVisitor
HTTP用GET方法獲取數(shù)據(jù),底層也是復(fù)用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)細(xì)節(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風(fēng)格的客戶端 這個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包括默認(rèn)的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) {
// 細(xì)節(jié)暫時忽略
}
Post
了解了REST Client和Object的大致產(chǎn)生邏輯后,我們再回過頭來看發(fā)送的方法
// RESTful接口風(fēng)格中,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)建請求解析的詳細(xì)內(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)容,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-03-03
KubeSphere接入外部Elasticsearch實戰(zhàn)示例
這篇文章主要為大家介紹了KubeSphere接入外部Elasticsearch實戰(zhàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12
Kubernetes關(guān)鍵組件與結(jié)構(gòu)組成介紹
這篇文章介紹了Kubernetes的關(guān)鍵組件與結(jié)構(gòu)組成,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-03-03
Kubernetes scheduler啟動監(jiān)控資源變化解析
這篇文章主要為大家介紹了Kubernetes scheduler啟動監(jiān)控資源變化解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11
Kubernetes(K8S)容器集群管理環(huán)境完整部署詳細(xì)教程-中篇
本系列文章主要介紹了Kubernetes(K8S)容器集群管理環(huán)境完整部署的詳細(xì)教程,分為上中下三篇文章,此為中篇,需要的朋友可以參考下2022-01-01
kubernetes數(shù)據(jù)持久化StorageClass動態(tài)供給實現(xiàn)詳解
這篇文章主要為大家介紹了kubernetes數(shù)據(jù)持久化StorageClass動態(tài)供給實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11
Kubernetes Visitor設(shè)計模式及發(fā)送pod創(chuàng)建請求解析
這篇文章主要為大家介紹了Kubernetes Visitor設(shè)計模式及發(fā)送pod創(chuàng)建請求解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11

