丨深入解析聲明式api二編寫自定義控制器_第1頁
丨深入解析聲明式api二編寫自定義控制器_第2頁
丨深入解析聲明式api二編寫自定義控制器_第3頁
丨深入解析聲明式api二編寫自定義控制器_第4頁
丨深入解析聲明式api二編寫自定義控制器_第5頁
已閱讀5頁,還剩26頁未讀, 繼續(xù)免費閱讀

付費下載

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

接下來,我就和你一起通過編寫代碼來實現(xiàn)這個過程。這個項目和上一篇文章里的代碼是同一個項目,你可以從這個 庫里找到它們。我在代碼里還加上了豐富的注釋,你可以隨時參考??偟脕碚f,編寫自定義控制器代碼的過程包括:編寫main數(shù)、編寫自定義控制器的定首先,我們來編寫這*個自定義控制器的main函數(shù)main數(shù)的主要工作就是,定義并初始化一個自定義控制器(CustomController),然代funcmain()3cfg,err:=cmd.BuildConfigFromFlags(masterURL,kube,err:=network,err:= networkInformerFactory:=informers.NewSharedInformerFactory(network, controller:=NewController(kube,network goiferr=controller.Run(2,stopCh);err!=nilglog.Fatalf("Errorrunningcontroller:%s", 21可以看到,這個main函數(shù)主要通過三步完成了初始化并啟動一個自定義控制器的第一步:main函數(shù)根據(jù)我提供的Master配置(APIServer的地址端口和kubeconfig的路徑),創(chuàng)建一個Kubernetes的 )和Network對象的 )但是,如果我沒有提供Master配置呢這時,main函數(shù)會直接使用一種名叫InClusterConfig的方式來創(chuàng)建這個 式,會假設(shè)你的自定義控制器是以Pod的方式運行在Kubernetes集群里的。而我在第15篇文章《深入解析Pod對象(二):使用進階》中曾經(jīng)提到過,Kubernetes里所有的Pod都會以Volume的方式自動掛載Kubernetes的默認ServiceAccount。所以,這個控制器就會直接使用默認ServiceAccount數(shù)據(jù)卷里的信息,來第二步:main函數(shù)為Network對象創(chuàng)建一個叫作InformerFactory(即:networkInformerFactory)的工廠,并使用它生成一個Network象的Informer,傳遞第三步:main數(shù)啟動上述的Informer,然后執(zhí)行controller.Run,啟動自定義控制至此,main函數(shù)就結(jié)束了看到這,你可能會感到非常困惑:編寫自定義控制器的過程難道就這么簡單嗎?這Informer又是個什么東西呢別著急接下來,我就為你詳細解釋一下這個自定義控制器的工作原Kubernetes目中,一個自定義控制器的工作原理,可以用下面這樣一幅流程圖來表1定義控制器的工作流程示意圖我們先從這幅示意圖的最左邊看這個控制器要做的第一件事,是從KubernetesAPIServer獲取它所關(guān)心的對象,也就是我定義的Network對象。這個操作,依靠的是一個叫作Informer(可以翻譯為:通知器)的代碼庫完成的。InformerAPI象是一一對應(yīng)的,所以我傳遞給自定義控制器的,正是一個Network對象的Informer(NetworkInformer)。不知你是否已經(jīng)注意到,我在創(chuàng)建這個Informer工廠的時候,需要給它傳遞一network事實上,NetworkInformer正是使用這個network,跟APIServer建立了連接。不過,真正負責(zé)這個連接的,則是Informer所使用的Reflector包。更具體地說,Reflector用的是一種叫作ListAndWatch的方法,來“獲取”并“監(jiān)聽”這些Network對象實例的變化。在ListAndWatch機制下,一旦APIServer端有新的Network實例被創(chuàng)建、刪除或者更新,Reflector都會收到“通知”。這時,該及它對應(yīng)的API對象這個組合,就被稱為增量(Delta),它會被放進一個DeltaFIFOQueue(即:增量先進先出隊列)中。而另一方面,Informe會不斷地從這個DeltaFIFOQueue里(Pop)增量。每拿到一個增量,Informer就會判斷這個增量里的類型,然后創(chuàng)建或者更新本地對象的緩存。這個緩存,在Kubernetes里一般被叫作Store。比如,如果類型是Added(添加對象),那么Informer就會通過一個叫作Indexer的庫把這個增量里的API對象保存在本地緩存中,并為它創(chuàng)建索引。相反地,如果增量的類型是Deleted(刪除對象),那么Informer就會從本地緩存中刪除這個對象。這個同步本地緩存的工作,是Informer的第一個職責(zé),也是它最重要的職而Informer的第二個職責(zé),則是根據(jù)這些的類型,觸發(fā)事先好的ResourceEventHandler。這些Handler,需要在創(chuàng)建控制器的時候給它對應(yīng)的接下來,我們就來編寫這個控制器的定義,它的主要內(nèi)容如下所代funckubesetnetworksetnetworkInformerinformers.NetworkInformer)*Controllercontroller:=kube kubenetworkset:network AddFunc:UpdateFunc:func(old,newinterface{})oldNetwork:=newNetwork:=ifoldNetwork.ResourceVersion==newNetwork.ResourceVersion DeleteFunc:return26我前面在main函數(shù)里創(chuàng)建了兩個(kubeset和networkset),然后在這段代碼里,使用這兩個和前面創(chuàng)建的Informer,初始化了自定義控制器。值得注意的是,在這個自定義控制器里,我還設(shè)置了一個工作隊列(workqueue),它正是處于示意圖中間位置的WorkQueue。這個工作隊列的作用是,負責(zé)同步Informer和控實際上,Kubernetes目為我們提供了很多個工作隊列的實現(xiàn),你可以根據(jù)然后,我為networkInformer了三個Handler(AddFunc、UpdateFunc和DeleteFunc),分別對應(yīng)API對象的“添加”“更新”和“刪除”。而具體的處理操作,都是將該對應(yīng)的API對象加入到工作隊列中。需要注意的是,實際入隊的并不是API對象本身,而是它們的Key,即:該API對象<namespace>/<name>而我們后面即將編寫的控制循環(huán),則會不斷地從這個工作隊列里拿到這些Key,然后開始綜合上面的講述,你現(xiàn)在應(yīng)該就能明白,所謂Informer,其實就是一個帶有本地緩存和索引機制的、可以EventHandler的。它是自定義控制器跟APIServer進行數(shù)據(jù)更具體地說,Informer過一種叫作ListAndWatch方法APIServer的API其中,ListAndWatch方法的含義是:首先,通過APIServer的LISTAPI“獲取”所有最新版本的API對象;然后,再通過WATCHAPI來“”所有這些API對象的變化。而通過到的變化,Informer就可以實時地更新本地緩存,并且調(diào)用這些對應(yīng)的EventHandler了。此外,在這個過程中,每經(jīng)過resyncPeriod指定的時間,Informer的本地緩存,都會使用最近一次LIST返回的結(jié)果強制更新一次,從而保證緩存的有效性。在Kubernetes需要注意的是,這個定時resync操作,也會觸發(fā)Informer的“更新”。但此時,這個“更新”對應(yīng)的Network對象實際上并沒有發(fā)生變化,即:新、舊兩個Network象的ResourceVersion一樣的。在這種情況下,Informer不需要對這個這也是為什么我在上面的UpdateFunc里,先判斷了一下新、舊兩個Network象以上,就是Kubernetes中的Informer庫的工作原理了接下來,我們就來到了示意圖中最后面的控制循環(huán)(ControlLoop)部分,也正是我在main函數(shù)最后調(diào)用controller.Run()啟動的“控制循環(huán)”。它的主要內(nèi)容如下所示:代func(c*Controller)Run(threadinessint,stopCh<-chanstruct{})errorifok:=cache.WaitForCacheSync(stopCh,worksSynced);!okreturnfmt.Errorf("failedtowaitforcachesto 6fori:=0;i<threadiness;i++gowait.Until(c.runWorker,time.Second,}return}可以看到,啟動控制循環(huán)的邏輯非常簡首先,等待Informer完成一次本地緩存的數(shù)據(jù)同步操然后,直接通過goroutine啟動一個(或者并發(fā)啟動多個)“無限循環(huán)”的任務(wù)所以接下來,我們就來編寫這個自定義控制器的業(yè)務(wù)邏輯,它的主要內(nèi)容如下所示代func(c*Controller)runWorker()forcessNextWorkItem()}45func(c*Controller)processNextWorkItem()boolobj,shutdown:=8 err:=func(objinterface{})erroriferr:=c.syncHandler(key);err!=nilreturnfmt.Errorf("errorsyncing'%s':%s",key, return return2527func(c*Controller)syncHandler(keystring)errornamespace,name,err:=network,err:=iferr!=niliferrors.IsNotFound(err)glog.Warningf("Networkdoesnotexistinlocalcache:%s/%s,willdeleteitnamespace,glog.Warningf("Network:%s/%sdoesnotexistinlocalcache,willdeleteitnamespace,//FIXME:callNeutronAPItodeletethisnetworkby//neutron.Delete(namespace,return}return glog.Infof("[Neutron]Trytoprocessnetwork:%#v...",//FIXME:Do//actualNetwork,exists:=neutron.Get(namespace,//if!exists neutron.Create(namespace,//}elseif!reflect.DeepEqual(actualNetwork,network) neutron.Update(namespace,// return65可以看到,在這個執(zhí)行周期里(processNextWorkItem),我們首先從工作隊列里(workqueue.Get)了一個成員,也就是一個Key(Network對象的namespace/name)然后,在syncHandler方法中,我使用這個Key,嘗試從Informer的緩存中拿到了它所對應(yīng)的Network對象??梢钥吹?,在這里,我使用了networksLister來嘗試獲取這個Key對應(yīng)的Network對象。這個操作,其實就是在本地緩存的索引。實際上,在Kubernetes的源碼中,你會經(jīng)??吹娇刂破鲝母鞣NLister獲取對象,比如:podLister、nodeLister等,它們使用的都是Informer和緩存機制。而如果控制循環(huán)從緩存中拿不到這個對象(即:networkLister返回了IsNotFound錯誤),那就意味著這個Network對象的Key是通過前面的“刪除”添加進工作隊列的。所以,盡管隊列里有這個Key,但是對應(yīng)的Network對象已經(jīng)被刪除了。這時候,我就需要調(diào)用NeutronAPI,把這個Key應(yīng)的Neutron絡(luò)從真實的里刪除而如果能夠獲取到對應(yīng)的Network象,我就可以執(zhí)行控制器模式里的對比“期望狀其中,自定義控制器“千辛萬苦”拿到的這個Network對象,正是APIServer里保存的“期望狀態(tài)”,即:用戶通過YAML提交到APIServer的信息。當(dāng)然,在我們的例子里,它已經(jīng)被Informer緩存在了本地。那么,“實際狀態(tài)”又從哪里來呢當(dāng)然是來自于實際的集群了所以,我們的控制循環(huán)需要通過NeutronAPI來查詢實際的網(wǎng)絡(luò)情比如,我可以先通過Neutron來查詢這個Network對象對應(yīng)的真實網(wǎng)絡(luò)是否存如果不存在,這就是一個典型的“期望狀態(tài)”與“實際狀態(tài)”不一致的情形。這時,我就需要使用這個Network對象里的信息(比如:CIDR和Gateway),調(diào)用NeutronAPI如果存在,那么,我就要這個真實網(wǎng)絡(luò)的信息,判斷它是否跟Network對象里的信息一致,從而決定我是否要通過Neutron來更新這個已經(jīng)存在的真實網(wǎng)絡(luò)。這樣,我就通過對比“期望狀態(tài)”和“實際狀態(tài)”的差異,完成了一次調(diào)協(xié)(Renle)的過程。至此,一個完整的自定義API對象和它所對應(yīng)的自定義控制器,就編寫完畢備注:與euron相關(guān)的業(yè)務(wù)代碼并不是本篇文章的重點,所以我僅僅通過注釋里的偽代碼為你表述了這部分內(nèi)容。如果你對這些代碼感的話,可以自行完成。最簡單的情況,你可以自己編寫一個euronMock,然后輸出對應(yīng)的操作日志。接下來,我們就一起來把這個項目運行起來,查看一下它的工作情代#Clone$git /resouer/k8s-controller-custom-resource$cdk8s-3###Skipthispartifyoudon'twantto#Install$go $godep#$gobuild-osamplecrd-controller11$./samplecrd-controller-kubeconfig=$HOME/.kube/config-12I0915 27159controller.go:84]Settingupevent13I0915 27159controller.go:113]StartingNetworkcontrol14I0915 27159controller.go:116]Waitingforinformercachesto15E0915 27159 16你可以看到,自定義控制器被啟動后,一開始會報這是因為,此時Network對象的CRD還沒有被創(chuàng)建出來,所以Informer去APIServer里“獲取”(List)Network對象時,并不能找到Network這個API資源類型的定義,代1Failedtolist*v1.Network:theservercouldnotfindtherequestedresource(get所以,接下來我就需要創(chuàng)建NetworkCRD,這個操作在上篇文章里已經(jīng)介紹過在另一個 窗口里執(zhí)行代1$kubectlapply-f這時候,你就會看到控制器的日志恢復(fù)了正常,控制循環(huán)啟動成代I0915 27159controller.go:116]WaitingforinformercachestoI0915 25245controller.go:121]StartingI0915 25245controller.go:127]Started接下來,我就可以進行Network刪改查操作了。首先,創(chuàng)建一個Network對象:代$catexample/example-apiVersion:kind:name:example- cidr: gateway:9$kubectlapply-fexample/example-network.samplecrd.k8s.io/example-network這時候,查看一下控制器的輸代I0915 27159controller.go:84]SettingupeventI0915 27159controller.go:113]StartingNetworkcontrolI0915 27159controller.go:116]WaitingforinformercachestoI0915 25245controller.go:121]StartingI0915 25245controller.go:127]StartedI0915 25245controller.go:229][Neutron]Trytoprocessnetwork:I0915 25245controller.go:183]Successfullysynced'default/example-可以看到,我們上面創(chuàng)建example-network操作,觸發(fā)了EventHandler“添加”事緊接著,控制循環(huán)就從隊列里拿到了這個對象,并且打印出了正在“處理”這個對象的日志可以看到,這個NetworkResourceVersion,也就是API象的版本號,是479015,而它的Spec字段的內(nèi)容,跟我提交的YAML文件一摸一樣,比如,它的CIDR網(wǎng)段是:這時候,我來修改一下這個YAML文件的內(nèi)容,如下所代$catexample/example-apiVersion:kind:name:example- cidr: gateway:可以看到,我把這個YAML件里的CIDRGateway段的修改成了網(wǎng)段然后,我們執(zhí)行了kubectlapply命令來提交這次更新,如下所代$kubectlapply-fexample/example-network.samplecrd.k8s.io/example-network這時候,我們就可以觀察一下控制器的輸代2I0915controller.go:229][Neutron]Trytoprocessnetwork:3I0915controller.go:183]Successfullysynced'default/example-可以看到,這一次,Informer的“更新”被觸發(fā),更新后的Network對象Key被添加到了工作隊列之中所以,接下來控制循環(huán)從工作隊列里拿到的Network對象,與前一個對象是不同的:它的ResourceVersion的值變成了479062;而Spec里的字段,則變成了/16網(wǎng)最后,我再把這個對象刪除代$kubectldelete-fexample/example-這一次,在控制器的輸出里,我們就可以看到,Informer的“刪除”被觸發(fā),并且控制循環(huán)“調(diào)用”NeutronAPI“刪除”了真實環(huán)境里的網(wǎng)絡(luò)。這個輸出如下所示:代W0915 25245controller.go:212]Network:default/example-networkI0915 25245controller.go:215][Neutron]Deletingnetwork:I0915 25245controller.go:183]Successfullysynced'default/example-以上,就是編寫和使用自定義控制器的全部流程實際上,這套流程不僅可以用在自定義API源上,也完全可以用在Kubernetes生的默認API對象上。比如,我們在main函數(shù)里,除了創(chuàng)建一個NetworkInformer外,還可以初始化一個Kubernetes認API象的Informer廠,比如Deployment象的Informer。這個代funcmain()3456789

kubeInformerFactory:=kubeinformers.NewSharedInformerFactory(kube,controller:=NewController(kube,example,go在這段代碼中,我們首先使用Kubernetes (kube)創(chuàng)建了一個工廠然后,我用跟Network類似的處理方法,生成了一個Deployment接著,我把DeploymentInformer遞給了自定義控制器;當(dāng)然,我也要調(diào)用Start法來啟動這個DeploymentInformer。而有了這個DeploymentInformer,這個控制器也就持有了所有Deployment的信息。接下來,它既可以通過deploymentInformer.Lister()來獲取Etcd里的所有Deployment對象,也可以為這個DeploymentInformer具體的Handler來。更重要的是,這就使得在這個自定義控制器里面,我可以通過對自定義API對象和默API對象進行協(xié)同,從而實現(xiàn)更加復(fù)雜的編排功能比如:用戶每創(chuàng)建一個新的Deployment,這個自定義控制器,就可以為它創(chuàng)建一個對應(yīng)的Network供它使用。這些對KubernetesAPI程范式的更高級應(yīng)用,我就留給你在實際的場景中去探索和實踐在今天這篇文章中,我為你剖析了KubernetesAPI程范式的具體原理,并編寫了一個自這其中,有如下幾個概念和機制,是你一定要理解清楚所謂的Informer,就是一個自帶緩存和索引機制,可以觸發(fā)Handler客戶端庫。這個本地緩存在Kubernetes中一般被稱為Store,索引一般被稱為Index。Informer用了Reflector,它是一個可以通過ListAndWatch制獲取并監(jiān)視API對Refle

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論