天天看點

為什麼Uber微服務架構使用多租戶

雲栖号資訊:【 點選檢視更多行業資訊

在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

Uber服務的高性能主要依賴于在目前平台上快速以及穩定的開發新特性能力,和對應服務使用什麼技術棧無關。Uber平台最根本的能力是基于微服務架構,這是一種常用的結構化風格,也就是由各種互操作的服務組成的應用。

微服務架構可以提供很好的伸縮性,同時也能夠支援穩定的部署和子產品化。在Uber,不同的工程師團隊都是基于互操作的服務來工作,是以確定我們的技術棧既要能夠安全的釋出新的變動,也要能夠基于子產品化方式可靠的使用架構的已有部分,這點非常重要。總而言之,這些功能能夠提高開發者的開發速度以及快速的釋出周轉(turnaround)次數,另外還可以給我們提供基于獨立排程(schedules)建構的靈活性,同時依然可以滿足服務級别協定(SLAs)。

在一個微服務架構中允許多系統共存是利用微服務穩定性以及子產品化最有效的方式之一,這種方式一般被稱為多租戶(multi-tenancy)。租戶可以是測試,金絲雀釋出,影子系統(shadow systems),甚至服務層或者産品線,使用租戶能夠保證代碼的隔離性并且能夠基于流量租戶做路由決策。對于傳輸中的資料(data-in-flight)(例如,消息隊列中的請求或者消息)以及靜态資料(data-at-rest)(例如,存儲或者持久化緩存),租戶都能夠保證隔離性和公平性,以及基于租戶的路由機會。多租戶可以幫助我們在一個簡單的微服務棧上實作多種功能,比如改進的內建測試架構,影子流量路由,記錄和重放流量,為了實驗功能做的實時流量的密封重放,容量規劃,實際的性能測試,甚至一次運作多個産品線。

綜上所述,多租戶的好處是利用了一個更靈活和可伸縮的微服務架構,這個架構能夠帶來更大的生産效能以及改進的應用程式性能,這些會讓工程師以及平台使用者這類人受益。

微服務架構(landscape)

微服務架構能夠讓開發團隊不依賴于其他服務就可以推出新的功能以及bug修複,這可以增加開發者的開發速度。例如,一個團隊擁有四個服務(作為一個整體稱為系統1),這些服務具有約定的SLA,這些約定的SLA定期與具有其自己的SLA的多個其他服務互動。

下面的圖表1說明了這四個服務,A,B,C和D之間的互動。在這個圖表中,服務A從系統2中接收請求。系統1通過連接配接到服務B來處理請求,而服務B去連接配接服務C和服務D,最終完成請求處理。

為什麼Uber微服務架構使用多租戶

圖表1。在系統1中,服務A通過連接配接到服務B來處理系統2的請求,而服務B去連接配接服務C和服務D,最終完成請求處理

在這個例子中,如果我們對服務B做出改變,我們需要確定它仍然能夠和服務A,C,D正常互動。在微服務架構中,我們需要做這些內建測試場景,也就是測試和該系統中其他服務的互動。通常來說,微服務架構有兩種基本的內建測試方式:并行測試和生産環境測試。

并行測試

并行測試需要一個和生産環境一樣的過渡(staging)環境,并且隻是用來處理測試流量。如下圖表2所示,這個環境棧一直啟動并且運作生産環境代碼,但是和生産環境棧是隔離的,并且它的規模要比生産環境要小:

為什麼Uber微服務架構使用多租戶

圖表2。并行測試需要工程師建立一個過渡環境來處理測試流量并且考察生産環境棧最終是否滿足SLA

在并行測試中,工程師團隊首先完成生産服務的一次變動,然後将變動的代碼部署到測試棧。這種方法可以在不影響生産環境的情況下讓開發者穩定的測試服務,同時能夠在釋出前更容易的識别和控制bug。

并行測試需要確定測試流量不能夠洩漏到生産環境棧,這個可以通過實體隔離方式給它們配置設定獨立的網絡以及確定測試工具僅僅在測試棧中操作。

盡管并行測試是一種非常有效的內建測試方法,但是它也帶來了一些可能影響微服務架構成功的挑戰:

  • 額外的硬體成本:需要給測試提供整個棧,以及所有的資料存儲,消息隊列和其他基礎元件,這意味着需要額外的硬體和維護成本。
  • 同步問題:測試棧隻有在和相應的生産棧保持一緻才有作用。當兩個棧有偏差時,測試棧對于生産棧的反映(mirror)變得越來越困難,并且對于基礎元件來說,要保持兩個棧的同步也需要額外的負擔。
  • 不可靠性測試:當團隊把他們實驗性的并且有潛在bug的代碼部署到測試棧,這些服務可能無法正常的運作,這就會導緻測試失敗。例如,擁有服務A的團隊執行了并行測試去檢視他們的新代碼是否正常,但事實由于服務B存在一個bug導緻測試失敗了。因為我們測試的建構和生産環境的完全不一樣,這會讓我們很難定位bug;此外,我們隻有在測試通過整個流程之後才會知道對于服務A做的變動是否是安全的,這意味着我們需要等待擁有服務B的團隊把他們幹淨的代碼部署回測試棧。可以通過使用路由架構将流量路由到另一個沙盒環境(在該沙盒環境中待測試的服務已經啟動)來緩解這一特殊缺點。
  • 不精确的負載容量(capacity)測試:為了評估整個棧或者子棧的負載容量,我們需要在測試棧上運作測試負載。如果要測試特定容量,則必須先增加測試棧的容量,然後才能将增量負載(即目标容量相對于目前生産環境負載的增加)施加到測試棧上。此增量負載可能無法使測試棧飽和,這會導緻我們不清楚應向生産棧中增加多少容量以實作目标容量。

生産環境測試

微服務架構中的另一種內建測試方法是使生産棧支援多租戶并且允許測試流量和生産流量流入。下圖3就是這樣的一個例子:

為什麼Uber微服務架構使用多租戶

圖表3。利用多租戶生産棧,我們可以在運作生産服務的同時測試新增或者更新的微服務。

這種方法很有野心,因為這需要確定棧中的每一個服務要能夠處理生産請求以及測試請求。

使用這種方法,我們可以把待測試的服務B在一個隔離的沙盒環境中啟動,并且在沙盒環境下可以通路生産服務C和D。我們把測試流量路由到服務B,同時保持生産流量正常流入到生産服務。服務B僅僅處理測試流量而不處理生産流量。另外要確定生産流量不要被測試流量影響。

上面的例子隻是一個簡化的視角,但是卻能夠很好的解釋多租戶怎樣解決內建測試的問題。生産中的測試提出了兩個基本要求,它們也構成了多租戶體系結構的基礎:

  • 流量路由:能夠基于流入棧中的流量類型做路由。
  • 隔離性:能夠可靠的隔離測試和生産中的資源,這樣可以保證對于關鍵業務微服務沒有副作用。

這裡的隔離要求特别廣泛,因為我們希望隔離所有可能的靜态資料,包括配置,日志,名額,存儲(私有或公共)和消息隊列。這種隔離要求不僅僅作用于待測試的服務,對于整個棧也是一樣。

除了內建測試意外,多租戶也為其他用例鋪平了道路,例如分階段部署以及流量重放。

金絲雀部署

當開發者對他們的服務做了變動,即使這個變動已經被嚴格的審查和測試,我們也不能夠一次就把變動部署到所有運作的服務執行個體上。這是為了確定在變動有問題或者bug的情況下,整個使用者群不會受到攻擊。理想的做法是首先将變動應用到一小部分執行個體上,這被稱為金絲雀。然後,我們使用回報回路監視金絲雀,并逐漸應用代碼變動到所有服務執行個體上。

在多租戶架構中金絲雀可以被視為另一種租戶形式,并且可以把金絲雀作為請求中的一個屬性來做路由選擇。使用金絲雀的時候,資源在部署中也是隔離的。在任何給定時間,一個服務都有可能部署了金絲雀,并且所有金絲雀流量都會路由給已經部署的金絲雀。可以在靠近架構的邊緣基于請求自身的屬性來采樣金絲雀請求,例如使用者類型,産品類型,以及使用者位置。

捕獲/重放和影子流量

對于所做變動的安全性,一個行之有效的測量方法是在為實際的生産流量服務的同時,能夠看到服務費用(service fare)發生怎樣的變化。在一個封閉的安全環境中重放之前捕獲的實時流量或者重放實時生産流量的一份影子拷貝是另一種多租戶的用例。

為什麼Uber微服務架構使用多租戶

圖表4。建立影子流量到測試服務涉及到把生産流量的拷貝路由出生産棧并且路由進一個安全的測試環境。

在這個案例中,我們對被測試執行個體所有出站調用都添加了響應。捕獲和重放生産流量可以被視為內建測試的一個子類别,因為這些用例都屬于測試和實驗範圍。

從技術上說,重放流量是測試流量并且可以是測試租戶的一部分,允許和其他租戶隔離。通過重放流量,我們可以靈活地配置設定單獨的租戶,用來進一步隔離其他測試流量。

是以,多租戶體系架構的一個重要功能是其保護和隔離多個關鍵業務産品線或不同使用者群的能力。

面向租戶的架構

在面向租戶的微服務體系架構中,租戶被視為一等對象(first class object)。傳輸中的資料和靜态資料都有租戶的概念。做一個多租戶微服務架構涉及到給入站請求綁定上下文(context),然後在請求的生命周期内把上線文傳遞下去,這樣可以讓使用者基于上下文做路由。

租戶上下文

由于微服務架構是在互連的網絡上運作的一組完全不同的服務,是以我們需要能夠将租戶上下文附加到執行序列上。當請求進入邊緣網關時,我們可以将包含了租戶相關資訊的上下文附加到請求上。我們希望該上下文的生命周期要和被附加上的請求相同,并能夠傳播到同一業務邏輯上下文中生成的任何新請求中,進而保留請求序列的租戶資訊。

下面是一個簡單的租戶上下文格式和一些案例:

為什麼Uber微服務架構使用多租戶

上下文傳播

通常來說,當調用鍊中的任何一個服務接收到一個請求時,我們都希望能夠獲得租戶上下文資訊,因為這個服務可能會利用租戶上下文資訊作為業務邏輯的一部分。但是,這就要求服務在處理該請求時并且需要進一步發出其他請求時能夠傳播上下文。

大部分服務一般都不需要租戶上下文資訊,但是有一些需要通路請求中的上下文用來避開一些業務邏輯。例如,需要驗證使用者手機号的審計服務可能需要避開對測試流量的檢測,因為測試請求中的使用者都是測試使用者。另外,當測試流量通過一個事務處理服務,這個服務和銀行網關對接用來處理使用者的轉賬業務,我們可以屏蔽這個銀行網關或者和銀行的測試網關通信(如果有用于測試的網關),這樣可以避免真實的轉賬。租戶上下文的傳播可以通過開源工具來實作,比如OpenTracing和Jaeger,這些可以使用一種語言并且跨傳輸層方式來實作分布式上下文傳播。

租戶上下文也應該可以被傳播到其他傳輸中的資料對象中,比如Kafka消息隊列中的消息。 較新版本的Kafka支援添加标頭,并且可以使用開源跟蹤工具向消息添加上下文。

我們也希望租戶上下文能夠被傳播到靜态資料中,包括各個服務用來存儲持久化資料的所有資料存儲系統,比如MySQL,Apache Cassandra以及AWS。像Redis和Memcached這樣的分布式緩存也可以被歸類為靜态資料。架構中使用的所有存儲系統以及緩存都需要支援存儲上下文以及一個合理的資料粒度,這樣才能夠基于租戶上下文來對資料進行查詢和存儲。在一個更高的次元上,靜态資料元件要求能夠基于租戶資訊來對資料和流量進行隔離。

究竟如何隔離資料以及如何将租戶上下文與資料一起存儲,這些實作細節都和特定存儲系統有關。

基于租戶的路由

一旦我們能夠給請求附上租戶資訊,我們就可以基于它的租戶資訊做路由。這種路由對于生産,記錄/重放以及影子流量的測試很重要。當然,金絲雀部署也需要把金絲雀請求路由到運作在隔離環境下的特定服務執行個體。

在确定無縫運作且無開銷的路由解決方案時,考慮部署和服務技術當棧非常重要。當選擇一個通用的路由方案時,服務編寫的語言以及他們之間互相通信的傳輸和編碼也需要考慮。像Envoy或者Istio開源服務網格(Service Mesh)工具也非常适合提供基于租戶的路由功能,并且該路由和服務語言以及所使用的傳輸或編碼無關。

通常,可以在服務的出口或入口實作基于租戶的路由。在出口處,服務發現層可以根據請求的租戶資訊決定和什麼服務通信。另一種方法是在入口處做路由判斷,然後請求被重新路由到正确的執行個體,如下圖5所示:

為什麼Uber微服務架構使用多租戶

圖5。我們可以在入口處做路由判斷,上面的例子是從生産環境服務Ap發送測試流量到測試執行個體A1。

在圖5例子中,可以使用一個邊車(sidecar)來轉發一個測試請求到測試執行個體上。邊車可以是一個代理程序,它負責代理所有進入該服務的流量,并且和服務部署在一起。流量先是被服務邊車接收到,然後邊車檢測請求的租戶上下文,接着基于上下文做出路由決策。

基于我們想要的用例,我們可以在租戶上下文中增加額外的中繼資料。例如,對于生産中的測試,我們想把測試流量重定向到一個服務的測試執行個體上。我們可以在上下文中增加額外的資訊,這個可以允許以下的行為發生:

為什麼Uber微服務架構使用多租戶

當做路由決策時,我們可以檢測請求的租戶資訊是否是測試的以及請求的接收者是否是處于測試下。如果這些請求都滿足,我們可以路由該請求到這個<測試執行個體ID>。

資料隔離

我們想去建構一個架構,在這個架構中每一個基礎元件都能夠了解租戶資訊,并且能夠基于租戶路由隔離流量,同時在我們的平台中允許對運作不同的微服務有更多的控制,比如名額和日志。在微服務架構中典型的基礎元件是日志,名額,存儲,消息隊列,緩存以及配置。基于租戶資訊隔離資料需要分别處理基礎元件。例如,我們可以生成租戶資訊,然後将其作為服務生成的所有日志和名額的一部分。這個可以幫助開發者基于租戶資訊做過濾,同時也可能有助于避免錯誤警報或防止啟發式或訓練資料出現偏差。

同樣的,當考慮存儲服務時,底層的存儲架構需要考慮能夠有效的在租戶之間建立隔離。一些存儲架構很容易實作多租戶。兩種進階方法是将租戶概念(notion)顯式地嵌入到資料旁邊,并将不同的租戶資訊和資料共同放置,或者根據租戶資訊顯式地分離資料,如下圖6所示:

為什麼Uber微服務架構使用多租戶

圖6。使用租戶資訊路由資料和消息隊列測試流量到處于測試下的不同元件,這樣可以隔離該測試流量,是以不會幹擾生産系統。

後一種方法提供了更好的隔離保證,而前一種方法通常需要較少的操作開銷。對于像Kafka這種消息隊列系統,我們可以給租戶推出一個新的主題或者配置設定一個單獨的Kafka叢集。

對于資料隔離,上下文需要能夠被傳播到基礎元件。確定服務在資料隔離方面的開銷最小,這一點很重要。理想的情況下,我們希望服務不需要顯示的處理租戶資訊。另外,我們也希望将隔離邏輯放置在所有資料流經的中央扼流點。網關就是其中一種可以實作隔離邏輯的扼流點,它是我們的首選方法。用戶端庫可以是實作基于租戶隔離的另一種方法,盡管編碼語言的多樣性使在所有特定語言的用戶端庫之間保持邏輯同步變得有點困難。

同樣的,對于配置隔離,我們希望對于特定租戶下的服務配置是互相獨立的,是以確定對于一個租戶配置的變動不會影響到其他租戶的配置。

展望未來

基于微服務的體系結構仍在發展,并已成為開發人員和整個組織的靈活性工具促進者(facilitators)。 精心計劃的多租戶架構可以提高開發人員的生産力并支援不斷發展的業務線。

Uber的多租戶實施帶來了各種好處,例如使代碼和配置的自動釋出更安全,進而提高了開發人員的速度。 多租戶架構的隔離保證使Uber可以出于各種目的(包括測試流量)重新利用同一微服務棧。

【雲栖号線上課堂】每天都有産品技術專家分享!

課程位址:

https://yqh.aliyun.com/live

立即加入社群,與專家面對面,及時了解課程最新動态!

【雲栖号線上課堂 社群】

https://c.tb.cn/F3.Z8gvnK

原文釋出時間:2020-05-12

本文作者:王歡

本文來自:“

dockone

”,了解相關資訊可以關注“dockone”

繼續閱讀