實施微服務應該具備哪些先決條件?
作者|Phil Cal?ado
編輯|薛命燈
2017 年 5 月,Phil Cal?ado 在布達佩斯 Craft 大會上呈現了有關微服務經濟的演講。他在演講中簡要討論了一系列實施微服務的先決條件,這些先決條件是每個組織在考慮大規模採用微服務架構之前應該要具備的。在這篇文章里,Phil 詳細地解釋了這些先決條件。
先決條件的重要性
當你決定採用微服務,你將經歷從單一應用到一個複雜系統的轉變過程。在這個系統里,你會遇到很多無法預測的行為,因為團隊和服務在持續地發生變化,它們被創建、被修改,然後被銷毀。系統的快速變更能力為你的組織帶來了巨大的好處,不過你需要確保你有一些安全護欄,否則你的交付會因為無窮無盡的變更而駐足不前。
這些護欄就是我們將要討論的先決條件。如果不具備這些先決條件,或者只具備了其中的一部分,仍然可以成功地應用一項新技術,不過如果能夠充分滿足這些條件,不僅可以增加成功的幾率,而且能夠減少遷移過程中的噪音和疑惑。
這裡列出的先決條件有點多,可能需要大量的投入,不過這要取決於你的組織的具體文化和基礎設施。不過這些前期的成本是有必要的。微服務架構不會比其他類型的架構簡單,你需要確保在採用微服務之前已經對投入所能帶來的回報進行過評估。
這麼多先決條件
確實,實施微服務有很多先決條件。我試著讓你們能夠輕鬆地理解它們,不過你們要知道,對於小規模的服務來說,它們不是必需的。除非你們有大量的服務,否則我不認為你們真的需要微服務。
對於這些先決條件,你可能還沒有成熟的想法或經驗。即使是像 DigitalOcean 和 SoundCloud 這樣成熟的公司,我們也只是從實現基本的條件開始。在開始的時候,我們經歷了大量的探索和拷貝黏貼式的工作。
對於這裡列出的每一項,你應該要有自己的想法,但不要被它們糾纏住了。你不一定要一下子給出長期的解決方案,你可以在這個過程中邊做邊學。在這個過程中,這個領域的技術也會更加成熟,它們當中有一些會變成最佳實踐。
另外一種途徑是忘記微服務,並專註於粒度更粗的服務架構上。更少的組件意味著更少的先決條件,隨著你的工程組織和平台的日益成熟,你可以不斷地減小每個服務的規模和範圍。
先決條件清單
在之前的一個演講中,我介紹了我們在 SoundCloud 採用微服務的經歷,我非常感謝 Martin Fowler 在總結微服務先決條件方面所做的工作。Martin 和他的 ThoughtWorkers 同事總結的先決條件清單如下。
快速配置:具備在短時間內配置好一台伺服器的能力。
基本的監控:生產環境的很多輕度耦合的服務在一起協作容易出現問題,而這些問題在測試環境難以被發現。所以我們需要一個有效的監控機制來快速地檢測這些問題。
快速部署:因為需要管理的服務太多,所以需要儘快地部署它們,不管是在測試環境還是在生產環境。
SoundCloud 在 Martin 發表他們的清單之前就已經開始了向微服務架構的遷移工作,不過我們得出的結論是相似的。在 DigitalOcean 實現大規模微服務的過程中,我更加肯定了這些先決條件存在的必要性。同時,我也發現了其他幾點,它們對於成功實施微服務來說也是至關重要的。
易於分配的存儲
易於訪問的外圍
認證和授權
標準化的 RPC
所以,我的關於微服務先決條件的完整清單如下(按照優先順序從上到下)。
計算資源的快速分配
基本的監控
快速部署
易於分配的存儲
易於訪問的外圍
認證和授權
標準化的 RPC
1、計算資源的快速分配
Martin 說:
你應該具備在幾個小時內搭建一個新伺服器的能力。在雲計算平台上,這是很基本的行為,不過它也不一定非要藉助雲服務。你可以通過自動化來實現快速配置——剛開始可以不用完全自動化,但在進入微服務架構之後需要實現完全的自動化。
Martin 在他的話里使用了伺服器這個詞,但在現今的 IT 環境,既可以使用真實的伺服器,也可以使用虛擬機、容器、函數,或這些組件的組合。這也是為什麼我使用了「計算資源」這個詞,它是指任何可以為你提供 CPU 和內存來運行代碼的資源。
十多年前,我們需要將應用程序部署到應用伺服器上。這個層多路復用了單獨的計算單元,多個應用程序或服務可以同時運行在伺服器上面,而且這種部署架構沿用了多年。那個時候,每秒鐘處理幾百個請求就可以稱得上是「互聯網規模」了。企業可以最大化對硬體的使用,多個不同的服務可以運行在同一個硬體上,有時候甚至多個公司共享一個應用伺服器,在上面運行多租戶服務。
隨著硬體的發展,計算資源成本在下降,本地數據中心和雲端都可以提供這些計算資源。這樣,對應用伺服器的需求就被弱化了。儘管應用伺服器為應用程序提供了拆箱即用的服務(比如安全、服務發現、管理面板等),但這些伺服器太過笨重了。另外,隨著流量的增長,我們從垂直伸縮轉向了水平伸縮,而這些伺服器在這方面並未能提供很好的支持。
基於上述的各種原因,我們才有了現今的部署架構,服務實例和計算資源之間是一對一的關係。
這種一對一的關係直接影響到了微服務架構。儘管針對微服務還沒有最終的定義,不過當人們提及微服務時,他們一定是指很多小型的服務。按照上面的這種部署架構,我們就會有很多計算單元。這就要求計算單元的配置必須是自動化、快速和彈性的,以便滿足微服務的需求。
我在 2015 年加入 DigitalOcean 的時候,我和同事們花了很多時間思考我們的內部系統——雲控制面板。那個時候,控制面板由三個單體應用組成,它們運行在 Chef Data Bag 的虛擬機上。但我們很快發現這種方式不僅複雜,而且容易出錯,無法伸縮。我們需要在轉向微服務之前改進我們的配置能力。
我們決定使用容器和 Kubernetes 作為新的計算平台,我們在 2016 年的頭六個月把所有的新服務部署到了新平台上,同時把遺留的單體也遷移到上面。我們因此可以在對架構做出變更的同時發布新產品。事實上,我們的監控和告警組件就是在新平台上開發出來的。
2、基本監控
Martin 說:
生產環境的很多輕度耦合的服務在一起協作容易出現問題,而這些問題在測試環境難以被發現。所以我們需要一個有效的監控機制來快速地檢測這些問題。最起碼要能夠檢測技術問題(計數器錯誤、服務可用性等),不過如果能夠同時檢測出業務問題(比如訂單數的下降)就更好了。如果突然出現了問題,你要確保能夠快速回滾……
微服務是一個複雜的系統,我們可以控制和預測的東西很有限。造成這種混亂主要是因為持續的變更,微服務幾乎每天要部署好幾次。
不過這些問題不是微服務獨有的。事實上,十多年前,John Allspaw 等人在 Flickr 和 Etsy 就構建了一些工具用於解決這類問題,他們當時的架構還是單體。當時,Allspaw 在文檔中記錄了一些有關如何應付快速變更的方法:
換句話說,MTTR 比 MTBF 更重要。我並不是說故障是可接受的。我只是假定故障是一定會發生的,所以把時間和精力花在處理故障上要比花在如何避免故障上更有意義。我很同意 Hammond 的看法,他說:「如果你認為你可以避免故障,那麼你就不會想著怎麼提升處理故障的能力。」故障平均時間(MTBF)是指系統故障之間的時間間隔。修復平均時間(MTTR)是指修復一個故障所花掉的平均時間。簡單地說,MTBF 會告訴你系統有多經常發生故障,而 MTTR 則告訴你故障在被檢測到之後修復得有多快。在一個持續變化的系統里,你無法控制 MTBF,所以最好把時間和精力投入到如何改進 MTTR 上。
在你想辦法改進 MTTR 的時候,你會發現,加快從故障中恢復的速度所能帶來的好處越來越少。用於從故障中恢復的時間並非事故管理的唯一步驟,有時候,它佔用的時間並不是最多的。我發現,事故管理當中最令人感到痛苦的是用於檢測故障的時間(MTTD)。這個指標反映了故障從發生到被檢測出來的時間。
你會因此認為需要把時間和精力投入在問題檢測上。這個問題在各種架構中都存在,只不過微服務架構所面臨的挑戰更為嚴峻。在單體架構里,你會很容易地找出問題所在:你只要找出是哪個類或者哪個函數出現了問題。使用 NewRelic 這個工具就可以從代碼級別找出問題的根源。
這些工具也可以用在微服務架構里,但前提是你要事先找到是哪個服務出現了問題。因為在微服務架構里,很多服務一起合作處理一個請求,你要確保你能夠比較每一個服務,找出發生異常的那個,而不會被環境問題干擾。
所以,你要對整個微服務生態系統的所有服務進行基本的監控,而不僅僅是監控部分核心的服務。
在 SoundCloud,我們使用標準化的儀錶盤和告警系統來監控微服務。我們保證每個服務都會暴露出一些常用的度量指標,然後使用它們來構建儀錶盤。我們先是使用了 Graphite,後來改用 Prometheus,這樣我們就可以比較不同服務的度量指標。
我們根據儀錶盤來降低 MTTD,不過很快我們就發現這樣子還遠遠不夠。幾十個小型的團隊有數百個服務需要部署,你需要將出現的問題與各個團隊關聯起來,包括新代碼的部署和基礎設施的變更。我們構建了一個小型的服務,它能夠返回由工程師和自動化工具所做出的所有變更的清單。我們修改了我們的部署工具,確保每一個變更都能夠報告給這個服務,哪怕只是添加了一兩個用於伸縮容量的服務實例。
在我們的事故檢測流程里,檢查最近發生的變更是首要的事情。
3、快速部署
Martin 說:
因為需要管理的服務太多,所以需要儘快地部署它們,不管是在測試環境還是在生產環境。這通常需要用到部署管道,有了部署管道,部署過程在幾個小時內就可以完成。在早期允許部分的人工干預,但在後面需要進行完全自動化。
Martin 建議讓這一項先決條件緊跟前面一項,因為從事故中快速恢復涉及到新代碼或配置變更的部署,而且部署需要儘快完成。
我完全同意他的看法,不過我要補充一點。對於單體來說,一個笨重的非自動化部署流程是沒有問題的。即使單次部署的成本很高,比如繁瑣的步驟和出錯的幾率,但這些付出是能夠得到相應的回報的,因為每一次部署都包含了大量的變更,這些變更影響到很多功能,而且是由多個團隊共同開發的。
而對於微服務來說,事情卻是這樣的:一個功能的一個變更可能需要部署多個服務。你需要部署多個服務,但每個服務的部署成本都不高,風險也不大。正如 Martin 所說的,管道剛好適用於這種場景。
我們在 SoundCloud 時為這個先決條件掙扎了好一陣子。我們使用 Capistrano 和 shell 腳本來部署單體應用,流程很長,而且需要人工參與。文件里包含了複雜的部署指令,而且需要考慮到許多情況。
剛開始,我們決定使用任意一門編程語言來開發我們的服務,這樣的話運行時團隊也會感到很適應。這樣確實有一些好處,不過它的不足在於我們無法預測該如何部署應用。我們有 JAR 包,有 Ruby 腳本,有 Go 二進位包……於是我們決定對它們進行標準化。
每個服務需要在代碼的根目錄提供一個 Makefile 文件。這個文件里必須包含一個 build 目標,就算它只是要調用其他的構建系統,比如 SBT 或 Rake。
在 make 命令執行完畢之後,部署工具會創建一個 SquashFS 文件,裡面包含了目錄、代碼、資源文件和二進位包。
代碼里應該要包含一個 Heroku 風格的 procfile 文件,這個文件描述了如何執行每一個流程。在部署好 SquashFS 鏡像後,需要更新流程的版本,就像在 Heroku 里所做的那樣。
通過這種方式,我們可以對服務進行伸縮,不過整個過程需要太多的人工參與,因此帶來了較高的風險。更糟糕的是,這些底層的原語無法直接支持其他的部署技術,比如藍綠部署、canary 伺服器或 AB 測試。為了解決這些問題,很多團隊基於這些工具添加了自己開發的膠水代碼。由於這些腳本屬於輔助項目,所以代碼質量參差不齊。我們生產環境的一些大事故就是因為這些腳本引起的。
當我們的服務規模從幾十個發展到上百個時,我們開發了更好的部署工具。最大的不同在於,我們把部署從工程師的開發機移動到部署管道上。高度的自動化帶來了更快的構建流程,正好滿足了每天部署數百個服務的需求。
4、易於分配的存儲
大部分從單體轉向微服務的公司都有一個大型的資料庫伺服器,經過多年沉澱,這個資料庫被調教得相當得當,它有多個複本,並且與其他系統很好地集成在一起,如搜索引擎和數據分析引擎。
不過,使用這個單體資料庫仍然面臨著一些挑戰,這些挑戰主要與 schema 的更新有關。在改變或移除數據表或數據表的欄位時,要手動檢查是否有其他代碼依賴了舊 schema。多年之後,幾乎所有的資料庫重構模式都在這個單體資料庫上得到應用,並催生了很多內部工具用於處理各種常見的問題。
然而,對於微服務開發團隊來說,仍然需要重用公共的 schema。「新增一兩個表、欄位或試圖不是什麼大問題」,但類似的事情重複成千上萬次,無異於在一點一點把工程師推向死亡的邊緣。複雜的變更管理會拖你的後腿,你忙於協調服務之間的數據耦合,而實際上,這些服務之間本應該對彼此一無所知。
這種趨勢在很多正在向微服務遷移的企業里蔓延,因為這些企業傾向於在配置和部署上做大量的投入,卻忽視了為團隊提供一個合理的存儲系統。搭建一個 MySQL 伺服器只要幾分鐘時間,但要將它放到生產環境里,仍然要注意許多事項,如複本、備份、安全、調優、監控等。而你的工程師可能在這方面沒有什麼經驗。
在雲原生架構里,你可以將這些運維任務外包給其他提供了資料庫即服務的廠商。但在 DigitalOcean,我們做不到這點。我們曾經有一個中期的計劃,就是為內部提供一個快速簡單的 MySQL 資料庫分配方案,但向微服務遷移的優先順序就要因此做出一些讓步。後來,我們並沒有從頭去構建一套複雜的工具,而是把時間花在整理和文檔化相關的資料和腳本上,各個團隊可以根據這些資料自己去搭建生產級別的 MySQL 伺服器。
5、易於訪問的外圍
企業的第一個微服務通常是由一個人或一個小型的團隊開發的,他們尋求的解決方案是為了解決他們在開發過程中遇到的問題。由於這個微服務涉及的領域太小了,開發人員可以在開發環境和測試環境很快地完成開發任務。但是在推向生產環境時,他們就會面臨一些問題:如何向本地網路之外的用戶暴露這些服務呢?
這個與資料庫的問題類似,主要問題在於沒有人事先考慮過這個問題。單體被暴露給公網的用戶,它已經具備了各種功能特性,可以保護好你的服務,比如速率限定、日誌、功能標記、安全、監控、告警、請求路由等。
對於微服務來說,常見的做法是通過不同的機器名或特定的路徑來發布它們。
這種方式要求客戶端(移動客戶端或單頁應用)協調發送給多個端點的請求。如果只有一兩個服務,這種方式是可行的,但隨著服務數量的增長,這種方式逐漸暴露出它的缺點。
不僅僅客戶端的代碼會變得複雜,向公網發布服務本身就不是個輕鬆的活兒。如果你重視你的用戶,那麼就要確保能夠處理好互聯網系統所有可能出現的問題,從惡意用戶的破壞到非預期的流量高峰。如果每一個服務都要考慮這些問題,那麼總體的成本就可見一斑。
這也是我們在 SoundCloud 開發第一批微服務時碰到的問題。面對數百萬用戶,我們很快意識到,我們需要對向外暴露的服務做一些限制。我們考慮過將所有的服務置於網關之後,但我們的過程團隊很小,同時有許多功能需要交付,我們需要的是快速直接的解決方案。
於是,我們使用了一個單體作為我們的網關。
請求首先會達到單體網關,網關會調用後端的服務。這種方式有一定的用處,不過也存在一些問題。首先,它讓網關和服務之間建立了耦合關係,一旦服務發生變更,網關也要做出改動,然後重新部署。另外,我們的網關運行在舊版本的 Rails 上,因為 Rails 對並發支持得不是很好,發送給新服務的請求處理幾乎是串列的。隨著服務數量的增長,請求的轉發時間也隨之變長。
後來我們採用了 BFF 模式,同時引入了一種更好的網關。
在 DigitalOcean 的時候,我們遵循了相同的模式,不過因為我們有 3 個單體,所以更早地使用了網關。
6、認證和授權
在微服務即將進入生產環境時,我們需要面臨另一個非常重要的問題:微服務如何知道是誰在發送請求以及他們有哪些許可權?最簡答的辦法就是要求每個請求里包含用戶識別信息,然後將其與後端的認證和授權系統或資料庫進行比對。
如果只有一兩個服務問題不大,但如果有很多的服務,就會給授權系統帶來很大的負擔。
SoundCloud 的單體網關已經將用戶的許可權信息緩存在內存里。我們修改了客戶端的代碼,在 HTTP 請求頭部加上這些信息。
在採用了外圍網關方案之後,我們讓網關向認證服務發送請求,把用戶的 URN 和地理位置信息以及 OAuth 信息轉發到下游的服務——在音樂行業,你所能訪問到的資源不僅取決於你是誰,也取決於你所在的國家。
7、標準化 RPC
最後一點是標準化 RPC,這是無可爭議的,不過仍然很重要。微服務生態系統里的組件之間需要大量的協作,而且它們的行為很難預料。你要確保它們之間能夠進行良好的交互,也就是說,它們需要能夠理解它們之間發送的消息,也需要理解它們之間的協議。
在 SoundCloud,我們最開始使用 HTTP 和 JSON,但只使用 HTTP 和 JSON 並不能滿足所有的需求。在 HTTP 和 JSON 之外,還有很多問題需要解決,比如如何發送認證信息,如何進行分頁,如何進行跟蹤,選擇何種 RPC 架構風格,如何處理故障等。基於文本的協議也給我們帶來了嚴重的性能問題,於是,數據密集型的團隊就轉向使用 Thrift。
對於採用了重度分布式架構的公司來說,我會建議他們將 gRPC 作為內部 RPC 的標準。除此之外,對於消息的序列化,比如將消息發送到 Kafka 上,你應該使用 protobuf,這樣一來,消息的發送端和獲取端都可以使用相同的序列化協議。
gRPC 和 protobuf 本身並不會提供你所需要的一切。在 SoundCloud 和 DigitalOcean 的時候,我們的一個團隊專門圍繞 RPC 構建了一套工具。最近,我們開始對 service mesh 感興趣,它是「一個特定的基礎設施層,讓服務之間的通信更加安全、快捷和可靠」。因為長時間地使用 Finagle,我會更喜歡 linkerd。不過,我們總會有其他更多的選擇。
http://philcalcado.com/2017/06/11/calcados_microservices_prerequisites.html
今日薦文
點擊展開全文


※Go 語言狂人許式偉:編程的意義就是讓世界變得有趣一些
※矽谷人工智慧技術、內容時代的技術實戰,值得關注的QCon上海2017
※快速學習 BAT 中高級工程師的 13 大必備技能
TAG:InfoQ |
※文學閱讀應該具備哪些條件
※分手應該做好哪些準備呢?
※裝備應該先做什麼?
※女性孕前應該做哪些工作?這幾件事你準備好了嗎?
※產後媽媽需要哪些營養物質的補充?6種必備你應該先了解
※選擇實習應該避開哪些坑
※孩子成長發育階段應該補充哪些營養?應該吃些什麼?
※去日本旅行應該準備哪些裝備?
※婚姻中的夫妻應該具備哪些素質?
※合格的痛點文案,應該具備哪些要素?
※孕婦應該禁用哪些化妝品呢?
※發現血尿,您應該注意哪些情況?
※家裝應該選擇板式傢具還是實木傢具?讓你不再為選擇傢具煩惱!
※出車禍了第一件事情應該幹嘛?究竟應該怎麼處理?
※女人要想結婚,首先應該具備這三個條件!
※施展:規劃未來?我們應該先做這三件事……
※多囊患者應該怎樣備孕呢?
※骨質疏鬆患者應該做哪些檢查?
※確診無精——應該做哪些檢查?
※求職者必看!關於簡歷那些事兒,這些你應該要知道