在雲原生環境中,基于Kubernetes的工具鍊一方面簡化了開發者的許多日常瑣碎,另一方面也帶來了許多新的概念和工作方式的改變。本篇文章将聚焦于雲原生基礎設施,談談如何在面向雲原生的開發流程中,高效地進行開發調測以及釋出。
首先,從通用意義上講,作為一個開發者,你期望怎樣的研發流程?

我了解的理想研發過程
基于這個理想的研發流程,當研發基礎設施遷移到雲原生和微服務架構之後,在開發、調試和釋出方面會遇到什麼問題,又要如何解決?
一個典型的研發過程包含三個環節:開發、測試和部署。
開發主要指的是代碼的編寫和自測。當你寫好代碼和單元測試之後,需要在一個運作環境中進行功能性驗證。本地IDE提供了大量用于調測的功能,并且本地服務的重新啟動速度也比較快。相比将代碼部署到遠端的測試環境,能夠完全在本地進行編寫、啟動、debug,是最理想的工作方式。
實際的工作中,為了驗證一個特定功能場景,往往需要配合其它的外部依賴,比如目前我編寫的這個服務依賴什麼别的服務;還有什麼其他服務需要調用我的服務,我才能做一個完整的驗證?
這些問題都需要很好的解決,才能真正享受到本地開發的便利。
測試一般指的是在CI環境中的各種自動化測試和驗收環境中的測試驗證,本文的重點不在這裡,是以假設我們已經完成了這些操作。來到了部署的環節。
雖然已經經過了很多的驗證,我們對要釋出的版本的品質已經有了相當程度的信心。但釋出發上線之後,也不可避免的時不時會引入一些缺陷,是以如何讓出現的這些問題的影響面最小,就是所謂的穩健釋出。
首先來關注在雲原生下的開發和調試。
随着微服務技術和各類開源服務元件普及,如今的軟體系統或多或少的都會包含數個互相獨立的服務實體,之間通過接口調用互相連接配接。是以在本地進行服務測試的時候,難免涉及到與上下遊的其他服務的互動,特别是在進行完整功能驗證時,常常需要在本地将上下遊鍊路的所有服務全部啟動起來。然而随着系統的演進和服務的增多,本地資源很快就無法支撐整體系統啟動了。那麼,是否能夠将測試環境中的公共服務節點和本地服務串聯在一起,行成完整的測試鍊路呢?
在雲原生的環境下,測試環境被Kubernetes的叢集網絡邊界所隔離。從叢集外部通路測試叢集中的服務需要經過統一的Ingress網關,且隻能通路配置了網關路由的一小部分部分服務。同時由于開發者的本地主機通常沒有公網IP位址,從測試環境中完全無法連接配接到本地的服務執行個體。
為此雲效創造了kt-connect工具來解決本地測試時的網絡聯通問題,它能夠在開發者的本地環境和Kubernetes測試叢集之間,建立起一條虛拟的雙向網絡通路。
kt-connect是一款簡單易用的指令行工具。對于從本地連接配接測試環境的情況,它提供了一個connect指令,利用在叢集中部署一個作為網絡代理的Pod節點,使得從本地網絡能夠直接通路叢集中的任意Service域名、IP位址和任意Pod的IP位址。而對于其從叢集通路本地的情況,kt-connect提供了exchange指令,通過另一個反向的代理節點實作将叢集中流入指定服務的所有請求導向到本地的指定端口。
對于個人開發者的使用場景來說,以上兩個指令就能夠完全滿足日常工作了。然而對于團隊開發的場景下,則會帶來新的問題。當一個開發者使用了exchange指令,将特定服務執行個體的流量全部導向本地,在同一個叢集中工作的所有其他開發者都會随之受到影響。為了避免這樣的互相幹擾,kt-connect又創造了第三種指令mesh,它的功能與exchange指令相似,但并不會将網絡中的所有流量全部導入到開發者本地,而是基于特定的網格規則,隻将符合要求的測試流量導向開發者的本地環境,進而實作測試環境資源的最大化使用率和多項目的和平共處。
從本質上來說,kt-connect主要利用了Kubernetes原生指令行的端口轉發和開源SSH工具的四層網絡代理能力實作,對應用本身不産生任何侵入,目前我們已經将它的所有源代碼在Github開源。
接下來來到釋出的環節。
雲原生基礎設施内置了滾動釋出的能力,可以很好的滿足釋出本身的可靠性的需求,保證整個釋出過程是優雅的。但這個模式有一些問題,比如釋出和復原的時間都比較長,且無法暫定下來進行業務運作狀态的觀察。一個進階的模式是藍綠釋出,新啟動一個副本,然後把所有流量全切到新的版本,這樣釋出和復原都很快了,但所有流量還是一次性切換的,沒法進行增量驗證。金絲雀釋出可以解決這個問題,可以通過路由控制,逐漸将流量導入到新版本上。但一般是采用流量百分比的方式,所有沒法指定特定人群使用新版本。
一個更加可控的金絲雀方式,需要給每個使用者設定一個流量标志,比如使用cookie。也就是說通過一個類似interpcetor的機制,判斷目前使用者是否應該是一個灰階使用者,如果是的話,就給他設定一個cookie,後續來自該使用者的所有流量都會帶上這個cookie。有了這個流量标志,就可以在流量入口處根據cookie的值判斷該請求應該到新版本還是老版本。
有了這個路由機制,還是不夠。因為我們實際的應用程式并不是隻有一個服務,而是像圖中的由多個服務互相調用而組成。比如當我要釋出服務B的時候,因為服務B并不是直接面向浏覽器的,所有無法接收到使用者的cookie。這時就需要有一個流量表自動傳遞機制。一般的做法是在請求的入口處把灰階标記存在一個ThreadLocal中,然後在應用的出口處,比如一個OkHttpClient的調用處再把這個ThreadLocal中的值放到cookie中繼續往下傳遞。
我們已經了解了“全鍊路可控金絲雀釋出”的做法,接下來要探讨在技術上如何實作。在阿裡巴巴,我們使用了一個叫做統一接入的技術,所有的請求(包括入口流量和内部服務之間互相調用的流量)都會經過通過接入,然後有統一接入決定該将這個請求分發到哪裡。
到了雲原生時代,Service Mesh的概念興起,其實本質上就是一個“分布式統一接入”。這個統一接入不再是一個中心化的服務,而是随着每個服務的每個執行個體一起部署在一起的程序,這個程序負責接收改執行個體的入口流量,并轉發給實際的服務;同時也攔截執行個體的出口流量,并決定下一跳應該是誰。
Istio是Service Mesh的一個被廣泛采用的實作。
了解了這個原理之後,從上圖中,可以看到一次釋出過程是什麼樣子。
這個釋出過程中涉及到多次Kubernetes資源的更新操作,如果完全采用原生指令+手工配置來操作,不僅複雜還容易出錯。為此雲效對雲原生的各種常見釋出模式都進行了産品化封裝,開發者隻需要配置一些簡單的釋出和路由規則,就可以輕松地實作安全可控釋出過程。
可以了解更多雲效雲原生開發内容,也歡迎大家直接加入kt connect釘釘交流群(釘釘群号:23314962),将你的想法随時告訴我們。