構建高性能微服務架構的實踐

隨着移動互聯網時代的興起,提供高性能、高可用性、高擴展性的服務已經不僅僅是大公司的專利,而逐漸成爲所有互聯網+公司的標配需求,本問會介紹如何利用多年的互聯網架構經驗進行架構改進、微服務化、性能調優。

傳統架構之痛



當前的時代稱爲互聯網的時代,互聯網應用的特點往往是,新型的應用迅速出現顛覆舊的商業模式,一旦商業模式稍有起色便會有大量的廠商蜂擁而至,使得藍海變成紅海,經過短時間的殘酷競爭,熱度往往持續較短時間後,大量廠商退出市場,僅僅前二名到三名能夠存活下來,最終這一波浪潮被下一波取代。我們回想視頻網站,團購網站,社交網站,微博,打車,直播等,全都呈現這種模式。


所以互聯網市場只有一招,天下武功,唯快不破。


當一種商業模式出現的時候,爲了迅速切入市場,佔領商業獻祭,快速驗證商業模式,往往軟件的設計會採取傳統的單體架構。



傳統的單體架構往往分三層,最下面一層是數據庫,中間是應用程序層,所有的商業邏輯都會在這一層,最上面是頁面。


如果商業模式比較成功,則應用會添加新的功能,一種方式是全部添加到原有的商業邏輯中,另一種方式是開發一個全新的三層結構來支撐新的功能。



由於是單體結構,一方面應用層裏面的功能越來越多,如圖中一個功能變爲三個功能,將來可能三十個功能,另一方面很多代碼和邏輯都不能複用,如圖中功能2,每個應用都有,這種功能常見的有認證模塊,消息的編碼和解碼模塊等。


這種單體結構會帶來三方面靈活性比較差。


第一,時間靈活性:應用快速迭代,縮短客戶需求到產品上線的時間。


互聯網應用的需求隨時改變,因而應用開發的迭代速度要求會比較快,傳統的軟件可能半年或者一年發佈一個功能,而互聯網應用則可能每週都發布新的功能。而且互聯網產品會時常搞活動,比如雙十一,每次活動不可能提前很長時間策劃,從而給開發充分的產品週期。


然而單體結構的應用如果有了30個模塊,每個模塊由兩到三個人負責,則修改的成本會非常的大,從開發人員看來,整個架構牽一髮動全身,每次修改必須要做好良好的前期設計,並且讓整個團隊評審,如果新的需求要改多個模塊,則代碼的管理和合並就成爲很大的問題。


而且無論測試,聯調,上線,擴展,縮減,升級,回滾都需要重新搭建環境,需要配置軟件,需要進行迴歸測試,運維人員需要反覆的部署環境,而且無法保證環境的一致性,任何一個環境配置的小問題,都有可能導致軟件使用有問題。


第二,空間靈活性:應用彈性伸縮,應對業務量突然增長後較短時間恢復。


互聯網應用往往是針對終端用戶的,終端用戶的行爲往往不如企業用戶那樣容易預測,終端用戶可能因爲促銷,過節等因素導致訪問量的迅速的增長,當訪問遭遇峯值的時候,我們希望應用可以快速擴展。



然而對於單體架構,應用擴展的過程如萬丈高樓平地起,一層一層慢慢蓋。



如果部署在物理機上面,則還需要採購新的物理設備,如果有虛擬化平臺,則需要申請新的虛擬機,並且配置好網絡和存儲。然而僅僅一個空的虛擬機是沒有用的,上面什麼環境都沒有,接下來需要安裝應用環境,比如Tomcat, Apache等,然後就是將應用,頁面配置到應用環境中,還沒有結束,新啓動的是一個獨立的系統,還需要將這個系統加入到當前的系統中,才能一起承擔訪問量,例如加入到負載均衡器的配置裏面。這樣每部署一套都需要從頭來一遍,實在是運維人員的噩夢。


第三,管理靈活性:易部署,易遷移,服務發現,依賴管理,自動修復,負載均衡。


現在很多互聯網應用都需要多地,多機房部署,有時候會從一個機房遷移到另一個機房,如果每次變動都如上面一樣從底層到頂層都做一遍,成本比較大,時間比較長。



當一個應用依賴於另一個應用,被依賴的應該宕機之後,修復需要手動進行,從底層到頂層配置一遍,而且修復好的系統往往IP地址也變了,則依賴於此應用的所有應用都需要修改配置。


微服務化之路


要解決上面所述的三個問題,對應的有三個步驟。


第一步,去狀態化,從而實現程序的可擴展。


單體架構的程序往往很多數據是保存在內存裏面的,或者是本地文件系統的,例如用戶訪問的session數據,例如用戶上傳的照片。所謂的去狀態化,就是使得應用程序僅僅運行商業邏輯,而將數據的保存全部交給外部的存儲服務。內存裏面的數據可以放在緩存redis裏面,結構化數據放在統一的數據庫服務裏面,文件存放在對象存儲裏面。這樣應用程序就變成了一個只有商業邏輯的應用,可以隨時擴展。



這裏面有一個問題就是應用程序的狀態外置化了,放在統一的緩存,數據庫,對象存儲裏面了,可是應用程序宕機了是沒有問題了,再啓動一個就可以,如果緩存,數據庫,對象存儲宕機了,數據不也是沒有了麼?


其實主流的開源的緩存Redis,數據庫MySQL,對象存儲swift等,他們設計的時候就是考慮了高可用和容災的情況的,所以數據存儲的工作就應該讓專業的模塊來做這件事情,而應用程序應該關注在商業邏輯的實現,從而加速開發的速度。當然這些存儲模塊的維護則是另外的專業人士在做的,這部分人士是緩存的專家,數據庫的專家,對象存儲的專家,不需要懂商業邏輯。


第二步,容器化,可編排。


與傳統IaaS架構不同,容器提供的不僅僅是基礎資源,而是將操作系統,應用運行環境以及代碼打包成一個不可修改的鏡像進行交付,容器的機制可以保證無論在哪裏,什麼時候運行,都能保持環境的一致性,也就是Docker所宣稱的“Build, Ship, and Run Any App, Anywhere(一次構建,隨處運行)”。


容器特別適合部署無狀態的服務,上一步的無狀態化,給容器化奠定了良好的基礎。


一個服務往往會包含多個組件,因而會封裝成爲多個容器,容器之間會有相互的依賴,相互的調用,Kubernetes可以實現服務的編排、自發現、自修復,使得複雜服務的部署變成了一次API調用。



如圖所示,Kubernetes管理了相互依賴的四個服務,全部部署在容器裏面,分別運行在不同的機器上面。服務之間的調用通過服務名稱進行,而非固定IP進行,而服務名稱Kubernetes會管理起來。



當一臺服務器宕機的時候,服務B和服務C會被自動調度到另外兩臺機器上,由於服務是無狀態的,所以沒有問題。然而服務A和服務D如何再找到服務B和服務C能,這兩個服務的物理主機變了,很可能IP也變了。其實是沒有問題的,Kubernates會自動將服務名和對應的IP地址關聯起來,服務之間只要配置的是服務名,而非IP地址,就依然能夠相互訪問。


有了容器和Kubernates這兩個工具後,解決了管理複雜性的問題,但是需要專門的團隊和技術力量,去玩轉Kubernates.


第三步,DevOps,可迭代。


從前面的一張圖中,Dev和Ops,開發和運維之間隔着長長的流程,導致迭代速度很慢。DevOps就是可以加快迭代速度的一種方法。


然而如何讓開發直接進入到運維流程中呢?容器的鏡像不可改變性提供了方案。



Docker可以保證容器中的運行環境,業務代碼無論在哪個環境都是一致的,唯一不同的是不同環境的不同配置,可以通過環境變量注入的方式設置。有了這個模式,開發人員可以從很早就使用容器鏡像的方式進行開發,並且以容器鏡像的方式交付給測試,測試使用同樣的鏡像得到同樣的環境進行測試用例的執行,當決定發佈的時候,也確定真正到了生產環境的時候,同測試環境是一樣的。這樣避免了環境不斷重複的部署過程。


容器鏡像可以手動維護和交付,但是也可以藉助CICD持續集成的工具,來監控代碼庫的更改,當有程序員提交代碼的時候,會觸發一個hook,這個hook會調用CI工具,告知他代碼已經有更新了,可以根據最新的代碼打成最新的鏡像,CI工具根據配置好的Dockerfile,將代碼打包成鏡像,上傳到鏡像庫,每次打鏡像都應該有新的版本,而不應該總使用latest。


鏡像打好了以後,接下來CD的工具會將鏡像部署到測試環境,測試人員可以就這個新的測試環境進行一輪測試,如果測試成功,則可以告知線上管理人員,可以更新新的版本。


線上管理人員在恰當的時間,使用編排工具,將容器鏡像的版本改爲最新的版本,從而生產環境也就更新了。如果發現生產環境有問題,新的版本有Bug,沒有問題,只要將鏡像改爲上個版本的鏡像即可,可以保證原來那個能用的版本,所有的配置和原來一樣,從而功能也一樣,實現了升級和回滾功能。


當然這套持續集成的工具和流程,需要開發人員和開發流程進行改進,纔可以順利使用。


構建高性能微服務架構



前面的三板斧,去狀態化、容器化、持續集成,分別解決了空間靈活性,管理靈活性,時間靈活性。但是需要招聘一個DBA,Redis的專家,持續集成的專家,容器的專家,Kubernetes的專家,對於一個創業公司來講,這些專家往往比較難招聘到,而且與核心的業務邏輯沒有關係。


下面就介紹一下幫助企業構建高性能微服務架構背後的黑科技。

第一,高性能的IaaS平臺。

第二,MySQL內核開發能力和獨立分支,保證主從切換時數據零丟失。


如果一個MySQL數據可以滿足需求的情況下,主從同步複製的方式,可以保證主從之間數據的一致性,在使用開源MySQL進行主從複製的時候,雖然主從切換可以配置,但是無法保證數據完全不丟失。這對需要強事務性的業務來講,是個很大的問題。


如果一個MySQL數據庫不足以滿足需求的情況下,就需要分庫分表了,這個就需要分佈式數據庫來處理這個事情。

第三,容器的優化。

第四,編排層Kubernetes的優化。

從上面的敘述可以看出,要構建高性能的微服務架構,既需要基礎架構層面的性能調優,也需要服務編排層面的調度優化,也不能缺少應用層面的微服務化,是一個端到端的工作。



如圖所示,綠色的部分是基於多年的互聯網經驗,在各個層面進行了優化後,推出的組件,作爲一個創業公司,不需要招聘一個IaaS平臺管理的團隊,數據庫DBA的團隊,分佈式存儲的團隊,容器和服務編排的團隊,網絡調優的團隊,而僅僅只需要聚焦企業核心的業務層面就可以了,如圖中紅色的部分。這樣才能將主要的力量集中在產品快速上線,搶佔新一輪互聯網+風口。


企業只需要逐步做到以下三步,就可以實現微服務和快速交付。

1. 去狀態化:將內存數據寫入緩存,將持久化數據寫入數據庫,將文件寫入對象存儲。

2. 容器化:可彈性伸縮,自我修復,動態遷移。

3. 微服務化:可快速迭代,持續集成。


QA環節

Q1:如何確定每個服務的邊界?


A1:其實從實踐角度來講,服務的拆分是在開發流程過程中逐漸體會,優化出來的,沒有特別硬性的標準。雖然說是每個服務都僅僅包含一個功能,但是功能也有大有小,如果當前的系統狀態是一個很大的系統,貿然拆成很多很散的部分也是不現實的。一般實踐的方法是,當你發現每次發佈一個版本的時候,需要過多的代碼和合並,就是需要拆分模塊的時候了。當你發現更新線上模塊的發佈影響面比較大,老是開會需要加入很多部門的時候,也是需要拆分模塊的時候了。所以微服務化不是一個一蹴而就的事情,需要不斷的循環進行。


Q2:服務之間如何發現彼此?是否採用統一的協議?如果一個服務無法與其他服務通信會怎樣?


A2:服務之間的發現有很多種方式,例如在使用容器之前,Java應用程序之間的服務發現往往通過zookeeper,etcd等組件進行。在容器平臺,服務發現往往通過DNS的方式,組件之間不是通過IP而是通過域名進行訪問,同一個服務的多個副本共享同一個域名,也可以進行負載均衡。當一個服務宕機的時候,容器平臺會將服務重啓,如果IP更換,則會將域名和IP的映射關係更新,所以服務還是會被訪問到。如果一個服務無法訪問另一個服務,往往是網絡的問題,如果使用平的大二層網絡,這種問題比較不容易出現,因爲服務之間的訪問和使用普通的服務器之間的訪問時一樣的。當然如果使用了NAT等方式進行訪問,可能有的應用是不支持跨NAT的互相訪問的,會造成不通的情況,則建議改變容器啓動的網絡模式。


Q3:服務可能還要與並非自己團隊創建的系統通信,例如:數據庫、緩存、消息隊列、郵件交付系統等。能不能直接將這些外部系統封裝爲平臺上的服務?


A3:容器內部訪問外部的網絡是沒有任何問題的,如果將數據庫等外部系統全部部署在和服務同一個租戶網絡裏面,使用內網訪問也是沒有任何問題的。這些外部系統都可以通過容器化的方式放到容器平臺上,當然例如數據庫等有狀態的服務,應該基於有狀態的容器,也即容器重啓數據不丟失。當然已經將數據庫,緩存封裝成高可用的PaaS服務了,只要調用接口使用就可以,不需要自己搭建和維護。


Q4:可以讓多個服務共享同一個系統嗎?


A4:多個服務可以共同調用同一個系統,其實在微服務中,還是比較建議同一個功能在整個系統中僅僅存在一份,例如認證系統,例如消息系統,都是其他的服務共享的。這個問題的另一種理解是多個服務是否要部署在同一套容器平臺上?答案是可以的,容器本來就是提供資源隔離的,如果是私有云,共享同一套容器平臺,甚至共享同一個物理機器都沒有問題,如果是公有云,則需要做比較好的租戶隔離,除了認證的隔離,具體到每一個節點上,也需要租戶的隔離,不同的租戶不要運行在同一個節點上,還有網絡的隔離,不同的租戶應該在不同的私有網絡裏面。


Q5:怎麼做容器的防攻擊的? 


A5:容器是做資源隔離的,但是並沒有做內核的隔離,所以安全問題是被人經常提起的。所謂的容器的防攻擊,一個是從外網到內網的攻擊,這些通過防火牆,DDoS是都能夠解決的,第二種攻擊的方式就是在內部攻擊,由於租戶之間是網絡隔離的,並且在虛擬交換機上,可以啓用一些防治IP,MAC欺騙等措施,防止租戶網絡之間的攻擊,第三種方式更加直接,就是進入容器內部,對於宿主機或者同一個宿主機上的其他容器進行攻擊,在這方面,可以通過限制容器內部用戶的權限的方式進行限制,公有模式下不要給高級權限,另外對於內核方面的攻擊,還是建議不同的租戶不要共享宿主機。



Q6:在Kubernetes環境中是否有比較好的解決方案從部署層面,使有依賴條件的A和B服務保持相同的發佈版本?(藍綠髮布?)而出問題時能使兩個服務都回滾到之前的版本(A服務和B服務不在相同的Pod中)?


A6:A服務和B服務其實是相互依賴的兩個服務,服務之間的依賴,以及同時的發佈,同時的回滾可能需要自己來維護,兩個服務所依賴的容器的版本號都不一定相同。相對依賴比較複雜的系統之間,可能需要有依賴和通知系統的工具配合來實行


Q7:有用Kubernetes一鍵創建一個Hadoop集羣的案例嗎?各個容器實例分佈在多個虛擬機上?


A7:容器是可以部署hadoop集羣的,這個時候,容器就是有狀態的了,因爲hadoop的HDFS需要保存數據在本地盤,如果使用遠程的網絡雲盤,由於hadoop運行map-reduce計算的時候,會造成本地的I/O比較大,因而不建議使用遠程雲盤。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章