上線:準備和部署軟件包時開發和運維的角色
這篇文章里,我們會探討開發團隊、運維團隊和其它相關方如何通過協作來準備一個“好”的部署軟件包。“好”的軟件包能減少部署中出錯的可能,并在需要自定義環境時提高部署的透明性。
此外,我們還會檢視為何一個結構良好的部署包更易于轉為自動化部署,提升生產率和可靠性,同時減少軟件開發和維護生命周期中的錯誤和等待時間。
區分部署過程中的擔憂:為什么 vs. 如何做
顯而易見,部署包需要包含應用程序的所有組件。而不僅僅是你自己的二進制包——EARs,WARs之類——通常這些包由集成構建產生,還應包含靜態內容、配置文件、共享庫、防火墻配置等等。特別還應包括應用服務器的參數和設置,比如消息隊列、連接工廠、數據源或類環境變量。實際上,軟件包應包含軟件生命周期中的所有內容,也就是那些需要一起被部署、升級和取消部署的內容。
確保軟件包是“完備的可部署單元”對于一次可靠的部署來說是至關重要的,特別是在大規模環境中。指望目標中間件棧(middleware stack)已經用“正確值”設置好是導致部署失敗的一個常見原因,這通常導致耗費很多時間來找出不同環境中的細微差異,這往往是令人沮喪的過程。
部署包中只包含配置信息,比如共享“服務”,例如為多個應用所共享的消息隊列或數據源,這種情形不鮮見。這些配置顯然應該按照有版本管理的、可部署的對象來處理,意味著這些配置文件按照和“普通”應用程序一樣嚴格的流程來發布。實際上,平穩、可靠的部署共享服務通常更為關鍵。
除了確保軟件包最完整的描述了包含什么使特定版本的應用程序或配置正確工作,軟件包還需要指出它們能夠運行所需的其它東西。換句話說,部署軟件包需要明確界定他們的先決條件和依賴,以便在匹配的環境上部署。
準確的說,哪個部門負責部署和組織結構有很大關系。多數情況下是開發團隊,他們通常能使用持續構建工具使某些發布流程變為自動化,但發布過程還可能涉及其它團隊。舉例來說,讓DBA在發布前確認SQL腳本,或要中間件競爭中心(Center of Competence)檢查中間件配置的情況并不少見。
請不要增量式發布(Delta Release)
通常,開發的一個目標就是盡量減小對目標環境的影響。顯然,如果你的應用需要“不間斷運行”或多個應用程序共享一個集群,不必要的重啟服務器是要盡量避免的。
為了達到這個目標,一個通常的做法是要求開發團隊提交一個“增量”發布包,其中值包含新的或修改過的系統模塊,并且只部署這些模塊。經驗顯示,其實這是一個冒險的策略,應盡量避免,原因如下:
- 準備一個增量發布通常都是手工任務,完全依賴開發人員判斷哪些是修改過的模塊而哪些不是。這是一個耗時的又容易犯錯的過程,因而這個過程幾乎不可重復 。
- 某個應用程序部署包的版本不適合實際部署——在最壞的情況下;可能所有的組件都需要上一版本。部署到一個“干凈”的環境(設想需要快速設置幾個干凈的鏡像來重現一個壓力問題)則更加復雜,失敗的風險也因所有早期版本的包必須還要保留、并按照順序保留而增大。這是因為,當然,簡單來說就是數據庫增量備份的問題。
- 除非應用程序的每個版本都部署到了每個環境,否則在升級到相同版本時會產生問題,因為需要從不同的版本升級。這很快會在選擇了錯誤的增量發布包時導致混亂和產生錯誤。
- 不包括完整的包,而只是一些碎片的發布倉庫不是一個好的最終軟件庫的候選。
上述各點都無法讓減小部署影響的目標失效,這是顯而易見的。但部署包不能解決問題 。實際上,部署流程才能應該通過在部署時推知哪些組件應增加、哪些被修改和哪些應移除,并據此實施來滿足這個需求。
在時限壓力下手工實現上述部署流程是非常困難的(開發人員總是首先要做增量發布包),而一個好的自動部署系統能夠很容易地實現上述目標。實際上,這是引入自動化部署的一個主要好處。
在上述情況下,通常會設立專門的發布管理小組負責協調各種交付物,部署包按照便于恰當的人更新、修改和批準部署包的內容來組織是非常重要的。
構建部署包是一個需要多個步驟的工作流,需要很多部門參與并耗時很長,也會產生準備“不完整”部署包的情況。這種情況下,發布管理系統不產生這種“不完整(draft)”發布包非常重要。
我們建議由恰當的人員對已批準和已發布的包進行數字化簽名,這樣所有的包在部署前都通過驗證,為部署安全提供更進一步保障。
深入到比特和字節
選擇部署包格式主要是從方便起見。當然,部署包應該:
- 便于移動——最好是個單一文件
- 便于檢查
- 可壓縮,因為文本文件,如SQL或配置文件壓縮率很高
- 支持某種錯誤修正
- 可移植(比如開發人員在Windows環境開發,生產環境是UNIX)
- 可進行數字化簽名,目的是可以驗證待部署的包是通過審批的包。
所以通常來說,選擇一種壓縮文件格式,比如ZIP、TGZ或RPM,作為發布包的格式,但具體選擇哪種完全取決于你自己。
目前關于build文件格式沒有多少可說,因為除了WAR就是WAR。然而對于配置文件,情況則大不相同。目前,如果消息隊列和數據源的定義,甚至包本身(在發給運維團隊的成員的電子郵件中碰見這種情況很常見)通常都只是由人們可讀的文檔來描述的,比如發布注記(release notes)或按模板寫的Word文檔,這些文檔非常容易引起歧義并難于檢查修改。
基于上述原因,配置文件也應該由機器可識別的格式來定義,比如XML。不幸的是,在這個方面目前還沒有什么標準可推薦。事實上,這方面也是我們非常希望和用戶及供應商合作的。
最好避免使用某種特定中間件定義的格式,比如WebSphere的XML配置。有時直接從已有的配置文件中抽取配置項看上去很方便,但這樣會限定于某個中間件供應商,在部署到不同的服務器環境時可能會遇到困難。通常,這是完全可以避免的,因為應用程序通常不會用到特定中間件的某個特定特性,因此可以在自己的配置文件中很容易的定義更“一般”的JMS隊列,數據源等等。
最后, 聲明文件或清單應該能描述包的內容。應該在這里列出包的依賴關系,給操作者的描述和任何其它不容易找到的信息,比如開發人員的聯系信息,項目名稱,等等。聲明文件還需指出包內項目內容的類型,這些內容往往不容易通過名字判斷出來:EAR文件顯然是EAR但是一個ZIP文件可能包含HTTP服務器的的靜態內容、應用程序配置文件等等。
再次指出,文件格式應該是機器可讀并可驗證的,同時也應該是容易自動生成的(比如從構建系統生成)。
我們現在有了方便的、可移植的并能自動部署的軟件包,這個軟件包也是待部署應用程序要包含什么內容的完整描述。到現在,我們還沒說任何如何部署的內容。發布注記在哪兒?部署和回滾計劃呢?
理想情況下,應該沒有這些東西。更準確的說,標準的Java EE應用程序的部署流程應該,以部署包內容為基礎,是足以完成正確部署的。應用程序特定的部署步驟意味著滿足該應用程序的些特殊要求,這些特殊要求在之前只有開發人員了解。當凌晨2點鐘時需要進行一次緊急部署,而無法聯系上開發人員,運維人員犯錯幾乎是不可避免的。簡單來說,開發人員應該知道什么應該被部署,他們不應該為如何部署而操心。
當然,組織內有標準的Java EE部署流程是實現上述目標的先決條件,當你的組織有幾類不同的應用時可能有幾個流程。這個流程要足夠復雜,以便能覆蓋在部署時所需的各個組件和配置,但限制可選項的數量以保證流程的可維護性、測試性和可靠性也同樣重要。
這必然會給開發技術加上一些限制條件。然而,相比在當前的情況下維護成本是運行應用程序中最大的日常開支這一事實,減小犯錯可能性、更可靠的部署流程帶來的好處勝過最大化開發靈活性。當然,在每個組織都需要依據自身的要求來平衡這兩者。
標準的部署流程不只是更容易維護。它也使運維人員在壓力下更可靠的執行操作,相比于依照大量針對不同應用程序的不同版本的發布注記手工操作容易得多。標準部署流程也會讓自動化變得更簡單,讓運維人員從重復任務中解脫出來,同合適的訪問控制相結合,可以讓開發人員自己執行部署。如果有適合的集成點,將構建和發布系統連接起來實行持續部署以達到真正的端到端自動化也是有可能的。
此外,實現更多的自動化部署流程來支持更復雜的場景,讓開發團隊在避免臨時流程時更加靈活性更容易。不過隨著自動化流程變得越來越復雜而難于實現和測試,是否采取復雜的自動化流程需要仔細考慮。但是相對手工流程明顯的優勢是,一旦自動化流程通過測試和校驗,你可以確保流程的所有步驟是執行結果是可預測的。對人們來說,不斷成功的執行復雜的、步驟眾多的流程的目標是非常難以達到的。
從部署包到運行中的應用軟件:為目標環境定制化軟件包
目前為止,我們探討了將所有內容打為一個自描述的包,這是發布管理中的一個好實踐。這樣做,我們默認為一個軟件包可以“原封不動”地部署到不同環境。然而在當前的部署、測試、驗證和生產過程中,我們其實還須針對不同的目標環境“調整”應用程序、相關配置和資源——設想一下配置文件或者數據源、用戶名及口令等配置項。
有些公司在虛擬化和虛擬設備的道路上更進一步,這會讓上述情況有所改變,但當前構建部署包和部署流程應簡單、可靠并在部署時透明定制應用程序組件和配置文件。
在缺少已有標準,甚至是指導原則的情況下,為了解決上述問題人們嘗試了很多解決方案,從如JMX等非常優雅的方法到簡陋的方案如字符串搜索替代 。
此外,不同的中間件平臺有各種不同程度的定制化支持:通常,門戶、ESB和過程服務器提供某些解決這些問題的“本地化”解決方案,但是應用服務器傾向于讓用戶自己弄好這些配置。
時常,結果定制化方法因不同項目、目標平臺和部門而異變成大雜燴。像以前一樣,這會導致維護開支增加和提高潛在的混亂和錯誤。
那什么是“標準“定制化解決方案應提供的呢?當比較不同的方案時,下面這幾件事應牢記在心:
- 方便:定制化過程應便于設置和快速實現。對有時需要在部署到開發環境時進行定制化操作的開發人員來說這很重要。
- 可視化:便于授權用戶查看特定部署環境上的定制項(應用程序中可定制的部分)和賦給這些定制項的值。
- 安全的:當缺少定制項的值或設置了錯誤值(比如忘記設置超時值或在生產環境上設置為測試環境上的值)時,應該能夠快速檢測到。寧可在部署時檢查到定制值缺少或錯誤也要比在運行時出現模棱兩可的錯誤好。
- 有版本控制和訪問控制:只有授權用戶才能查看和修改應用程序定制項的值。最好能記錄這些值的變更歷史。這有助于比較同一應用程序不同版本的定制值,也允許用戶比較同一應用程序在不同部署目標環境上的定制值。
無論使用那種技術,定制化方案主要分為兩類:基于指針的定制化和基于符號的定制化。
基于指針的替換只是“搜索并替換”的一種時髦的說法,它稍微先進一些的同類,基于XPath的替換,通常能在門戶或ESB環境中見到。這種方法是脆弱的,因為部署包包含有效的定制值,因此存在隱蔽錯誤的風險很大。更進一步,部署包的定制值基本上不可見。諷刺的是,這種方法在為那些不能定制化的包打“補丁”時很有用。
總的來說,準備基于指針方式定制化的包的確更容易——簡單來說就是將開發環境或其它“創建(authoring)”環境上的設置導出,或者做一個快照。當然,這個導出過程可以是“符號化(tokenized)”的(用符號來替代需要定制的值),但這是易出錯的并且引起高昂的手工開支,特別在開發過程中經常需要導出時是個問題。
顯然,基于指針的定制化僅在需定制的設置或定義用某種方式結構化的情況下才可行——否則不可能構造一個指向待修改項的指針。XML和資源定義(比如properties文件)歸為這一類,但類似純文本文件則不屬于這一類。
基于符號的替換,是另一種方式,主要指占位符——部署包包含(構件、資源定義 等)特殊的符號,在部署時這些符號被給定的值替代。基于符號的定制化要求部署包提供者,通常是開發人員,專門準備部署構件。
這意味著,首先,所有待替換的符號在交付時要清楚,應用程序有了一個正確定義的定制項集合,這是額外的好處。符號的特殊語法也讓驗證替換值是否都給出提供了可能。
即使校驗失敗,應用程序通常因符號的語法錯誤而終止——符號一般來說不是合法值——這也時額外的安全機制。
當然,在開發人員來看這種錯誤保護機制也會是麻煩的。因為除非符號被替換否則應用程序無法正確工作,這要求必須建立構建流程來實現它,同時這個流程還要是快速的,當然是在開發環境。
基于指針的替換方案對于那些沒設計成可定制化的應用程序也適用,然而基于符號的替換方案在錯誤保護和可視度方面有明顯優勢。另外,基于符號的替換方案便于傳播通過特定環境的只有運維人員才能訪問的的定制值(想一下如生產環境數據庫口令)來得知應用程序在哪些地方需要定制(開發人員應該知道并詳細列出)的知識。
因為部署通常都是關鍵任務,這些實實在在的好處應使基于符號替換的方案在任何可能的時候作為首選。
事實上,應用程序的定制項應列在包的聲明文件中。基于特定的符號語法,通常能夠(并推薦)依據這個定制項列表對包內容進行檢查。在部署時,也可用同樣的方式驗證,為安全的部署提供了額外的保障。
請不要區分開發、測試、驗收和生產環境的部署包
任何嘗試過,在開發過程中,按含糊的說明來判斷要替換什么、這些定制項在哪個配置文件中以便讓應用程序運行起來的人,可能會納悶為什么不能用不易出錯的方法做事。如果沒有合適的自動化部署方案,一個常用的替代方法是嘗試將定制作為構建和發布流程的一個環節。
從技術角度看,一個容易想到的選擇是:目前有許多好的持續構建和發布產品,開源的和商業軟件都有,并且大多數組織已經有了。很多工具都支持“profile”這個概念或類似區分build細節的機制,并且如果沒提供這類機制的話也提供了讓你加入自己的搜索替換功能的鉤子。
然而,有些嚴重的缺陷導致這個方法作為合適的定制化方案并不合適:
- 在構建過程中需要訪問環境相關的細節。這不應只是覺得“錯誤”,有些信息可能是高度敏感的——比如生產環境數據庫的口令——讓這個方案從安全角度來看不可行。
- 持續構建工具一般不能提供恰當的有版本控制的、有安全機制的倉庫存儲環境相關的定制值,很容易犯臭名昭著的復制粘貼env.properties文件的錯誤和其它錯誤。
- 有了環境相關的build,幾乎不可避免的是,在某些時候你會不小心在生產環境上運行測試build。
結論
順暢和可靠的部署流程從簡單的第一步開始:以一種自動的、可檢查校驗的方法整合并發布一個結構化的,完整的部署包,該部署包定義了特定版本的應用程序所有的組件、配置和依賴關系。這能顯著的減少因不合法或缺少定制值、組件或必須的服務而產生的錯誤。
計算機可讀的包也是引入部署自動化的第一步,這里提到的自動化不僅是檢查校驗自動化,還包括按照自定義的標準流程部署這些包,并按照環境自動定制。除了確保一致性和提高可靠性之外,這可顯著提高效率并可以免除開發和維護生命周期內的一個最常見的瓶頸。
關于作者
作為一位早期就相信Java有能力去交付“企業級”軟件的人,Andrew Philips很快關注于高產能、有彈性和可擴展的J2EE應用軟件開發。特別是在并發和高性能開發方面,Andrew通過不斷在多個國家從事復雜的并有挑戰性的企業應用環境工作積累了豐富的經驗。他一直關注有效的集成有前途的新Java開發到公司軟件開發,Andrew于2009年3月加入XebiaLabs,在那兒他作為公司部署自動化產品Deployit開發團隊的一員。和其他人一起,他也為開源Java STM實現Multiverse和領先的Java云計算庫jclouds貢獻代碼。
更一步閱讀
[1] Hamilton, James, "On Designing and Deploying Internet-Scale Services", Proceedings of the 21st Large Installation System Administration Conference (LISA '07) pp. 231-242
[2] Phillips, Andrew, "Customize This: Tailoring deployment packages to your target environments"
[3] Kumar Yadav, Vivek, "Structuring a Deployment Package, part 1: Understanding the complexity"
[4] Partington, Vincent, "Incremental deployments vs. full redeployments"
[5] van Loghem, Robert, "So what is a deployment really?"
查看英文原文: Going Live: The Role of Development and Operations in Preparing and Deploying Software Packages