基于Docker的開發模式驅動持續集成落地實施
嘉賓簡介
資深質量優化專家,12年軟件測試與質量管理經驗
《軟件性能測試診斷分析與優化》等多本IT暢銷書作者
演講實錄
今天主要交流的主題是基于Docker的開發模式如何驅動持續集成落地實施,這里會涉及兩個主要的話題,一個是所謂Docker的開發模式是怎樣的,與傳統的開發模式有什么區別;另外一個是持續集成作為敏捷開發的最佳實踐,結合Docker來實施會有什么樣的效果,會不會有更好的促進作用,尤其是在傳統企業中實施。
也希望跟大家一起交流一下關于Docker的一些具體實施問題、實施經驗
首先我們來看看,在傳統的開發運維模式下,會存在哪些問題。我稍微歸納總結了一下,大概會有以下三個問題:
- 從需求到版本上線中間是個黑箱子,風險不可控
- 開發設計時未過多考慮運維,導致后續部署及維護的困難
- 開發各自為政,煙囪式開發,未考慮共享重用、聯調,開發的資產積累不能快速交移到運維手中
應對這樣的問題,我們通常倡導的解決之道是:運維前移,統一運維,建立持續交付服務體系。
但是這樣空喊是沒用的,我們要來點實際的,嘗試利用一些技術手段真正解決開發運維一體化的問題,具體推進DevOps的實施。
那好,我們看看最近1、2年非常火的Docker技術,看能否在解決上述問題上帶來一些具體的突破,或者給我們帶來一些解決具體問題的啟發。
我們先從Docker的一些技術特性來看,這些新的技術可能帶來的改變:
- Docker首先是一個容器級虛擬化技術,相比傳統虛擬化技術,容器級的虛擬技術是操作系統內核層的虛擬,所以能節省更多資源、提升性能,意味著單位機器資源消耗下,能承載更復雜更龐大的應用系統架構。
- 啟動速度更快,毫秒級的啟動速度,這對于快速部署開發測試及運維環境非常有利。
- 鏡像分層,部署時可以按需獲取鏡像生成容器并快速啟動運行,因此有利于快速的部署擴容,解決運維中水平擴容的問題。
- 由于Docker鏡像的天然可移植性,就像集裝箱一樣快速打包應用以及依賴項,在開發、測試、運維之間移動,所以可以推進 開發-測試-運維 環境的統一,持續集成(CI)能發揮更大的作用,例如通過CI構建應用、檢查代碼,打包到Docker、分發部署到測試和準生產環境,進行各類測試,都可以更方便快捷和統一。
既然Docker的一些技術特性看起來確實能給開發測試運維帶來一些新的東西,那么我們接下來就具體看看,這些新的技術特性具體如何應用,以及應用過程中可能帶來的問題。
容器可以把應用以及它的依賴項進行打包,這樣帶來的好處就是應用隔離,能否把這種隔離特性用于解決一些架構問題呢?因為架構設計不好的話,給開發、測試,尤其是運維以及后續的維護管理帶來很多麻煩,例如隨著業務復雜度越來越高,可維護性和敏捷程度隨之變差。
#p#
傳統的應用通常采用所謂的三層架構,例如:界面層 – 業務邏輯層 – 數據層,通常我們會把所有實現業務邏輯層的代碼編譯構建后部署到中間件,再通過負載均衡、集群等解決分流、災備等問題。但是這種架構設計帶來的問題是:
開發效率低
隨著應用復雜度的增加,越來越少開發人員對應用能有全局性的深度理解。新功能開發和缺陷修復難度呈幾何性增加。代碼修改的正確性無法保障。而龐大的代碼庫需要更龐大的開發團隊來維護,無形中又增添了管理、溝通和協調的成本。另外,新加入的團隊成員需要花費大量的時間和精力來熟悉一個復雜的代碼庫。
交付周期長
在單一進程的單塊架構下,任何微小的改動都需要重新編譯、集成、測試和部署整個應用。隨著應用體積的增大,交付流程和反饋周期都會相應變長,應用發布的代價也隨之增加。于是應用交付周期變緩,交付間隙積累的代碼變動增加,從而對于下次交付產生更大的壓力,形成惡性循環。
技術轉型難
單一進程、單塊架構意味著中心化的技術選型。比如,應用的不同邏輯組建通常需要采用相對統一的編程語言、框架和技術棧。這些在項目初始階段便已定型。之后,即便是應用中全新的邏輯組件,也很難采用不同的技術棧。而當應用達到一定規模后,全局化的技術棧更新會面臨很高的風險。所以,單塊架構應用一旦定型,就很難再享受行業技術變更、發展所帶來的紅利。
有了容器技術之后,微服務設計也被大家慢慢提到架構設計的前沿進行討論了,這方面推薦大家看一本書《Building Microservices》。
微服務架構的誕生和容器技術的流行,幾乎是同時發生的,這并非偶然,而是互聯網時代倒逼傳統技術和架構而產生的變革,而以Docker為代表的容器技術則為微服務理念提供了匹配的實現機制。
在微服務架構下,我們將原本單一的應用按照功能邊界分解成一系列獨立、專注的微服務。每個微服務對應傳統應用中的一個組件,但是可以獨立編譯、部署和擴展。相對單塊架構,微服務具備以下優勢:
(1)復雜度可控:在將應用分解的同時,規避了原本復雜度無止境的積累。每一個微服務專注于單一功能,并通過定義良好的接口清晰表述服務邊界。由于體積小、復雜度低,每個微服務可由一個小規模開發團隊完全掌控,易于保持高可維護性和開發效率。
(2)獨立部署:由于微服務具備獨立的運行進程,所以每個微服務也可以獨立部署。當某個微服務發生變更時無需編譯、部署整個應用。由微服務組成的應用相當于具備一系列可并行的發布流程,使得發布更加高效,同時降低對生產環境所造成的風險,最終縮短應用交付周期。
(3)技術選型靈活:微服務架構下,技術選型是去中心化的。每個團隊可以根據自身服務的需求和行業發展的現狀,自由選擇最適合的技術棧。由于每個微服務相對簡單,當需要對技術棧進行升級時所面臨的風險較低,甚至完全重構一個微服務也是可行的。
微服務架構中,可為每個服務選擇一個新的適合業務邏輯的數據庫系統,比如MongoDB、PostgreSQL。這樣做的好處:首先我們可以根據業務類型(讀多還是寫多等)來決定使用哪種類型的數據庫,其次這樣可以減小單個數據庫的負載。
(4)容錯:當某一組建發生故障時,在單一進程的傳統架構下,故障很有可能在進程內擴散,形成應用全局性的不可用。在微服務架構下,故障會被隔離在單個服務中。若設計良好,其他服務可通過重試、平穩退化等機制實現應用層面的容錯。
(5)擴展:單塊架構應用也可以實現橫向擴展,就是將整個應用完整的復制到不同的節點。當應用的不同組件在擴展需求上存在差異時,微服務架構便體現出其靈活性,因為每個服務可以根據實際需求獨立進行擴展。
看起來微服務的架構設計優勢明顯,也是未來架構設計的一大趨勢方向,但是,如果采用微服務的架構設計,基于容器包裝微服務進行部署,給運維會帶來哪些新的挑戰呢?我認為會從以下幾個方面體現:
(1)運營開銷:更多的服務也就意味著更多的運營,需要保證所有的相關服務都有完善的監控等基礎設施,傳統的架構開發者只需要保證一個應用正常運行,而現在卻需要保證幾十甚至上百道工序高效運轉,這是一個艱巨的任務。
(2)DevOps要求:使用微服務架構后,團隊需要高品質的DevOps和自動化技術,需要懂更多不同類型的技術棧。
(3)隱式接口:服務和服務之間通過接口來“聯系”,當某一個服務更改接口格式時,可能涉及到此接口的所有服務都需要做調整。
(4)分布式系統的復雜性:微服務通過REST API或消息來將不同的服務聯系起來,這在之前可能只是一個簡單的遠程過程調用。分布式系統也就意味著開發者需要考慮網絡延遲、容錯、消息序列化、不可靠的網絡、異步、版本控制、負載等,而面對如此多的微服務都需要分布式時,整個產品需要有一整套完整的機制來保證各個服務可以正常運轉。
幸好,業界已經從運維管理的角度出發,做了不少工作來嘗試解決上述問題,例如Google開源的Kubernetes容器集群管理系統,提供應用部署、維護、 擴展機制等功能,利用Kubernetes能方便地管理跨機器運行容器化的應用,其主要功能如下:
- 使用Docker對應用程序包裝(package)、實例化(instantiate)、運行(run)。
- 以集群的方式運行、管理跨機器的容器。
- 解決Docker跨機器容器之間的通訊問題。
- Kubernetes的自我修復機制使得容器集群總是運行在用戶期望的狀態。
#p#
前面我們探討了一下基于容器的微服務架構設計給傳統開發運維模式帶來的改變,看起來更多的還是正面的改變。
容器化微服務解決的更多的是架構設計的問題,按軟件工程來講,設計之后的下一步就是開發實現的事情了,在這個階段,傳統的開發測試會有不少的問題,例如環境的問題:
- 軟件安裝麻煩、來源不一致、安裝方式不一致、雜亂無章。
- 共用一個服務器開發環境,隔離性差,互相沖突。
- 可移植性差,例如和生產環境不一致,開發人員之間也無法共享;新人入職通常又折騰一遍開發環境,無法快速搭建。
那么,隨著容器技術的引入,是否能帶來一些改觀呢?答案是明顯的!開發測試環境的容器化帶來的是標準化的開發測試環境,這對開發測試及運維的意義體現在:
(1)對開發測試的意義:測試環境搭建效率的提高,高效利用硬件資源同時又能敏捷輕便地搭建功能完備的開發測試環境,例如在一個資源有限的環境下面(比如開發人員的筆記本電腦)例如Chef 把系統的各個模塊按照清晰的邏輯結構部署并運轉起來,從而快速高效地進行開發與測試
(2)對運維部署的意義:開發測試(環境、依賴包) - Docker Hub - 運維(環境、依賴包) 的環境一致性
關于Docker在測試領域的一些應用,大家可以看看我寫的一篇文章,《淺談Docker在測試領域的應用》。
在傳統模式下,開發自測通常沒問題,但是到了測試或生產環境程序無法運行,讓開發團隊排查,經過長時間排查最后發現是測試環境的一個第三方庫過時了。這樣的現象在軟件開發中很普遍,已經不適用如今的快速開發和部署。
而在Docker模式下,應用是以容器的形式存在,所有和該應用相關的依賴都會在容器中,因此移植非常方便,不會存在像傳統模式那樣的環境不一致。
在開發測試環境容器化的背景下,研發模式的改變:
項目開始,架構師根據項目預期創建好需要的基礎鏡像(Nginx、Tomcat、MySQL鏡像),或者將Dockerfile分發給所有開發人員,開發人員根據Dockerfile創建的容器或從內部倉庫下載的鏡像來進行開發,如果開發過程中需要添加新的軟件,則向架構師申請修改基礎鏡像的 Dockerfile;
開發任務結束后,架構師調整Dockerfile或image,分發給測試部門,測試部門馬上就可以進行測試,消除了部署困難等難纏的問題。
在開發測試環境容器化的背景下,持續集成的模式也會發生一些必然的改變:持續集成各步驟圍繞任務Docker模塊進行工作。
開發人員的代碼嵌入會觸發開發環境中新的Docker鏡像的構建(代碼的編譯、構建、檢查、部署都基于Docker進行),測試人員發現有新的Docker 鏡像構建出來,就會部署到測試環境中進行各類測試和驗證,測試通過后,會把鏡像放到公共鏡像庫,由運維人員進行生產環境的部署和發布。
好,那看完Docker在開發測試階段的應用,尤其是對持續集成方式的改變后,我們來看看,Docker對于傳統業務上線方式帶來的改變。
傳統業務上線存在的問題是:
- 上線環節多,跨越多個部門,溝通成本高;
- 涉及多個部門的審核,包括資源的申請、環境權限的批準、需求測試驗收等,耗時長;
- 再加上上線部署過程涉及到的技術環節多,例如代碼獲取、編譯構建、代碼檢查、軟硬件安裝配置、應用裝載、啟動,導致發布周期很長。
#p#
針對傳統應用交付過程中的這些問題,Docker的引入帶來了明顯的改變,Docker把應用及相關依賴項打包成一個輕量、可移植、自包含的容器,讓應用的部署和發布基于容器進行,而不是基于代碼部署。由此,Docker重新定義了打包程序的方法:
Docker容器 + 用戶應用 = 部署單位(構件)
容器級部署帶來的最大的好處就是開發者本地測試、CI服務器測試、測試人員測試,以及生產環境運行的都可以是同一個Docker鏡像。因此可以實現應用的自動化快速部署及上線發布。
由于Docker技術的標準化通用化,可以方便地實現云服務器之間的移植,不依賴具體的某一個云服務商,降低了云部署的風險,因此,從這個角度來看,Docker能極大地促進企業做應用云部署的積極性,難怪各大IaaS和PaaS供應商都熱烈地擁抱Docker容器技術。
基于Docker的應用部署和發布,還可以方便地實施藍-綠部署,例如:保持兩套一樣的生產環境,而實際上只有一套環境真正的對外提供服務(綠環境),而另一套環境則處于待機狀態(藍環境)。
先上線到藍環境,如果測試沒問題,再將路由切換到新的服務上。帶來的好處是明顯的:
- 最小化停機時間
- 快速回滾
- 熱備份
但是具體實現藍綠部署可能還會碰到更多細節的問題,例如數據的處理,這些都有待實踐。
最后,我們來聊聊容器微服務運維,前面我們有聊到基于容器化微服務設計給運維帶來的挑戰以及一些應對措施。
在容器微服務的模式下,功能模塊被隔離到一個個可單獨開發、測試和部署的容器中,所以當某個功能出現問題的時候,可以單獨把其對應的容器撤銷,開發進行修復,再上線,在這個過程中其它的功能(容器)不受影響,仍然正常運行。
微服務帶來的運維問題:
拆分成微服務后,部署變得復雜,監控的復雜度也隨之提高,要理解在微服務之間產生的復雜交互,需要優秀的診斷與監控工具,同時對運維技能的要求也提高了,例如要了解多種技術棧。
我覺得可以從以下幾個方面去考慮:
(1)自動化管理:自動構建、自動測試、自動部署、自動運維...,盡可能自動化一切,從而控制好復雜度、工作量、降低人為操作失誤的風險;
(2)圍繞業務能力建模服務:運維需要對開發提出需求,例如:服務部署的獨立性、失敗隔離性、可監控性,構建中心化的配置服務管理;
(3)充分利用容器技術實施服務流控,例如:降級、限流;
(4)充分利用容器技術實施服務恢復,多考慮故障快速恢復而非避免。
最后,給大家推薦一些Docker相關的書,有些是國內的,有些是國外的。