按語
Heroku是云應用平臺的先驅,從對外提供服務以來,他們已經有上百萬應用的托管和運營經驗。其創始人Adam Wiggins根據這些經驗,提出了十二要素應用宣言 。The Twelve-Factor App定義了一個優雅的互聯網應用在設計過程中,需要遵循的一些基本原則。
不過, The Twelve-Factor是在特定的時期,針對特定的平臺實踐所總結出來的。12元素依然璀璨,很多原則依然具有普適性;但是在應用全面遷移到云端的今天,Cloud Native時代的應用開發,需要有更多與時俱進的實踐新原則。The Twelve-Factor的哪些原則依然是適用的?哪些在實踐需要與時俱進?這些都是眾多Cloud Native的實踐者需要探求的答案。
新原則1:微服務應該以無狀態的方式運行
The Twelve-Factor指出一個應用在運行時,應該以一個或者多個無狀態的進程來運行。進程之間需要做到無狀態,并且無共享。任何需要持久化的數據,都應該寫入到后端服務中,譬如數據庫。這一原則,目的是避免進程和進程之間的耦合。讓每一個進程,能夠獨立于其他實體單獨構建和運行。
在很多在線系統中,會使用session(會話)來傳遞當前的狀態。當被調用方接收到session后,會將它保存下來,后續的請求,都會被路由到這個服務中。這時請求和請求之間,會有嚴格的調用順序。Cloud Native的實踐原則是需要避免這種情況。
在Cloud Native理念中,每一個功能實體實際上是以一個微服務的形式運行的。微服務是一個單獨的自治系統,它可以不依賴其他服務而單獨存在。所有的微服務,都應該是以一種無狀態的接口對外提供服務(譬如Restful API)。微服務自身不應該保存任何的狀態。如果微服務需要持續地保存數據,應該采用的做法是:調用服務化的存儲,將數據保存在另一個存儲服務里面。微服務之所以小塊頭能發揮大能量,正是源于無狀態的設計從而保證可以同時由多個實例提供服務,讓服務能力能夠自由的伸縮。
當然,在實際的實踐過程中,特別是針對大的系統,很難做到完全無狀態的拆分。對一個完整的微服務架構,會分為事務性服務和存儲型服務。通常說來,事務性服務需要做到無狀態設計;所有的持久化數據,可以通過存儲型服務來進行存儲和讀取。存儲型服務盡量使用云端提供的集中式的服務化存儲能力,譬如云端的對象存儲。
新原則2:應通過配置來唯一定義一個系統
The Twelve-Factor的第五條原則提到了應用需要嚴格區分構建、發布、運行這三個步驟:譬如直接修改處于運行狀態的代碼是非常不可取的做法,因為這些修改很難再同步回構建步驟。The Twelve-Factor的第五原則,很好的規范了一個應用從開發到運營的標準化的操作流程。但是第五原則,只是保證了應用源碼的唯一性。很多時候,一個系統的故障,并不僅僅是由源碼導致的,很大一部分故障,和配置以及環境的變化存在關系。所以The Twelve-Factor的第五原則在Cloud Native的理念下,需要與時俱進。
Cloud Native在實踐時,是通過Docker+微服務的方式去實現的。Docker實現了Build、Ship、Run的統一。任何系統,都是可以通過一些配置,唯一定義出來的。當這個系統定義出來以后,應該是能夠重構出來,不隨著時間、運行環境或依賴條件變化而變化。同時,這個系統不管處于開發階段、測試階段還是運營階段,其狀態都是唯一的。
譬如,在實踐過程中,可以通過Docker Compose的YAML文件唯一的定義一個容器間互聯的系統。這個系統的配置是唯一確定的,可以在任何的容器環境下去執行。會有一個唯一的倉庫(Docker Registry)來存儲鏡像。所有的鏡像,都是通過Dockerfile唯一定義的。當觸發了源碼的變更以后,這個系統的各個組件可以自動的去構建,然后更新到“倉庫”,在通過Docker Compose組裝起來。只要遵循了這套標準化的流程,系統不會區分開發環境,測試環境,運營環境。所有的東西,就只有一個環境;這個環境,是唯一的、可定義的。
在實踐這條原則時,需要遵循以下幾條規范:
需要有唯一的源碼庫,并保證任何的變更都是在源碼托管庫中進行
通過Dockerfile來構建鏡像而非通過運行中的容器來打包鏡像
通過配置來定義容器之間的連接關系而非手動的部署
新原則3:應該使用服務化存儲
The Twelve-Factor 有一條原則是“把后端服務(backing services)當作附加資源”,后端服務是指程序運行所需要的通過網絡調用的各種服務,如數據庫(MySQL,CouchDB)、消息/隊列系統(RabbitMQ,Beanstalkd)、SMTP 郵件發送服務(Postfix)以及緩存系統(Memcached)。這些都是作為“資源”來對待,目的是為了保證模塊之間的松耦合。
作為一個系統中最重要的角色,存儲模塊當然必不可少。存儲有很多種類:塊存儲、對象存儲、數據庫存儲、緩存。傳統上,我們接觸比較多的是塊存儲。在Cloud Native的理念下,一個系統中,所有的組成實體都可以服務化。有事務型的服務,也有存儲型服務。微服務之間是一種松散耦合的關系。服務和服務之間,是沒有任何耦合關系的,只有調用關系。當一個服務,需要存儲數據怎么辦?一種方案是把數據存在在服務內部,這樣勢必導致了服務存在狀態,從而限制了服務的橫向擴展的能力。前面一節,我們提到了一個重要原則是,事務型服務盡量保證是無狀態的。所以當一個事務型服務需要存儲數據時,應該是將數據存儲到另外一個存儲型服務中,通過存儲型服務提供的標準接口和協議讀取或者寫入數據。在云上我們通常使用的對象存儲,或者云端的數據庫,就是這種服務化的存儲方式。
使用服務化存儲,可以帶來三個方面的好處:
將存儲和邏輯處理分離,讓系統能夠自動化的重構出來
統一存儲,不需要單獨考慮服務內部存儲的容錯、備份等機制
做到事務型服務無狀態,便于橫向擴展
總結
The Twelve-Factor APP作為網絡應用開發的最佳實踐原則,的確具有強大的生命力。每一條原則都是應用開發的珠璣。但是技術總是不停的發展,在云的時代,所有的應用都會遷移到云端,完全基于云的架構設計和開發模式需要一套全新的理念去承載。這個時候,Cloud Native的思想應運而生。在Cloud Native時代,The Twelve-Factor 的核心思想依然璀璨,但是應該隨著這種新的理念去延伸、發展。文章提到的三條新原則,也正是The Twelve-Factor 的延伸。未來,隨著Cloud Native 思想在開發中使用越來越廣泛,一定會延伸出更多的實踐的原則。
資料附:Cloud Native時代12新要素
從一個代碼庫部署到多個環境——一個代碼庫,包括生產環境軟件包,確保了單一的信任源,從而保證了更少的配置錯誤和更強的容錯和復原能力。 依賴管理是聲明式的——云平臺根據這些聲明管理依賴,確保云應用所需的庫和服務。 配置信息保存在環境中——環境變量是一種清楚、容易理解和標準化的配置方法,特別適合用不同編程語言編寫的無狀態應用的使用。 將后臺服務視為附加的資源——將每一種資源都視為一種遠程的資源,應用因此具有容錯和復原能力,因為它一方面要求編碼時就要考慮資源不可用的情況,另外一方面也增強微服務方法的好處。 區分構建、發布和運行階段——Cloud Native應用的構建流程把大部分發布配置挪到開發階段,包括實際的代碼構建和運行應用所需的生產環境配置。 作為無狀態進程運行——盡量保持應用棧每一層的輕量級,保證Cloud Native基礎設施的速度和效率。 通過端口綁定對外暴露服務——Cloud Native應用的服務接口優先選擇 HTTP API 作為通用的集成框架。 通過添加無狀態進程實現橫向擴展——強調無狀態、無共享的設計,這意味著依賴底層平臺就能實現橫向擴展,不需要技術難度高的多線程編碼。 快速地啟動,優雅地關停——假設任何進程隨時都能啟動和關停。 開發、預發布和生產環境運行同樣的應用和依賴配置——由于強調自動化和在每個階段使用同一個云平臺,如果每個人用同樣的服務器配置,那么“應用在我這里是可以的”就意味著在其他人或者環境那里也是可以的。 日志輸出到標準輸出,方便日志聚合和事件響應——當日志是由云平臺而不是應用包含的庫處理時,日志處理機制必須保持簡單。 臨時任務作為短時進程運行——在Cloud Native中,管理任務也是一個進程,而不是特別的工具;同樣重要的是,管理任務的進程不應使用秘密的 API 或者內部機制。作者簡介
劉永峰,騰訊云高級產品經理,布道師,十余年的產品及研發管理經驗。國內最早一批從事可信計算研究的探索者。曾就職于中興通訊,負責流媒體Server后臺架構設計,Linux內核相關的研究。2011年起加入騰訊,一直從事公有云的產品設計,云計算市場趨勢分析和研究,云計算技術的推廣與普及。在技術和產品領域均具有豐富的行業經驗。目前主要關注Docker,微服務,Cloud Native, 企業云化趨勢等相關領域。