本文爲 CSDN 博主「祈晴小義」(黃鑫鑫:騰訊雲 CODING DevOps 研發工程師、Nocalhost 項目的核心開發者)的原創文章,並根據作者在 CSDN 雲原生 Meetup 深圳站的演講內容進行了整理,主要分享 Nocalhost 在解決雲原生開發問題上的思路和探索,並展示 Nocalhost 爲雲原生開發帶來的全新體驗。
雲原生場景下的開發痛點
當我們的應用架構從傳統應用過渡到雲原生應用的時候,會發現應用架構的複雜性大大提升了,原來的傳統應用組件少,部署簡單,我們往往可以在本地開發完一個傳統應用後,把它丟到服務器上就能跑起來。而對於雲原生應用來說,應用被拆分成一個一個粒度更小的微服務,各個服務之間有着錯綜複雜的關係,從而讓開發環境的搭建和服務的調試變得異常困難。
本地部署 VS 集羣部署
當我們要開發雲原生微服務應用時,如何將我們的開發環境搭建起來呢?常見的有兩種方式:本地部署和集羣部署。
本地部署是將一整套微服務應用部署到本地的開發機器上,如下圖所示:
這種方式會帶來以下幾個問題:
1. 影響開發機器的性能。微服務應用往往規模比較大,動輒幾十上百個服務,都泡在自己的開發機上可能會讓電腦變得很卡,影響工作效率。
2. 環境無法共享,資源浪費嚴重。當我們需要在本地部署起整套規模比較大的微服務應用的時候,就需要使用配置較高的開發機器,並且每臺開發機器的開發環境只有一個開發人員能使用,即便該開發人員只需要開發其中一個或某幾個服務,也無法將其它使用不到的服務共享給其它開發人員使用。
3. 對於一些規模很大的微服務應用,本地機器可能還沒有辦法跑起來。
另外一種方式將微服務應用部署到雲上的 K8s 集羣裏,如下圖所示:
這種部署方式可以較好地提高資源利用率,但是它會讓開發和調試應用時的反饋鏈路被大大拉長。
我們開發傳統應用時的工作流是:在本地編寫好代碼 -> 把代碼進行編譯 -> 運行程序查看結果,如下圖所示:
這個過程往往很快,所以我們可以在做完一次代碼的小改動以後,就把它運行起來查看結果。
但是在開發 K8s 集羣上的應用時,工作流變成了:修改代碼 -> 編譯程序 -> 將程序打包到 Docker 鏡像 -> 將 Docker 鏡像推送到鏡像倉庫 -> 修改集羣中容器的鏡像版本,等待 K8s 將新版本的鏡像部署上去 -> 查看結果,如下圖所示:
這個流程可能需要耗時幾分鐘,當這個循環反饋被大大拉長了以後,無疑會讓開發的效率大大降低。
目前主流的雲原生開發方式
手動打包推送鏡像
這種方式是最原始的方式,工作流大體如下:
編寫完代碼以後,在本地編譯生成二進制文件或者 jar 包之類,然後通過 Dockerfile 構建出 Docker 鏡像,再將鏡像推送到 Docker 倉庫,再通過修改工作負載的 yaml 定義中鏡像版本,部署新版本容器的任務則交給 K8s 去做,只不過部署調度的過程可能有點漫長,需要等待 K8s 將新版本的 Pod 調度運行起來之後,我們才能看到代碼修改的效果。如果代碼改動得頻繁,這個流程顯然是非常繁瑣的。
CI/CD 流水線
這種方式和第一種方式的流程大體上是一樣的,只不過是通過 CI/CD 的能力,把手動的操作改成了自動化的流程:
這種方式的工作流是:在本地修改完代碼,把代碼推送到代碼倉庫,從而觸發代碼倉庫配置好的 CI 流程,把代碼編譯構建成應用程序(如二進制或 Jar 包),並打包成鏡像,之後會觸發所謂的持續交付(Continuous Delivery)機制,將鏡像推送到製品倉庫裏,最後再觸發持續部署(Continuous Delivery)流程,將新版本的容器調度部署到集羣中。雖然使用 CI/CD 以後,可以減少大部分手工操作環節,但整個流程花費的時間仍然很多,事實上,CI/CD 更適合在發佈應用環節使用,而不是在開發應用環節。開發環節更注重的是能夠快速得到反饋從而驗證自己的想法,當我們的修改需要提交到代碼倉,在 CI/CD 流水線跑完了以後才能看到效果,會限制我們使用簡單的嘗試,來從幾種方案中找出最優的一種,或者定位 bug 的原因。
流量轉發
流量轉發的思路是:將集羣裏訪問開發中服務的流量轉發到本地。如下圖所示:
當需要開發 D 服務時,將集羣中訪問 D 服務的流量轉發到本地開發機器上的某個端口上,在本地寫完代碼以後,直接將應用程序在本地跑起來即可。實現這種方式的相關產品有:kt-connect 和 telepresence。
在本地直接運行應用程序固然可以縮短循環反饋,提高開發效率,但這種方式也有一個很大的問題:許多運行在 K8s 集羣上的服務會依賴其它 K8s 資源,例如依賴 ServiceAccount、ConfigMap、Secret、PVC 等等,這樣的服務要在本地跑起來並不太容易。
在容器裏進行開發
這種方式的思路是:當我們要對某個服務進入開發時,先讓要開發的服務進入開發模式,然後將代碼同步到容器中,直接在容器中把開發中的代碼運行起來:
這種方式同時解決了開發循環反饋過慢和服務依賴集羣問題,是目前雲原生開發中較好的實踐,也是 Nocalhost 支持的主要開發方式之一。
Nocalhost 初體驗
第三部分主要是以 Demo 演示的方式來帶大家體驗 Nocalhost 的特性,感興趣的同學可以前往深圳站 Meetup 視頻回放,從 01:26:15 處開始觀看。
https://live.csdn.net/room/csdnnews/yCHrYqnM
Nocalhost 核心機制
Nocalhost 是如何實現在容器中進行應用程序的開發的呢?在一個服務進入開發模式時,Nocalhost 所做的核心工作有以下 4 個步驟。
縮減副本數
開發應用程序時,我們只需要在一個容器裏運行正在開發中的應用程序,如果存在多個副本,我們通過 Service 訪問該服務時,就無法控制流量只訪問到我們正在開發中的應用程序所運行的那個副本,所以 Nocalhost 需要先將工作負載的副本數縮減爲 1。
替換開發容器鏡像
生產環境運行的容器往往會使用很輕量級的鏡像,鏡像裏僅包含運行業務程序所必須的組件,而缺少編譯構建業務程序所需的相關工具(如 JDK)。在對某個工作負載進行開發的時候,Nocalhost 會將容器鏡像替換成包含完整開發工具的開發鏡像。
增加 SideCar 容器
爲了將本地的源代碼改動同步到容器中,我們需要在容器裏運行一個文件同步服務器。爲了使文件同步服務器進程和業務進程解耦,Nocalhost 將文件同步服務器運行在一個獨立的 sidecar 容器中,該容器與業務容器掛載相同的同步目錄,因此,同步到 sidecar 容器中的源代碼在業務容器中也可以訪問。
啓動文件同步客戶端
由於文件同步服務器監聽在容器裏的某個端口上,我們在本地無法直接訪問,所以 Nocalhost 會把一個本地隨機端口轉發到容器裏文件同步服務器監聽的端口,打通文件同步服務器和客戶端的網絡,然後再啓動本地的文件同步客戶端。文件同步客戶端啓動後會通過剛剛轉發的本地隨機端口和文件同步服務器建立通信,之後便會開始進行文件的同步。
在以上步驟完成後,Nocalhost 會自動打開一個進入到遠程容器的終端,通過該終端,我們就可以把實時同步到容器裏到源代碼直接運行起來。
Nocalhost 高級特性
Duplicate 開發模式
Nocalhost 默認的開發模式是將集羣中原本正常運行着的服務給替換成開發容器,如下圖:
這種方式存在以下問題:
1. 開發時會影響環境的使用。當對某個服務進行開發時,該服務在開發過程中可能會由於代碼修改有問題導致異常甚至奔潰,集羣裏又有其他服務依賴該服務,從而影響到整個環境的使用。
2. 無法支持多人開發同一個服務。
爲此,我們可以使用 Duplicate 開發模式,這種模式下,Nocalhost 不會對原有的服務做任何修改,而是複製出和一個原有服務一樣的副本來進行開發,如下圖所示:
這種模式下,多人可以對同一個服務進行開發,每個人都擁有自己的開發副本,集羣原有的環境也不會受到任何影響。
Nocalhost Server
Nocalhost 除了通過 IDE 提供方便開發 K8s 應用的插件外,還提供了一個適合企業級開發環境管理的 Nocalhost Server,以下是 Nocalhost Server 的管理界面:
Nocalhost Server 可以提供集羣、應用、人員權限上的管理,關於 Nocalhost Server 的詳細介紹,可以參考官方文檔上的介紹。
Mesh 模式
前面我們提到,如果要實現多人開發同一個服務,可以使用 Duplicate 開發模式,但這種方式也有一個侷限性,就是隻能在本地通過 API 接口請求去訪問開發中的副本,沒辦法通過應用的入口地址來訪問。對於需要通過統一的應用入口來訪問開發中服務的場景,我們可以使用 Nocalhost 的 Mesh 模式。
Mesh 模式會爲每個人分配一個 MeshSpace,不同的 MeshSpace 通過在流量中帶入不同的 Header 來控制從應用入口進來的流量的訪問鏈路。
使用 Mesh 模式要求開發環境通過 Nocalhost Server 管理,並且應用需要有 Header 透傳和使用 Istio 進行流量轉發的能力,關於 Mesh 模式的使用可以參考官網文檔 (文檔目前還不是很完善,更詳細的信息可以直接通過 GitHub 聯繫 Nocalhost 的開發團隊)。