VMware在今年4月份突然釋出了業内第一個開源的PaaS——CloudFoundry。釋出至今的這幾個月裡,筆者一直關注它的演進,并從它的架構設計中獲益良多,覺得有必要寫出來與大家分享一下。
本文會分為兩個部份:第一部份主要介紹CloudFoundry的架構設計,從它所包含的子產品介紹起,到各部份的消息流向,各子產品如何協調合作;第 二部份會在第一部份的基礎上,以如何在你的資料中心裡面用CloudFoundry部署一個私有PaaS為目标,把第一部分介紹到的架構知識使用起來。
第一部份講的很多内容,會引用Pat在10月12日的VMwareCloud Forum上面關于CloudFoundry架構的演講。Pat是CloudFoundry Core的負責人,他的那次演講很值得一聽。如果你當時在場,并且了解他所說的内容,本部份可以選擇直接跳過。我除了會把說的内容講具體點外,不太可能可 以講得比他好。
一、架構及子產品
從總體地看,CloudFoundry的架構如下:

這個架構圖以及下文所用到的各子產品架構圖均來自Pat的PPT。從上圖能夠看到CloudFoundry主要有以下幾大元件組成:
1、 Router:顧名思義,Router元件在CloudFoundry中是對所有進來的Request進行路由。進入Router的request主要有兩類:首先是來自VMCClient或者STS的,由CloudFoundry使用者發出的,管理型指令。
例如:列出你所有apps的vmcapps,送出一個apps等等。這類request會被路由到AppLife Management元件,又叫CloudController元件去;第二類是外界對你所部署的apps通路的request。這部份requests 會被路由到Appexecution,又或者叫做DEAs的元件去。所有進入CloudFoundry系統的requests都會經過Router元件, 看到這裡可能會有朋友會擔心Router成為單點,進而成為整個雲的瓶頸。
但是CloudFoundry作為雲系統,其設計的核心就是去單點依賴,元件平行擴充,且可替代的以保證擴充性,這是CloudFoundry,甚 至所有雲計算系統的設計原則,後文會讨論CloudFoundry如何做到這點,目前隻要知道,系統可以部署多個Routers共同處理進來的 requests,但是Router上層的LoadBalance不在CloudFoundry的實作範圍,CloudFoundry隻保證所有的 request是無狀态的,這樣就使上層均衡附載選擇面非常非常大了,例如可以通過DNS做,也可以部署硬體的LoadBalancer,或者簡單點,弄 台ngnix作負載均衡器,都是可行的。
Router元件,目前版本是對nginx的一個簡單封裝。熟悉ngnix的朋友應該知道,它可以一個套接字檔案(.sock檔案)作為輸入輸出。所有安裝CloudFoundry的Router元件伺服器都會安裝一個nginx,其ngnix.conf檔案有以下配置:
從整體的來看,Router元件的結構如下:
外界httprequest進入CloudFoundry伺服器,nginx會首先接到request,nginx通過sock與 router.rb進行互動,于是真正處理請求的是Router元件。router.rb裡面根據傳入的url,使用者名密碼等,進行邏輯判斷,到 CloudController元件或者DEA元件取資料并且返通過與niginx連接配接的.sock檔案傳回。
router.rb是對nginx進行了邏輯封裝。熟悉CloudFoundry的朋友肯定知道,CloudFoundry給每一個app配置設定了一 個url通路,如果直接使用VMware所托管的CloudFoundry.com的話,那你的app的url可能就是 xxx.cloudfoundry.com,無論通過指令給你的app擴充了多少個instances,都是從這個url通路的,這裡面的url轉換路由 就是由router.rb實作的。
2、DEA(Droplet Execution Agency): 首先要解析下什麼叫做Droplet。Droplet在CloudFoundry的概念裡面是指一個把你送出的源代碼,以及CloudFoundry配套 好的運作環境,再加上一些管理腳本,例如Start/Stop這些小腳本全部壓縮好在一起的tar包。還有一個概念,叫做Stagingapp,就是指制 作上面描述這個包,然後把它存儲好的過程。CloudFoundry會自動儲存這個Droplet,直到你start一個app的時候,一台部署了DEA 子產品的伺服器會來拿一個Droplet的copy去運作。是以如果你擴充你的app到10個instances,那這個Droplet就被會複制十份,讓 10個DEA伺服器拿去運作。
下圖是DEA子產品的架構圖:
Cloud Controller子產品(下面會介紹)會發送start/stop等基本的apps管理請求給DEA,dea.rb接收這些請求,然後從NFS裡面找到 合适的Droplet。前面說到Droplet其實是一個帶有運作腳本的,帶運作環境的tar包,DEA隻需要把它拿過來解壓,并即行裡面的start腳 本,就可以讓這個app跑起來。到此,app算是可以通路,并start起來了,換句話說就是有這台伺服器的某一個端口已經在待命,隻要有request 從這個端口進來,這個app就可以接收并傳回正确的資訊。
接着dea.rb要做些善後的工作:1、把這個資訊告訴Router子產品。我們前面說到,所有進入CloudFoundry的requests都是 由Router子產品處理并轉發的,包括使用者對app的通路request,一個app起來後,需要告訴router,讓它根據loadbalance等原 則,把合适的request轉進來,使這個app的instance能夠幹起活;2、一些統計性的工作,例如要把這個使用者又新部署了一個app告訴 CloudController,以作quota控制等;3、把運作資訊告訴HealthManager子產品,實時報告該app的instance運作情 況。另外DEA還要負責部份對Droplet的查詢工作,譬如,如果使用者通過CloudController想查詢一個app的log資訊,那DEA需要 從該Droplet裡面取到log傳回等等。
3、CloudController:CloudController是CloudFoundry的管理子產品。主要工作包括:
a) 對apps的增删改讀;
b) 啟動、停止應用程式;
c) Staging apps(把apps打包成一個droplet);
d) 修改應用程式運作環境,包括instance、mem等等;
e) 管理service,包括service與app的綁定等;
f) Cloud環境的管理;
g) 修改Cloud的使用者資訊;
h) 檢視Cloud Foundry,以及每一個app的log資訊。
這似乎有點複雜,但簡單的說,可以很簡單:就是與VMC和STS互動的伺服器端。VMC和STS與CloudFoundry通信采用的是 restful接口,另一方面CloudController是一個典型的Rubyon Rails項目,從VMC或者STS接到JSON格式的協定,然後寫入CloudController Database,并發消息到各模快去控制管理整個雲。和其他ROR項目一樣,CloudController的所有API可以從 conf/routes.rb裡看到。開放的Restful接口好處在于第三方應用開發和內建,企業在用CloudFoundry部署私有雲的時候,可以 通過這些接口來自動化控制管理整個Cloud環境。這部份内容将在第二部份論述。
下圖是Cloud Controller的架構圖:
圖中Health Manager和DEA是外部子產品,CCDatabase就是CloudController Database,這個是整個CloudFoundry不能做HP的地方。CloudController Database的并發性不會很多,應用級别的資料庫通路是由底下的Service子產品處理的,這個資料庫存的是Cloud的配置資訊。讀操作主要來自 DEA啟動,作為初始化DEA的依據;以及healthmanager子產品會從這裡讀取預期的狀态資訊,這部份資料會與從DEA得到的實際狀态資訊進行比 對。
NFS是多個CloudController的共享存儲,CloudController其中一個重要工作就是StagingApps。 Droplets的存儲是在叢集環境的唯一的。而CloudController是叢集運作,換言之,就是每一個控制Request可能由不同的 CloudController處理,假設一個簡單的使用者場景:我們需要部署一個app到CloudFoundry中。我們在敲完那條簡單的push指令 後,VMC開始工作,在做完一輪的使用者鑒權、檢視所部署的apps數量是否超過預定數額,問了一堆相關app的問題後,需要發4個指令:
1.發一個POST到”apps”,建立一個app;
2.發一個PUT到”apps/:name/application”,上傳app;
3.發一個GET到”apps/:name/”,取得app狀态,看看是否已經啟動;
4.如果沒有啟動,發一個PUT到”apps/:name/”,使其啟動。
如果第2和第4步由不同的Cloud Controller來處理,而又無法保證他們能找到同一個Droplet,那第4步将會因為找不到對應的Droplet而啟動失敗。如何保證這一連串指 令過來所指向的Droplet都是同一個呢?使用NFS,使CloudController共享存儲是最簡單的方法。但是這個方法在安全性等方面并不完 美。在10月12日的VMwareCloud Forum上,Pat告訴我們下一版本的CloudFoundry這裡将會有大調整,但是在那部份代碼公開前,我不友善在這評價太多。
4、HealthManager: 做的事情不複雜,簡單的說是從各個DEA裡面拿到運作資訊,然後進行統計分析,報告等。統計資料會與CloudController的設定名額進行比對, 并提供Alert等。HealthManager子產品目前還不是十分完善,但是CloudManage棧裡面,自動化health管理、分析是一個很重要 的領域,而這方面可以擴充的地方也很多,結合OrchestrationEngine可以使雲自管理、自預警;而與BI方面技術結合,可以統計營運情況, 合理配置設定資源等。這方面CloudFoundry還在發展之中。
5、Services:Cloud Foundry的Service子產品從源代碼控制上看就知道是一個獨立的、可Plugin的子產品,以友善第三方把自己的服務整合入 CloudFoundry生态系統。在Github上看到service是與CloudFoundry Core項目vcap獨立的一個repository,為vcap-service。Service子產品其中設計原則是友善第三方服務提供商提供服務。在 這方面CloudFoundry做得很成功,從Github上看,已經有以下服務提供:a)MongoDB; b) mysql; c) neo4j; d) PostgreSql; e) RabbitMQ; f) Redis; g)vBlob。基類都是放在base檔案夾中。
第三方如果需要自己開發CloudFoundry的服務,需要繼承改寫它裡面的兩個基礎類:Node和Gateway;而裡面一些操作, 如:Provision,可以在base的provisioner.rb基礎上加入自己的邏輯,同樣的還有Service_Error和 Service_Message等。關于如何寫自己的Service,ELC的部落格會推出相應文章詳細論述,并不在本文的讨論範圍裡面,從架構了解上來 說,隻要知道服務間的關系,知道個服務與base間透過繼承關系來橫向擴充,而CloudFoundry與apps調用Service是通過base來完 成這一簡單的架構方法即可。
CloudFoundry是一個多子產品的分布式系統,支援子產品自發現,錯誤自檢,且子產品間低耦合。其核心原理就是基于消息釋出訂閱機制。每個台服務 器上的每個子產品會根據自己的消息類别,向MessageBus釋出多個消息主題;而同時也向自己需要互動的子產品,按照需要的資訊内容的消息主題訂閱消息。 譬如:一個DEA被加入CloudFoundry叢集中,它需要向大家吼一下,以表明它已經準備好服務了,它會釋出一個主題是”dea.start”的消 息:
@ hello_message_json中包括DEA的UUID,ip, port, 版本資訊等内容。
再例如,CloudController需要啟動一個Droplet的instance:
a)首先一個DEA在啟動的時候,會首先會對自己UUID的消息主題進行訂閱。
其他子產品需要通過’’dea.#{uuid}.start”這個主題發送消息來使它啟動,一旦這個DEA接收到消息,就會觸發process_dea_start(msg)這個方法來處理啟動所需要的工作。
b)Cloud Controller或者其他子產品發送消息,讓UUID為xxx的DEA啟動。
c)DEA子產品接收到消息後,就會觸發process_dea_start(msg)方法。msg是由其他子產品發送過來的消息内容,包 括:droplet_id,instance_index, service, runtime等内容,process_dea_start會取得這些啟動DEA必須的資訊,然後進行一系列操作,例如從NFS中取得Droplet,解 壓,修改必要環境配置,運作啟動腳本等等。等一切都準備好後,然後需要給Router發個消息,告訴它這個Droplet已經随時準備好報效國家,以後有 相應的request記得讓它來處理。
d)Router子產品在啟動時就已經訂閱”router.register”消息主題。
收到前面DEA發出的資訊後,會觸發register_droplet方法,去綁定Droplet。到此啟動一個Droplet的instance工作完成。
我們可以看到整個CloudFoundry的核心就是一套消息系統,如果想了解CloudFoundry的來龍去脈,去跟蹤它裡面複雜的消息機制是 非常好的方法。另一方面,CloudFoundry是一套基于消息的分布式系統,面向消息的架構是它節點橫向擴充,元件自發現等雲特性的基礎。
Cloud Foundry的架構簡單介紹至此,其實作為第一款開源的PaaS,CloudFoundry架構有很多可以學習借鑒的地方,很多細節上的處理是很精妙 的,這些内容如果有可能會在後續文章繼續探讨,本文題雖為深入CloudFoundry,其實也隻是淺嘗即止,把總體架構介紹一下,目标在于使我們有足夠 的背景知識去用CloudFoundry搭建企業内部的私有PaaS。總結一下,筆者從CloudFoundry的結構中學到的東西:
1、基于消息的多元件架構是實作叢集的簡單、且有效方法。消息可以使叢集節點間解耦,使自注冊,自發現這些在大規模資料中心中很重要的功能得到實作;
2、适當的抽象層,模闆模式的使用,友善第三方可以友善在CloudFoundry開發擴充功能。CloudFoundry在DEA及 Service層都做了抽象層處理,相對應地使開發者可以容易地為CloudFoundry開發Runtime和Service。例如,在 CloudFoundry剛推出的時候,隻支援Node.js,Java, Ruby,但第三方提供商、開源社群快速跟進,為CloudFoundry添加了PHP,Python的支援。這得益于CloudFoundry精巧的 DEA架構設計,如何開發新的Runtime支援,會在後續博文中有所論述.
二、源碼導讀
1、vcap: Cloud Foundry的Core,又或者稱作Kernel;
2、vcap-service: Cloud Foundry的Service元件。Cloud Foundry的service是作為插件提供的,這出于它友善第三方開發service而設計的;
3、vmc: VMware Cloud CLI. 是一個Ruby應用,與Cloud Foundry的CLI互動。主要通過分析使用者輸入的CLI,向CloudFoundry發送Restful請求;
4、vcap-java: 如果你的app是用java開發,且需要與Cloud Foundry互動,例如取得目前serviceserver的ip位址等,你可能需要這個jar,裡面對我們Java開發常用架構有所支援,它底層也是 對CloudFoundry的Restful請求的包裝;
5、vcap-java-client: Cloud Foundry的Restful API的Java封裝,與上面的項目不一樣,它隻是個簡單的讀取CloudFoundry資訊,并放如JavaBean中;
6、vcap-test: Cloud Foundry的test cases;
7、vcap-test-assets: Cloud Foundry一些apps示例。