您的位置:首頁>正文

從分散式到微服務,深挖Service Mesh

原文:Pattern: Service Mesh (作者/Phil Calçado, 翻譯/雁驚寒, 責編/魏偉 )

摘要:在前一段時間, 我們CSDN推出了《深度剖析Service Mesh服務網格新生代Istio》一文, 大家應該深刻理解了扛起Service Mesh大旗的Istio的架構和功能, 該篇文章可以看作是這篇的前傳, 本文由易到難地介紹了分散式系統到服務網格的演化過程, 從而讓讀者對Service Mesh有了更加深刻的認識, 以下是譯文。

自從幾十年前第一次引入分散式系統這個概念以來, 出現了很多原來根本想像不到的分散式系統使用案例, 但同時也引入了各種各樣的新問題。

當這些系統還是比較少比較簡單的時候, 工程師可以通過減少遠端交互的次數來解決複雜性問題。

處理分散式問題最安全的方法是盡可能避免遠端交互, 雖然這可能意味著要在多個系統上存放重複的邏輯和資料。

行業上的需求推動著我們前進的步伐, 分散式系統的組成從幾個大型的中央電腦發展成為數以千計的小型服務。 在這個新的世界裡, 我們必須走出困境, 應對新的挑戰和開放性問題。 首先, 具體問題具體分析, 針對某個問題給出有針對性的解決辦法, 然後再提供更先進更複雜的解決方案。 隨著我們對問題領域越來越熟悉、提出的解決辦法越來越好, 我們開始將一些最常見的需求總結歸納為模式、庫, 以及最終的平臺。

當電腦第一次聯網

由於人們首先想到的是讓兩台或多台電腦相互通訊,

因此, 他們構思出了這樣的東西:

互相之間可以通訊的兩個服務可以滿足最終使用者的一些需求。 但這個示意圖顯然過於簡單了, 缺少了包括通過代碼操作的位元組轉換和線上路上收發的電信號轉換在內的多個層。 雖然, 一定程度上的抽象對於我們的討論是必需的, 但還是讓我們來添加網路通訊協定棧元件以增加一點細節內容吧:

上述這個修改過的模型自20世紀50年代以來一直使用至今。 一開始, 電腦很稀少, 也很昂貴, 所以兩個節點之間的每個環節都被精心製作和維護。 隨著電腦變得越來越便宜, 連接的數量和資料量大幅增加。 隨著人們越來越依賴網路系統, 工程師們需要保證他們構建的軟體能夠達到使用者所要求的服務品質。

當然, 還有許多問題急需解決以達到使用者要求的服務品質水準。 人們需要找到解決方案讓機器互相發現、通過同一條線路同時處理多個連接、允許機器在非直連的情況下互相通信、通過網路對資料包進行路由、加密流量等等。

在這其中, 有一種叫做流量控制的東西, 下面我們以此為例。 流量控制是一種防止一台伺服器發送的資料包超過下游伺服器可以承受上限的機制。 這是必要的, 因為在一個聯網的系統中, 你至少有兩個不同的、獨立的電腦, 彼此之間互不瞭解。 電腦A以給定的速率向電腦B傳送的位元組, 但不能保證B可以連續地以足夠快的速度來處理接收到的位元組。 例如, B可能正在忙於並行運行其他任務, 或者資料包可能無序到達, 並且B可能被阻塞以等待本應該第一個到達的資料包。 這意味著A不僅不知道B的預期性能, 而且還可能讓事情變得更糟, 因為這可能會讓B超載, B現在必須對所有這些傳入的資料包進行排隊處理。

一段時間以來, 大家寄希望于建立網路服務和應用程式的開發者能夠通過編寫代碼來解決上面提出的挑戰。 在我們的這個流程控制示例中, 應用程式本身必須包含某種邏輯來確保服務不會因為資料包而超載。 這種重聯網的邏輯與業務邏輯一樣重要。 在我們的抽象示意圖中, 它是這樣的:

幸運的是,技術的發展日新月異,隨著像TCP/IP這樣的標準的橫空出世,流量控制和許多其他問題的解決方案被融入進了網路通訊協定棧本身。這意味著這些流量控制代碼仍然存在,但已經從應用程式轉移到了作業系統提供的底層網路層中:

這個模型相當地成功。幾乎任何一個組織都能夠使用商業作業系統附帶的TCP/IP協定棧來驅動他們的業務,即使有高性能和高可靠性的要求。

當第一次使用微服務

多年以來,電腦變得越來越便宜,並且到處可見,而上面提到的網路通訊協定棧已被證明是用於可靠連接系統的事實上的工具集。隨著節點和穩定連接的數量越來越多,行業中出現了各種各樣的網路系統,從細細微性的分散式代理和物件到由較大但重分散式元件組成的面向服務的架構。

這樣的分散式系統給我們帶來了很多有趣的更高級別的案例和好處,但也出現了幾個難題。其中一些是全新的,但其他的只是我們在討論原始網路時遇到難題的更高版本而已。

在90年代,Peter Deutsch和他在Sun公司的同事工程師們撰寫了“分散式運算的八大錯誤”一文,其中列出了人們在使用分散式系統時通常會做出的一些假設。Peter認為,這些假設在更原始的網路架構或理論模型中可能是真實的,但在現代世界中是不成立的:

網路是可靠的

延遲為零

頻寬是無限的

網路是安全的

拓撲是不變的

有一個管理員

傳輸成本為零

網路是同構的

大家把上面這個列表斥為“謬論”,因此,工程師們不能忽視這些問題,必須明確地處理這些問題。

為了處理更複雜的問題,需要轉向更加分散的系統(我們通常所說的微服務架構),這在可操作性方面提出了新的要求。 之前我們已經詳細討論了一些內容,但下面則列出了一個必須要處理的東西:

計算資源的快速提供

基本的監控

快速部署

易於擴展的存儲

可輕鬆訪問邊緣

認證與授權

標準化的RPC

因此,儘管數十年前開發的TCP/IP協定棧和通用網路模型仍然是電腦之間相互通訊的有力工具,但更複雜的架構引入了另一個層面的要求,這再次需要由在這方面工作的工程師來實現。

例如,對於服務發現和斷路器,這兩種技術已用於解決上面列出的幾個彈性和分散式問題。

歷史往往會重演,第一批基於微服務構建的系統遵循了與前幾代聯網電腦類似的策略。這意味著落實上述需求的責任落在了編寫服務的工程師身上。

服務發現是在滿足給定查詢準則的情況下自動查找服務實例的過程,例如,一個名叫Teams的服務需要找到一個名為Players的服務實例,其中該實例的environment屬性設置為production。你將調用一些服務發現進程,它們會返回一個滿足條件的服務清單。對於更集中的架構而言,這是一個非常簡單的任務,可以通常使用DNS、負載等化器和一些埠號的約定(例如,所有服務將HTTP伺服器綁定到8080埠)來實現。而在更分散的環境中,任務開始變得越來越複雜,以前可以通過盲目信任DNS來查找依賴關係的服務現在必須要處理諸如用戶端負載均衡、多種不同環境、地理位置上分散的伺服器等問題。如果之前只需要一行代碼來解析主機名稱,那麼現在你的服務則需要很多行代碼來處理由分散式引入的各種問題。

斷路器是由Michael Nygard在其編寫的“Release It”一書中引入的模式。我非常喜歡Martin Fowler對該模式的一些總結:

斷路器背後的基本思路非常簡單。將一個受保護的函式呼叫包含在用於監視故障的斷路器物件中。一旦故障達到一定閾值,則斷路器跳閘,並且對斷路器的所有後續調用都將返回錯誤,並完全不接受對受保護函數的調用。通常,如果斷路器發生跳閘,你還需要某種監控警報。

這些都是非常簡單的設備,它們能為服務之間的交互提供更多的可靠性。然而,跟其他的東西一樣,隨著分散式水準的提高,它們也會變得越來越複雜。系統發生錯誤的概率隨著分散式水準的提高呈指數級增長,因此即使簡單的事情,如“如果斷路器跳閘,則監控警報”,也就不那麼簡單了。一個元件中的一個故障可能會在許多用戶端和用戶端的用戶端上產生連鎖反應,從而觸發數千個電路同時跳閘。而且,以前可能只需幾行代碼就能處理某個問題,而現在需要編寫大量的代碼才能處理這些只存在於這個新世界的問題。

事實上,上面舉的兩個例子可能很難正確實現,這也是大型複雜庫,如Twitter的Finagle和Facebook的Proxygen,深受歡迎的原因,它們能避免在每個服務中重寫相同的邏輯。

大多數採用微服務架構的組織都遵循了上面提到的那個模型,如Netflix、Twitter和SoundCloud。隨著系統中服務數量的增加,他們發現了這種方法存在著各種弊端。

即使是使用像Finagle這樣的庫,專案團隊仍然需要投入大量的時間來將這個庫與系統的其他部分結合起來,這是一個代價非常高的難題。根據我在SoundCloud和DigitalOcean的經驗,我估計在100-250人規模的工程師組織中,需要有1/10的人員來構建模型。有時,這種代價很容易看到,因為工程師被分配到了專門構建工具的團隊中,但是更多的時候,這種代價是看不見的,因為它表現為在產品研發上需要花費更多的時間。

第二個問題是,上面的設置限制了可用於微服務的工具、運行時和語言。用於微服務的庫通常是為特定平臺編寫的,無論是程式設計語言還是像JVM這樣的運行時。如果開發團隊使用了庫不支援的平臺,那麼通常需要將代碼移植到新的平臺。這浪費了本來就很短的工程時間。工程師沒辦法再把重點放在核心業務和產品上,而是不得不花時間來構建工具和基礎架構。那就是為什麼一些像SoundCloud和DigitalOcean這樣的中型企業認為其內部服務只需支援一個平臺,分別是Scala或者Go。

這個模型最後一個值得討論的問題是管理方面的問題。庫模型可能對解決微服務架構需求所需功能的實現進行抽象,但它本身仍然是需要維護的元件。必須要確保數千個服務實例所使用的庫的版本是相同的或至少是相容的,並且每次更新都意味著要集成、測試和重新部署所有服務,即使服務本身沒有任何改變。

下一個邏輯

類似於我們在網路通訊協定棧中看到的那樣,大規模分散式服務所需的功能應該放到底層的平臺中。

人們使用高級協定(如HTTP)編寫非常複雜的應用程式和服務,甚至無需考慮TCP是如何控制網路上的資料包的。這種情況就是微服務所需要的,那些從事服務開發工作的工程師可以專注于業務邏輯的開發,從而避免浪費時間去編寫自己的服務基礎設施代碼或管理整個系統的庫和框架。

將這個想法結合到我們的圖表中,我們可以得到如下所示的內容:

不幸的是,通過改變網路通訊協定棧來添加這個層並不是一個可行的任務。許多人的解決方案是通過一組代理來實現。這個的想法是,服務不會直接連接到它的下游,而是讓所有的流量都將通過一個小小的軟體來透明地添加所需功能。

在這個領域第一個有記載的進步使用了邊三輪(sidecars)這個概念。“邊三輪”是一個輔助進程,它與主應用程式一起運行,並為其提供額外的功能。在2013年,Airbnb寫了一篇有關Synapse和Nerve的文章,這是“邊三輪”的一個開源實現。一年後,Netflix推出了Prana,專門用於讓非JVM應用程式從他們的NetflixOSS生態系統中受益。在SoundCloud,我們構建了可以讓遺留的Ruby程式使用我們為JVM微服務構建的基礎設施的“邊三輪”。

雖然有這麼幾個開源的代理實現,但它們往往被設計為需要與特定的基礎架構元件配合使用。例如,在服務發現方面,Airbnb的Nerve和Synapse假設了服務是在Zookeeper中註冊,而對於Prana,則應該使用Netflix自己的Eureka服務註冊表 。

隨著微服務架構的日益普及,我們最近看到了一波新的代理浪潮,它們足以靈活地適應不同的基礎設施元件和偏好。 這個領域中第一個廣為人知的系統是Linkerd,它由Buoyant創建出來,源于他們的工程師先前在Twitter微服務平臺上的工作。很快,Lyft的工程團隊宣佈了Envoy的發佈,它遵循了類似的原則。

Service Mesh

在這種模式中,每個服務都配備了一個代理“邊三輪”。由於這些服務只能通過代理“邊三輪”進行通信,我們最終會得到類似於下圖的部署方案:

Buoyant的首席執行官威廉·摩根表示,代理之間的互連形成了服務網格。 2017年初,威廉寫下了這個平臺的定義,並稱它為服務網格:

服務網格是用於處理服務到服務通信的專用基礎設施層。它負責通過複雜的服務拓撲來可靠地傳遞請求。實際上,服務網格通常被實現為與應用程式碼一起部署的羽量級網路代理矩陣,並且它不會被應用程式所感知。

這個定義最強大的地方可能就在於它不再把代理看作是孤立的元件,並承認它們本身就是一個有價值的網路。

隨著微服務部署被遷移到更為複雜的運行時中去,如Kubernetes和Mesos,人們開始使用一些平臺上的工具來實現網格網路這一想法。他們實現的網路正從互相之間隔離的獨立代理,轉移到一個合適的並且有點集中的控制面上來。

來看看這個鳥瞰圖,實際的服務流量仍然直接從代理流向代理,但是控制面知道每個代理實例。控制面使得代理能夠實現諸如存取控制和度量收集這樣的功能,但這需要它們之間進行合作:

最近公佈的Istio專案是這類系統中最著名的例子。

完全理解服務網格在更大規模系統中的影響還為時尚早,但這種架構已經凸顯出兩大優勢。首先,不必編寫針對微服務架構的定制化軟體,即可讓許多小公司擁有以前只有大型企業才能擁有的功能,從而創建出各種有趣的案例。第二,這種架構可以讓我們最終實現使用最佳工具或語言進行工作的夢想,並且不必擔心每個平臺的庫和模式的可用性。

幸運的是,技術的發展日新月異,隨著像TCP/IP這樣的標準的橫空出世,流量控制和許多其他問題的解決方案被融入進了網路通訊協定棧本身。這意味著這些流量控制代碼仍然存在,但已經從應用程式轉移到了作業系統提供的底層網路層中:

這個模型相當地成功。幾乎任何一個組織都能夠使用商業作業系統附帶的TCP/IP協定棧來驅動他們的業務,即使有高性能和高可靠性的要求。

當第一次使用微服務

多年以來,電腦變得越來越便宜,並且到處可見,而上面提到的網路通訊協定棧已被證明是用於可靠連接系統的事實上的工具集。隨著節點和穩定連接的數量越來越多,行業中出現了各種各樣的網路系統,從細細微性的分散式代理和物件到由較大但重分散式元件組成的面向服務的架構。

這樣的分散式系統給我們帶來了很多有趣的更高級別的案例和好處,但也出現了幾個難題。其中一些是全新的,但其他的只是我們在討論原始網路時遇到難題的更高版本而已。

在90年代,Peter Deutsch和他在Sun公司的同事工程師們撰寫了“分散式運算的八大錯誤”一文,其中列出了人們在使用分散式系統時通常會做出的一些假設。Peter認為,這些假設在更原始的網路架構或理論模型中可能是真實的,但在現代世界中是不成立的:

網路是可靠的

延遲為零

頻寬是無限的

網路是安全的

拓撲是不變的

有一個管理員

傳輸成本為零

網路是同構的

大家把上面這個列表斥為“謬論”,因此,工程師們不能忽視這些問題,必須明確地處理這些問題。

為了處理更複雜的問題,需要轉向更加分散的系統(我們通常所說的微服務架構),這在可操作性方面提出了新的要求。 之前我們已經詳細討論了一些內容,但下面則列出了一個必須要處理的東西:

計算資源的快速提供

基本的監控

快速部署

易於擴展的存儲

可輕鬆訪問邊緣

認證與授權

標準化的RPC

因此,儘管數十年前開發的TCP/IP協定棧和通用網路模型仍然是電腦之間相互通訊的有力工具,但更複雜的架構引入了另一個層面的要求,這再次需要由在這方面工作的工程師來實現。

例如,對於服務發現和斷路器,這兩種技術已用於解決上面列出的幾個彈性和分散式問題。

歷史往往會重演,第一批基於微服務構建的系統遵循了與前幾代聯網電腦類似的策略。這意味著落實上述需求的責任落在了編寫服務的工程師身上。

服務發現是在滿足給定查詢準則的情況下自動查找服務實例的過程,例如,一個名叫Teams的服務需要找到一個名為Players的服務實例,其中該實例的environment屬性設置為production。你將調用一些服務發現進程,它們會返回一個滿足條件的服務清單。對於更集中的架構而言,這是一個非常簡單的任務,可以通常使用DNS、負載等化器和一些埠號的約定(例如,所有服務將HTTP伺服器綁定到8080埠)來實現。而在更分散的環境中,任務開始變得越來越複雜,以前可以通過盲目信任DNS來查找依賴關係的服務現在必須要處理諸如用戶端負載均衡、多種不同環境、地理位置上分散的伺服器等問題。如果之前只需要一行代碼來解析主機名稱,那麼現在你的服務則需要很多行代碼來處理由分散式引入的各種問題。

斷路器是由Michael Nygard在其編寫的“Release It”一書中引入的模式。我非常喜歡Martin Fowler對該模式的一些總結:

斷路器背後的基本思路非常簡單。將一個受保護的函式呼叫包含在用於監視故障的斷路器物件中。一旦故障達到一定閾值,則斷路器跳閘,並且對斷路器的所有後續調用都將返回錯誤,並完全不接受對受保護函數的調用。通常,如果斷路器發生跳閘,你還需要某種監控警報。

這些都是非常簡單的設備,它們能為服務之間的交互提供更多的可靠性。然而,跟其他的東西一樣,隨著分散式水準的提高,它們也會變得越來越複雜。系統發生錯誤的概率隨著分散式水準的提高呈指數級增長,因此即使簡單的事情,如“如果斷路器跳閘,則監控警報”,也就不那麼簡單了。一個元件中的一個故障可能會在許多用戶端和用戶端的用戶端上產生連鎖反應,從而觸發數千個電路同時跳閘。而且,以前可能只需幾行代碼就能處理某個問題,而現在需要編寫大量的代碼才能處理這些只存在於這個新世界的問題。

事實上,上面舉的兩個例子可能很難正確實現,這也是大型複雜庫,如Twitter的Finagle和Facebook的Proxygen,深受歡迎的原因,它們能避免在每個服務中重寫相同的邏輯。

大多數採用微服務架構的組織都遵循了上面提到的那個模型,如Netflix、Twitter和SoundCloud。隨著系統中服務數量的增加,他們發現了這種方法存在著各種弊端。

即使是使用像Finagle這樣的庫,專案團隊仍然需要投入大量的時間來將這個庫與系統的其他部分結合起來,這是一個代價非常高的難題。根據我在SoundCloud和DigitalOcean的經驗,我估計在100-250人規模的工程師組織中,需要有1/10的人員來構建模型。有時,這種代價很容易看到,因為工程師被分配到了專門構建工具的團隊中,但是更多的時候,這種代價是看不見的,因為它表現為在產品研發上需要花費更多的時間。

第二個問題是,上面的設置限制了可用於微服務的工具、運行時和語言。用於微服務的庫通常是為特定平臺編寫的,無論是程式設計語言還是像JVM這樣的運行時。如果開發團隊使用了庫不支援的平臺,那麼通常需要將代碼移植到新的平臺。這浪費了本來就很短的工程時間。工程師沒辦法再把重點放在核心業務和產品上,而是不得不花時間來構建工具和基礎架構。那就是為什麼一些像SoundCloud和DigitalOcean這樣的中型企業認為其內部服務只需支援一個平臺,分別是Scala或者Go。

這個模型最後一個值得討論的問題是管理方面的問題。庫模型可能對解決微服務架構需求所需功能的實現進行抽象,但它本身仍然是需要維護的元件。必須要確保數千個服務實例所使用的庫的版本是相同的或至少是相容的,並且每次更新都意味著要集成、測試和重新部署所有服務,即使服務本身沒有任何改變。

下一個邏輯

類似於我們在網路通訊協定棧中看到的那樣,大規模分散式服務所需的功能應該放到底層的平臺中。

人們使用高級協定(如HTTP)編寫非常複雜的應用程式和服務,甚至無需考慮TCP是如何控制網路上的資料包的。這種情況就是微服務所需要的,那些從事服務開發工作的工程師可以專注于業務邏輯的開發,從而避免浪費時間去編寫自己的服務基礎設施代碼或管理整個系統的庫和框架。

將這個想法結合到我們的圖表中,我們可以得到如下所示的內容:

不幸的是,通過改變網路通訊協定棧來添加這個層並不是一個可行的任務。許多人的解決方案是通過一組代理來實現。這個的想法是,服務不會直接連接到它的下游,而是讓所有的流量都將通過一個小小的軟體來透明地添加所需功能。

在這個領域第一個有記載的進步使用了邊三輪(sidecars)這個概念。“邊三輪”是一個輔助進程,它與主應用程式一起運行,並為其提供額外的功能。在2013年,Airbnb寫了一篇有關Synapse和Nerve的文章,這是“邊三輪”的一個開源實現。一年後,Netflix推出了Prana,專門用於讓非JVM應用程式從他們的NetflixOSS生態系統中受益。在SoundCloud,我們構建了可以讓遺留的Ruby程式使用我們為JVM微服務構建的基礎設施的“邊三輪”。

雖然有這麼幾個開源的代理實現,但它們往往被設計為需要與特定的基礎架構元件配合使用。例如,在服務發現方面,Airbnb的Nerve和Synapse假設了服務是在Zookeeper中註冊,而對於Prana,則應該使用Netflix自己的Eureka服務註冊表 。

隨著微服務架構的日益普及,我們最近看到了一波新的代理浪潮,它們足以靈活地適應不同的基礎設施元件和偏好。 這個領域中第一個廣為人知的系統是Linkerd,它由Buoyant創建出來,源于他們的工程師先前在Twitter微服務平臺上的工作。很快,Lyft的工程團隊宣佈了Envoy的發佈,它遵循了類似的原則。

Service Mesh

在這種模式中,每個服務都配備了一個代理“邊三輪”。由於這些服務只能通過代理“邊三輪”進行通信,我們最終會得到類似於下圖的部署方案:

Buoyant的首席執行官威廉·摩根表示,代理之間的互連形成了服務網格。 2017年初,威廉寫下了這個平臺的定義,並稱它為服務網格:

服務網格是用於處理服務到服務通信的專用基礎設施層。它負責通過複雜的服務拓撲來可靠地傳遞請求。實際上,服務網格通常被實現為與應用程式碼一起部署的羽量級網路代理矩陣,並且它不會被應用程式所感知。

這個定義最強大的地方可能就在於它不再把代理看作是孤立的元件,並承認它們本身就是一個有價值的網路。

隨著微服務部署被遷移到更為複雜的運行時中去,如Kubernetes和Mesos,人們開始使用一些平臺上的工具來實現網格網路這一想法。他們實現的網路正從互相之間隔離的獨立代理,轉移到一個合適的並且有點集中的控制面上來。

來看看這個鳥瞰圖,實際的服務流量仍然直接從代理流向代理,但是控制面知道每個代理實例。控制面使得代理能夠實現諸如存取控制和度量收集這樣的功能,但這需要它們之間進行合作:

最近公佈的Istio專案是這類系統中最著名的例子。

完全理解服務網格在更大規模系統中的影響還為時尚早,但這種架構已經凸顯出兩大優勢。首先,不必編寫針對微服務架構的定制化軟體,即可讓許多小公司擁有以前只有大型企業才能擁有的功能,從而創建出各種有趣的案例。第二,這種架構可以讓我們最終實現使用最佳工具或語言進行工作的夢想,並且不必擔心每個平臺的庫和模式的可用性。

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示