歡迎光臨
每天分享高質量文章

WePay服務網格系統的高可用實踐

在本系列的前面兩篇文章,《WePay為什麼選擇Linkerd作為服務網格代理?》以及《Sidecar和DaemonSet,WePay是如何選擇的?》中,我們深入探討了一些服務網格的細節,分別是服務網格代理( Linkerd)和這些代理的容器樣式。
在本系列的第三部分中,我們將站在一個更高層面來看待服務網格系統。具體來說,我們將會從監控和告警兩個角度來查看服務網格系統的健康性,並講述如何使用各組資料來定義WePay基礎設施中服務網格架構的高可用。

 

全景圖

和我們在本系列中前面討論的服務網格設定一樣,這裡給出的例子是一個跑在Google Kubernetes Engine(GKE)的Kubernetes集群上的高可用及模塊化的服務網格。
我們在此之前經歷了幾次服務網格架構設計的迭代,而支持模塊化的那個最能滿足我們的需求。在我們的模塊化設計中,服務網格的資料面板或者說代理(Linkerd)以及服務發現或者說控制面板(Namerd)被分成兩個獨立的模塊,以便維護和監控。在這樣的服務網格架構中,服務之間可以使用Namerd相互發現,並且可以借助Linkerd將請求路由到其他服務,此外它還提供負載均衡和監控指標。
圖1:服務網格模塊,Namerd服務和Linkerd代理
圖1展示了Namerd和Linkerd作為WePay基礎設施中實現服務網格的模塊,還有將WePay的Sensu設定當成核心,讓我們能夠在出現任何問題時查看服務網格系統的狀態,並且在某些地方無法正常工作時發出告警。
正如前面所提到的那樣,在這篇文章中,我們將會把重點放在如何通過監控工具簡化並實現一個高可用的服務網格系統。我們還將詳細介紹監控系統涉及到的一些內容,一起來看看在一個系統里什麼操作被認為是正常的,什麼則是異常。

 

服務網格監控的ABC
上一節介紹了我們是如何通過明確定義的模塊來簡化服務網格的架構。使用該架構時,我們把一些時間花在了確定系統中每個模塊的監控方式,隨後是該系統整體。我們只想簡單的回答這樣一個問題,“應該怎樣監控系統才能在遇到問題時拿到第一手資料,還有,我們的告警物件是誰?”
通過解答這些問題,我們發現並改進了整個系統的各個部分。例如,在我們的POC階段,我們最開始使用的一個解決方案,其中將Namerd當作Kubernetes中Linkerd的sidecar。一旦我們發覺兩個服務各自擁有自己的生命周期和健康檢查定義會是一個更好的做法時,我們將Namerd放到了HAProxy後面的集群,後端由Google Compute Engine的實體組成。隨後,經歷多次迭代後,我們把Namerd重構成了Kubernetes的service,在那裡它會通過一個Kubernetes的load balancer暴露給Linkerd代理。

圖2:通過Kubernetes的namer發現的服務網格棧
在圖2中,Namerd會在Kubernetes集群中做複製同步,Linkerd代理通過它的Kubernetes load balancer與Namerd通信,它會以sidecar容器的形式運行Kubernetes proxy來監視每個Kubernetes集群的更改,而每個Kubernetes代理會向它的master上報集群更改的信息,並傳回到Namerd。
這隻是一個示例,其中明確每個模塊的適用範圍極大地簡化了服務網格系統里服務的監控和生命周期管理。在確定了每個模塊的適用範圍後,我們根據系統中模塊的高可用要求對它們進行了測試:
  • 在可能的情況下,系統應當能夠自愈。

  • 監控系統必須能夠上報系統中每個模塊內部和外部的健康狀態。

  • 盡可能地提高系統的自愈能力,併在未能自愈或無法修複時發出告警。

下麵部分介紹了每個Namerd和Linkerd模塊的一些重要的監控單元。我們還會詳細介紹我們開發的一些工具,針對每個Google Cloud專案和Kubernetes集群,分別在監控和告警方面實現模塊外部和內部的可見性。

 

服務發現,A-OK
服務發現是服務網絡系統的重要組成部分,對我們來說,它是基礎設施里的一項關鍵服務,它能給出在每個資料中心裡發現了多少服務。服務發現如果中斷可能會影響路由,如果中斷延長到幾分鐘,那麼可能會讓所有通過服務網格發現彼此的服務的路由停止工作。因此,如何監控namerd的服務發現取決於Namerd本身的心跳,以及代理的運行狀況(如圖2所示),它會將服務端點的更改上報給Namerd。
發現服務的心跳
作為服務網格的發現服務,Namerd內部的健康狀況通過監控它的每個實體實現。我們將Namerd遷移到Kubernetes的主要理由之一是為了得到更棒的自愈能力和監控,這和我們所監控和觀察的跑在資料中心裡的所有微服務一樣。

圖3:Namerd internals with Kubernetes health probe
我們再仔細研究一下運行在Kubernetes環境中的Namerd的副本,圖3顯示了集群中該服務的N個副本。Kubernetes的health scheduler會監視每個副本,以確保每時每刻均有N個實時的副本存在。此外,每個副本配置成在一個很短的時間間隔內使用Kubernetes的health scheduler做ping,比如每隔幾秒鐘,這樣來衡量它的響應能力。如果這些檢查中有任何一次不成功或是無法響應,那麼Kubernetes會重啟受影響的副本。這樣一來,副本將會重新啟動然後重新系結之前配置的名稱。
此外,我們可能會遇到所有副本同時受影響的情況。在這種情況下,我們會通過設置一個更高級別的心跳檢測來獲悉Namerd完整的停機時間。這個心跳檢測配置在Kubernetes的外部,並且它會在Namerd的Kubernetes load balancer後面尋找至少一個健康且可用的後端實體。我們通過在資料中心裡使用Sensu系統的check runner來實現這一標的。

圖4:Namerd使用Sensu進行外部檢查
Sensu的check會每隔一個很短的時間間隔(每10秒左右)運行一次,以確保始終至少有一個後端實體是可用的。這個Sensu的check即是針對Kubernetes中Namerd的load balancer做簡單的HTTP ping。在圖4中,Sensu服務端向其中一個check runner發出信號,讓它去ping Namerd,然後它會向事先配置好的通知系統上報結果。如果心跳連續多次執行失敗,它會向通知服務發送告警,便於運維團隊進一步調查。
我們讓每個副本彼此相對獨立地運行,從而能夠對Namerd服務進行一次完整的健康測試,這主要是通過將每個實體的名稱唯一對應一份副本實現。在該樣式下,每個副本都是服務本身的一個完整表達,而除了其配置之外沒有其他的依賴項。此外,我們還解決了服務需要橫向擴展來應對高交易量的可擴展性。
所有服務,隨時發現
如今我們可以通過監控Namerd的操作來發現任何可能存在的運行時問題,我們可以把精力放在識別可能存在的服務發現問題,即圖4中的Discovery check。這類監控的目的是用來測試Namerd使用的不同型別的代理或namer。
我們嘗試實現了一個自定義的Sensu check,它會針對被測試的發現型別設置對應的dtab配置。這些dtab配置定義在一個特定的範疇,也稱為命名空間:
  1. GET /api/1/dtabs/<namespace> HTTP/1.1
  2. HTTP/1.1 200 OK
  3. {
  4.    {
  5.        "prefix":"/srv/default",
  6.        "dst":"/#/io.l5d.k8s/prefix/portName"
  7.    },
  8.    {
  9.        "prefix":"/svc",
  10.        "dst":"/srv"
  11.    }

代碼示例1:從Namerd獲取實時的dtab配置
命名空間的一個例子就是用於HTTP1協議的出向Linkerd代理的dtab配置。參考代碼示例1中的一個命名空間配置,其中包含一個微服務名稱和命名空間,要求Namerd解析該名稱來發現該微服務的檢查嘗試次數:
  1. POST /dtab/delegator.json HTTP/1.1
  2. {
  3.    "dtab":<dtab_configuration>,
  4.    "namespace":<namespace_name>,
  5.    "path":"/prefix/service"
  6. }
  7. HTTP/1.1 200 OK
  8. {
  9.   "type":"delegate",
  10.   "path":"/prefix/service",
  11.   "delegate":{
  12.      "type":"alt",
  13.      "path":"/srv/prefix/service",
  14.      "dentry":{
  15.         "prefix":"/svc",
  16.         "dst":"/srv"
  17.      },
  18.      "alt":[
  19.         {
  20.            "type":"neg",
  21.            ...
  22.         },
  23.         {
  24.            "type":"leaf",
  25.            "path":"/#/io.l5d.k8s/prefix/portName/service",
  26.            "dentry":{
  27.               "prefix":"/srv/default",
  28.               "dst":"/#/io.l5d.k8s/prefix/portName"
  29.            },
  30.            "bound":{
  31.               "addr":{
  32.                  "type":"bound",
  33.                  ...
  34.               },
  35.               "id":"/%/io.l5d.k8s.daemonset/mesh/...",
  36.               "path":"/"
  37.            }
  38.         },
  39.         ...
  40.      ]
  41.   }
  42. }
代碼示例2:通過Namerd解析一個服務的名字
在代碼示例2的API呼叫中可以看到,命名空間A中發現了Service X,因此在Namerd的響應主體中傳回了“type”:“leaf”物件。在同一請求中,所有其他的發現路由都傳回了“type”:“neg”,沒有從API呼叫中的請求主體里識別出到Service X的路徑。
該check用到的每個命名空間都和協議及路由器型別,入向/出向,設置等有關。比如,HTTP/1.1協議有一條用於發送方路由的dtab,以及用於接收路由的另一個dtab,而且為了簡單起見,這還只是考慮了在一個可發現域範圍的情況,其中不包括外部服務或物體。
由於服務發現是每個微服務環境的核心,對於全域性服務發現的健康性而言,所有服務發現的檢查都被視為非常關鍵的指標,如果問題在短時間內無法自己糾正,那麼將會觸發相應地告警。
探測監聽的路由
就像監控Namerd的服務發現一樣,監控和測試生產環境里使用Linkerd做代理的資料面板包含兩個維度,每個維度從不同視角為我們講述了服務網格代理是如何工作的。一個維度是使用一個外部的watcher監控代理運行的健康狀況,在這裡即是Kubernetes的health scheduler。另一個維度則是,如果在給定代理是健康的條件下,它們是否可以成功地將請求路由到同一集群或是跨集群的其他節點上的代理呢?
檢查代理的運行健康狀況的標的是為了發現那些可以通過重啟有問題的容器解決的問題。因此,我們需要配置一個健康檢查,確保經過代理的一個完整迴圈,它的響應碼可以被轉換成Kubernetes里的物件,即響應碼是200表明狀態是healthy,而非200的響應碼則會將容器標記成unhealthy:
  1. GET /admin/ping HTTP/1.1
  2. HTTP/1.1 200 OK
  3. pong
代碼示例3:針對每個代理做簡單的健康檢查
這些健康檢查可以根據基礎設施環境自定義成任意的複雜度,但是在基礎設施里,一般基本的健康檢查就能發現潛在的問題,作為監控檢查的一部分,代碼示例3展示了對代理容器的簡單ping探測。

圖5:帶有Kubernetes健康檢查探測的Linkerd sidecar代理
圖5展示了基礎設施中每個代理在一個很短的時間間隔里發生的ping,無論每個代理組使用什麼樣的容器樣式。
當我們開始查看本節開頭提到的第二個維度,即檢查路由的內部運行情況時,代理的檢查會變得更加有趣。換句話說,如果路由情況從外面看起來很好的話,我們就得看看代理是否健康並且是否能夠路由到不同的域:

圖6:兩個Kubernetes集群之間內部和外部的探測
要實施更明確一點的探測測試的話,我們需要在每個域里種下一個服務,在這裡即是兩個不同的Kubernetes集群,這樣一來便可以按需從內部探測任何微服務。在圖6中,在集群A中啟動探測,指示探測服務A向其代理髮送探測標的為服務1的請求。想要在這一設定下實現全域改寫的話,我們可以在集群B中使用探測服務B啟動一個相同的探測任務,其中探測本身是從集群B中發起,但是最終是以集群A的服務1作為標的服務。相同的樣式可以擴展到任意數量的集群,使用我們的探測服務按需進行任意數量的迭代。
此外,一套基礎設施環境中存在多種協議的情況也是適用的,比如,同時有HTTP/1.1或HTTP/2,可以將探測服務配置成在單次探測檢查中使用這些協議中的任意一種來探測標的:

圖7:使用探測服務探測REST及gRPC應用
就像服務發現的健康檢查那樣,任何路由方面的健康檢查也是至關重要的,如果問題沒有自愈,那麼會觸發通知或告警。如果在服務網格的環境中某些東西不能正常工作,我們可以通過這樣端到端的監控形式,填補基礎設施中可能出錯的地方。
高可用,乃至更遠
服務網格是一個技術棧,它將控制面板,資料流和負載平衡三塊獨立開來,而且通過一套高可用的設定,我們得以持續享受它所帶來的所有好處,並且提供更好的保障。
在我們的高可用設定中,我們:
  • 將控制面板和資料平面單獨分開,從而最小化我們的監控範圍,而且對於監控棧來說監控物件也變得更加具象。

  • 從不同的維度實施健康檢查,並確保容器操作和功能檢查是分別單獨監控的。

  • ……並且,使用這些監控設定時,我們讓可操作的監控事件變得更加清晰而且可以告警出來。

鑒於從高可用的服務網格設定下得到的信心,我們將越來越多的微服務遷移到了服務網格,而且利用了服務網格附帶的所有功能。因此,在本系列的下一篇文章中,我們將深入探討我們的應用程式,REST或gRPC應用,是如何利用服務網格的,以及我們如何在WePay的基礎架構中管理棧的生命周期。
原文鏈接:https://wecode.wepay.com/posts/ha-mesh-wepay
赞(0)

分享創造快樂