《阿里技術專家:持續交付與微服務背后的實踐邏輯》要點:
本文介紹了阿里技術專家:持續交付與微服務背后的實踐邏輯,希望對您有用。如果有疑問,可以聯系我們。
崔力強
阿里巴巴技術專家
大家好,我是崔力強.目前在阿里巴巴任職.負責一款持續交付領域的SaaS產品的開發.非常高興能夠和大家分享持續交付和微服務的話題.
本次分享的重點是持續交付.也會提到一些微服務的概念,以及持續交付和微服務之間的關系.今天會涉及的一些實踐可能大家或多或少有所耳聞.我會著重講述這些實踐背后的邏輯,及它們之間的關系.
先看一看提綱:
關于持續交付的概念.從《持續交付》這本書的副標題可見一斑:“發布可靠軟件的系統方法”.可以看到這本書中講的“持續交付”主要是技術相關的實踐,雖然近年有些朋友把持續交付的概念進行了延伸,把精益需求管理和精益創業也包含了進來,不過今天我還是會按照它最初的內涵,只講技術相關的實踐.
這里其實有兩個概念,第一個是,怎么樣才算持續交付,也就是目標;第二個是,如何做到持續交付,也就是一些技術實踐.
為了了解“怎么樣才算持續交付”,讓我們先解剖一下軟件開發的過程.
針對單個需求來說,我們會先進行需求分析、細化.然后開發和測試.部署時,需要拷貝一些文件到一些機器,運行一些腳本,有時候還需要改一些配置文件或者做一些數據遷移等.
然而在實際的項目中,肯定不會只有一個需求.需求會源源不斷地輸入到開發團隊.下面是一個開發團隊隨著時間進行需求開發的示意圖.藍色的條是開發某個功能的開始時間和結束時間點.有的需求占用時間長,有的需求占用時間短.
在每個功能的完成時刻,對于業務方來說,都可能是一個可以發布的狀態(也有可能不是,比如業務方認為相關的幾個需求必須都做完了才能一起上,或者需要等到某個特定的時間點才能上).那么如果立馬就能夠進行一次發布,并且能夠快速并且安全的完成這次發布,則能夠對業務的發展具有非常積極的作用.也就是在下圖的這些時刻.
這些時刻,不一定每個都需要發布.但作為開發團隊,要給業務方足夠的靈活度.所以持續交付的目標,并不是每次提交都進行發布,而是每次提交都是可發布的狀態.這就回答了“怎么樣才算持續交付”這個問題.再換一種方式來說,持續交付是對業務方友好的一種,開發團隊的開發節奏.
接下來就要討論使用什么樣的技術實踐來達到這種開發節奏.為了討論具體的技術實踐,首先來看看在軟件開發中有什么因素會阻礙我們達到這種節奏.
第一個問題是:上線前總還是要做測試的吧,至少做一遍重要功能的回歸測試(手工回歸本身其實也是有章可循的,推薦我同事的一篇文章:https://yq.aliyun.com/articles/6898).隨著一個系統上的功能越來越多,每次上線前需要測試的東西就越來越多.慢慢的,發布就慢下來了.我曾經工作過的一個系統,上面承載了三塊業務,有將近40多個人在上面工作.每次發布前都要先進行幾天的回歸測試,順利的話,從確定要發布的版本到驗證完該版本,確定可以發布,也要差不多一周的時間.
另一個我工作過的項目規模要小一些,做一次發布也需要一天的時間來回歸,通常都會發現一些問題.順利的話,可以在當天把所有的問題修復掉,進行發布.不過因為bug沒修完而不能當天發布,拖到第二天的這種情況也時常發生.
甚至有一次,到了晚上九點多的時候還沒有修完bug,但當時大家都憋著一口氣,硬是要發,結果發到了晚上一點多.
因為成本高,所以不敢做的太頻繁;做的不頻繁的話,就會在發布中積累更多的功能,從而進一步增加出問題的可能性,從而形成一個惡性循環.
作為一個妥協,人們不得不使用瀑布或者迭代內小瀑布的開發模式.
把開發的周期分解為一個一個的迭代,比如兩周到四周的時間.在迭代開始前,保證該迭代內計劃的需求分析完畢.然后在迭代內部開發.順利的話,會在接近迭代末尾時完成迭代內計劃的所有任務,然后拉一個發布分支出來,開始測試,然后發布.
做出上述妥協的直接原因就是“測試和部署”花費的時間過長.如果只花費一個人一個小時就能夠完成回歸和發布,那顯然團隊就更愿意去頻繁的發布.
一個行之有效的方法就是進行自動化測試.
自動化測試大致可以分為幾種:單元測試、API測試、驗收測試/功能測試/端到端測試.在不同的技術棧下,分類可能會略有不同的,但本質上來講是類似的.不同層次的測試有自己的側重點,組要組合使用來達到一個比較好的效果.如上圖所示:
這里以Java Spring項目為例來列舉不同層次的測試工具:單元測試使用Junit;集成測試使用Spring Test + Junit;功能測試使用cucumber+ capybara+selinium或者robotframework+ selinium.
如果使用了前端框架,比如Angular、ReactJS等,它們本身也提供了相應的測試框架.
底層的單元測試,測試的范圍較小,一般只涉及一個或者幾個類,不會調用網絡或者數據庫.所以編寫和運行起來都比較快.在這個級別應該覆蓋盡量多的分支和邏輯.這個級別的測試能夠達到比較高的覆蓋率,所以在它的保護下,可以放心大膽的做重構或者添加新代碼,只需要花上幾秒鐘的時間運行一遍單元測試,就能夠知道這次修改是否引入了問題.前陣子做了很長時間的nodejs開發,單元測試對這種弱類型的語言尤其重要.因為像變量未定義,傳參數的個數錯誤等很低級的問題IDE都無法給出有效的提示.
因為單元測試需要隔離被測類和系統的其它代碼,所以需要有一些測試替身來代替真實的類.有很多工具可以做這樣的事情,比如Java中的mockito.
如上圖所示,它會創造一個假的ClassB的實例出來,并傳給ClassA的實例.然后對ClassB的行為做出一些假設,在此假設的基礎上對ClassA的行為進行測試.
但是一個類或者幾個類的正確,并不能讓你對系統的正確性有足夠的信心.因為單元測試中充滿了對其它類行為的假設.所以一旦這個假設錯誤,就會出現測試依然能通過,但整個系統的行為已經錯了的尷尬情況.所以我們還需要覆蓋面更大的集成測試.這種測試在服務內部不使用任何的測試替身.但對外部的服務進行打樁.對基于HTTP的服務進行打樁的工具包括moco(https://github.com/dreamhead/moco)和pact(https://github.com/realestate-com-au/pact,其實pact能做的事情不止打樁,更多的是做“契約測試”).這種測試更真實,但運行起來會慢,所以這個層面的測試主要保證的是連通性.不需要100%的覆蓋率.
集成測試覆蓋面很大,但它仍然是白盒測試,因為它直接調用了函數(比如上頁的controller).如果這個服務只提供API,那么這種測試就夠了.但如果這個服務是提供頁面的,也就是一個web應用,那么就還需要一層直接操作網頁來進行基于用戶行為的測試,我們一般稱之為驗收測試,或者功能測試.上面列舉的cucumber和robotframework是非常流行的兩款功能測試工具.他們是通用的測試框架,與具體的被測系統是無關的.我也另一位前同事都對這兩種工具比較熟悉,并且寫了文章作總結:http://www.infoq.com/cn/articles/cucumber-robotframework-comparison
如果要測試web系統的話,就需要能夠驅動網頁的驅動程序.現在非常主流的驅動是selinium(https://github.com/SeleniumHQ/selenium
),當然我更喜歡的是在其上包了一層的capybara(https://github.com/jnicklas/capybara),它是用ruby編寫的,封裝的API更好用.
建議至少對核心的流程編寫功能測試,以保證上線不要出現嚴重的故障.前段時間我們的功能測試發現了一個bug.這個bug對于老用戶都不會有問題,但是用戶首次登錄就會500.而用戶首次登錄的場景恰恰是平時自測的時候很容易忽略的,因為準備數據還是有點麻煩的.當時發現這個問題的時候大家并沒有什么感覺,因為已經習慣有測試保護的軟件了.但如果跳出來想想,這個問題要是在一周后的“迭代末尾”才發現,會多么的打擊氣勢.如果上線才發現,那么產品的新用戶增長量一定會直線下降.
端到端測試其實也就是使用功能測試的工具在更大的范圍進行測試,也就是包含所有的服務.
下面總結一下各個層次測試的特點.
回歸測試是迭代開發中必不可少的一個步驟.我們能做的就是通過自動化測試去盡量減小這個時間.
很多人對于測試有一些顧慮,覺得會花費很多時間.而且當代碼結構調整時,測試也要跟著改.
我的看法是,測試代碼也是代碼,維護測試代碼的代價跟測試代碼本身的質量是直接相關的.所以對測試代碼也需要及時重構,提高可維護和可重用性.具體一點對于測試來說,提高可維護性和看重用性,無非就是要在數據準備、斷言工具方面去抽取一些庫.比如Ruby的factory girl就是一個極好的基于ActiveRecord的數據準備庫.有了它,寫測試的代價大大降低.那如果你不用Ruby怎么辦,那只好自己實現一個其它語言版本的factory girl嘍.我上一個項目用的是nodejs,就寫了一個nodejs版本的factory girl.
而單元測試能夠給你帶來的好處不僅僅是回歸這么簡單.有了完備的單元測試,你才有信心,有動力去做一些重構.只要測試通過,我就知道我的重構是正確的,你才敢不斷的去重構,優化代碼,才能使得代碼更易維護.所以可以說寫測試是保證你代碼可維護性的必由之路.不要考慮寫不寫測試,而是考慮,如何低成本的寫測試.
當我開發新功能時候,編寫好測試運行一下,就知道功能正確與否,這樣就不用把服務器啟起來,減小反饋的周期.在這個場景下,它會直接節省你的時間,雖然你寫了更多的代碼.
關于代碼改動,測試也要跟著改的問題,我想說兩點:
第二,要善用IDE幫你做改動.測試代碼也是代碼,當你修改一個函數簽名時,IDE會幫你把所有的調用處都改掉,包括測試代碼.所以IDE用得好,修改代碼也不是那么痛苦的事情.
那么有了這些測試之后,我應該什么時候運行它們呢.是迭代結束時嗎?不!我們應該在每次提交時都完整的運行一遍這些測試.這樣一旦出了問題我就可以第一時間知道.這就是持續集成的基本概念.
每次提交代碼觸發編譯、測試、靜態檢查、打包歸檔、然后再運行驗收測試(AT),然后再部署到類生產環境進行性能測試,再部署到端到端測試環境運行端到端測試.并且把每一步的結果反饋給開發團隊.
我們把上圖稱為持續集成流水線.可以使用很多工具來實現,比如最常見的開源工具Jenkins.或者我目前所負責產品:crp.aliyun.com.
關于更多的持續集成的實踐和流水線設計,因為內容很多,這里只討論幾個要點.
我們使用自動化測試加持續集成解決了第一個發布前回歸測試耗時的問題.
第二個問題就是:“別人的功能還沒做完”.假設現在團隊正在進行單分支開發(也就是說所有的功能都提交在一個分支上,不會為了一個功能單獨 開出一個長期存在分支).就拿圖中紅色線這個時間點來講,第三個需求完成了,業務人員也認為可以做一次發布.但是同時還有另外三個需求正在開發.如果做發布的話,就會把做了一半的東西也給發上去.這是不可以接受的.
解決這個問題有兩個思路:那就是功能分支和功能開關.
先看看功能分支:
每開一個新功能,就開一個分支.這個分支存活的時間通常是“周”這個數量級的.哪個功能開發完成了就合入到主干,進行一次發布.這樣,其它未完成的功能還沒有合入到主干,就不會造成影響.但功能分支有很多的問題,最嚴重的一個問題是:它和持續集成的理念是沖突的.持續集成是希望你每次提交都能夠放在一起進行驗證.但使用了分支的話,就只能在合并的時刻,才能真正把所有的東西放在一起進行驗證.而這時發現的問題可能一周前已經發生了.
另一個方法,功能開關,會給任何一個新開發的功能在代碼級別加上一個開關,使得可以簡單的修改一個配置就把一個功能完全隱藏掉.默認所有的開關都是關閉的,如果一個功能做完了,想上,則修改配置,打開開關,進行一次發布即可.聽起來很理想,但事實上也需要花費不少的代碼來把這件事情真正做好.
關于功能分支和功能開關今天不展開細講了.有興趣的朋友可以參看我之前寫的一篇文章:http://www.infoq.com/cn/articles/function-switch-realize-better-continuous-implementations
接下來我們聊一聊發布.因為我們希望發布也是快速并安全可靠的.
發布是一件麻煩事.一次發布可能會需要部署多個應用,每個應用都要部署多臺機器,有時候除了改代碼之外,還需要修改配置,比如nginx配置等.大多運維團隊都會有一些腳本來做這些變更.但這些腳本通常都藏在某些只有運維團隊才知道(并有權限)的機器上,開發和業務團隊都已經就緒之后,還需要等待運維團隊抽出時間來做些變更,這就無形中增加的時間成本.還是在前面提到的那個項目中,作部署就是有專門的運維團隊, 排期來對該應用進行部署.通常又會再多等一兩天.
DevOps是一種團隊合作的模式,即開發人員自己可以按需進行部署,不需要等待一個專門的發布團隊的時間.DevOps其實現在還是沒有一個標準的翻譯,我的一個前同事將它翻譯為“開發自運維”,我覺得還是挺貼切的.
在這種模式下,原先的運維團隊應該轉換自己的職責,從負責具體業務的變更,變成基礎資源的提供者.比如當開發團隊需要一臺虛擬機,或者一個Docker集群時,能夠通過簡單的調用API,在很快的時間得到它,而不需要繁雜冗長的審批流程.運維團隊還可以提供有效的監控、告警工具等,同樣把他們以基礎服務的形式提供給開發團隊.就像現在AWS和阿里云做的事情.
其實很多小團隊,包括我自己所在的團隊,都采用了DevOps的合作模式.但是做歸做,如何能做好呢?如何能夠保證每個開發(甚至是入職不久的開發)能夠安全快速的完成一次發布呢?
答案是自動化加可視化.自動化就是部署一個應用時,應該有腳本能夠一鍵從構建物倉庫拉取出正確版本的構建物,然后部署到相應的機器(或者多臺機器)上.更重要的是這個自動化腳本不應該藏在一個秘密機器的角落,因為這樣的話, 就很難告訴團隊成員如何去使用它們.所以應該把它可視化,而可視化的最好的平臺就是上面提到的那個持續集成流水線,在流水線的后面再加上一個部署線上的環節.
這樣,開發人員就能夠在一個統一的入口去了解從驗證、打包,再到生產環境部署的的全流程.當然我們的持續集成流水線也就順理成章的變成了持續交付流水線.
在發布方面,還有一個重要的課題就是環境管理.
現在大多的線上部署模式是:申請幾臺虛擬機,標明每一臺的用途,然后開始在各個虛擬機上安裝各自需要的基礎軟件,比如nginx、tomcat等.然后寫一個腳本進行各個應用程序的部署(這個腳本最終會集成到持續交付流水線中),注意這里的腳本僅僅負責應用程序的部署,而不包含前面提到的基礎軟件.如果基礎軟件需要升級,或者安裝新的基礎軟件,或者需要調整系統參數,這些過程都需要手動進行.這種模式在大多數情況下是沒有問題的,但一旦機器出了問題,或者需要擴容時,就需要花費大量時間來重新安裝一臺和之前那臺一模一樣的機器,再修改部署腳本把軟件部署上去.這個過程不但耗時,而且非常不可靠,因為你沒法保證你裝出來的這臺機器就和之前的那臺一模一樣,很有可能就給未來埋下了一顆定時炸彈.
解決這個問題的思路就是所謂的“基礎設施即代碼”.也就是把環境的創建過程使用代碼的形式描述出來,并且提交到代碼庫中.任何的環境變更都必須通過修改代碼、提交,然后總是使用代碼庫中的最新版本重新構建環境.禁止直接在機器上進行任何環境變更,比如裝軟件,升級軟件,該軟件配置等.這樣所有機器的狀態就是可預測的,并且是一致的.
基礎設施及代碼的相關工具有很多,比如最早流行的chef和puppet,到后來的Ansible.這里我們就拿Ansible為例子講一講環境管理和部署自動化.
Ansible是一個Agent Less的通用部署及環境管理工具.也就是說不需要在目標機器上預先安裝任何客戶端軟件,這點與chef有所差別.它依賴的就是簡單的ssh命令.你說這跟我直接寫shell或者ruby腳本ssh到目標機器,然后執行一些腳本有什么差別呢?
差別當然是有的,它會在以下幾個方面給你提供便利:
第一:Inventory管理.Inventory,即你要管理的那些機器.使用Ansible,你可以在一個集中的文件中,以結構化的形式列出所有你需要管理的機器,及如何登陸它們,也就是ssh的用戶名和密碼 等信息.不同機器的用途不同,比如這三臺是web服務器,那兩臺是搜索引擎等.那么Ansible也提供了對機器進行分組的能力.有了這些分組后,就可以很容易的在命令行中指明我這次要對那些機器做變更.并且Ansible會自動地對所有這些機器做變更,省去了自己做循環的工作.
下面看幾個Ansible官方的Inventory例子:
第二個重要的點叫做變更操作的冪等性.舉個例子,某一次對機器的變更是在~/.bash_profile中添加一行對JAVA_HOME的配置:“export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_71.jdk/Contents/Home/”.那么我可以寫一個shell腳本完成這件事情:“echo export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_71.jdk/Contents/Home/ >> ~/.bash_profile”.但是如果下次我的shell腳本里面多了安裝apache web server的代碼.我就需要把這整個腳本再對目標機器運行一次.那么就會出現~/.bash_profile中出現兩行JAVA_HOME的配置的問題.雖然不至于引入錯誤,但也是很沒有必要的操作.
所謂冪等性,就是同一個腳本對同一臺機器運行多次后,機器的狀態應該都是一致的.Ansible中模塊(module)的概念就覆蓋了“冪等性”這個概念.所謂模塊是預先寫好的一些庫,然后可以在Ansible的腳本中進行調用.上面的在一個文件中添加一行的操作就可以使用“lineinfile”這個module來做.在Ansible腳本中的寫法是:“lineinfile: dest=~/.bash_profile line=/Library/Java/JavaVirtualMachines/jdk1.8.0_71.jdk/Contents/Home/”.再比如還有一個module,叫做service,Ansible腳本中對于service的一個調用示例是這樣的: “service: name=httpd state=started”.這個描述的含義就是:“保證名為httpd的service是started狀態”.所以你可以想象到它的具體實現就是先檢查下httpd這個service的狀態,如果已經是started的就什么都不做,否則就啟動它.
這種冪等性在配置管理方面是非常有用的,這樣我就可以放心的運行這些腳本,知道最終一定可以得到某個一致的狀態.而且可以節省運行這些腳本的時間,比如發現JDK已經裝好了,就不需要再裝一遍.
上面提到的Ansible編寫的腳本被稱作Playbook,下面是幾個playbook的例子:
這個playbook是一個完整的例子,其中包括了我要部署那些機器(hosts).是用什么賬戶登錄(root),運行哪些任務(tasks)等等.task中的name只是描述信息.
但是遺憾的是這種冪等性是不能完全保證的,有的module可以保證,比如上面提到的service和lineinfile.但有些是不行的,比如command module,它做的事情就是運行一條命令.Ansible無法判斷這條命令是否執行過.
所以在使用Ansible的過程中需要盡量使用能夠保證冪等性的module.這樣才能保證所有的機器在運行一段時間之后配置是相同的,避免“配置漂移”.當然還有一個避免配置漂移的方法就是每次都重新申請一臺新的機器,然后對著它運行一遍這些腳本.這也是可行的,我們后面對此進行討論.
Ansible作為一個完備的工具,在錯誤處理,回滾,調試等方面也都提供了便利的支持.詳情大家可以參看Ansible的官網.上面有關于Ansible本身的介紹,和一系列的擴展module.
最后再看一看Ansible整體的結構:
前面我們提到了一種模式,即每次都新做出來一臺機器,然后把這些Ansible(或者其它什么工具)腳本對著這些干凈的機器運行一遍.最后再把特定版本的軟件部署上去.也就是說每次部署都會把原來的虛擬機實例干掉,再重新生成一臺.在實際場景中,同一個應用會存在多個實例,我們沒有必要對每臺機器都這么做,只需要把一臺機器使用Ansible裝好之后,再打個鏡像,然后通過這個鏡像啟動多臺實例.
這種模式能夠帶來的好處是顯而易見的,不但保證了環境的一致性,且擴容非常容易,只需要把同一個鏡像再多啟動幾個實例,然后掛接到相應的負載均衡中即可.而且永遠不需要害怕線上機器crash掉.按照鏡像再啟動一個就可以了.但這種模式帶來的問題也是顯而易見的,首先打虛擬機鏡像的時間是很長的.其次這種做法就要求服務器是沒有狀態的,也就是不能在硬盤上存文件,寫log.還好現在的云服務提供商(AWS,阿里云等)都有相應的產品來解決這些問題.
對于上傳的附件和圖片等文件來說,有兩種方式:
這兩種方法能夠在一定程度上解決問題,但終究不是本地磁盤,在讀寫速度和并發寫的處理等方面都會多多少少存在一定問題.所以只能適用于對這些指標要求不高的場景.
對于log來說,也有兩種方式:
使用上述的方式時,所有的代碼,軟件和配置的變更都需要走這么一個流程:各種各樣的自動化測試、鏡像構建,實例化鏡像啟動服務.所有的變更,不管再小,都會走這樣的流程,而不會直接更改在實例機器上.所以實例機器就是不可變的了.這就是所謂“不可變服務器”的概念.
但這個過程是比較漫長的.所以對于緊急發布之類的場景,是很讓人捉急的.而Docker技術的出現就很好的解決了這個問題.Docker基于Linux Container(LXC)技術,能夠做到輕量級的虛擬化.
Docker采用了分層的文件系統,所以如果我在包含Java的鏡像的基礎上打一個包含Tomcat的鏡像,只需要創建出一層只包含Tomcat的鏡像,然后和原先的包含Java的鏡像疊加在一起,就可以形成一個完整的可運行的鏡像.
在這種分層的鏡像機制下,如果每次只修改最上面一層鏡像,則構建的速度是很快的.而最上面一層通常就是添加應用程序.以Java Web程序為例,包含Java和Tomcat的鏡像可以作為一個 基礎鏡像.然后每次生成的WAR包通過Dockerfile(用于構建Docker鏡像的描述文件)中的ADD指令添加到新的鏡像層中即可.
舉個例子:這里有一個Java的web應用,通過運行“./gradlew war”的命令會在本地目錄下生成’build/libs/bookstore.war’.然后編寫如上圖的Dockerfile,它會把本地生成的war包ADD到Docker鏡像中.運行’docker build . -t bookstore: <版本號>’就可以生成一個鏡像.
通過Docker的history命令可以看到1.5和1.3兩個版本的最上面一層是不同的,但它們的基礎鏡像層都是“25e98610c7d0”.最上面一層的大小是6.106M,也就是比一個war包稍微大了一點點.
綜上所述,可以看到相比使用虛擬機鏡像作為不可變服務器,使用Docker鏡像有如下優勢:
而前面提到的那些使用虛擬機作為不可變服務器時,需要解決的問題(本地文件,log等),使用Docker同樣會面對.而解決方法也是類似的.
既然Docker這么方便,那么使用虛擬機作為不可變服務器是否還有價值呢?這個其實主要還是看相關工具,及其成熟度.比如AWS和阿里云都提供了使用配置文件來編排虛擬機資源的能力,而且可以設置一些觸發器來自動以虛擬機為單位對應用程序進行擴展(scale).這種模式已經非常成熟了.
而對于容器而言,這些云提供商也開始逐漸推出容器服務,把上述的那些對虛擬機的操作也引入到了容器的領域.今年五月份阿里云的容器服務就已經商用化了.它提供了集群管理的能力,也可以設置觸發器對某一個應用進行擴容和縮容.關于阿里云容器服務提供的更多能力,因為時間關系,就不再贅述,有興趣的朋友可以在這里做詳細了解:https://yq.aliyun.com/teams/11.
Ansbile、虛擬機不可變服務器、Docker Image都是很有用的技術,但針對每個具體的技術,還是需要仔細評估你的應用是否能夠克服或者容忍前文提出的相應的限制和問題.并且需要看看這些技術能給你的業務帶來多大的好處.
最重要的一點就是無論你在部署階段使用的是何種技術,使用一條完整的從代碼提交到最終部署上線的持續交付流水線都是必須的.在流水線上看到的都只是一個一個的stage,并且某些stage(比如部署)應該需要手動批準觸發.至于點擊之后到底是調用了Ansible腳本,還是運行了docker pull都是實現細節了.下面是一個使用 http://crp.aliyun.com 配置出來的示例持續交付流水線,及其不同的狀態.
上圖是一個持續集成流水線的不同狀態的樣子.可以看到剛開始的兩個stage,代碼檢出和集成測試,是由代碼提交自動觸發的.到了第三個stage,也就是部署測試環境,就需要手工批準了,所以出現了一個按鈕給你按.后續的預發和生產環境也都類似.
持續交付部分就講到這里,下面是個小結:
接下來我們再聊聊微服務.
關于微服務的概念,《微服務設計》一書給出的定義是:一些協同工作的小而自治的服務.微服務能夠帶來很多的好處,幫助我們更好的進行持續交付.當然微服務本身也需要很多實踐的支撐,比如Martin Fowler就在他的bliki(http://martinfowler.com/bliki/MicroservicePrerequisites.html)中提到了“You must be this tall to use microservices”.而這個’tall’中的很多內容都已經涵蓋前面討論的那些持續交付的技術實踐中.所以可以說微服務和持續交付也是相輔相成的關系.
使用微服務之后,顯然你需要部署的服務就會增多.如果一個服務的自動化部署和相應的流水線都沒有做好,那么服務多了之后部署的復雜性就可想而知了.所以只有把持續交付的實踐先做好,才有可能順利地使用微服務.
反過來看,微服務架構下,每個服務都很小.因此如果我的某次修改只涉及了一個微服務的代碼,我只需要發布這一個服務即可.那么相應的測試工作也就簡單的多.
其實按理說,雖然服務拆開了,但是還是需要這些服務在一起才能完成整個系統的功能.所以只修改一個服務,還是有可能影響整個系統的功能的.但是因為它們是不同的服務,所以一定會有非常清晰的API接口.這種API接口其實跟一個單塊系統中的模塊化的概念很類似.只不過API容易做的清晰,而單塊系統中的模塊化的邊界很難維持.所以從這個角度看,微服務帶來的其實是“強制的模塊化”,從而帶來更好的設計.
好的,話說回來.既然每次發布只涉及到需要修改的那些微服務,那么影響的面就相應的較小,所以就可以更加放心大膽的去做發布,也就進一步促進了持續交付.
微服務所涉及的話題非常多,大家可以移步《微服務設計》這本書查看所有的話題.這里只分享一點,那就是使用漸進式的方式進行微服務化.當然其實“漸進式”是我做大部分變動時的一個通用原則,比如重構,架構變化等.
漸進式微服務化的一個場景就是當你要新做一塊相對來說比較大,而且比較獨立的功能時,就可以考慮,是否可以單獨寫在一個服務中.舉個例子,若干年前我在一個比較大的Java Spring項目上工作.然后客戶有了一塊新的業務,最終希望以主站上的一個tab頁的形式存在.但我們都不想在這個陳舊的系統上繼續開發.最終的方式就是新啟一個應用.使用當時開發效率最高的技術.
那么怎么做到存在主站上的一個tab呢?答案是使用nginx集成.為了不暴漏客戶信息,下面我們會用一些加的信息.這個應用的所有url都在/new_app/下.在主站的nginx配置中加上一條轉發的規則,把/new_app/*這樣的url,都轉發到新部署的應用上.
當然,這只是一種微服務的形態而已.關于更多的形態,大家可以了解一下淘寶的前后端分離技術:http://blog.jobbole.com/65513/.
好的,稍微總結一下今天的內容:
今天主要講了什么是持續交付的目標,為了達到這個目標需要使用哪些技術.然后還聊了聊微服務的方法論會給持續交付這件事情帶來怎樣的機遇和挑戰.最后舉了一個例子來說明如何逐步進行微服務化.
感謝大家的聆聽.
Q1:使用Docker部署微服務持續交付時,應該注意什么?你們的Docker使用情況是怎樣的?
A1:我現在做的是一款持續交付產品,本身會有一個構架集群,執行任務使用的是Docker,但集群軟件本身并沒有使用Docker來不熟.不然就是在Docker中運行Docker了,性能會有些影響.
前端的portal正在做Docker化,還沒有應用到生產環境中.一個可以分享的就是,要把自己的應用的相關配置都環境變量化,這樣對于Docker化比較友好.
我們還有一個產品是code.aliyun.com.這個產品也沒有Docker化,或者說『不可變服務器化』,原因就是因為要在本地磁盤寫代碼庫.
所以上面提到對本地讀寫要求比較高的應用做『不可變服務器』還是有些困難的.
Q2:你們目前關注的持續集成的量化指標有哪些?比如項目單元測試/接口測試/靜態檢查等方面的內容.整個持續集成有效運轉的效率如何?失敗了怎么辦?怎么保證交付系統的穩定性?
A2:指標主要是測試覆蓋率、代碼復雜度,及checkstyle檢查出來的一些問題.持續集成會有一個大屏幕把信息輻射出來,所以如果出錯了,所有人都能看到會要求把CI break的同學立即修復,并且在修復之前不允許新功能的提交.
Q3:問個小問題,崔力強老師描述的package能包括什么內容?
A3:可以是純粹的應用,比如war包.也可以是一個壓縮包,里面包含了war包和安裝腳本,這樣這個軟件包就是可以自安裝的.
Q4:Windows的架構會支持嗎?
A4:剛才提到的Ansible是支持Windows的.Docker的話,現在出了原生的Docker,可以做開發測試之用.但生產環境的性能如何,還需要測試一下.
Windows上有原生的Docker,我是mac用戶,win版的Docker其實也沒有用過,只是看到Docker官網的消息:https://www.docker.com/products/docker :)原生的mac版Docker的volume掛載的性能也很差,猜測可能win版本的也不會太好.
文章出處:DBAplus社群