微服務實戰(四):微服務化之無狀態化與容器化

原文連接:微服務化之無狀態化與容器化(來源:劉超的通俗雲計算)

 

1、爲何要作無狀態化和容器化

不少應用拆分紅微服務,是爲了承載高併發,每每一個進程扛不住這麼大的量,於是須要拆分紅多組進程,每組進程承載特定的工做,根據併發的壓力用多個副本公共承擔流量。

將一個進程變成多組進程,每組進程多個副本,須要程序的修改支撐這種分佈式的架構,若是架構不支持,僅僅在資源層建立多個副本是解決不了問題的。

不少人說,支撐雙十一是靠堆機器,誰不會?真正經歷過的會以爲,可以靠堆機器堆出來的,都不是問題,怕的是機器堆上去了,由於架構的問題,併發量仍然上不去。

阻礙單體架構變爲分佈式架構的關鍵點就在於狀態的處理。若是狀態所有保存在本地,不管是本地的內存,仍是本地的硬盤,都會給架構的橫向擴展帶來瓶頸。

狀態分爲分發、處理、存儲幾個過程,若是對於一個用戶的全部的信息都保存在一個進程中,則從分發階段,就必須將這個用戶分發到這個進程,不然沒法對這個用戶進行處理,然而當一個進程壓力很大的時候,根本沒法擴容,新啓動的進程根本沒法處理那些保存在原來進程的用戶的數據,不能分擔壓力。

因此要講整個架構分紅兩個部分,無狀態部分和有狀態部分,而業務邏輯的部分每每做爲無狀態的部分,而將狀態保存在有狀態的中間件中,如緩存、數據庫、對象存儲、大數據平臺、消息隊列等。

這樣無狀態的部分能夠很容易的橫向擴展,在用戶分發的時候,能夠很容易分發到新的進程進行處理,而狀態保存到後端。然後端的中間件是有狀態的,這些中間件設計之初,就考慮了擴容的時候,狀態的遷移,複製,同步等機制,不用業務層關心。數據庫

1.jpg


如圖所示,將架構分爲兩層,無狀態和有狀態。

容器和微服務是雙胞胎,由於微服務會將單體應用拆分紅不少小的應用,於是運維和持續集成會工做量變大,而容器技術能很好的解決這個問題。然而在微服務化以前,建議先進行容器化,在容器化以前,建議先無狀態化,當整個流程容器化了,之後的微服務拆分纔會水到渠成。後端

2、無狀態化的幾個要點

前面說對於任何狀態,須要考慮它的分發、處理、存儲。緩存

2.jpg


對於數據的存儲,主要包含幾類數據:網絡

  • 會話數據等,主要保存在內存中。
  • 結構化數據,主要是業務邏輯相關
  • 文件圖片數據,比較大,每每經過CDN下發
  • 非結構化數據,例如文本、評論等


若是這些數據都保存在本地,和業務邏輯耦合在一塊兒,就須要在數據分發的時候,將同一個用戶分到同一個進程,這樣就會影響架構的橫向擴展。數據結構

3.jpg


對於保存在內存裏的數據,例如Session,能夠放在外部統一的緩存中。架構

4.jpg


對於業務相關的數據,則應該保存在統一的數據庫中,若是性能扛不住,能夠進行讀寫分離,如文章《微服務化的數據庫設計與讀寫分離》。

若是性能仍是抗住不,則可使用分佈式數據庫。併發

5.jpg


對於文件,照片之類的數據,應該存放在統一的對象存儲裏面,經過CDN進行預加載,如文章《微服務的接入層設計與動靜資源隔離》。

對於非結構化數據,能夠存在在統一的搜索引擎裏面,例如ElasticSearch。

若是全部的數據都放在外部的統一存儲上,則應用就成了僅僅包含業務邏輯的無狀態應用,能夠進行平滑的橫向擴展。

而全部的外部統一存儲,不管是緩存、數據庫、對象存儲、搜索引擎、都有自身的分佈式橫向擴展機制。負載均衡

6.jpg


在實行了無狀態化以後,就能夠將有狀態的集羣集中到一塊兒,進行跨機房的部署,實現跨機房的高可用性。而無狀態的部分能夠經過Dubbo自動發現,當進程掛掉的時候,自動重啓,自動修復,也能夠進行多機房的部署。less

3、冪等的接口設計

可是還有一個遺留的問題,就是已經分發,正在處理,可是還沒有存儲的數據,確定會在內存中有一些,在進程重啓的時候,數據仍是會丟一些的,那這部分數據怎麼辦呢?

這部分就須要經過重試進行解決,當本次調用過程當中失敗以後,前序的進程會進行重試,例如Dubbo就有重試機制。既然重試,就須要接口是冪等的,也即同一次交易,調用兩次轉帳1元,不能最終轉走2元。

接口分爲查詢、插入、更新、刪除等操做。

對於查詢接口來說,自己就是冪等的,不用作特殊的判斷。

對於插入接口來說,若是每個數據都有惟一的主鍵,也能保證插入的惟一性,一旦不惟一,則會報錯。

對於更新操做來說,則比較複雜,分幾種狀況。

一種狀況是同一個接口,先後調用屢次的冪等性。另外一種狀況是同一個接口,併發環境下調用屢次的正確性。

爲了保持冪等性,每每要有一個冪等表,經過傳入冪等參數匹配冪等表中ID的方式,保證每一個操做只被執行一次,並且在實行最終一致性的時候,能夠經過不斷重試,保證最終接口調用的成功。

對於併發條件下,誰先調用,誰後調用,須要經過分佈式鎖如Redis,ZooKeeper等來實現同一個時刻只有一個請求被執行,如何保證屢次執行結果仍然一致呢?則每每須要經過狀態機,每一個狀態只流轉一次。還有就是樂觀鎖,也即分佈式的CAS操做,將狀態的判斷、更新整合在一條語句中,能夠保證狀態流轉的原子性。樂觀鎖並不保證更新必定成功,須要有對應的機制來應對更新失敗。運維

4、容器的技術原理

7.jpg


無狀態化以後,實行容器化就十分順暢了,容器的不可改變基礎設施,以及容器基於容器平臺的掛掉自動重啓,自動修復,都由於無狀態順暢無比。

關鍵技術一:Dockerfile

例以下面的Dockerfile。

8.png


爲何必定要用Dockerfile,而不建議經過保存鏡像的方式來生成鏡像呢?

這樣才能實現環境配置和環境部署代碼化 ,將Dockerfile維護在Git裏面,有版本控制,而且經過自動化的build的過程來生成鏡像,而鏡像中就是環境的配置和環境的部署,要修改環境應先經過Git上面修改Dockerfile的方式進行,這就是IaC。

關鍵技術二:容器鏡像

經過Dockerfile能夠生成容器鏡像,容器的鏡像是分層保存,對於Dockerfile中的每個語句,生成一層容器鏡像,如此疊加,每一層都有UUID。

容器鏡像能夠打一個版本號,放入統一的鏡像倉庫。

9.jpg

 

關鍵技術三:容器運行時

10.jpg


容器運行時,是將容器鏡像之上加一層可寫入層,爲容器運行時所看到的文件系統。

容器運行時使用了兩種隔離的技術。

一種是看起來是隔離的技術,稱爲namespace,也即每一個namespace中的應用看到的是不一樣的IP地址、用戶空間、程號等。

11.jpg


另外一種是用起來是隔離的技術,稱爲CGroup,也即明明整臺機器有不少的CPU、內存,而一個應用只能用其中的一部分。

CGroup

12.jpg

 

5、容器化的本質和容器化最佳實踐

不少人會將容器當成虛擬機來用,這是很是不正確的,並且容器所作的事情虛擬機都能作到。

若是部署的是一個傳統的應用,這個應用啓動速度慢,進程數量少,基本不更新,那麼虛擬機徹底可以知足需求。

  • 應用啓動慢:應用啓動15分鐘,容器自己秒級,虛擬機不少平臺能優化到十幾秒,二者幾乎看不出差異。
  • 內存佔用大:動不動32G,64G內存,一臺機器跑不了幾個。
  • 基本不更新:半年更新一次,虛擬機鏡像照樣可以升級和回滾。
  • 應用有狀態:停機會丟數據,若是不知道丟了啥,就算秒級啓動有啥用,照樣恢復不了,並且還有可能由於丟數據,在沒有修復的狀況下,盲目重啓帶來數據混亂。
  • 進程數量少:兩三個進程相互配置一下,不用服務發現,配置不麻煩。


若是是一個傳統應用,根本沒有必要花費精去容器化,由於白花了力氣,享受不到好處。

13.png


什麼狀況下,才應該考慮作一些改變呢?

傳統業務忽然被互聯網業務衝擊了,應用總是變,三天兩頭要更新,並且流量增大了,原來支付系統是取錢刷卡的,如今要互聯網支付了,流量擴大了N倍。

沒辦法,一個字:拆!

拆開了,每一個子模塊獨自變化,少相互影響。

拆開了,原來一個進程扛流量,如今多個進程一塊兒扛。

因此稱爲微服務。

微服務場景下,進程多,更新快,因而出現100個進程,天天一個鏡像。

容器樂了,每一個容器鏡像小,沒啥問題,虛擬機哭了,由於虛擬機每一個鏡像太大了。

因此微服務場景下,能夠開始考慮用容器了。

14.jpg


虛擬機怒了,老子不用容器了,微服務拆分以後,用Ansible自動部署是同樣的。

這樣說從技術角度來說沒有任何問題。

然而問題是從組織角度出現的。

通常的公司,開發會比運維多的多,開發寫完代碼就不用管了,環境的部署徹底是運維負責,運維爲了自動化,寫Ansible腳原本解決問題。

然而這麼多進程,又拆又合併的,更新這麼快,配置老是變,Ansible腳本也要常改,天天都上線,不得累死運維。

因此這如此大的工做量狀況下,運維很容易出錯,哪怕經過自動化腳本。

這個時候,容器就能夠做爲一個很是好的工具運用起來。

除了容器從技術角度,可以使得大部分的內部配置能夠放在鏡像裏面以外,更重要的是從流程角度,將環境配置這件事情,往前推了,推到了開發這裏,要求開發完畢以後,就須要考慮環境部署的問題,而不能當甩手掌櫃。

這樣作的好處就是,雖然進程多,配置變化多,更新頻繁,可是對於某個模塊的開發團隊來說,這個量是很小的,由於5-10我的專門維護這個模塊的配置和更新,不容易出錯。

若是這些工做量全交給少數的運維團隊,不但信息傳遞會使得環境配置不一致,部署量會大很是多。

容器是一個很是好的工具,就是讓每一個開發僅僅多作5%的工做,就可以節約運維200%的工做,而且不容易出錯。

然而原本原來運維該作的事情開發作了,開發的老大願意麼?開發的老大會投訴運維的老大麼?

這就不是技術問題了,其實這就是DevOps,DevOps不是不區分開發和運維,而是公司從組織到流程,可以打通,看如何合做,邊界如何劃分,對系統的穩定性更有好處。

因此微服務,DevOps,容器是相輔相成,不可分割的。

不是微服務,根本不須要容器,虛擬機就能搞定,不須要DevOps,一年部署一次,開發和運維溝通再慢都能搞定。

因此,容器的本質是基於鏡像的跨環境遷移。

鏡像是容器的根本性發明,是封裝和運行的標準,其餘什麼Namespace、CGroup,早就有了。這是技術方面。

在流程方面,鏡像是DevOps的良好工具。

容器是爲了跨環境遷移的,第一種遷移的場景是開發,測試,生產環境之間的遷移。若是不須要遷移,或者遷移不頻繁,虛擬機鏡像也行,可是老是要遷移,帶着幾百G的虛擬機鏡像,太大了。

第二種遷移的場景是跨雲遷移,跨公有云,跨Region,跨兩個OpenStack的虛擬機遷移都是很是麻煩,甚至不可能的,由於公有云不提供虛擬機鏡像的下載和上傳功能,並且虛擬機鏡像太大了,一傳傳一天。

15.jpg


因此如圖爲將容器融入持續集成的過程當中,造成DevOps的流程。

經過這一章,再加上第一章《微服務化的基石——持續集成》就構成了微服務,DevOps,容器化三位一體的統一。

16.jpg


對於容器鏡像,咱們應該充分利用容器鏡像分層的優點,將容器鏡像分層構建,在最裏面的OS和系統工具層,由運維來構建,中間層的JDK和運行環境,由核心開發人員構建,而最外層的Dockerfile就會很是簡單,只要將jar或者war放到指定位置就能夠了。

這樣能夠下降Dockerfile和容器化的門檻,促進DevOps的進度。

6、容器平臺的最佳實踐

容器化好了,應該交給容器平臺進行管理,從而實現對於容器的自動化管理和編排。

17.png


例如一個應用包含四個服務A、B、C、D,她們相互引用,相互依賴,若是使用了容器平臺,則服務之間的服務發現就能夠經過服務名進行了。例如A服務調用B服務,不須要知道B服務的IP地址,只須要在配置文件裏面寫入B服務服務名就能夠了。若是中間的節點宕機了,容器平臺會自動將上面的服務在另外的機器上啓動起來。容器啓動以後,容器的IP地址就變了,可是不用擔憂,容器平臺會自動將服務名B和新的IP地址映射好,A服務並沒有感知。這個過程叫作自修復和自發現。若是服務B遭遇了性能瓶頸,三個B服務才能支撐一個A服務,也不須要特殊配置,只須要將服務B的數量設置爲3,A仍是隻須要訪問服務B,容器平臺會自動選擇其中一個進行訪問,這個過程稱爲彈性擴展和負載均衡。

當容器平臺規模不是很大的時候,Docker Swarm Mode仍是比較好用的:

  • 集羣的維護不須要ZooKeeper,不須要Etcd,本身內置
  • 命令行和Docker同樣的,用起來順手
  • 服務發現和DNS是內置的
  • Docker Overlay網絡是內置的


總之Docker幫你料理好了一切,你不用太關心細節,很容易就可以將集羣運行起來。

並且能夠經過Docker命令,像在一臺機器上使用容器同樣使用集羣上的容器,能夠隨時將容器當虛擬機來使用,這樣對於中等規模集羣,以及運維人員仍是比較友好的。

固然內置的太多了也有缺點,就是很差定製化,很差Debug,很差干預。當你發現有一部分性能不行的時候,你須要改整個代碼,所有從新編譯,當社區更新了,合併分支是很頭疼的事情。當出現了問題的時候,因爲Manager大包大攬幹了不少活,不知道哪一步出錯了,反正就是沒有返回,停在那裏,若是重啓整個Manager,影響面又很大。

18.jpg


當規模比較大,應用比較複雜的時候,則推薦Kubernetes。

Kubernetes模塊劃分得更細,模塊比較多,並且模塊之間徹底的鬆耦合,能夠很是方便地進行定製化。

19.jpg

並且Kubernetes的數據結構的設計層次比較細,很是符合微服務的設計思想。例如從容器->Pods->Deployment->Service,原本簡單運行一個容器,被封裝爲這麼多的層次,每次層有本身的做用,每一層均可以拆分和組合,這樣帶來一個很大的缺點,就是學習門檻高,爲了簡單運行一個容器,須要先學習一大堆的概念和編排規則。可是當須要部署的業務愈來愈複雜時,場景愈來愈多時,你會發現Kubernetes這種細粒度設計的優雅,使得你可以根據本身的須要靈活的組合,而不會由於某個組件被封裝好了,從而致使很難定製。例如對於Service來說,除了提供內部服務之間的發現和相互訪問外,還靈活設計了headless service,這使得不少遊戲須要有狀態的保持長鏈接有了很好的方式,另外訪問外部服務時,例如數據庫、緩存、headless service至關於一個DNS,使得配置外部服務簡單不少。不少配置複雜的大型應用,更復雜的不在於服務之間的相互配置,能夠有Spring Cloud或者Dubbo去解決,複雜的反而是外部服務的配置,不一樣的環境依賴不一樣的外部應用,External Name這個提供和很好的機制。包括統一的監控cAdvisor,統一的配置ConfigMap,都是構建一個微服務所必須的。然而Kubernetes當前也有一個瓶頸——集羣規模還不是多麼大,官方說法是幾千個節點,因此超大規模的集羣,仍是須要有很強的IT能力進行定製化。可是對於中等規模的集羣也足夠了。並且Kubernetes社區的熱度,可使得使用開源Kubernetes的公司可以很快地找到幫助,等待到新功能的開發和Bug的解決。

相關文章
相關標籤/搜索