本文節(jié)選自普元信息即將出版的《微服務(wù)企業(yè)架構(gòu)最佳實(shí)踐》一書守屉,本文作者普元云計(jì)算架構(gòu)師宋瀟男,為該書的合著者之一。轉(zhuǎn)載本文需注明出處:微信公眾號EAWorld,違者必究涝开。
作者自序:
12原則的提出已有五年之久,可惜業(yè)界一直缺乏一篇對其進(jìn)行簡明解讀的指導(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ā)更具可靠性和擴(kuò)展性诚欠、更加易于維護(hù)的云原生應(yīng)用。距離12原則的提出已有五年之久漾岳,12原則的有些細(xì)節(jié)可能已經(jīng)不那么跟得上時代聂薪,也有人批評12原則的提出從一開始就有過于依賴Heroku自身特性的傾向。不過不管怎么說蝗羊,12原則依舊是業(yè)界最為系統(tǒng)的云原生應(yīng)用開發(fā)指南藏澳,我們可以把它作為一個非常有力的參考,但是也千萬不要教條耀找。
這個原則不管對微服務(wù)模式還是其他軟件開發(fā)模式來說都非骋笛拢基本,所以被列為12原則的第一條蓄愁,該原則包括如下四個子原則:
使用代碼庫管理代碼双炕,一般是Git或者SVN,這個要求非常初級撮抓,相信本書的讀者都會遵守妇斤。
一份基準(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)代碼拆分為多個死相。
不允許多個應(yīng)用共享一份基準(zhǔn)代碼,如果確實(shí)需要共享咬像,那就把需要共享的基準(zhǔn)代碼的穩(wěn)定版本發(fā)布為類庫算撮,然后通過依賴管理策略進(jìn)行加載。
同一應(yīng)用的多份部署可以使用同一份基準(zhǔn)代碼的不同版本县昂,但是不可以使用不同的基準(zhǔn)代碼肮柜,類似原則2,使用不同基準(zhǔn)代碼的應(yīng)用不應(yīng)被視為同一應(yīng)用倒彰。
違反子原則2和3素挽,會給代碼管理和編譯工作帶來麻煩:
如果一份基準(zhǔn)代碼可以編譯出多個應(yīng)用,那么這幾個應(yīng)用之間必然會存在不清晰的依賴關(guān)系狸驳,隨著時間的推移,這種依賴關(guān)系會變得愈加混亂缩赛,以至于修改一個應(yīng)用的代碼耙箍,會給其他應(yīng)用帶來不可預(yù)知的影響。這樣的基準(zhǔn)代碼顯然極難維護(hù)酥馍。
基準(zhǔn)代碼的劃分和應(yīng)用的劃分非常類似辩昆,也是系統(tǒng)邊界的一種體現(xiàn),如果一個應(yīng)用需要從多份基準(zhǔn)代碼編譯旨袒,那么多數(shù)情況下這個應(yīng)用的內(nèi)外部邊界問題會存在問題汁针。如果邊界不存在問題,那么請將多份基準(zhǔn)代碼合并為一份砚尽,而不是維持這種古怪的設(shè)計(jì)施无。
如果多個應(yīng)用不是通過類庫,而是直接共享一份基準(zhǔn)代碼必孤,那么這份被共享的基準(zhǔn)代碼會很難維護(hù)猾骡,對這份基準(zhǔn)代碼的修改必須謹(jǐn)慎考慮對多個應(yīng)用可能造成的影響瑞躺。正確的方式是將這份基準(zhǔn)代碼發(fā)布為類庫,保持清晰的邊界和接口約定供其它應(yīng)用調(diào)用兴想。
原則2:顯式聲明依賴關(guān)系
這里的依賴指所有的依賴幢哨,包括應(yīng)用程序本身的類庫和操作系統(tǒng)層面被應(yīng)用程序所使用的庫文件或者其他二進(jìn)制文件,都必須進(jìn)行顯示聲明嫂便,并對版本做出明確的指定捞镰。不要假定運(yùn)行環(huán)境中已經(jīng)存在應(yīng)用所需要的任何依賴項(xiàng),而是應(yīng)該假定什么都沒有(即使有也很可能不是應(yīng)用所需要的版本)毙替。
如果使用容器方式進(jìn)行部署岸售,容器的基礎(chǔ)鏡像很可能是Busybox或者Alpine之類的迷你Linux,那么就幾乎等于什么都沒有蔚龙。如果使用微服務(wù)模式冰评,理想情況下,微服務(wù)之間的依賴關(guān)系也應(yīng)該進(jìn)行顯示聲明木羹。
以前我們往往不會對依賴做如此嚴(yán)格的管理甲雅,因?yàn)閼?yīng)用不會有太大規(guī)模的部署,也不會進(jìn)行頻繁的發(fā)布坑填,如果發(fā)現(xiàn)運(yùn)行環(huán)境里缺少某些依賴抛人,那么就臨時手工處理一下,也不是什么太大的問題脐瑰。如今在微服務(wù)模式下妖枚,應(yīng)用的部署規(guī)模大、發(fā)布頻率高苍在,還記得前文所說的“不可變服務(wù)器”嗎绝页?如果這個時候還是使用原有的模式,則會帶來混亂寂恬。
聲明依賴的方式有很多续誉,常見的方式是使用依賴清單,根據(jù)依賴清單進(jìn)行依賴檢查初肉,同時使用依賴隔離工具保證應(yīng)用不會調(diào)用系統(tǒng)中存在但是依賴清單中未聲明的依賴項(xiàng)酷鸦;另一種方式是使用容器技術(shù),將應(yīng)用和依賴打包為容器鏡像牙咏,依賴的聲明和隔離就一并解決了臼隔。
首先需要明確的是,這里的配置指與部署環(huán)境有關(guān)的配置妄壶,例如:
數(shù)據(jù)庫摔握、消息代理、緩存系統(tǒng)等后端服務(wù)的連接配置和位置信息丁寄,如URL盒发、用戶名例嘱、密碼等。
第三方服務(wù)的證書宁舰。
每份部署獨(dú)有的配置拼卵,例如:域名、連接數(shù)蛮艰、與部署目標(biāo)環(huán)境資源規(guī)模有關(guān)的JVM參數(shù)等腋腮。
所有部署中都相同的信息,例如原則2里講到的依賴信息壤蚜,不在本原則所討論的范圍內(nèi)即寡。一些雖然在不同的部署中有所差異、但是和業(yè)務(wù)相關(guān)的信息袜刷,例如資金結(jié)算的轉(zhuǎn)換比例聪富,也不屬于本原則所討論的配置。
我想大多數(shù)的開發(fā)者都知道如何通過使用配置文件實(shí)現(xiàn)配置和代碼的分離著蟹,但是這種方式仍然存在一些缺點(diǎn)墩蔓,例如:
配置文件容易被開發(fā)人員不小心提交到代碼庫中,造成密碼萧豆、證書等敏感信息泄露奸披。提交到代碼庫中的配置文件還容易被和應(yīng)用一起部署到目標(biāo)環(huán)境中,很可能會導(dǎo)致在目標(biāo)環(huán)境中應(yīng)用了錯誤的配置或者造成配置沖突涮雷。
配置文件會分散在不同的目錄中阵面,并且有不同的格式(配置文件的格式往往與開發(fā)語言和框架相關(guān)),這會給配置的統(tǒng)一管理造成困難洪鸭。
為了避免上述問題样刷,本原則要求將在環(huán)境中存儲配置。一種典型的方式是把配置存儲在環(huán)境變量中览爵,這會使配置和代碼徹底的分離置鼻,格式上也與開發(fā)語言和框架再無瓜葛,并且也不會被誤提交到代碼庫中拾枣。還可以使用Spring Cloud Config Server這類配置管理服務(wù)進(jìn)行配置推送,并將配置的歷史版本和變更原因也一起管理起來盒让。
原則4:把后端服務(wù)當(dāng)作附加資源
這里的后端服務(wù)指的是應(yīng)用運(yùn)行所依賴的各種服務(wù)梅肤,例如數(shù)據(jù)庫、消息代理邑茄、緩存系統(tǒng)等姨蝴,對于云原生應(yīng)用來說,往往還會有日志收集服務(wù)肺缕、對象存儲服務(wù)左医、以及各種通過API訪問的服務(wù)授帕;當(dāng)作附加資源指的是把這些服務(wù)作為外部的、通過網(wǎng)絡(luò)調(diào)用的資源浮梢。
該原則有如下幾層含義:
不要將這些服務(wù)放在應(yīng)用本地:云原生應(yīng)用要求應(yīng)用本身無狀態(tài)化跛十,那么狀態(tài)信息就應(yīng)該存儲在外部服務(wù)中(參見不可變服務(wù)器)。同時秕硝,微服務(wù)模式要求應(yīng)用責(zé)權(quán)單一以實(shí)現(xiàn)可靠性和擴(kuò)展性芥映,如果在應(yīng)用本地放置數(shù)據(jù)庫,那么微服務(wù)平臺將無法通過更換應(yīng)用的故障實(shí)例實(shí)現(xiàn)應(yīng)用的高可用性远豺,也無法通過自動化的橫向伸縮實(shí)現(xiàn)擴(kuò)展性奈偏,因?yàn)閼?yīng)用實(shí)例內(nèi)包含兩種性質(zhì)完全不同的軟件(應(yīng)用和數(shù)據(jù)庫),無法對兩者使用同一種方式進(jìn)行橫向擴(kuò)展躯护。另外惊来,如果將這些服務(wù)放在應(yīng)用本地,那么也無法通過充分利用云平臺提供的能力簡化運(yùn)維工作棺滞,例如裁蚁,如果在應(yīng)用本地放置數(shù)據(jù)庫,而不是使用云平臺提供的數(shù)據(jù)庫服務(wù)检眯,那么顯然無法利用數(shù)據(jù)庫服務(wù)提供的自動備份厘擂、安全、和高可用等特性锰瘸。
通過URL或者服務(wù)注冊/認(rèn)證中心訪問這些后端服務(wù):應(yīng)用應(yīng)該能夠在不進(jìn)行任何代碼修改的情況下刽严,在不同的目標(biāo)環(huán)境中進(jìn)行部署,應(yīng)用不應(yīng)該和后端服務(wù)的任何一種具體實(shí)現(xiàn)存在緊耦合關(guān)系避凝。
類似“顯式聲明依賴關(guān)系”原則舞萄,應(yīng)用最好也能夠?qū)ζ涫褂玫倪@些后端服務(wù)進(jìn)行顯示聲明,以方便云平臺對服務(wù)資源進(jìn)行自動綁定管削,在后端服務(wù)出現(xiàn)故障的時候倒脓,云平臺也能夠?qū)ζ溥M(jìn)行自動恢復(fù)。
原則5:嚴(yán)格分離構(gòu)建含思、發(fā)布和運(yùn)行
在本原則中崎弃,構(gòu)建、發(fā)布和運(yùn)行這三個概念可能和從前有所不同含潘,因此有必要首先對其進(jìn)行明確:
構(gòu)建指的是將應(yīng)用代碼轉(zhuǎn)化為執(zhí)行體的過程:構(gòu)建時會拉取特定版本的代碼和依賴項(xiàng)饲做,將其編譯為二進(jìn)制文件(針對編譯型語言),并和資源文件一起打包遏弱。
發(fā)布指的是將構(gòu)建的結(jié)果和部署所需的配置相結(jié)合盆均,并將其放置于運(yùn)行環(huán)境之中。
運(yùn)行指的是將發(fā)布的結(jié)果啟動為運(yùn)行環(huán)境中的一個或多個進(jìn)程漱逸。
本原則要求構(gòu)建泪姨、發(fā)布和運(yùn)行這三個步驟嚴(yán)格區(qū)分:
禁止直接修改運(yùn)行狀態(tài)的代碼或者對應(yīng)用進(jìn)行打補(bǔ)丁游沿,因?yàn)檫@些修改很難再同步回構(gòu)建步驟,這時運(yùn)行狀態(tài)的代碼就成為了“孤本”肮砾。同時诀黍,也不應(yīng)該在運(yùn)行期間修改應(yīng)用的配置,配置的修改應(yīng)該僅限于發(fā)布階段(參見不可變服務(wù)器)唇敞。
運(yùn)行這一步驟應(yīng)該非常簡單蔗草,僅限于啟動進(jìn)程,資源文件的關(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ā)布就不能修改。這么做的好處是:
每一份運(yùn)行狀態(tài)的代碼都可以在對應(yīng)的發(fā)布和構(gòu)建階段找到它的來源鞋屈,這是實(shí)現(xiàn)重新發(fā)布范咨、故障實(shí)例的自動替換、發(fā)布出錯后的版本回退等機(jī)制的基礎(chǔ)厂庇。
運(yùn)行步驟非常簡單渠啊,這樣在硬件重啟、實(shí)例故障和橫向擴(kuò)展等情況下权旷,應(yīng)用可以簡單和快速的實(shí)現(xiàn)重啟替蛉。
原則6:以一個或多個無狀態(tài)的進(jìn)程
運(yùn)行應(yīng)用
本原則要求應(yīng)用進(jìn)程的內(nèi)部不要保存狀態(tài)信息,任何狀態(tài)信息都應(yīng)該被保存在數(shù)據(jù)庫拄氯、緩存系統(tǒng)等外部服務(wù)中躲查。應(yīng)用實(shí)例之間的數(shù)據(jù)共享也要通過數(shù)據(jù)庫和緩存系統(tǒng)等外部服務(wù)進(jìn)行,直接的數(shù)據(jù)共享不但違反無狀態(tài)原則译柏,還引入了串行化的單點(diǎn)镣煮,這會為應(yīng)用的橫向擴(kuò)展帶來障礙。
在微服務(wù)模式下鄙麦,應(yīng)用不應(yīng)該在自身進(jìn)程內(nèi)部緩存數(shù)據(jù)以供將來的請求使用典唇,因?yàn)槲⒎?wù)模式以多實(shí)例方式運(yùn)行應(yīng)用,將來的請求多半會被路由到其他實(shí)例胯府,此時雖然可以使用粘滯會話將請求保持在同一個實(shí)例上介衔,但是無論是云原生應(yīng)用還是微服務(wù)模式都極力反對使用粘滯會話,原因如下:
很難對粘滯會話實(shí)現(xiàn)負(fù)載均衡盟劫,因?yàn)檎硿挼木庑圆粌H決定于負(fù)載均衡策略夜牡,還和會話本身的行為相關(guān)与纽,例如侣签,可能存在應(yīng)用某些實(shí)例上的會話已經(jīng)大量退出塘装,而另一些實(shí)例上的會話依然處于活動狀態(tài),此時這兩部分實(shí)例的負(fù)載處于不均衡狀態(tài)影所,而負(fù)載均衡器無法將活動會話轉(zhuǎn)移到空閑的應(yīng)用實(shí)例蹦肴。
啟動新的應(yīng)用實(shí)例不會立即提高應(yīng)用的整體處理能力,因?yàn)檫@些新實(shí)例只能承接新會話猴娩,舊的會話依舊粘滯在舊的應(yīng)用實(shí)例上阴幌。
-
應(yīng)用實(shí)例退出會導(dǎo)致會話丟失,所以在實(shí)例發(fā)生故障時卷中,即使云平臺可以對故障實(shí)例進(jìn)行自動替換矛双,也會導(dǎo)致用戶數(shù)據(jù)丟失。即使是對應(yīng)用實(shí)例進(jìn)行人工維護(hù)蟆豫,也需要在維護(hù)之前對該實(shí)例上的會話進(jìn)行轉(zhuǎn)移议忽,這往往意味著需要編寫復(fù)雜的業(yè)務(wù)代碼。
在傳統(tǒng)模式下十减,可以通過在雙機(jī)之間進(jìn)行會話復(fù)制來實(shí)現(xiàn)對用戶無感知的單機(jī)下線維護(hù)(雖然會付出處理能力減半的代價)栈幸,但是在微服務(wù)模式下,應(yīng)用的實(shí)例數(shù)量往往遠(yuǎn)不止兩個帮辟,在大量的實(shí)例之間進(jìn)行會話復(fù)制會使實(shí)例之間原本非常簡單的邏輯關(guān)系復(fù)雜化速址,此時將無法通過云平臺對其進(jìn)行無差別的自動化維護(hù)。另外由驹,在實(shí)例之間進(jìn)行會話復(fù)制也意味著實(shí)例之間存在著直接的數(shù)據(jù)共享芍锚,這會為應(yīng)用的橫向擴(kuò)展帶來障礙。
所以荔棉,粘滯會話是應(yīng)用實(shí)現(xiàn)可用性和擴(kuò)展性的重要障礙闹炉,使用粘滯會話顯然是種得不償失的選擇。更好的實(shí)現(xiàn)方式是將會話信息存儲在緩存服務(wù)中润樱。
原則7:通過端口綁定提供服務(wù)
服務(wù)端應(yīng)用通過網(wǎng)絡(luò)端口提供服務(wù)渣触,這點(diǎn)毋庸置疑,但是本原則還有如下兩個深層次的含義:
-
無論是云原生應(yīng)用還是微服務(wù)模式都要求應(yīng)用應(yīng)該完全自我包含壹若,而不是依賴于外部的應(yīng)用服務(wù)器嗅钻,端口綁定指的是應(yīng)用直接與端口綁定,而不是通過應(yīng)用服務(wù)器進(jìn)行端口綁定店展。
如果一定要使用應(yīng)用服務(wù)器养篓,那就使用嵌入式應(yīng)用服務(wù)器,無論是云原生應(yīng)用還是微服務(wù)模式都極力反對將多個應(yīng)用放置于同一個應(yīng)用服務(wù)器上運(yùn)行赂蕴,因?yàn)樵谶@種模式下柳弄,一個應(yīng)用出錯會對同一個應(yīng)用服務(wù)器上的其他應(yīng)用造成影響,也無法針對單一應(yīng)用做橫向擴(kuò)展。
端口綁定工作應(yīng)該由云平臺自動進(jìn)行碧注,云平臺在實(shí)現(xiàn)應(yīng)用到端口的綁定之外嚣伐,還需要實(shí)現(xiàn)內(nèi)部端口到外部端口的映射和外部端口到域名的映射。在應(yīng)用的整個生命周期內(nèi)萍丐,應(yīng)用實(shí)例會經(jīng)歷多次的重新部署轩端、重啟或者橫向擴(kuò)展,端口會發(fā)生變化逝变,但URL會保持不變基茵。
原則8:通過進(jìn)程模型進(jìn)行擴(kuò)展
與通過進(jìn)程模型進(jìn)行擴(kuò)展相反的方式是通過線程模型進(jìn)行擴(kuò)展,這是一種相對較為傳統(tǒng)的方式壳影,典型的例子是Java應(yīng)用拱层。當(dāng)我們啟動一個Java進(jìn)程的時候,通常會通過JVM參數(shù)為其設(shè)置各個內(nèi)存區(qū)域的容量上下限宴咧,同時還可能會在應(yīng)用層面為其設(shè)置一個或者多個線程池的容量上下限舱呻,當(dāng)外部負(fù)載變化時,進(jìn)程所占用的內(nèi)存容量和進(jìn)程內(nèi)部的線程數(shù)量可以在這些預(yù)先設(shè)置好的上下限之間進(jìn)行擴(kuò)展悠汽,這種方式也被稱為縱向擴(kuò)展或者垂直擴(kuò)展箱吕。
但是這種方式存在一些問題,首先柿冲,在進(jìn)程的內(nèi)存容量和線程數(shù)量提高時茬高,應(yīng)用的某些性能指標(biāo)可能不會得到同步提高,甚至可能會下降(這往往是因?yàn)槌绦驅(qū)δ承o法擴(kuò)展的資源進(jìn)行爭用所造成的)假抄,這種參差不齊的性能擴(kuò)展對外部負(fù)載提高的承接能力會很不理想怎栽,有時甚至?xí)m得其反;
其次宿饱,為了使進(jìn)程本身可以完成縱向擴(kuò)展熏瞄,還需要在虛擬機(jī)層面或者容器層面為其預(yù)留內(nèi)存資源和對應(yīng)的CPU資源,這會造成大量的資源浪費(fèi)(當(dāng)然谬以,也可以使虛擬機(jī)或者容器跟隨進(jìn)程一起進(jìn)行縱向擴(kuò)展强饮,這在技術(shù)上是可行的,但是會為虛擬機(jī)或者容器管理平臺的資源調(diào)度造成一些不必要的困難为黎,例如頻繁的虛擬機(jī)遷移或者容器重啟)邮丰。
所以,現(xiàn)在更為推崇使用“固定的”進(jìn)程(對前面Java應(yīng)用的例子來說铭乾,就是固定的內(nèi)存容量和線程池容量)剪廉,在外部負(fù)載提高時,啟動更多的進(jìn)程炕檩,在外部負(fù)載降低時斗蒋,停止一部分進(jìn)程,這種方式就是本原則所說的通過進(jìn)程模型進(jìn)行擴(kuò)展,有時候也被稱為橫向擴(kuò)展或者水平擴(kuò)展泉沾。
這種擴(kuò)展方式的好處是骤星,在進(jìn)程數(shù)量增加的時候,應(yīng)用的各種性能指標(biāo)會得到同步的提高爆哑,這種提高即使不是線性的,也會按照一種平滑和可預(yù)期的曲線展開舆吮,可以更為穩(wěn)定的應(yīng)對外部負(fù)載的變化揭朝。
云原生應(yīng)用和微服務(wù)模式極力推崇將通過進(jìn)程模型進(jìn)行擴(kuò)展作為唯一的擴(kuò)展方式,除了前文所述色冀,還有一個原因是進(jìn)程是云平臺可以操作的最小運(yùn)行單元(當(dāng)然潭袱,可以通過其他技術(shù)手段去操作線程,但是那不會成為云平臺的通用技術(shù)特性)锋恬,云平臺可以根據(jù)各個層面的監(jiān)控數(shù)據(jù)屯换,通過預(yù)設(shè)規(guī)則決定是否為應(yīng)用增加或者減少進(jìn)程,例如与学,當(dāng)前端的負(fù)載均衡器檢測到訪問某個后端應(yīng)用的并發(fā)用戶數(shù)超過某個閾值時彤悔,可以立即為這個后端應(yīng)用啟動更多的進(jìn)程,以承接更大的負(fù)載索守,同時還可以選擇是否對該應(yīng)用后端的數(shù)據(jù)庫進(jìn)行擴(kuò)展晕窑。
如果此時選擇對應(yīng)用進(jìn)行縱向擴(kuò)展,則云平臺既不知道應(yīng)用處理能力的變化卵佛,也無法對這種變化進(jìn)行預(yù)期管理杨赤,更無法使應(yīng)用的前后端對這種變化進(jìn)行聯(lián)動,即該應(yīng)用的擴(kuò)展行為脫離了云平臺的管理截汪。在微服務(wù)模式下疾牲,如果大量的進(jìn)程都采用縱向擴(kuò)展方式,則會為平臺的資源調(diào)度帶來極大的混亂衙解。
注3:該原則似乎更適合被稱為橫向擴(kuò)展原則阳柔,但是為了和12原則的原文保持一直,這里我們?nèi)匀粚⑵浞Q為“通過進(jìn)程模型進(jìn)行擴(kuò)展”蚓峦。
原則9:快速啟動和優(yōu)雅終止
可最大化健壯性
該原則要求應(yīng)用可以瞬間(理想情況下是數(shù)秒或者更短)啟動和停止盔沫,因?yàn)檫@將有利于應(yīng)用快速進(jìn)行橫向擴(kuò)展和變更或者故障后的重新部署,而這兩者都是程序健壯性的體現(xiàn)枫匾。
前文不止一次提到過應(yīng)用的快速啟動架诞,在理念章節(jié)的開頭,我們提到過平價的進(jìn)程生成對多道程序設(shè)計(jì)至關(guān)重要干茉,而微服務(wù)模式在某種程度上可以認(rèn)為是多道程序設(shè)計(jì)在Web領(lǐng)域和分布式系統(tǒng)下的進(jìn)一步擴(kuò)展谴忧,這里所說的平價進(jìn)程生成指的是操作系統(tǒng)的一種特性,是應(yīng)用快速啟動的基礎(chǔ),除此之外為了保證應(yīng)用可以在數(shù)秒內(nèi)完成啟動沾谓,還需要大量的優(yōu)化工作委造,需要開發(fā)人員掌握復(fù)雜的調(diào)優(yōu)技術(shù)與工具,有些工作必須在應(yīng)用的初始設(shè)計(jì)階段完成均驶,例如:如果應(yīng)用體積過大或者是引用了太多的庫文件昏兆,那么再多的后期優(yōu)化也無法將啟動時間降低到數(shù)秒以內(nèi)。
在“原則5:嚴(yán)格分離構(gòu)建妇穴、發(fā)布和運(yùn)行”中我們還提到爬虱,應(yīng)用的運(yùn)行步驟應(yīng)該非常簡單,這里的“簡單”也隱含著快速的意思腾它,目的是為了在硬件重啟跑筝、實(shí)例故障和橫向擴(kuò)展等情況下,應(yīng)用可以快速的實(shí)現(xiàn)重啟瞒滴。除此之外曲梗,“原則6:以一個或多個無狀態(tài)的進(jìn)程運(yùn)行應(yīng)用”也與應(yīng)用的快速啟動有關(guān),遵守?zé)o狀態(tài)原則妓忍,使用云平臺提供的緩存服務(wù)虏两,而不是在應(yīng)用內(nèi)部加載緩存,可以避免在應(yīng)用啟動期間進(jìn)行耗時的緩存預(yù)熱世剖。
比起應(yīng)用的快速啟動碘举,優(yōu)雅終止(Graceful Shutdown)需要考慮的問題會更為廣泛一些。優(yōu)雅終止需要盡可能降低應(yīng)用終止對用戶造成的不良影響(對于微服務(wù)應(yīng)用搁廓,用戶可能是人引颈,也可能是其他微服務(wù))。
對于短任務(wù)來說境蜕,這一般意味著拒絕所有新的請求蝙场,并將已經(jīng)接收的請求處理完畢后再終止;對于長任務(wù)來說粱年,這一般意味著應(yīng)用重啟后的客戶端重連和為任務(wù)設(shè)置斷點(diǎn)并在重啟后繼續(xù)執(zhí)行售滤。除此之外,優(yōu)雅終止還需要釋放所有被進(jìn)程鎖定的資源台诗,并對事務(wù)的完整性和操作的冪等性做出完備的考慮完箩。
最后,應(yīng)用還必須應(yīng)對突如其來的退出拉队,在硬件出現(xiàn)故障時或者進(jì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ā)人員和測試人員大量的精力以保證這些適配器和代碼確實(shí)可以按預(yù)期工作降盹,在應(yīng)用的整個開發(fā)周期中与柑,這將積累極大的額外工作量,是一種非常不必要的資源浪費(fèi)蓄坏。
近年來個人電腦的性能大幅提高价捧,開發(fā)人員一度得以在本地開發(fā)環(huán)境中運(yùn)行與生產(chǎn)環(huán)境中一致的軟件棧,而不是像曾經(jīng)那樣采用輕量的替代方案剑辫。但是隨著云原生應(yīng)用和微服務(wù)模式的流行,情況又發(fā)生了微妙的變化:開發(fā)微服務(wù)時需要依賴云平臺提供的基礎(chǔ)服務(wù)和其他微服務(wù)渠欺,越來越難以把這些服務(wù)完整的運(yùn)行在本地妹蔽,與此同時,完全的在線開發(fā)愈發(fā)成為一種趨勢挠将,那樣的話至少在軟件棧上開發(fā)環(huán)境和線上環(huán)境就真的沒有任何區(qū)別了胳岂。
在我編寫這段文字的時候,Red Hat公司剛好在洽購在線開發(fā)環(huán)境創(chuàng)業(yè)公司Codenvy用以充實(shí)他們的云平臺產(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)境夜郁,運(yùn)維人員只關(guān)心線上環(huán)境什燕,開發(fā)人員和運(yùn)維人員在工作上鮮有交集,這是開發(fā)環(huán)境和線上環(huán)境在人員上的巨大差異竞端。
對于前一個差異屎即,本原則要求更為密集和頻繁的向線上環(huán)境發(fā)布更新,要求建立機(jī)制以保障開發(fā)人員可以在數(shù)小時甚至數(shù)分鐘內(nèi)既可將更新發(fā)布到線上事富,這也正是本章理念部分中持續(xù)交付所提倡的技俐;對于后一個差異,本原則要求開發(fā)人員不能只關(guān)心開發(fā)環(huán)境中自己的代碼统台,更要密切關(guān)注代碼的部署過程和代碼在線上的運(yùn)行情況雕擂,這也正是DevOps所提倡的。
原則11:把日志當(dāng)作事件流
應(yīng)用程序應(yīng)該將其產(chǎn)生的事件以每個事件一行的格式按時間順序輸出贱勃,這點(diǎn)毋庸置疑捂刺,但是本原則想說的其實(shí)是:應(yīng)用程序不要自行管理日志文件谣拣。
以前我們習(xí)慣將應(yīng)用程序產(chǎn)生的事件分門別類的輸出到不同的日志文件,并為每個日志文件指定在本地文件系統(tǒng)上的存儲位置族展,為了避免單一日志文件過大森缠,還會為它們配置輪轉(zhuǎn)策略。
該原則極力反對上述做法仪缸,而是要求應(yīng)用程序?qū)⑷罩疽允录鞯姆绞捷敵龅綐?biāo)準(zhǔn)輸出STDOUT和標(biāo)準(zhǔn)錯誤輸出STDERR贵涵,然后由運(yùn)行環(huán)境捕獲這些事件流,并轉(zhuǎn)發(fā)到專門的日志處理服務(wù)進(jìn)行處理恰画。這樣做的原因是:
1. “原則6:以一個或多個無狀態(tài)的進(jìn)程運(yùn)行應(yīng)用”要求應(yīng)用程序無狀態(tài)宾茂,那么應(yīng)用程序就不應(yīng)該將日志文件這種價值信息存儲在本地文件系統(tǒng)上。當(dāng)然拴还,可以在本地運(yùn)行一個日志收集進(jìn)程讀取日志文件跨晴,并將其轉(zhuǎn)發(fā)到專門的日志處理服務(wù),以保證價值信息不被意外丟棄片林,但是這帶來了如下問題:
需要提供一種機(jī)制以保證日志收集進(jìn)程可靠運(yùn)行端盆。
需要通過配置文件告知日志收集進(jìn)程去哪里讀取日志文件。
需要在應(yīng)用程序所在的虛擬機(jī)或者容器上為日志收集進(jìn)程開放一個網(wǎng)絡(luò)端口以供其發(fā)送日志內(nèi)容费封,這不僅增加了網(wǎng)絡(luò)的復(fù)雜度焕妙,還給網(wǎng)絡(luò)安全帶來了隱患。
由此可見弓摘,直接將日志輸出到STDOUT和STDERR并由運(yùn)行環(huán)境對其進(jìn)行捕獲遠(yuǎn)比這種方案來的簡潔和可靠焚鹊。
2. 在存在專門的日志處理服務(wù)時,由應(yīng)用程序自行對日志進(jìn)行分類顯得死板和毫無必要韧献;只需將日志以事件流方式發(fā)送給日志處理服務(wù)末患,日志處理服務(wù)可以對這些日志按不同視角進(jìn)行靈活的分類,而不是受限于一種既定的分類規(guī)則锤窑。
3. “原則6:以一個或多個無狀態(tài)的進(jìn)程運(yùn)行應(yīng)用”中還提到“微服務(wù)模式以多實(shí)例方式運(yùn)行應(yīng)用阻塑,將來的請求多半會被路由到其他實(shí)例”,所以單個應(yīng)用實(shí)例的日志無法描述完整的業(yè)務(wù)操作果复,不具備業(yè)務(wù)層面的價值陈莽。必須將應(yīng)用所有實(shí)例的日志匯總到日志處理服務(wù),由日志處理服務(wù)按特定規(guī)則(如按用戶ID或者對象ID)對其進(jìn)行聚合虽抄,才能完整展現(xiàn)應(yīng)用在業(yè)務(wù)層面的操作過程走搁。
應(yīng)用在以多實(shí)例方式運(yùn)行時,應(yīng)用的單個實(shí)例可能會因?yàn)檐浻布收隙貑⒙蹩撸蛘弑粰M向擴(kuò)展機(jī)制創(chuàng)建和銷毀私植,所以必須將應(yīng)用所有實(shí)例的日志匯總,才能完整的描述應(yīng)用的運(yùn)行情況车酣。
這是12原則的最后一條曲稼,也是最晦澀的一條索绪。如果你在看過原文之后覺得哪里有些不大對,不必?fù)?dān)心贫悄,因?yàn)楹芏嗳说南敕ê湍阋粯尤鹎T诒竟?jié)的開頭曾提到有人批評12原有過于依賴Heroku自身特性的傾向,這些批評多半可能是本原則導(dǎo)致的窄坦。
事實(shí)上唤反,通過SSH接入線上環(huán)境并使用腳本語言執(zhí)行管理任務(wù)的做法已經(jīng)不再被提倡,無論是云原生應(yīng)用還是微服務(wù)模式都極力反對這種做法鸭津,原因可以參見“理念五:不可變服務(wù)器”和“理念六:提供聲明式接口”彤侍。另外還有一個原因顯而易見:你的應(yīng)用有數(shù)個或者數(shù)十個實(shí)例,那么應(yīng)該登錄到哪個實(shí)例中執(zhí)行管理任務(wù)呢逆趋?如果在管理任務(wù)執(zhí)行的過程中盏阶,所在實(shí)例因?yàn)檐浻布收现貑ⅲ蛘弑粰M向擴(kuò)展機(jī)制銷毀闻书,那又該怎么辦名斟?
正確的做法是,如果管理任務(wù)是修改應(yīng)用配置惠窄,那么應(yīng)該通過配置管理服務(wù)進(jìn)行操作蒸眠,參見“原則3:在環(huán)境中存儲配置”漾橙;如果管理任務(wù)是批處理任務(wù)杆融,例如數(shù)據(jù)的遷移、清洗或者檢查霜运,那么應(yīng)該通過云平臺的批處理機(jī)制進(jìn)行操作脾歇,大多數(shù)的云平臺都會提供這種機(jī)制,例如Kubernetes的Jobs淘捡。
本原則還提到“應(yīng)用的管理進(jìn)程應(yīng)該和應(yīng)用的常駐進(jìn)程運(yùn)行于同一環(huán)境藕各,并使用相同的代碼、版本和配置”焦除,這是一條比較有價值的建議激况,可以避免由于環(huán)境或代碼等不一致造成的一些潛藏問題。雖然現(xiàn)在不提倡通過SSH接入應(yīng)用常駐進(jìn)程所在的環(huán)境并執(zhí)行管理任務(wù)膘魄,但是如果你使用容器技術(shù)乌逐,那么很容易通過容器模板創(chuàng)建一個和應(yīng)用常駐進(jìn)程一致的運(yùn)行環(huán)境,并在其中執(zhí)行管理任務(wù)创葡。
關(guān)于作者:宋瀟男
Unix和分布式系統(tǒng)專家浙踢,網(wǎng)格時代的幸存者,在云計(jì)算行業(yè)有近十年的研發(fā)和市場工作經(jīng)驗(yàn),對操作系統(tǒng)、中間件和云平臺有深入研究和大型項(xiàng)目經(jīng)驗(yàn)经窖。曾在華為云計(jì)算部門負(fù)責(zé)產(chǎn)品規(guī)劃竭鞍、商業(yè)洞察蓝纲、以及EMEA地區(qū)的市場推廣與技術(shù)合作工作⊙扰鳎現(xiàn)任普元云計(jì)算架構(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光绕,長按二維碼關(guān)注
閱讀原文:https://mp.weixin.qq.com/s?__biz=MzI5MDEzMzg5Nw==&mid=2660395450&idx=1&sn=ca01797f9a999972cfa70168bf2ae522&chksm=f7424b5cc035c24a13b41c30ad37d8e7a74cc54aec03a2d0c82cab021fbc5db9a265bf71d048#rd