如何在Kubernetes中使用OpenTelemetry的沙盒
譯文譯者 | 李睿
審校 | 重樓
51CTO讀者成長計劃社群招募,咨詢小助手(微信號:CTOjishuzhan)
OpenTelemetry項目于2019年推出,是之前就已經存在的OpenTracing和OpenCensus這兩個項目的結合,其目標是成為從基于分布式微服務的應用程序中提取遙測數據的單一開放標準。該項目是一個規范、工具和庫的集合,旨在幫助以日志、度量和跟蹤的形式從應用程序收集遙測數據,然后可以將其轉發到支持聚合、可視化和內省這些數據的可觀察性工具。
本文將探索一個利用OpenTelemetry(縮寫為OTel)的新用例,特別是由OTel啟用的分布式場景傳播,以創建稱為“沙盒”的輕量級環境,然后可以用于以可擴展的方式啟用各種形式的微服務測試。此外,還將研究哪種類型的應用程序最自然地使用這個模型,以及它如何幫助用戶在開發生命周期的早期獲得高質量的測試反饋。
一、超越OpenTelemetry的可觀察性
OTel的設計用例之一是跟蹤。跟蹤提供了對分布式系統如何處理用戶或應用程序請求的理解,該分布式系統涉及許多獨立的組件。
1.跟蹤的工作原理
每個微服務在處理請求時,都會發出一些被稱為“span”的信息,其中包含一些關于它正在處理的請求的場景。而一旦所有這些span被發送到某個后端,它們就可以拼接在一起,告訴整個請求流,這被稱為“跟蹤”。跟蹤可用于了解特定請求發生了什么,也可用于在聚合場景中發現和處理可能影響某些請求子集的模式。這些關于請求通過由多個微服務組成的系統所經過路徑的信息,可以有效地隔離和推斷生產問題發生時的根本原因。
2.廣義場景傳播
當查看下面的圖表時,可以看到每個單獨的服務只發出它自己的“span”,這與它在本地處理請求的方式有關。因此,為了將屬于單個請求的范圍綁定到單個跟蹤中,需要在這些標識請求本身的服務之間傳播某種形式的場景,這被稱為跟蹤場景。
OTel通過它的庫來實現這一點,它負責沿著同步/異步請求鏈在服務之間傳遞跟蹤標識符。更重要的是,對于一些動態語言,它只需要放入相應的OTel庫,該庫“自動檢測”字節代碼以透明地執行分布式場景傳播。
這種沿請求流傳播場景片段的想法雖然起源于跟蹤,但并不是專門與跟蹤相關。
它是一種更通用的機制,可以利用它在微服務之間傳播更多與跟蹤相關的信息。有關Baggage規格的解釋明確地說明了這一點。
該規范定義了一個標準,用于表示和傳播一組與分布式請求或工作流執行相關的應用程序定義的屬性。這是獨立于跟蹤場景規范的。無論是否使用分布式跟蹤,都可以使用Baggage。
該規范標準化了應用程序定義的屬性的表示和傳播。與其相反,跟蹤場景規范標準化了啟用分布式跟蹤場景所需元數據的表示和傳播。
現在開始利用這個機制來實現超越遙測和追蹤的用例。
二、使用OpenTelemetry的沙盒
沙盒是一種輕量級環境,可用于測試和驗證對堆棧中微服務子集的更改。沙盒與傳統環境的關鍵區別之一是,沙盒使用一組未更改的共享依賴項(其名稱為“基線”),以測試一些已經更改的服務。
這個屬性使得沙盒資源的效率很高,并且設置起來也很快。反過來,這意味著用戶可以啟動一個沙盒來測試單個提交,或者對每個微服務的單個更改,這在具有中等規模(>20左右)的傳統微服務環境中變得成本高昂。
這種方法的另一個關鍵優勢是,這些沙盒可以部署到現有的環境中,例如部署或生產環境中,以針對最新的依賴項測試更改,從而消除了過時依賴關系的問題,以及由于它們之間的差異而不得不在“更高”的環境中反復重新測試的問題。
以下將了解如何利用OTel中的分布式場景傳播在Kubernetes中創建沙盒。
1.請求路由
由于采用OTel庫和工具,一旦應用程序能夠跨請求攜帶任意場景片段,現在就可以開始考慮使用它來隔離請求及其相互影響。
可以通過利用請求級多租戶來創建沙盒。這是通過為每個請求分配一個特定的“租戶”來完成的。租戶只是指與每個請求相關聯的標識符,告訴系統如何處理和路由特定的請求。在下面的示例中,租賃由“租戶”標頭(或者說L7協議元數據)指定,并具有一個值,該值標識應將其路由到服務的哪個特定測試版本。該租賃元數據使用OTel中的Baggage機制在服務之間傳遞,并在后臺使用HTTP/gRPC標頭。
以一個簡單的微服務應用程序為例,它包含一個前端和后端,可以設置如下內容:
可以教會前端根據租約有條件地將正在發出的出站請求路由到后端。在Kubernetes中,做出路由決策是很簡單的,因為有幾種機制可以在應用程序不參與決策的情況下處理它。例如,可以使用服務網格(如Istio),這使得這種路由操作很容易在網格本身的規則中表示。創建一個Sidecar容器,利用特使或自定義代碼來攔截請求,并在每個微服務上透明地代理請求,這是非常簡單的。
上面創建的這個設置也可以擴展到測試中的多個工作負載。這種設置可以很容易地在現有的登臺和生產集群中進行設置,只需很少的工作。登臺和生產通常已經有一個持續部署(CD)流程,用于更新服務的穩定版本(基線),并且在同一集群中部署這些沙盒們能夠在不測試這些依賴關系時重用它們。
只需使用上面所示的請求隔離,這個沙盒就可以用于測試對無狀態服務的更改、運行API測試等。但是它仍然有局限性,因為還沒有解決隔離可能產生副作用的數據突變、通過消息隊列隔離異步工作流或調用第三方API的問題。以下將解決這些問題,使沙盒更加健壯,并能夠在微服務環境中支持更多類型的測試。
2.狀態隔離
使用這種方法在更多用例中執行更復雜的測試的一個重要步驟是解決狀態隔離問題。在這里需要注意的一點是,并不是每個涉及微服務測試的用例都需要額外的狀態隔離。例如,在人工或使用自動化為特定流運行API或E2E測試時,如果在特定流中,能夠創建和清理在測試中使用的實體,那么已經實現了氣密測試,而實際上不需要額外的狀態隔離。
但是,在有些情況下需要額外的隔離,例如在數據存儲上執行DDL更改,或者如果可能執行一些有副作用的突變。在這些情況下,可能希望部署可能受到影響的數據存儲的隔離版本,并使沙盒工作負載使用它們,而不是基線環境使用的數據存儲。
人們可以看到,在實踐中,盡可能地使用邏輯隔離而不是基礎設施隔離要有效得多。例如,在像MySQL和PostgreSQL這樣的數據庫以及大多數托管云計算數據庫中,數據基礎設施本身提供了一個內置的邏輯隔離概念。這種邏輯隔離的選擇使沙盒保持輕量級、資源高效,并且仍然能夠在幾秒鐘內啟動,而這對于大多數情況來說已經足夠了。當然,如果有必要,總是可以啟動新的物理基礎設施來與沙盒相關聯,但在實踐中發現這種需求非常罕見。
建立基礎設施只是解決方案的一部分;播種數據是關鍵,必須在環境啟動時/之前執行。可以將所有這些短暫數據存儲的生命周期與環境本身的生命周期聯系起來,也可以選擇設置和維護預先預熱的數據存儲,這些數據存儲是根據需要提供給服務的測試版本的。后一種使用預熱測試數據存儲的方法可以幫助準備好高質量的數據,并且在實踐中,即使對于大型微服務堆棧,需要保留的此類測試數據存儲數量也不是特別多。一些沙盒方法的高級用戶更進一步,實現了將租賃與特定類型的數據集相關聯的機制,例如在Uber關于SLATE的博客文章中。
使用短暫的隔離數據存儲,可以提供高質量的數據進行開發/測試,同時確保在環境之外沒有可見的副作用。使用邏輯隔離的想法特別有助于擴展沙盒的數量,同時將額外的基礎設施或運營成本降至最低。與環境選擇最高隔離級別作為默認隔離級別的流行概念相反,可以根據應用程序的用例和性質為沙盒選擇最合適的隔離類型。
3.消息隊列
消息隊列在現代微服務堆棧中無處不在,當涉及到這些沙盒時,它提出了一個有趣的挑戰。考慮到消息隊列,消息隊列的大多數(如果不是全部)實現允許在消息中設置一些報頭/元數據。這意味著消息本身包含一些關于它們來自哪里的請求/流的標識的信息,這提供了一些關于如何進行消息路由的想法。
對于消息隊列,通常也首選邏輯隔離,而不是物理隔離。因此,如果Kafka是消息隊列,更愿意設置單獨的主題,而不是建立新的孤立的Kafka集群。
雖然這個解決方案保證有效,但實際上,在大型微服務環境中有許多生產者和消費者,這使得它不太理想,這是因為它可能最終要求孤立地支持每個微服務。一種更清潔的隔離方法是教會消費者更有選擇性地以“租戶意識”的方式消費。
這種讓使用者能夠感知租戶的方法需要在應用程序層進行一些更改。該功能可以很容易地封裝到一個“消息路由器”庫中,可以與使用者客戶端一起使用。這個庫將根據使用者服務的標識(它是基線嗎?它屬于哪個沙盒?)和包含租賃信息的消息元數據。這個庫需要了解集群中不同沙盒的狀態信息,以便在每個使用者中做出決策,這要求它與運行在Kubernetes集群中的某些路由控制器(或服務網格)進行對接。
這種方法解決了一個長期存在的問題,即消息隊列在微服務堆棧中難以測試,它為具有租戶意識的消費者提供了一種多路復用不同流的干凈方式,并且不需要額外的基礎設施設置,這使得它的設置非常快。
4.第三方API
現代微服務棧利用許多第三方API和外部服務來提供重要的功能,例如支付、集成等,這些功能可以通過外部API端點和Webhook與Kubernetes中的微服務進行對接。在第三方API中,傳播租戶信息的方法因系統而異。
在許多情況下,這些API只是“即發即棄”,或者具有由應用程序本身控制的副作用,在這種情況下,可能不需要通過外部系統進行租賃傳播。
在需要外部系統回調或Webhook在微服務中執行一些額外處理的系統中,HTTP回調URL中的查詢參數可以用于傳播租賃信息。Kubernetes集群內部的路由機制將查找包含租賃信息的特定查詢字符串,并使用該查詢字符串將這些回調請求路由到適當的沙盒工作負載。
在實踐中,這種機制通常需要對應用層進行一些較小的修改。微服務還必須知道它自己的租期。也就是說,擁有關于它所屬的沙盒的信息,這樣它就可以在回調時設置適當的租期元數據。
三、在實踐中實現沙盒
沙盒方法已經在Lyft、Uber和Doordash的數百個微服務中大規模實現,這允許一個非常有效和可擴展的機制來快速測試和迭代。用戶在與企業合作中吸取了一些經驗教訓,可以幫助他們在自己的基礎設施中采用沙盒。
在實踐中,對于想要開始采用這種方法的用戶,驗證的最簡單的方法是首先將沙盒集成到一些微服務的持續集成(CI)流程中。這些沙盒可以在現有的登臺環境中創建,這只需要很少的額外設置,并消除了與共享登臺環境相關的瓶頸。從這一點開始,隨著OTel在企業內的采用和場景傳播中的空白被填補,很容易擴展沙盒,以在不同微服務之間進行變更的預合并測試、端到端測試和性能測試。隨著采用率的增長,隨著越來越多的用例可以更快、更容易地使用沙盒解決,對額外測試環境或staging環境的需求急劇下降是很典型的。
此外還發現,服務之間的合并前測試,例如將屬于不同服務的測試沙盒放在一起,并通過它們運行流量,在開發生命周期中非常有用,并且在開發迭代和反饋方面比目前常見的合并后測試要快得多。
在生產環境中使用沙盒進行測試是終極目標,它最終能夠完全消除分段。一旦租賃控制和數據隔離機制在組織內足夠成熟,這最終是可能的,但對于希望使用沙盒的組織來說,這通常不是最佳的起點。
雖然大多數微服務通信模式都很適合這個范例,但也有一些模式是困難的,例如,測試Kubernetes基礎設施組件或在自定義協議上使用低級網絡的組件。對于這些情況,更傳統的設置環境的方式或不同的隔離方法可能比使用沙盒更合適。
四、結論
本文探索了沙盒的范例,沙盒是一種利用請求級隔離來提供可擴展的微服務測試輕量級環境。還研究了在內部實現有效沙盒解決方案的不同技術,例如使用OTel傳播場景、請求路由、狀態隔離以及處理消息隊列和第三方API。最后還研究了對于希望在內部實現沙盒的組織的一些實際考慮。
沙盒方法已經在一些行業中被有效地使用,以幫助加快和改進開發生命周期。Signadot正在構建一個Kubernetes原生解決方案,使創建和部署沙盒變得容易,能夠幫助實現這一點。
原文鏈接:https://dzone.com/articles/sandboxes-in-kubernetes-using-opentelemetry