1. 程式人生 > >《Docker——容器與容器雲》:第五章 構建自己的容器雲

《Docker——容器與容器雲》:第五章 構建自己的容器雲

我們在第1章介紹了一個雲端計算平臺應有的層次結構,其中平臺即服務層(PaaS)是本書重點著墨描述的。儘管在一些經典PaaS平臺中,容器技術已經扮演了一個至關重要的角色,但很遺憾,大部分經典PaaS平臺中容器功能被侷限在了“資源隔離”這狹小的技術範圍當中了。當擁有了像Docker這樣的容器技術後,是時候開始從一個新的角度來思考容器在雲端計算平臺當中扮演的角色和地位了。

5.1 再談雲平臺的層次架構

回顧一下第1章描述的雲端計算平臺層次,IaaS平臺接管了所有的資源虛擬化工作,通過軟體定義的方式來為雲租戶提供虛擬的計算、網路和儲存資源。PaaS平臺接管了所有的執行時環境和應用支撐工作,雲平臺的租戶因此可以申請配額內的計算單元而不是虛擬機器資源來執行自己的服務。當前不少經典PaaS平臺已經採用容器作為計算單元,那些仍然依靠虛擬機器提供應用執行時支援的“PaaS平臺”在本書中將被稱為“IaaS+平臺”。雲平臺排程這些計算單元用以部署和執行租戶的程式碼製品。在這兩層的基礎上,使用者部署的應用和服務通過API響應的方式組成一系列集合服務於終端使用者,這就是所謂SaaS1

。上述過程其實描述了一個清晰可見的層次結構,如圖5-1所示。

影象說明文字

圖5-1 一個傳統雲端計算平臺的分層結構

在經典雲平臺層次體系裡,應用例項執行在PaaS平臺所提供的容器環境中,容器在虛擬機器基礎上完成了第二層次基礎設施資源的劃分;容器封裝了應用正常執行所需的執行時環境和系統依賴;同時,容器也成為了租戶排程應用、構建應用多例項叢集的最直接手段。與IaaS層不同,通常在PaaS層可以採用更貼近應用的資源排程策略。可是,目前遵循這個體系結構構建的經典PaaS平臺中存在一個有趣的現象:租戶從始至終都無法感受到容器的存在!

相比於基於虛擬機器提供執行時支援的IaaS+平臺(比如AWS),經典PaaS平臺的租戶甚至都不能進入自己的計算單元(容器)中,這類PaaS平臺就如同一個黑盒,所有"扔"進去的應用就完全脫離了租戶的控制,進入了完全被託管的狀態。誠然,如果一切都有條不紊的運作,該模式可謂完美,因為“所有使用者都是最懶的”這個假設總是成立,而殘酷的現實卻是:“錯誤總是會發生在任何意想不到角落裡”。

舉個簡單的例子,一旦應用執行過程中有錯誤發生,雲平臺的PaaS層首先會刪除故障例項,然後立即在其他位置恢復這個例項和容器。這個過程中,甚至平臺預設沒有儲存現場的過程。浙江大學SEL實驗室雲端計算團隊曾在Cloud Foundry中增加了從Websocket日誌元件收集容器中的應用日誌到ElasticSearch的機制,通過該機制在一定程度上能給使用者提供方便的日誌資訊診斷處理,但對於日誌中無法體現的異常還是無能為力,更談不上除錯程式碼和儲存環境上下文了。這樣的先天缺陷,也是後來雲端計算領域會出現大量“雲DevOps工具”,提供一個類似“白盒PaaS”解決方案的重要原因之一。

經典PaaS平臺中,出於安全、封裝等各個方面的考慮,容器總是被故意隱藏在整個雲平臺的執行過程當中(如圖5-1所示),因此開發和運維人員失去了往日對應用及其執行時環境的完全掌控能力,試圖重新獲得控制權所做的努力往往要求助於過分晦澀的互動方式和hack般的自定義過程。再加上經典PaaS平臺通常在應用架構選擇、支援的軟體環境服務等方面有較強限制。因此在生產環境下,部分企業和個人開發者會傾向於放棄PaaS層,直接依靠運維力量來分配和排程虛擬機器,靠大量自動化工具來維護和支撐所有執行時、應用環境配置、服務依賴、作業系統管理等。這時,傳統雲平臺分層結構就會進化成如圖5-2所示的狀態,即”IaaS+“雲平臺。

圖5-2 一個典型的“IaaS+”雲平臺

本書認為,這種“返璞歸真”的做法是一種值得一試的雲端計算運維方法,尤其是在大部分IaaS都能夠提供標準而豐富的API的今天。高效便捷的虛擬機器DevOps工具在很大程度上彌補了IaaS平臺脫離應用的缺陷;以虛擬機器映象為基礎可以保證生產環境、測試環境、開發環境上的嚴格一致;IaaS提供商還在不斷推出關係資料庫、NoSQL、日誌、Search、物件儲存等構建在虛擬機器上的可對外提供服務的映象。事實上,基於IaaS的雲生態環境已經具有相當高的成熟度。

當然,如果沒有Docker的話。

經典PaaS平臺和IaaS加DevOps工具組成的“IaaS+平臺”還在分庭抗禮,隨著容器技術逐漸步入視野,雲平臺建設已經有了新的思路。

相比“IaaS+平臺”,Docker容器啟停速度比虛擬機器提高了一個量級,而在資源利用率上容器獨有的高密度部署能力也非普通IaaS提供商所能提供的。更有吸引力的是,大小僅幾十MB的Docker映象就完整封裝了Web容器、執行配置、啟動命令、服務hook和所需環境變數,提供了一種全新的應用分發方式,給應用開發者帶來了彌足珍貴的“全環境一致性”保證。相比之下,動輒GB級的虛擬機器映象在應用部署和分發上就很難再有競爭力了。

相比經典PaaS平臺,Docker的出現使得構造一個對開發和運維人員更加開放的容器PaaS雲成為可能,基於容器映象的應用釋出流程不僅能覆蓋整個應用生命週期,還減少了經典PaaS平臺對應用架構、支援的軟體環境服務等方面的諸多限制,將更多控制力交還給開發和運維人員。這種“降維攻擊”把曾經引起不少爭論的話題再次擺在了開發者面前:PaaS應該以何種形態存在?

本書無意給上述問題尋找一個完美答案,更希望能與讀者一起研究和探索基於容器的雲平臺究竟以什麼形態出現才更合理。因此,本書為讀者介紹多種型別的容器雲平臺。它們中一類更偏向經典PaaS平臺,提供各類“一鍵xx”服務,它們給予使用者最大的方便和更高度的自動化,也因此附加了對應用架構和開發運維自由度限制;另一類則給使用者最大的開發運維自由度,但自動化程度較低,使用相對複雜。也許到閱讀完第二部分內容後,讀者就能找到怎樣的容器雲平臺才是最適合自己的。

值得一提的是,不論是採用哪種形態的雲平臺,隨著Docker等面向開發者的容器大行其道,本書下面將要著重討論的雲平臺都已經變成了圖5-3所示的結構。

一個基於容器的雲平臺

圖5-3 一個基於容器的雲平臺

在Docker等容器技術很有成為未來應用釋出事實標準的當下,必須指出本書進行討論的一個基本立足點:由於當前容器在核心完整性、安全性和隔離性上的固有缺陷,使得目前在大部分場景下我們必然需要虛擬機器、虛擬網路、虛擬儲存的支援;同時,在本書中將不會再過分強調所謂IaaS、PaaS、SaaS三層雲計算劃分方式,更多的是將這些概念視為經典技術作為容器雲的對照。

本書接下來討論的所有以容器為核心的雲平臺都不會鎖定在某種具體IaaS或者PaaS上面,將始終堅持容器雲平臺應無差別地工作在物理機上或者虛擬機器上這樣樸素的思想,並以此為基礎剖析各類容器雲的原理與本質。這或許不太容易,比如Kubernetes天生就是為GCE定製的,而幾乎所有的容器平臺也都以AWS和DigitalOcean作為預設的下層資源依賴。浙江大學SEL實驗室雲端計算團隊所做的努力將盡可能遮蔽掉這類外部因素,以中立的技術態度貫穿本書的始終。

5.2 從小工到專家

當一個開發者拿到Docker文件之後,一定會按捺不住內心的激動,把所有的Getting Started跑一遍,當然中間可能會碰到問題,仔細閱讀過本書第一部分的讀者應該能更加遊刃有餘地做完這些事情。甚至嘗試過第4章的高階玩法後,當第一個DEMO開始工作時,終於可以當仁不讓地宣佈自己已經是個高階玩家。

那麼問題來了:接下來該做什麼?

以一個開發者的視角繼續往下,往後事情的發展無外乎兩種可能:第一,開發者默默地記住這些技能,然後把Docker當作自己的獨門武器,以至於最後老闆都開始懷疑這傢伙的開發效率怎麼會突然變得這麼高。第二,熱心的開發者開始向全組推廣Docker,甚至鼓動運維也加入Docker行列。一般情況下,我們會稱讚第二種開發者為“願意當將軍的士兵”。

於是,我們的“將軍開發者”決定從最簡單的需求開始演示自己的計劃,只用了幾分鐘,他就搭建好了一個容器叢集,這是一個來自The Docker Book的例子,與第2章中我們手把手搭建過的“第一個Docker叢集”有點類似。

一個Node.js應用和ELK組合的例項

圖5-4 一個Node.js應用和ELK組合的例項

在這個組合例項中,他成功地將一個Node.js應用執行起來,並且使用Redis叢集來儲存這個應用的session資訊,最後還使用ELK組合(ElasticSearch+Logstash+Kibana)完成了應用和Redis的日誌轉發、儲存和檢索功能。當然最酷的一定是這些內容全是跑在Docker容器裡的,他只用了幾條命令外加幾分鐘的時間就全部搞定了。團隊裡的其他人只要把Dockerfile拿走,幾分鐘就可以搭建一套一模一樣的環境出來!

“真不錯!”大家紛紛稱讚這種基於容器來構建服務棧的方式是多麼地優雅。

“可是要上線的話,負載均衡總要有的吧?”一位不大討人喜歡的開發經理提出了第一個需求

的確,在經典網際網路應用場景裡,無論後端系統多麼地複雜強大,最前面放置一般都該是負載均衡裝置而非Web伺服器,並且Load Balancer這一環節還有很多必須額外設定的配置(比如session sticky、靜態動態內容分離、URL重定向等)。在此基礎上,應用還往往被複製成多份,在負載均衡管理下統一提供對外服務,這項技術對於分流、灰度釋出、高可用以及彈性伸縮都是必需的。好在有了Docker的幫助,這一切都不算難,“將軍開發者”撓撓頭的功夫就build了一個HAProxy映象啟動起來,然後又啟動了一個完全相同的Node.js應用的容器(得益於映象,這類操作非常便捷),最後將兩個應用容器的IP和埠配置到了HAProxy的backend servers裡面,完成了負載均衡實現的所有工作。

一個添加了負載均衡的Node.js應用和ELK組合的例項

圖5-5 一個添加了負載均衡的Node.js應用和ELK組合的例項

“好像可以工作了呢。”

“等等,負載均衡裡怎麼能把後端的例項配置成固定引數呢?”

不招人喜歡的開發經理又提出了第二個需求,而且還有點棘手。

首先要面對的問題是,怎麼才能保證後端應用容器失敗重啟或者升級擴充套件之後,HAProxy能及時更新自己的配置檔案呢?要知道Docker容器可沒有靜態IP這個說法(至少在學習過本書高階網路實踐之前是這樣的)。不過如果求助於GitHub情況就不一樣了,“將軍開發者”很快找到一個專門負責配置檔案遠端修改的元件confd。

第二個要面對的問題是,哪個元件負責探測應用容器退出或者建立的事件發生然後通知confd修改HAProxy配置檔案呢?這可讓“將軍開發者”著實花了一番心思。

“這好像是一個服務發現的場景呢。”

沒錯!所有應用容器都應該把本身IP和埠資訊註冊到etcd當中去(etcd是服務發現儲存倉庫,將在第6章詳細介紹),然後依靠confd定時查詢etcd中資料變化來更新HAProxy的配置檔案。不需要寫很多程式碼,只需要配置一下confd,然後build一個新的HAProxy和一個etcd容器就足夠了。話音未落,“將軍開發者”的新系統又上線了。

一個添加了負載均衡和自發現特性的Node.js應用和ELK組合的例項

圖5-6 一個添加了負載均衡和自發現特性的Node.js應用和ELK組合的例項

這樣應該可以了吧。確切地說,這個服務棧不僅擁有了負載均衡和多例項的功能,還能夠以一種“發現”的方式向負載均衡節點註冊或者解註冊應用例項,而且整個過程都是平滑升級的,不會出現服務中斷。

“應用健康檢查怎麼辦?”一直在旁默不作聲的運維終於坐不住了。

“自發現”機制確實保證了容器自身高可用能力,但是容器中執行著的應用程序實際上並不是完全保險的。最典型的場景是Java Web Server:當應用異常的時候,Web Server是完全有可能不退出的,使用者只能拿到4XX或者5XX的返回值。所以,在一個真實的應用平臺需求下,“垂直監控”是非常有必要的,至少需要能檢測到應用訪問的返回值是2XX。

這還不算完。“將軍開發者”雖然構建了一個多例項的應用叢集,但生產環境下,這些例項應該將會分佈在不同伺服器上。這又會帶來新的問題,如下所示。

  • 如何保證同一個應用的不同容器例項分佈在不同或者指定的宿主節點上?
  • 當一個宿主節點意外退出的時候,如何保證該節點上的容器例項能夠在其他宿主節點上恢復?
  • 如何比較當前容器例項的執行情況同期望的執行狀態的差異,用以決定是否要進行上述高可用動作?
  • 如何構建一個覆蓋“測試-開發-上線”完整流程的執行機制來充分發揮Docker映象的一致性?
  • Docker容器本身的網路應如何配置,尤其是在跨主機環境下怎麼處理,是否需要靜態IP?
  • 當開發者建立的映象非常多時,複雜的映象關係會大大拖延容器建立和啟動速度,這時該如何處理複雜關係對容器效能的影響?
  • 大量刪除操作可能帶來不可預知的“孤兒”容器,不光佔用大量資源,還可能帶來各種莫名異常,造成大量“孤兒”容器的局面該如何應對?
  • 掛載Volume的資料該如何進行備份,是否需要實現高可用或跨主機遷移?磁碟寫滿該如何處理?
  • 所有CPU、MEM、DISK資源限制如何才算合理?不合理的資源限制加上欠考慮的排程策略會不會產生新的資源浪費?

  • ……

“將軍開發者”突然發現,原來說服別人接受自己計劃所面臨的困難要遠比搭建DEMO大得多,尤其是需要涉及現有的生產環境時。事實上,Docker是運維友好的,相比傳統運維方式,通過流程和規範來保證環境一致性的做法,Docker映象已經給運維工作帶來了很大便利,更不用說它幾乎可以忽略的啟動時間和簡單高效的配置方式了。同樣,Docker更是開發者友好的,光是它伸手即來的安裝和啟動方式以及靈活通用的Dockerfile就足以讓傳統PaaS提供商汗顏。此外,它不存在任何供應商鎖定和引入特殊依賴的問題了。可是,就是這樣一種對各利益方都友好的技術,在真正用於生產環境時卻需要解決一個棘手的問題:如何使用Docker特性來提供、升級和簡化現有生產環境已經具備的運維能力?

引發這個問題的原因其實很簡單,Docker給工業界帶來的不只是一項技術——容器技術已經足夠普及了——它帶來的更多是一種思維轉變。遺憾的是,Docker的思考方式與目前任何一項業務執行的方式都不是原生相容的。

這解釋了為什麼我們在自建的環境中使用Docker能如魚得水,一旦想要將它推廣至生產環境中,就會感到十分棘手。而且我們發現,這些困難往往不是來自容器技術本身,而是與容器相關的網路、儲存、叢集、高可用等已經在傳統場景中得到解決的“泥潭”。為了解決這些問題,我們的“將軍開發者”就不得不經歷一次又一次“從小工到專家”的歷練,他開始研究HAProxy和etcd,開始寫Docker scheduler、health checker、stager、builder、deployer,終成一代“Docker大神”。

最後,我們的開發經理終於提出了一個終極需求:能不能讓開發愉快地開發,運維輕鬆地運維,大家一起快樂地做Docker小夥伴呢?

答案當然是:“可以的。”

現在就讓我們來聊聊“容器雲”吧。

“容器雲”最直觀的形態都是一個頗具規模的容器叢集。但它與開發者或者運維人員自己維護的“裸”容器叢集不同。容器雲中會被按功能或者依賴敏感性劃分成組,不同容器組之間完全隔離,組內容器允許一定程度共享。容器之間的關係不再簡單依靠docker link這類原生命令來進行組織,而往往是藉助全域性網路管理元件來進行統一治理。容器雲使用者也不需要直接面對Docker API,而是藉助某種控制器來完成使用者操作到Docker容器之間的呼叫轉譯,從而保證底層容器操作對終端使用者的友好性。大多數容器雲還會提供完善的容器狀態健康檢查和高可用支援,並儘可能做到旁路控制而非直接侵入Docker體系,從而免除“將軍開發者”們不得不重複造輪子的尷尬。“容器雲”會提供一個高效、完善、可配置的排程器,排程器可以說是容器雲系統需要解決的第一要務,這也正是“將軍開發者”最頭痛的事情——他面對的容器越多,運維和管理困難程度往往會呈指數級上升。在接下來的章節中,讓我們一起來逐層揭開“容器雲”的面紗。

它們或來自於小而美的創業團隊,或來自於數一數二的業界巨頭;有的專注於服務釋出,有的專注於資料儲存;有的只解決編排與運維,有的卻幾乎可以媲美一個傳統IaaS。但是,無論是那些靈活輕巧的編排工具還是龐大複雜的容器服務,它們都試圖為熱愛Docker並嘗試真正應用Docker的“將軍開發者”們解決一個核心問題:如何邁過從“容器執行”到“生產使用”之間的這條鴻溝。

1這個描述見http://www.programmableweb.com/news/new-enterprise-big-data-mobile-and-saas-api-economy/analysis/2013/09/25。

from: http://www.ituring.com.cn/tupubarticle/4444