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

