開發與維運

雲原生下的開發測試實踐

1.png

【以下為分享實錄,有刪節】

測試環境管理之困與阿里巴巴的解決之道

在雲原生時代下,軟件的迭代速度越來越快,對測試的要求也越來越高,很多開發者開始使用Kubernetes來管理測試環境。在這個過程中,開發者會遇到很多困難,其中最主要的兩個問題是:一、本地環境與Kubernetes集群網絡不通問題;二、共用測試環境時,相互干擾的問題。

在阿里巴巴內部,我們主要通過扁平的內網IP和項目環境兩個機制來解決以上痛點。扁平的內網IP主要是基於CNI(Conteinre Network Interface) 機制改造Kubernetes的IP邏輯實現的,可以使集群中的每個Pod分配到的IP與本地機器分配到的IP處於一個大的網絡環境下,這樣就可以解決本地環境和集群之間互通的問題。項目環境是基於RPC、消息中間件的虛擬環境,從表面上看,每個項目環境都是一套獨立完整的測試環境,由一系列服務組成集群,而實際上,除了個別當前使用者想要測試的服務,其餘服務都是通過路由系統和消息中間件虛擬出來的,指向公共基礎環境的相應服務。這樣操作的好處是,第一不會佔用大量的開發資源;第二,不會影響公共基礎環境的穩定性。

2.jpg

阿里巴巴的這種測試環境帶來的測試體驗非常爽,本地與集群雙向互通,每個子項目都可以獨佔一個“項目環境”,彼此不會干擾。但是這種測試環境管理方式實施起來比較複雜,只適合大型的集團公司,我們希望將這種測試體驗以 “更輕”的方式實現,普惠更多開發者。於是雲效團隊推出了雲原生測試環境工具箱,主要包括kt-connect和kt-virtual-environment這兩個開源工具。

通過kt-connect解決本地與集群雙向互通問題

首先,我們來聊一聊如何使用Kubernetes測試環境開源工具箱中的kt-connect解決 “本地與集群雙向互通”的問題。

我們假設有一位主人公叫“程序員小黑”,他所在公司採用了微服務相關的技術實踐。小黑在做本地開發的過程中,會遇到兩種聯調場景,即我需要聯調其它服務和其它服務調用我。

我們先來看第一個場景:聯調其它服務。前面講到本地環境和K8S集群中的網絡是相互隔離的,那麼在本地完成代碼開發後,如何高效的與其它服務進行聯調呢?最“簡單粗暴”的一種方式,就是在本地完整的部署一套測試環境,這種方式的優點很明顯,效率最高,而且沒有外部依賴。缺點也同樣明顯,一是對開發人員的素質要求非常高,二是要佔用大量本地的開發資源,對本地開發機的性能要求很高。第二種方式是利用Kubernetes等工具在本地部署一套K8S集群,這種方式可以在一定程度上降低開發人員“上手成本”,但是也同樣存在佔用大量本地開發資源的問題。還有一種處理方式是利用VPN調用K8S集群中的服務,這種方式存在兩個問題,一個是本地的服務調用是脫離了Istio的流量控制的,第二個問題VPN這種方式只解決了本地到集群的通訊,對於回調這種方式是解決不了的。
3.jpg

那麼,有沒有更簡單的方式呢?答案是肯定的,通過KT-Connect工具可以讓開發者一鍵建立本地到Kubernetes集群的網絡連接。開發者只需要運行一個“connect”命令,就可以自動在集群中創建一個代理容器,並且通過這個代理容器建立本地與集群的VPN連接,同時KT-Connect也會內置DNS服務,本地服務可以直接通過服務的名稱進行服務調用,就好像本地的程序運行在K8S集群中一樣。 在KT-Connect中,我們給這個代理容器取了一個名字“shadow pod”,即“影子”。它就像是本地服務在集群中的影子,通過它來完成本地服務與集群中服務的相互調用。

接下來,我們來看第二個場景:其它服務調用我。在聯調時,小黑不僅需要調用其它人的服務,同時作為服務的生產者又會被其它服務調用。這時只打通本地到集群的服務是不能滿足聯調測試需求的,必須同時讓集群中的服務也可以訪問本地的服務。

4.jpg

這就需要使用KT-Connect 的第二個命令——“Exchange”。Exchange(交換)命令會在集群內部署一個代理容器,並替換集群中指定服務的目標容器,從而將發往該服務的所有流量攔截並轉發到本地端口。這是一種“完全替換”,這就意味著在同一時間點,只有一位開發者可以將他本地的服務加入到集群中。為了解決這個問題,KT-Connect提供了第三個命令——Mesh(混合)。當我們在本地運營“Mesh”命令後,我們同樣會把本地服務加入到集群裡面,但是會保持原有的目標服務狀態不變,並且本地服務會繼承目標服務中所有的標籤。依照K8S本身服務發現的原理,請求流量會被隨機地轉發到原有服務或本地的服務。同時配合Istio的流量規則,就可以讓所有正常流量依然保持對原應用的訪問,而只對一些有特殊標記的的請求轉發到本地。從而可以實現在一套公用測試環境的基礎上各自獨立的完成本地的集成聯調。

KT-Connect背後的原理

我們通過KT-Connect的“connect”“Exchange” “Mesh”命令實現了本地到集群的雙向網絡互通,看起來似乎很神奇,其實背後並沒有什麼黑科技。我們只是綜合利用了Kubernetes原有的特性及SSH這個我們經常會用到的網絡協議,通過Kubectl的端口轉發可以實現將集群中服務的端口映射到本地,通過SSH協議建立本地與集群之間的隧道。

5.jpg

以“Connect”命令為例,瞭解一下KT-Connect背後的原理。如上圖所示,當開發者在本地運行“Connect”命令後,首先會創建一個代理容器(shadow pod),這個代理容器會運行“SSH Server”和“DNS Server”。當代理容器啟動成功之後,就可以通過“port-forward”將代理容器的22端口轉映射到本地,如2222端口。此時,本地服務就可以通過本地的2222端口建立與集群內部的連接。

6.jpg

“Exchange”和與“Connect”命令背後的原理類似,我們也會先創建一個代理容器,並通過“port-forward”將代理容器的端口映射到本地。然後根據Exchange的目標服務,判斷將代理容器的哪一個端口的請求全部轉發到本地的特定端口。

“Mesh”與“Exchange”的最大的差異在於“Exchange”會將原應用的副本數直接降到0,會將集群內所有對原應用的流量全部轉發到本地。而 “Mesh”則是在保持原有應用Pod不變的前提下,創建一個新的代理容器並且繼承原應用的所有標籤,還會增加一個隨機的version標籤。這時我們就可以通過Istio規則, 精確控制流量。

7.jpg

如前文所述,通過KT-Connect我們可以實現從本地到集群的雙向互通。我們可以看到這樣一個有趣的場景:小黑A可以通過“Mesh”命令把本地服務加入到測試環境裡,並且可以讓集群中一部分特定的流量轉發到本地,這樣他可以與集群中的其它服務進行聯調。小黑B通過“Connect”命令連接到集群,直接在本地進行測試。在某些情況下,小黑B需要依賴的服務剛好是小黑A正在開發的版本,小黑B只要按照Istio的流量規則設置可以調用小黑A服務的值,就可以跟小黑A的服務進行聯調。對於小黑C,他既需要調用集群中的服務,又需要集群中的服務回調到本地,所以他在本地既運營了 “Connect”又運行了“Mesh”。

上文描述的其實是一種理想情況,需要建立在我們的測試環境是一種穩定狀態的基礎之上。但在現實情況下,由於頻繁的代碼變更測試環境往往處於一種不太穩定的狀態。接下來我們會介紹如何使用雲原生測試環境工具箱中的kt-virtual-environment打造穩定的測試環境,讓開發者可以更好地進行協同研發工作。

共用測試環境相互干擾問題及常見解決方案

在一箇中大規模研發團隊負責的項目中,往往一個系統裡包含許多(微)服務,而且服務之間存在鏈式依賴,難以獨立啟動運行。這時就很容易出現“共用測試環境相互干擾”的問題,比如一個開發者重新部署、重啟測試環境時,可能會打斷所有正在測試的開發者;一個開發者提交了有BUG的代碼,所有開發者都可能受影響;一個開發者為了排查問題,單步調試測試環境服務時,所有開發者測試請求會被攔截。

如何來解決這個問題呢?以往的思路是準備多套測試環境。雖然這種方式可以暫時緩解開發過程中的相互影響,但是這會帶來額外的資源分配和管理問題,特別是當沒有那麼多並行開發時會產生非常嚴重的資源浪費。

於是出現了一種“改進”方法,企業通過用helm或自制工具自動化地快速創建一套環境,用完即刪。該方法在一定程度上解決閒置資源回收的問題,但是也沒有那麼“完美”。在實際操作過程中,環境的創建其實並沒有那麼“快速”,往往需要等待幾分鐘甚至幾十分鐘的時間。而且如果為每個子項目的成員分別拉一套環境,資源浪費依然嚴重。

在多人協同場景下,如何做到測試環境不相互干擾又不產生極大的資源浪費呢?在阿里巴巴內部主要通過“項目環境”的方案解決。

“項目環境”的本質是基於路由隔離實現的一個“虛擬環境”。我們通過一個實例來簡單瞭解一下。

8.jpg

如上圖中藍色部分所示,由服務A、服務B、服務C、服務D組成一個完整的測試環境,我們稱為“公共基礎環境”(也稱默認環境)。當某位開發者需要進行項目開發的時候,他不需要把應用或服務部署到公共基礎環境中,而是單拉出來一部分資源(服務C和服務D)並複用部分公共基礎環境資源(服務A和服務B)形成 “項目環境”。這樣操作的好處是,第一不會佔用大量的開發資源;第二,不會影響公共基礎環境的穩定性。

我們主要通過打“環境標”的形式形成獨立的“隔離域”,比如[dev]代表公共基礎服務實例的標籤值,[dev.proj]代表項目環境的服務實例。同一個環境標形成一個獨立的“隔離域”,這是基於路由規則實現的。如果服務請求是來自一個有環境標的服務實例,它的服務請求會優先尋找跟它具有相同環境標的實例,如果沒有,會尋找它上一級的環境標,這叫做“路由兜底”。比如上圖中服務請求來自帶有[dev.proj]環境標的實例C,需要調用服務B,但是發現沒有帶環境標[dev.proj]的服務B,於是尋找帶有上一級環境標[dev]的服務B。

9.jpg

我們還可以將本地開發機加入項目環境。比如開發者“小黑”在本地啟動了一個服務實例C,他給這個服務實例打的環境標是[dev.proj.local],通過前面介紹路由規則我們瞭解到帶環境標的服務發出的請求會優先尋找帶相同環境標的服務,如果找不到則會尋找帶有上一級環境標的服務,於是服務C[dev.proj.local]、服務D[dev.proj]和公共基礎環境中服務A[dev]、服務B[dev]就組成了一個新的的“項目環境”(圖中紅色部分)。

10.jpg

這時“小黑”的同事也加入了項目,他在本地啟動了一個服務A,如果他沒有對這個服務打“環境標”的話,他會默認使用“公共基礎環境”進行測試。這時小黑在他自己的“項目環境”中的任何調試都不會影響到小黑的同事,反之亦然。

11.jpg

後來小黑的同事和小黑加入了同一個子項目,他們之間需要“聯調”。這時,小黑的同事只要給他本地的服務打上一個和小黑的“項目環境”相同的環境標即可,如上圖紅色部分。

總結一下前面介紹的概念:“隔離域”是由路由規則形成的虛擬邊界;每個“環境標”都會形成一個獨立的“隔離域”;“隔離域”之間可以存在部分或完全重合;“隔離域”的成員會隨集群中服務實例所帶“環境標”動態變化。

如何使用kt-virtual-environment 打造項目環境

kt-virtual-environment是一種基於Service Mesh的微服務環境複用工具,源於阿里巴巴內部的項目環境實踐。通過Pod上的虛擬環境標籤,kt-virtual-environment能夠自動將測試環境網絡動態隔離成多個虛擬隔離域,同時以簡單規則在隔離域間局部複用Pod實例,從而達到只需很少資源成本即可創建大量不同微服務版本組合的獨立測試環境的目的。

12.jpg

下面我們來了解一下如何使用kt-virtual-environment打造項目環境。通過前面的介紹,我們知道相同的“環境標”會形成一個獨立的“隔離域”。所以首先我們需要為服務實例打上環境標,在kt-virtual-environment中是通過為為pod(或容器)添加約定的Label的方式實現的。在服務調用過程中,需要為應用程序添加一些邏輯,才能讓“環境標”順利在上下文之間傳遞,這個過程類似SkyWalking、Zipkin等鏈路追蹤工具的SDK端所做的事情,讓“環境標”通過HTTP頭在請求鏈路上一直保持傳遞。第三步,我們需要在集群中配置Virtual Environment類型的資源實例,詳細配置的結構如上圖所示。

13.jpg

kt-virtual-environment實現的基本原理是:觀察並持續監聽環境中的所有服務和開發資源,動態生成Service Mesh控制面規則,實現核心隔離邏輯 。當前kt-virtual-environment僅支持基於Istio的規則,未來會增加基於其它 控制面的Service Mesh規則的實現。

阿里巴巴使用項目環境的最佳實踐

14.jpg

“項目環境”在阿里巴巴內部已近發展多年,下面我將一些優秀的實踐分享給大家。理論上,“環境標”的層級可以無限多,但是我們的經驗是最好不要超過三級。因為三層的“環境標”基本上可以滿足95%以上的研發場景。

以三層環境標實現的項目環境舉例。第一層一般只有一個環境標,比如[dev],這是對應默認隔離域(公共環境)使用的環境標,這樣做會讓整個路由規則比較簡單,大家不用猜測“路由兜底”最後會“兜”到哪裡去。這個頂級的[dev]環境標對應的測試環境是不需要開發者自己去部署的,一般是通過“流水線”等自動化工具部署的穩定版本。對於某個子項目,我們可以基於頂級環境標建立一個二級環境標,如[dev.proj1],這樣會形成這個子項目的隔離域(項目環境)。在這個隔離域中,只需要開發者自己部署需要改動的服務實例即可,其它不需要改動的服務實例可以複用公共環境中實例。有的時候,某位開發者可能要對某服務進行比較大的改動或者他不希望這個服務被其它同事訪問到,他可以基於“項目環境”再創建一個“個人環境”。在這個個人環境中,他既可以調用子項目中的服務,也可以調試本地開發的新的服務版本,並且不會影響到其他開發者。以上,是我們比較推薦的項目環境的用法。

總結:

雲原生測試環境工具箱共包含兩款獨立的工具:kt-connect和kt-virtual-environment。kt-connect是一款本地工具,主要是幫助開發者打通本地和集群網絡,實現本地加入隔離域。kt-virtual-environment是一種基於Service Mesh的微服務環境複用工具,通過觀察並持續監聽環境中的所有服務和開發資源,動態生成Service Mesh控制面規則,實現核心隔離邏輯 。只需要一次性部署,開發者不會頻繁使用到。目前兩款工具已經開源,大家可以進入Github社區進行下載使用。

15.jpg

Leave a Reply

Your email address will not be published. Required fields are marked *