[雲原生微服務架構](十)微服務架構的基礎知識

程式設計師的貓發表於2022-09-14

今天我要和你分享的內容是微服務架構的基礎知識。

單體服務架構有哪些問題
單體服務,相信大家都有所瞭解,在我們最初接觸 Web 開發,寫一些網站或者 App 的時候,大多使用的是單體服務這種後端技術架構。比如在 Web 2.0 時期,最常見的部落格系統 WordPress,就是一個著名的單體服務。因為它所有的功能模組都在一個程式碼倉庫,並且部署在同一個程式內,是一個典型的單體架構。

事實上,在不需要快速迭代的業務上,單體服務也可以執行得很好,比如一個小的團隊維護一個變更速度沒那麼快的專案,就像 WordPress 和早期的入口網站。但面對如今移動網際網路業務的快速迭代,單體服務就顯得力不從心了,一個很大的問題就是,大量複雜的業務冗雜在一個單體應用中,造成了人員溝通成本和釋出迭代成本的急劇上升。

為了讓你更好地理解從單體服務轉向微服務的原因,下面我來具體說說單體服務的問題。

  1. 人員協作問題
    所有人員都在一個程式碼倉庫開發,當數量超過 10 人時,協作問題就開始凸顯了。比如一個新成員,需要在一個龐大的程式碼倉庫中,找到自己負責的模組部分,假如他在改動自己負責的部分的時候,程式碼組織並不是很好,就很容易涉及其他人寫的程式碼,這個時候就產生了不必要的溝通成本。即便溝通順利,也容易引起單體應用中其他部分的故障。

  2. 部署問題
    除了協作中的問題,還會產生上線部署效率的問題。當不同人員開發的不同模組,同時需要上線時,如果分別將分支程式碼合併到主幹程式碼,再按照流程上線,就會產生先後順序的等待時間;如果合併到一起上線,線上功能一旦出問題,就需要花費大量時間排查是哪個模組出現的問題。

  3. 架構演進問題
    當我們應用中的某個模組,遇到了效能瓶頸,我們希望以另外一種語言重新編寫此模組,這個時候單體應用完全無法演進。如果我們要替換語言,就得重寫整個應用,花費的成本可想而知。

  4. 程式穩定性問題
    在單體應用中,某個模組出現 Bug 時,比如因為疏忽造成了 OOM (程式記憶體不足),導致程式 panic,這會導致整個網站或者 App 不可用。一個模組的問題,導致了整個應用掛掉,這在生產環境中是無法接受的。

隨著網際網路業務的發展,整體業務越來越複雜,以上問題越來越明顯,單體服務已經無法應對如此複雜的業務場景了。網際網路行業也從架構上逐步開始了服務拆分,進入了 SOA (Service-Oriented Architecture 面向服務的架構)時代。

在 SOA 時代,比如淘寶網,採用了一種 ESB 匯流排的技術,就是所有服務的通訊需要透過一個集中式的匯流排服務(可以理解為集中式閘道器),這大大降低了服務的通訊效率並且增加了單點風險。但是隨著業務進一步擴大, ESB 匯流排的弊端逐步凸顯,比如每次服務變更節點都需要人工操作,這大大降低了網際網路業務的迭代效率。

微服務和 SOA 一脈相承,而微服務則不再強調傳統 SOA 架構裡面比較重的 ESB 企業服務匯流排。

微服務有哪些優勢
微服務是一種開發軟體的架構和組織方法,其中軟體由透過明確定義的 API 進行通訊的小型獨立服務組成,這些服務由各個小型獨立團隊負責。微服務架構使應用程式更易於擴充套件、更快開發,從而加速創新並縮短新功能的上市時間。與單體應用相比,微服務能夠更好地滿足網際網路時代業務快速變化的需要。

接下來我們看一看微服務相對於單體服務都有哪些優勢。

  1. 敏捷開發
    一個小的團隊,甚至一個人,負責一個或者多個服務,當有新的需求出現,可以快速對這個小型服務做出修改並上線。但是當維護一個龐大的程式碼倉庫構成的單體服務時,即使是一個簡單的需求、只修改了一行程式碼,開發人員也會因為這行程式碼可能會影響整個應用,而降低部署的頻率,從而累計多次程式碼提交再產生一次新的部署。

而我們知道兩個版本的程式碼變動越多,產生問題的可能性就越大,甚至造成更嚴重的線上問題。相比之下,微服務可以讓服務更敏捷地開發上線,這符合現在網際網路業務快速迭代的特徵。

  1. 技術自由
    微服務團隊,可以選擇自己喜歡的技術或者語言,並不需要侷限在同一種語言上。當出現了一種新的語言更適合解決現在的問題,團隊可以快速做出反應,用新的語言編寫新的服務或者改寫舊的服務,不必考慮採用新語言帶來的不確定性和成本問題。

  2. 彈性擴縮容
    微服務可以靈活地應對服務的擴縮容問題。在單體服務中,假設某個介面在高峰期有較大的訪問量,需要比平時增加一些機器應對,但我們很難根據 CPU 的水位做出準確的判斷,因為所有介面都在一個應用中,而在微服務中做出這種判斷要容易得多。

  3. 組織匹配
    在技術架構中,這是一個非常重要的概念。任何架構都應該和組織架構相匹配,在康威定律中已經明確地說明了這一點:如果技術架構和組織架構不匹配,會造成嚴重的跨部門溝通效率問題。隨著業務的不斷擴大,組織結構肯定會變得更復雜,而單體服務幾乎無法配合這種變化。

微服務架構可以非常好地與組織架構相匹配,比如一個短影片業務,在業務發展到一定階段,很自然地會拆分出使用者增長部門和內容部門。而我們的微服務架構,也可以將不同的服務拆分到相應的部門中,降低維護和溝通的成本。

沒有銀彈
與其他現存的架構和解決方案一樣,微服務架構也不是銀彈。當然它解決了單體服務很多問題,但同時也帶來了單體服務中一些沒有的問題,比如負載均衡、服務治理、服務註冊發現、如何拆分服務等,當然其中的大部分問題,都可以透過技術手段解決,但也增加了系統的複雜性。

下面,我們來進一步瞭解一下 Service Mesh 架構的演進。

Service Mesh,中文名叫作服務網格,簡單來說就是將可以配置的代理層和服務部署在一起,作為微服務基礎設施層接管服務間的流量,並提供通用的服務註冊發現、負載均衡、身份驗證、精準路由、服務鑑權等基礎功能。

Service Mesh 的演進歷程
sidecar 時代
實際上早在2013年,作為微服務架構的大規模應用方 Netflix, 就發現了微服務架構在跨語言上的問題。Netflix 大量使用 Java 技術棧,但因為公司的業務發展,使用單一技術棧是不現實的。語言總是有特定的應用場景,這個時候 Netflix 發現開發多語言的 SDK 要耗費大量的人力,畢竟 Spring Cloud 裡的元件可不是一般的多,為每個語言開發一套,顯然得不償失,所以就想到了 sidecar 模式,把 SDK 裡的功能轉移到 sidecar 中。

所謂 sidecar, 其實就是一個部署在本地的代理伺服器,它既接管了入口流量,也接管了出口流量。其實這種模式要追溯到 Web Server 的時代,比如 Nginx + Php-fpm 這種模式,實際上 Nginx 也是充當了 sidecar 的角色,只是通訊協議由比較常見的 HTTP ,變成了 FastCGI ,另外 Nginx 只是代理了入口流量,並未代理出口流量。

初代 Service Mesh
在 sidecar 模式中,就像 Netflix 的初衷一樣,它更多的是為了解決公司非主力語言的 SDK 開發問題,非主力語言透過 sidecar 連線,而主力語言還是透過 SDK 的方式。

技術不斷髮展,人們慢慢意識到統一流量處理模型的重要性,於是誕生了第一代 Service Mesh,其中比較出名的是 Linkerd 和 Envoy。在這一代 Service Mesh 中,已經不再依賴特定的基礎設施,最重要的是它的出發點已經不是解決多語言的問題,而是從統一流量處理模型的角度,形成了一套統一的流量控制的解決方案。

但是初代 Service Mesh 有一個致命的問題(其實也一直存在於微服務架構中),就是缺乏統一的管控手段,比如 sidecar 的服務治理相關配置檔案的維護,可能需要運維手動維護、無法集中管理,因為這個原因,控制面誕生了。

新一代 Service Mesh
新一代 Service Mesh,就是大家熟知的 Istio 了。它引入了控制面的概念,讓 Service Mesh 成為完全體。

Istio 使用 Envoy 作為資料面,控制面和 Kubernetes 深度繫結,早期版本將流量治理的功能放在 mixer 中,形成了一套完整的 Service Mesh 解決方案。控制面負責了資源管理、配置下發、證照管理等功能,解決了資料面配置難以管理的問題。

講完了 Service Mesh 的發展史,現在你應該對 Service Mesh 的有了初步的瞭解。下面我們就來看一下 Service Mesh 到底解決了傳統微服務架構中的哪些痛點吧。

Service Mesh 的優勢
1.語言無關
提到 Service Mesh 的優點,或許你最容易想到的就是業務語言無關性了,實際上我們在微服務架構也經常提到這個優點。但是對於傳統的微服務架構,Service Mesh 做到了真正的語言無關:傳統的微服務架構要為各種語言開發 SDK ,而 Service Mesh 將 SDK 的功能整合到 sidecar 中,實現了真正的語言無關性。

2.基礎設施獨立演進
傳統微服務架構中,業務程式碼和框架/SDK 混合在一起,框架想要升級就會變得非常困難而且被動,當微服務的數量變多時,想要在公司內推動框架的全量升級可謂是“天方夜譚”。而Service Mesh 架構將框架中和業務無關的通用功能放在 sidecar 中,升級時只要升級 sidecar 就可以了,這樣做到了基礎設施的獨立演進。

實際上這種做法也帶來了另外一個好處:寫框架的時候不用再考慮太多的向後相容性,降低了編寫程式碼的心智負擔。

3.可觀測性
可觀測性一般包含兩個部分:監控報警和鏈路追蹤。

監控報警可以透過資料面的 Metrics 整合,無感知地做到系統監控報警,減少了業務和框架的重複工作。

至於鏈路追蹤,因為需要透過 header 將 traceid 傳遞下去,所以還是需要客戶端的 SDK 將 traceid 透過 header 傳遞。不過這個做法也簡化了 Trace SDK 的封裝。

4.邊緣閘道器統一
實際上 sidecar 本身就是一個閘道器/反向代理,自然可以將以前 Nginx/Kong 之類的系統閘道器遷移到 sidecar 上來,這樣就可以維護一套統一的程式碼。

更進一步,可以將以前邊緣閘道器的工作,比如鑑權、 trace 初始化等工作下沉到 sidecar 上,進一步簡化系統閘道器的功能。

Service Mesh 的基礎元件及常見名詞
Service Mesh 一個最重要的變革,就是引入了資料面和控制面的概念,這個概念也並非 Service Mesh 新創的概念,實際上在 SDN (軟體定義網路)中就有了控制面和資料面的概念。在課程正式開始前,我們先簡單瞭解一下控制面和資料面。

資料面(Data Plane)

負責資料的轉發,一般我們常見的通用閘道器、Web Server,比如 Nginx、Traefik 都可以認為是資料面的一種。在 Service Mesh 的開源世界中,Envoy 可以說是最知名的資料面了。

另外資料面並非侷限於閘道器類產品,實際上某些 RPC 框架也可以充當資料面,比如 gRPC 就已經支援完整的 xDS(資料面和控制面的互動協議),也可以當作資料面使用。一般我們把負責資料轉發的資料面稱為 sidecar(邊車)。

控制面(Control Plane)

透過 xDS 協議對資料面進行配置下發,以控制資料面的行為,比如路由轉發、負載均衡、服務治理等配置下發。控制面的出現解決了無論是框架還是資料面、sidecar 都缺乏控制能力的弊端,而且之前只能透過運維批次修改 CONF 來控制資料面、導致規模上升時純人工維護成本以及大幅度上升的錯誤機率等問題也得到了很好的解決。

實際上 Service Mesh 需要的基礎元件和傳統的微服務沒有太大的差別,很多公司選擇自研控制面就是為了相容老版本微服務的基礎元件。

下面我們一起看一下 Service Mesh 的基礎元件。

服務註冊中心:服務間通訊的基礎元件。服務透過註冊自身節點,讓呼叫方服務發現被調方服務節點,以達到服務間點對點通訊的目的。

配置中心:用於服務的基礎配置更新,以達到程式碼和配置分離的目的。減少服務的釋出次數,配置釋出可以更快更及時地變更服務。

API 閘道器:透過統一的閘道器層,收斂服務的統一鑑權層、鏈路 ID 生成等基礎服務,並聚合後端服務為客戶端提供 RESTful 介面。另外 API 閘道器也負責南北向流量(外網入口流量)的流量治理。

服務治理:透過限流、熔斷等基礎元件,杜絕微服務架構出現雪崩的隱患。

鏈路追蹤:透過 trace 將整個微服務鏈路清晰地繪製出來,並進行精準的故障排查,極大地降低了故障排查的難度。

監控告警:透過 Prometheus 和 Grafana 這樣的基礎元件,繪製服務狀態監控大盤,針對資源、服務、業務各項指標,做精準的監控報警。

說完了基礎元件,再說一下一些常見的名詞解釋,便於你理解 Service Mesh。

Upstream: 上游服務,如果 A 服務呼叫 B 服務,在 A 服務的視角來看,B 服務就是上游服務,但是在中文的語境中,經常被叫作“下游服務”。所以在整個課程中,為了避免語言上的歧義,我會直接使用upstream,而不是中文翻譯。在中文的語境中,我更喜歡稱它為服務端或者被呼叫方。

Downstream: 下游服務,如果 A 服務呼叫 B 服務,在 B 服務的視角中,A 服務就是下游服務。在中文的語境中,我更喜歡叫客戶端或者呼叫方。

Endpoint:指的是服務節點,比如 A 服務有 192.168.2.11 和 192.168.2.12 兩個服務節點。

Cluster:指的是服務叢集,比如 A 服務有 192.168.2.11 和 92.168.2.12 兩個服務節點,那麼A服務就是 Cluster,也可以直接理解為 Service。

Node:在 Kubernetes 語境中,指的是承載 pod 的伺服器,但在微服務的語境中,更多的等同於Endpoint。

Route:指的是 Service Mesh 中的路由配置,比如 A 服務訪問 B 服務,要匹配到一定的規則,比如 header 中要帶有服務名(-H servicename:B),才能夠拿到 B 服務的訪問方式,透過服務發現或者靜態列表訪問到 B 服務的節點。

Listener:指的是 Service Mesh 的監聽埠,通常我們訪問 Service Mesh 的資料面,需要知道資料面的監聽埠。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
你還差得遠吶!

相關文章