全網(wǎng)首發(fā):逐一解讀云原生應(yīng)用開發(fā)“12-Factors”

本文節(jié)選自普元信息即將出版的《微服務(wù)企業(yè)架構(gòu)最佳實踐》一書,本文作者普元云計算架構(gòu)師宋瀟男,為該書的合著者之一。轉(zhuǎn)載本文需注明出處:微信公眾號EAWorld,違者必究。

作者自序:

12原則的提出已有五年之久,可惜業(yè)界一直缺乏一篇對其進行簡明解讀的指導(dǎo)性文章,所以我決定寫這樣一篇文章。在微服務(wù)模式的大背景下,力求對12原則的來龍去脈做出明確和完備的解釋,并對12原則原文的含糊之處做出澄清。如果各位讀者對本書的其他內(nèi)容感興趣,那么也敬請關(guān)注我們的公眾號eaworld。

12-Factors經(jīng)常被直譯為12要素,也被稱為12原則,12原則由公有云PaaS的先驅(qū)Heroku于2012年提出(原文參見12factor.net),目的是告訴開發(fā)者如何利用云平臺提供的便利來開發(fā)更具可靠性和擴展性、更加易于維護的云原生應(yīng)用。距離12原則的提出已有五年之久,12原則的有些細(xì)節(jié)可能已經(jīng)不那么跟得上時代,也有人批評12原則的提出從一開始就有過于依賴Heroku自身特性的傾向。不過不管怎么說,12原則依舊是業(yè)界最為系統(tǒng)的云原生應(yīng)用開發(fā)指南,我們可以把它作為一個非常有力的參考,但是也千萬不要教條。

原則1:一份基準(zhǔn)代碼,多份部署

這個原則不管對微服務(wù)模式還是其他軟件開發(fā)模式來說都非?;?,所以被列為12原則的第一條,該原則包括如下四個子原則

  1. 使用代碼庫管理代碼,一般是Git或者SVN,這個要求非常初級,相信本書的讀者都會遵守。

  2. 一份基準(zhǔn)代碼(即一個代碼庫)對應(yīng)一個應(yīng)用。如果通過一份基準(zhǔn)代碼可以編譯出多個應(yīng)用,那么應(yīng)該考慮將該基準(zhǔn)代碼按應(yīng)用拆分為多份;如果一個應(yīng)用需要多份基準(zhǔn)代碼,那么要么考慮將多份基準(zhǔn)代碼合并,要么考慮將該應(yīng)用按基準(zhǔn)代碼拆分為多個。

  3. 不允許多個應(yīng)用共享一份基準(zhǔn)代碼,如果確實需要共享,那就把需要共享的基準(zhǔn)代碼的穩(wěn)定版本發(fā)布為類庫,然后通過依賴管理策略進行加載。

  4. 同一應(yīng)用的多份部署可以使用同一份基準(zhǔn)代碼的不同版本,但是不可以使用不同的基準(zhǔn)代碼,類似原則2,使用不同基準(zhǔn)代碼的應(yīng)用不應(yīng)被視為同一應(yīng)用。

違反子原則2和3,會給代碼管理和編譯工作帶來麻煩:

  1. 如果一份基準(zhǔn)代碼可以編譯出多個應(yīng)用,那么這幾個應(yīng)用之間必然會存在不清晰的依賴關(guān)系,隨著時間的推移,這種依賴關(guān)系會變得愈加混亂,以至于修改一個應(yīng)用的代碼,會給其他應(yīng)用帶來不可預(yù)知的影響。這樣的基準(zhǔn)代碼顯然極難維護。

  2. 基準(zhǔn)代碼的劃分和應(yīng)用的劃分非常類似,也是系統(tǒng)邊界的一種體現(xiàn),如果一個應(yīng)用需要從多份基準(zhǔn)代碼編譯,那么多數(shù)情況下這個應(yīng)用的內(nèi)外部邊界問題會存在問題。如果邊界不存在問題,那么請將多份基準(zhǔn)代碼合并為一份,而不是維持這種古怪的設(shè)計。

  3. 如果多個應(yīng)用不是通過類庫,而是直接共享一份基準(zhǔn)代碼,那么這份被共享的基準(zhǔn)代碼會很難維護,對這份基準(zhǔn)代碼的修改必須謹(jǐn)慎考慮對多個應(yīng)用可能造成的影響。正確的方式是將這份基準(zhǔn)代碼發(fā)布為類庫,保持清晰的邊界和接口約定供其它應(yīng)用調(diào)用。

原則2:顯式聲明依賴關(guān)系

這里的依賴指所有的依賴,包括應(yīng)用程序本身的類庫和操作系統(tǒng)層面被應(yīng)用程序所使用的庫文件或者其他二進制文件,都必須進行顯示聲明,并對版本做出明確的指定。不要假定運行環(huán)境中已經(jīng)存在應(yīng)用所需要的任何依賴項,而是應(yīng)該假定什么都沒有(即使有也很可能不是應(yīng)用所需要的版本)。

如果使用容器方式進行部署,容器的基礎(chǔ)鏡像很可能是Busybox或者Alpine之類的迷你Linux,那么就幾乎等于什么都沒有。如果使用微服務(wù)模式,理想情況下,微服務(wù)之間的依賴關(guān)系也應(yīng)該進行顯示聲明。

以前我們往往不會對依賴做如此嚴(yán)格的管理,因為應(yīng)用不會有太大規(guī)模的部署,也不會進行頻繁的發(fā)布,如果發(fā)現(xiàn)運行環(huán)境里缺少某些依賴,那么就臨時手工處理一下,也不是什么太大的問題。如今在微服務(wù)模式下,應(yīng)用的部署規(guī)模大、發(fā)布頻率高,還記得前文所說的“不可變服務(wù)器”嗎?如果這個時候還是使用原有的模式,則會帶來混亂。

聲明依賴的方式有很多,常見的方式是使用依賴清單,根據(jù)依賴清單進行依賴檢查,同時使用依賴隔離工具保證應(yīng)用不會調(diào)用系統(tǒng)中存在但是依賴清單中未聲明的依賴項;另一種方式是使用容器技術(shù),將應(yīng)用和依賴打包為容器鏡像,依賴的聲明和隔離就一并解決了。

原則3:在環(huán)境中存儲配置

首先需要明確的是,這里的配置指與部署環(huán)境有關(guān)的配置,例如:

  • 數(shù)據(jù)庫、消息代理、緩存系統(tǒng)等后端服務(wù)的連接配置和位置信息,如URL、用戶名、密碼等。

  • 第三方服務(wù)的證書。

  • 每份部署獨有的配置,例如:域名、連接數(shù)、與部署目標(biāo)環(huán)境資源規(guī)模有關(guān)的JVM參數(shù)等。

所有部署中都相同的信息,例如原則2里講到的依賴信息,不在本原則所討論的范圍內(nèi)。一些雖然在不同的部署中有所差異、但是和業(yè)務(wù)相關(guān)的信息,例如資金結(jié)算的轉(zhuǎn)換比例,也不屬于本原則所討論的配置。

我想大多數(shù)的開發(fā)者都知道如何通過使用配置文件實現(xiàn)配置和代碼的分離,但是這種方式仍然存在一些缺點,例如:

  1. 配置文件容易被開發(fā)人員不小心提交到代碼庫中,造成密碼、證書等敏感信息泄露。提交到代碼庫中的配置文件還容易被和應(yīng)用一起部署到目標(biāo)環(huán)境中,很可能會導(dǎo)致在目標(biāo)環(huán)境中應(yīng)用了錯誤的配置或者造成配置沖突。

  2. 配置文件會分散在不同的目錄中,并且有不同的格式(配置文件的格式往往與開發(fā)語言和框架相關(guān)),這會給配置的統(tǒng)一管理造成困難。

為了避免上述問題,本原則要求將在環(huán)境中存儲配置。一種典型的方式是把配置存儲在環(huán)境變量中,這會使配置和代碼徹底的分離,格式上也與開發(fā)語言和框架再無瓜葛,并且也不會被誤提交到代碼庫中。還可以使用Spring Cloud Config Server這類配置管理服務(wù)進行配置推送,并將配置的歷史版本和變更原因也一起管理起來。

原則4:把后端服務(wù)當(dāng)作附加資源

這里的后端服務(wù)指的是應(yīng)用運行所依賴的各種服務(wù),例如數(shù)據(jù)庫、消息代理、緩存系統(tǒng)等,對于云原生應(yīng)用來說,往往還會有日志收集服務(wù)、對象存儲服務(wù)、以及各種通過API訪問的服務(wù);當(dāng)作附加資源指的是把這些服務(wù)作為外部的、通過網(wǎng)絡(luò)調(diào)用的資源。

該原則有如下幾層含義:

  1. 不要將這些服務(wù)放在應(yīng)用本地:云原生應(yīng)用要求應(yīng)用本身無狀態(tài)化,那么狀態(tài)信息就應(yīng)該存儲在外部服務(wù)中(參見不可變服務(wù)器)。同時,微服務(wù)模式要求應(yīng)用責(zé)權(quán)單一以實現(xiàn)可靠性和擴展性,如果在應(yīng)用本地放置數(shù)據(jù)庫,那么微服務(wù)平臺將無法通過更換應(yīng)用的故障實例實現(xiàn)應(yīng)用的高可用性,也無法通過自動化的橫向伸縮實現(xiàn)擴展性,因為應(yīng)用實例內(nèi)包含兩種性質(zhì)完全不同的軟件(應(yīng)用和數(shù)據(jù)庫),無法對兩者使用同一種方式進行橫向擴展。另外,如果將這些服務(wù)放在應(yīng)用本地,那么也無法通過充分利用云平臺提供的能力簡化運維工作,例如,如果在應(yīng)用本地放置數(shù)據(jù)庫,而不是使用云平臺提供的數(shù)據(jù)庫服務(wù),那么顯然無法利用數(shù)據(jù)庫服務(wù)提供的自動備份、安全、和高可用等特性。

  2. 通過URL或者服務(wù)注冊/認(rèn)證中心訪問這些后端服務(wù):應(yīng)用應(yīng)該能夠在不進行任何代碼修改的情況下,在不同的目標(biāo)環(huán)境中進行部署,應(yīng)用不應(yīng)該和后端服務(wù)的任何一種具體實現(xiàn)存在緊耦合關(guān)系。

  3. 類似“顯式聲明依賴關(guān)系”原則,應(yīng)用最好也能夠?qū)ζ涫褂玫倪@些后端服務(wù)進行顯示聲明,以方便云平臺對服務(wù)資源進行自動綁定,在后端服務(wù)出現(xiàn)故障的時候,云平臺也能夠?qū)ζ溥M行自動恢復(fù)。

原則5:嚴(yán)格分離構(gòu)建、發(fā)布和運行

在本原則中,構(gòu)建、發(fā)布和運行這三個概念可能和從前有所不同,因此有必要首先對其進行明確:

  • 構(gòu)建指的是將應(yīng)用代碼轉(zhuǎn)化為執(zhí)行體的過程:構(gòu)建時會拉取特定版本的代碼和依賴項,將其編譯為二進制文件(針對編譯型語言),并和資源文件一起打包。

  • 發(fā)布指的是將構(gòu)建的結(jié)果和部署所需的配置相結(jié)合,并將其放置于運行環(huán)境之中。

  • 運行指的是將發(fā)布的結(jié)果啟動為運行環(huán)境中的一個或多個進程。

本原則要求構(gòu)建、發(fā)布和運行這三個步驟嚴(yán)格區(qū)分:

  1. 禁止直接修改運行狀態(tài)的代碼或者對應(yīng)用進行打補丁,因為這些修改很難再同步回構(gòu)建步驟,這時運行狀態(tài)的代碼就成為了“孤本”。同時,也不應(yīng)該在運行期間修改應(yīng)用的配置,配置的修改應(yīng)該僅限于發(fā)布階段(參見不可變服務(wù)器)。

  2. 運行這一步驟應(yīng)該非常簡單,僅限于啟動進程,資源文件的關(guān)聯(lián)應(yīng)僅限于構(gòu)建階段,配置的結(jié)合應(yīng)僅限于發(fā)布階段。

同時,每一次發(fā)布都應(yīng)該對應(yīng)一個唯一的發(fā)布ID,發(fā)布的版本應(yīng)當(dāng)像一個只能追加的賬本,一旦發(fā)布就不能修改。這么做的好處是:

  1. 每一份運行狀態(tài)的代碼都可以在對應(yīng)的發(fā)布和構(gòu)建階段找到它的來源,這是實現(xiàn)重新發(fā)布、故障實例的自動替換、發(fā)布出錯后的版本回退等機制的基礎(chǔ)。

  2. 運行步驟非常簡單,這樣在硬件重啟、實例故障和橫向擴展等情況下,應(yīng)用可以簡單和快速的實現(xiàn)重啟。

原則6:以一個或多個無狀態(tài)的進程

運行應(yīng)用

本原則要求應(yīng)用進程的內(nèi)部不要保存狀態(tài)信息,任何狀態(tài)信息都應(yīng)該被保存在數(shù)據(jù)庫、緩存系統(tǒng)等外部服務(wù)中。應(yīng)用實例之間的數(shù)據(jù)共享也要通過數(shù)據(jù)庫和緩存系統(tǒng)等外部服務(wù)進行,直接的數(shù)據(jù)共享不但違反無狀態(tài)原則,還引入了串行化的單點,這會為應(yīng)用的橫向擴展帶來障礙。

在微服務(wù)模式下,應(yīng)用不應(yīng)該在自身進程內(nèi)部緩存數(shù)據(jù)以供將來的請求使用,因為微服務(wù)模式以多實例方式運行應(yīng)用,將來的請求多半會被路由到其他實例,此時雖然可以使用粘滯會話將請求保持在同一個實例上,但是無論是云原生應(yīng)用還是微服務(wù)模式都極力反對使用粘滯會話,原因如下:

  1. 很難對粘滯會話實現(xiàn)負(fù)載均衡,因為粘滯會話的均衡性不僅決定于負(fù)載均衡策略,還和會話本身的行為相關(guān),例如,可能存在應(yīng)用某些實例上的會話已經(jīng)大量退出,而另一些實例上的會話依然處于活動狀態(tài),此時這兩部分實例的負(fù)載處于不均衡狀態(tài),而負(fù)載均衡器無法將活動會話轉(zhuǎn)移到空閑的應(yīng)用實例。

  2. 啟動新的應(yīng)用實例不會立即提高應(yīng)用的整體處理能力,因為這些新實例只能承接新會話,舊的會話依舊粘滯在舊的應(yīng)用實例上。

  3. 應(yīng)用實例退出會導(dǎo)致會話丟失,所以在實例發(fā)生故障時,即使云平臺可以對故障實例進行自動替換,也會導(dǎo)致用戶數(shù)據(jù)丟失。即使是對應(yīng)用實例進行人工維護,也需要在維護之前對該實例上的會話進行轉(zhuǎn)移,這往往意味著需要編寫復(fù)雜的業(yè)務(wù)代碼。

    在傳統(tǒng)模式下,可以通過在雙機之間進行會話復(fù)制來實現(xiàn)對用戶無感知的單機下線維護(雖然會付出處理能力減半的代價),但是在微服務(wù)模式下,應(yīng)用的實例數(shù)量往往遠(yuǎn)不止兩個,在大量的實例之間進行會話復(fù)制會使實例之間原本非常簡單的邏輯關(guān)系復(fù)雜化,此時將無法通過云平臺對其進行無差別的自動化維護。另外,在實例之間進行會話復(fù)制也意味著實例之間存在著直接的數(shù)據(jù)共享,這會為應(yīng)用的橫向擴展帶來障礙。

所以,粘滯會話是應(yīng)用實現(xiàn)可用性和擴展性的重要障礙,使用粘滯會話顯然是種得不償失的選擇。更好的實現(xiàn)方式是將會話信息存儲在緩存服務(wù)中。

原則7:通過端口綁定提供服務(wù)

服務(wù)端應(yīng)用通過網(wǎng)絡(luò)端口提供服務(wù),這點毋庸置疑,但是本原則還有如下兩個深層次的含義:

  1. 無論是云原生應(yīng)用還是微服務(wù)模式都要求應(yīng)用應(yīng)該完全自我包含,而不是依賴于外部的應(yīng)用服務(wù)器,端口綁定指的是應(yīng)用直接與端口綁定,而不是通過應(yīng)用服務(wù)器進行端口綁定。

    如果一定要使用應(yīng)用服務(wù)器,那就使用嵌入式應(yīng)用服務(wù)器,無論是云原生應(yīng)用還是微服務(wù)模式都極力反對將多個應(yīng)用放置于同一個應(yīng)用服務(wù)器上運行,因為在這種模式下,一個應(yīng)用出錯會對同一個應(yīng)用服務(wù)器上的其他應(yīng)用造成影響,也無法針對單一應(yīng)用做橫向擴展。

  2. 端口綁定工作應(yīng)該由云平臺自動進行,云平臺在實現(xiàn)應(yīng)用到端口的綁定之外,還需要實現(xiàn)內(nèi)部端口到外部端口的映射和外部端口到域名的映射。在應(yīng)用的整個生命周期內(nèi),應(yīng)用實例會經(jīng)歷多次的重新部署、重啟或者橫向擴展,端口會發(fā)生變化,但URL會保持不變。

原則8:通過進程模型進行擴展

與通過進程模型進行擴展相反的方式是通過線程模型進行擴展,這是一種相對較為傳統(tǒng)的方式,典型的例子是Java應(yīng)用。當(dāng)我們啟動一個Java進程的時候,通常會通過JVM參數(shù)為其設(shè)置各個內(nèi)存區(qū)域的容量上下限,同時還可能會在應(yīng)用層面為其設(shè)置一個或者多個線程池的容量上下限,當(dāng)外部負(fù)載變化時,進程所占用的內(nèi)存容量和進程內(nèi)部的線程數(shù)量可以在這些預(yù)先設(shè)置好的上下限之間進行擴展,這種方式也被稱為縱向擴展或者垂直擴展。

但是這種方式存在一些問題,首先,在進程的內(nèi)存容量和線程數(shù)量提高時,應(yīng)用的某些性能指標(biāo)可能不會得到同步提高,甚至可能會下降(這往往是因為程序?qū)δ承o法擴展的資源進行爭用所造成的),這種參差不齊的性能擴展對外部負(fù)載提高的承接能力會很不理想,有時甚至?xí)m得其反;

其次,為了使進程本身可以完成縱向擴展,還需要在虛擬機層面或者容器層面為其預(yù)留內(nèi)存資源和對應(yīng)的CPU資源,這會造成大量的資源浪費(當(dāng)然,也可以使虛擬機或者容器跟隨進程一起進行縱向擴展,這在技術(shù)上是可行的,但是會為虛擬機或者容器管理平臺的資源調(diào)度造成一些不必要的困難,例如頻繁的虛擬機遷移或者容器重啟)。

所以,現(xiàn)在更為推崇使用“固定的”進程(對前面Java應(yīng)用的例子來說,就是固定的內(nèi)存容量和線程池容量),在外部負(fù)載提高時,啟動更多的進程,在外部負(fù)載降低時,停止一部分進程,這種方式就是本原則所說的通過進程模型進行擴展,有時候也被稱為橫向擴展或者水平擴展。

這種擴展方式的好處是,在進程數(shù)量增加的時候,應(yīng)用的各種性能指標(biāo)會得到同步的提高,這種提高即使不是線性的,也會按照一種平滑和可預(yù)期的曲線展開,可以更為穩(wěn)定的應(yīng)對外部負(fù)載的變化。

云原生應(yīng)用和微服務(wù)模式極力推崇將通過進程模型進行擴展作為唯一的擴展方式,除了前文所述,還有一個原因是進程是云平臺可以操作的最小運行單元(當(dāng)然,可以通過其他技術(shù)手段去操作線程,但是那不會成為云平臺的通用技術(shù)特性),云平臺可以根據(jù)各個層面的監(jiān)控數(shù)據(jù),通過預(yù)設(shè)規(guī)則決定是否為應(yīng)用增加或者減少進程,例如,當(dāng)前端的負(fù)載均衡器檢測到訪問某個后端應(yīng)用的并發(fā)用戶數(shù)超過某個閾值時,可以立即為這個后端應(yīng)用啟動更多的進程,以承接更大的負(fù)載,同時還可以選擇是否對該應(yīng)用后端的數(shù)據(jù)庫進行擴展。

如果此時選擇對應(yīng)用進行縱向擴展,則云平臺既不知道應(yīng)用處理能力的變化,也無法對這種變化進行預(yù)期管理,更無法使應(yīng)用的前后端對這種變化進行聯(lián)動,即該應(yīng)用的擴展行為脫離了云平臺的管理。在微服務(wù)模式下,如果大量的進程都采用縱向擴展方式,則會為平臺的資源調(diào)度帶來極大的混亂。

注3:該原則似乎更適合被稱為橫向擴展原則,但是為了和12原則的原文保持一直,這里我們?nèi)匀粚⑵浞Q為“通過進程模型進行擴展”。

原則9:快速啟動和優(yōu)雅終止

可最大化健壯性

該原則要求應(yīng)用可以瞬間(理想情況下是數(shù)秒或者更短)啟動和停止,因為這將有利于應(yīng)用快速進行橫向擴展和變更或者故障后的重新部署,而這兩者都是程序健壯性的體現(xiàn)。

前文不止一次提到過應(yīng)用的快速啟動,在理念章節(jié)的開頭,我們提到過平價的進程生成對多道程序設(shè)計至關(guān)重要,而微服務(wù)模式在某種程度上可以認(rèn)為是多道程序設(shè)計在Web領(lǐng)域和分布式系統(tǒng)下的進一步擴展,這里所說的平價進程生成指的是操作系統(tǒng)的一種特性,是應(yīng)用快速啟動的基礎(chǔ),除此之外為了保證應(yīng)用可以在數(shù)秒內(nèi)完成啟動,還需要大量的優(yōu)化工作,需要開發(fā)人員掌握復(fù)雜的調(diào)優(yōu)技術(shù)與工具,有些工作必須在應(yīng)用的初始設(shè)計階段完成,例如:如果應(yīng)用體積過大或者是引用了太多的庫文件,那么再多的后期優(yōu)化也無法將啟動時間降低到數(shù)秒以內(nèi)。

“原則5:嚴(yán)格分離構(gòu)建、發(fā)布和運行”中我們還提到,應(yīng)用的運行步驟應(yīng)該非常簡單,這里的“簡單”也隱含著快速的意思,目的是為了在硬件重啟、實例故障和橫向擴展等情況下,應(yīng)用可以快速的實現(xiàn)重啟。除此之外,“原則6:以一個或多個無狀態(tài)的進程運行應(yīng)用”也與應(yīng)用的快速啟動有關(guān),遵守?zé)o狀態(tài)原則,使用云平臺提供的緩存服務(wù),而不是在應(yīng)用內(nèi)部加載緩存,可以避免在應(yīng)用啟動期間進行耗時的緩存預(yù)熱。

比起應(yīng)用的快速啟動,優(yōu)雅終止(Graceful Shutdown)需要考慮的問題會更為廣泛一些。優(yōu)雅終止需要盡可能降低應(yīng)用終止對用戶造成的不良影響(對于微服務(wù)應(yīng)用,用戶可能是人,也可能是其他微服務(wù))。

對于短任務(wù)來說,這一般意味著拒絕所有新的請求,并將已經(jīng)接收的請求處理完畢后再終止;對于長任務(wù)來說,這一般意味著應(yīng)用重啟后的客戶端重連和為任務(wù)設(shè)置斷點并在重啟后繼續(xù)執(zhí)行。除此之外,優(yōu)雅終止還需要釋放所有被進程鎖定的資源,并對事務(wù)的完整性和操作的冪等性做出完備的考慮。

最后,應(yīng)用還必須應(yīng)對突如其來的退出,在硬件出現(xiàn)故障時或者進程崩潰時,應(yīng)用需要保證不會對其使用的數(shù)據(jù)造成損壞,遵守?zé)o狀態(tài)原則、將數(shù)據(jù)交由后端服務(wù)處理的應(yīng)用可以很容易的將應(yīng)對突然退出的復(fù)雜度外部化。

原則10:開發(fā)環(huán)境與線上環(huán)境等價

本原則的淺層次含義是要求在開發(fā)環(huán)境和線上環(huán)境中使用相同的軟件棧,并盡可能為這些軟件棧使用相同的配置,以避免“It works on my machine.”這類問題。本原則反對在不同的環(huán)境中使用不同的后端服務(wù),雖然可以使用適配器或者在代碼中做出兼容性考慮以消除后端服務(wù)的差異,但是這將牽扯開發(fā)人員和測試人員大量的精力以保證這些適配器和代碼確實可以按預(yù)期工作,在應(yīng)用的整個開發(fā)周期中,這將積累極大的額外工作量,是一種非常不必要的資源浪費。

近年來個人電腦的性能大幅提高,開發(fā)人員一度得以在本地開發(fā)環(huán)境中運行與生產(chǎn)環(huán)境中一致的軟件棧,而不是像曾經(jīng)那樣采用輕量的替代方案。但是隨著云原生應(yīng)用和微服務(wù)模式的流行,情況又發(fā)生了微妙的變化:開發(fā)微服務(wù)時需要依賴云平臺提供的基礎(chǔ)服務(wù)和其他微服務(wù),越來越難以把這些服務(wù)完整的運行在本地,與此同時,完全的在線開發(fā)愈發(fā)成為一種趨勢,那樣的話至少在軟件棧上開發(fā)環(huán)境和線上環(huán)境就真的沒有任何區(qū)別了。

在我編寫這段文字的時候,Red Hat公司剛好在洽購在線開發(fā)環(huán)境創(chuàng)業(yè)公司Codenvy用以充實他們的云平臺產(chǎn)品OpenShift,而另一家與Codenvy類似的創(chuàng)業(yè)公司Cloud9在差不多一年前被Amazon公司收購。

本原則的深層次含義是盡量縮小開發(fā)環(huán)境和線上環(huán)境中時間和人員的差異。開發(fā)環(huán)境中的代碼每天都在更新,而這些更新往往會累積數(shù)周甚至數(shù)月才會被發(fā)布到線上環(huán)境,這是開發(fā)環(huán)境和線上環(huán)境在時間上的巨大差異;開發(fā)人員只關(guān)心開發(fā)環(huán)境,運維人員只關(guān)心線上環(huán)境,開發(fā)人員和運維人員在工作上鮮有交集,這是開發(fā)環(huán)境和線上環(huán)境在人員上的巨大差異。

對于前一個差異,本原則要求更為密集和頻繁的向線上環(huán)境發(fā)布更新,要求建立機制以保障開發(fā)人員可以在數(shù)小時甚至數(shù)分鐘內(nèi)既可將更新發(fā)布到線上,這也正是本章理念部分中持續(xù)交付所提倡的;對于后一個差異,本原則要求開發(fā)人員不能只關(guān)心開發(fā)環(huán)境中自己的代碼,更要密切關(guān)注代碼的部署過程和代碼在線上的運行情況,這也正是DevOps所提倡的。

原則11:把日志當(dāng)作事件流

應(yīng)用程序應(yīng)該將其產(chǎn)生的事件以每個事件一行的格式按時間順序輸出,這點毋庸置疑,但是本原則想說的其實是:應(yīng)用程序不要自行管理日志文件。

以前我們習(xí)慣將應(yīng)用程序產(chǎn)生的事件分門別類的輸出到不同的日志文件,并為每個日志文件指定在本地文件系統(tǒng)上的存儲位置,為了避免單一日志文件過大,還會為它們配置輪轉(zhuǎn)策略。

該原則極力反對上述做法,而是要求應(yīng)用程序?qū)⑷罩疽允录鞯姆绞捷敵龅綐?biāo)準(zhǔn)輸出STDOUT和標(biāo)準(zhǔn)錯誤輸出STDERR,然后由運行環(huán)境捕獲這些事件流,并轉(zhuǎn)發(fā)到專門的日志處理服務(wù)進行處理。這樣做的原因是:

1.?“原則6:以一個或多個無狀態(tài)的進程運行應(yīng)用”要求應(yīng)用程序無狀態(tài),那么應(yīng)用程序就不應(yīng)該將日志文件這種價值信息存儲在本地文件系統(tǒng)上。當(dāng)然,可以在本地運行一個日志收集進程讀取日志文件,并將其轉(zhuǎn)發(fā)到專門的日志處理服務(wù),以保證價值信息不被意外丟棄,但是這帶來了如下問題:

  • 需要提供一種機制以保證日志收集進程可靠運行。

  • 需要通過配置文件告知日志收集進程去哪里讀取日志文件。

  • 需要在應(yīng)用程序所在的虛擬機或者容器上為日志收集進程開放一個網(wǎng)絡(luò)端口以供其發(fā)送日志內(nèi)容,這不僅增加了網(wǎng)絡(luò)的復(fù)雜度,還給網(wǎng)絡(luò)安全帶來了隱患。

由此可見,直接將日志輸出到STDOUT和STDERR并由運行環(huán)境對其進行捕獲遠(yuǎn)比這種方案來的簡潔和可靠。

2. 在存在專門的日志處理服務(wù)時,由應(yīng)用程序自行對日志進行分類顯得死板和毫無必要;只需將日志以事件流方式發(fā)送給日志處理服務(wù),日志處理服務(wù)可以對這些日志按不同視角進行靈活的分類,而不是受限于一種既定的分類規(guī)則。

3. “原則6:以一個或多個無狀態(tài)的進程運行應(yīng)用”中還提到“微服務(wù)模式以多實例方式運行應(yīng)用,將來的請求多半會被路由到其他實例”,所以單個應(yīng)用實例的日志無法描述完整的業(yè)務(wù)操作,不具備業(yè)務(wù)層面的價值。必須將應(yīng)用所有實例的日志匯總到日志處理服務(wù),由日志處理服務(wù)按特定規(guī)則(如按用戶ID或者對象ID)對其進行聚合,才能完整展現(xiàn)應(yīng)用在業(yè)務(wù)層面的操作過程。

應(yīng)用在以多實例方式運行時,應(yīng)用的單個實例可能會因為軟硬件故障而重啟,或者被橫向擴展機制創(chuàng)建和銷毀,所以必須將應(yīng)用所有實例的日志匯總,才能完整的描述應(yīng)用的運行情況。

原則12:后臺管理任務(wù)當(dāng)作一次性進程運行

這是12原則的最后一條,也是最晦澀的一條。如果你在看過原文之后覺得哪里有些不大對,不必?fù)?dān)心,因為很多人的想法和你一樣。在本節(jié)的開頭曾提到有人批評12原有過于依賴Heroku自身特性的傾向,這些批評多半可能是本原則導(dǎo)致的。

事實上,通過SSH接入線上環(huán)境并使用腳本語言執(zhí)行管理任務(wù)的做法已經(jīng)不再被提倡,無論是云原生應(yīng)用還是微服務(wù)模式都極力反對這種做法,原因可以參見“理念五:不可變服務(wù)器”和“理念六:提供聲明式接口”。另外還有一個原因顯而易見:你的應(yīng)用有數(shù)個或者數(shù)十個實例,那么應(yīng)該登錄到哪個實例中執(zhí)行管理任務(wù)呢?如果在管理任務(wù)執(zhí)行的過程中,所在實例因為軟硬件故障重啟,或者被橫向擴展機制銷毀,那又該怎么辦?

正確的做法是,如果管理任務(wù)是修改應(yīng)用配置,那么應(yīng)該通過配置管理服務(wù)進行操作,參見“原則3:在環(huán)境中存儲配置”;如果管理任務(wù)是批處理任務(wù),例如數(shù)據(jù)的遷移、清洗或者檢查,那么應(yīng)該通過云平臺的批處理機制進行操作,大多數(shù)的云平臺都會提供這種機制,例如Kubernetes的Jobs。

本原則還提到“應(yīng)用的管理進程應(yīng)該和應(yīng)用的常駐進程運行于同一環(huán)境,并使用相同的代碼、版本和配置”,這是一條比較有價值的建議,可以避免由于環(huán)境或代碼等不一致造成的一些潛藏問題。雖然現(xiàn)在不提倡通過SSH接入應(yīng)用常駐進程所在的環(huán)境并執(zhí)行管理任務(wù),但是如果你使用容器技術(shù),那么很容易通過容器模板創(chuàng)建一個和應(yīng)用常駐進程一致的運行環(huán)境,并在其中執(zhí)行管理任務(wù)。

關(guān)于作者:宋瀟男

Unix和分布式系統(tǒng)專家,網(wǎng)格時代的幸存者,在云計算行業(yè)有近十年的研發(fā)和市場工作經(jīng)驗,對操作系統(tǒng)、中間件和云平臺有深入研究和大型項目經(jīng)驗。曾在華為云計算部門負(fù)責(zé)產(chǎn)品規(guī)劃、商業(yè)洞察、以及EMEA地區(qū)的市場推廣與技術(shù)合作工作。現(xiàn)任普元云計算架構(gòu)師。

關(guān)于EAWorld

微服務(wù),DevOps,元數(shù)據(jù),企業(yè)架構(gòu)原創(chuàng)技術(shù)分享EAii(Enterprise Architecture Innovation Institute)企業(yè)架構(gòu)創(chuàng)新研究院旗下官方微信公眾號。微信搜索:EAWORLD


閱讀原文:http://platformplus.blog.sohu.com/324921436.html
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容