微服務架構下 CI/CD 如何落地

又拍雲發表於2020-12-03

本文系雲原生應用最佳實踐杭州站活動演講稿整理。杭州站活動邀請了 Apache APISIX 專案 VP 溫銘、又拍雲平臺開發部高階工程師莫紅波、螞蟻金服技術專家王發康、有贊中介軟體開發工程師張超,分享雲原生落地應用的經驗心得,以下是莫紅波《微服務架構下 CI/CD 如何落地》分享內容。

莫紅波,又拍雲平臺開發部高階工程師,目前專注於容器及虛擬化技術在又拍雲的私有云實踐,主要負責又拍雲容器雲的設計和開發工作。

大家好,今天分享的主題是《微服務架構下 CI/CD 如何落地》,圍繞以下兩部分展開:

  • 理論篇,討論從單體到微服務的過程中會面臨怎樣的挑戰,以及微服務的測試模型

  • 實踐篇,圍繞整合測試環境的服務發現需要怎麼做,如何落地持續交付和持續部署

背景

或許大家對於網際網路公司的共同印象是 996,對於我個人而言,網際網路公司還有另一個特點,那就是經常需要擁抱變化。在網際網路公司,新產品上線、下線、調整,這都是很家常便飯的事情。在這種情況下,一個好的鬆耦合的架構就顯得尤為重要。

剛好我最近就有遇到這個問題,我在做的專案,賬號這塊是對標 GitHub 的註冊制賬號機制的。原本的需求是使用者註冊我們的平臺,註冊完成後可以建立一個屬於自己的團隊,並將其他人拉入自己的團隊。但是當我們做完這部分內容後發現,客戶還是更偏好「賬號+子賬號」的模式,公司一個總的賬號,所有員工單獨開子賬號進行關聯。這讓我們已經做好的專案變得非常尷尬,需要立即擁抱變化,需要根據最新的需求進行調整。這時,我就發現擁有一套鬆耦合的架構的重要性,比如賬號這一部分,如果把它單獨拎出來,做好足夠的抽象,提供必要的對外介面,可能會更加靈活,擴充套件性更加好。

那怎樣擁有一套鬆耦合的架構?有什麼好的方案呢?在我看來有兩個,一個是幾年前出現的 SOA,即將服務進行單獨化,將每一塊都進行拆分;另一個就是最近幾年火熱的微服務了。我認為,微服務跟 SOA 其實是一回事,只不過微服務比 SOA 拆分粒度更細,功能也更小。

在調研微服務過程中,很多人會有疑問:“我們是一個很小的團隊,小團隊適不適合上微服務呢?”。因為上微服務就意味著一個服務可能就會被拆分成 10 個、20 個甚至更多個的服務,這就讓小團隊不得不去考慮自己的測試、部署、更新成本是不是會翻很多倍。

那麼我對於“小團隊適不適合上微服務”這個問題的答案是什麼呢?我認為是完全可以上的,不過你需要注意一點:做好自動化,能交給自動化來實現的,就不要人工介入了。

在聊如何做自動化整合測試(CI)之前,我先和大家談一談從單體如何到微服務,服務是如何拆分的,以及微服務的測試一般是怎麼做的。

從單體到微服務

如上圖所示,我們可以看到圖左邊是一個單體服務,右邊則是經過微服務拆解後的。我們可以看到它有 4 個特點:

  • 根據不同領域拆分

  • 服務之間通過網路協議通訊

  • 擁有獨立的資料庫

  • 擁有特定對外開放的介面

微服務測試模型

我們都知道,如果需要一個服務能夠穩定執行,那測試肯定是少不了的。而就像我們微服務化有一套理論一樣,微服務測試也擁有屬於自己的金字塔理論:最底層是單元測試,成本相對較低,像我們在 API 認證部分做的簽名校驗模組,它一般不需要依賴其他東西,因此測試效率也比較高;第二層是整合測試,這一層你就必須要依賴一些第三方的服務模組或者元件,比如我們一般會用到資料庫的測試,就屬於整合測試的範疇;第三層是 e2e 測試,它會模擬客戶端的行為來進行測試,大家也許都接觸過這類測試,像K8S 就有一個 e2e 測試,當你去申請 CNCF 的一致性認證時,就需要通過官方提供的 e2e 測試;最上層是 UI 測試,比如對於頁面的點選調整是否符合預期,這部分我們現在做的比較弱,還處在人工模式下,但我們也在努力將它更新成自動化。

從這個微服務測試金字塔我們可以看到,越靠近底層成本越低,你只需要幾行程式碼就能完成,效率也非常高。同時越底層它對於三方或元件的依賴也越低,自動化也就越簡單。到這裡可能就有人想問:“既然越底層的成本越低,那我們能不能只跑單元測試?”在解答這個問題前,大家先看下面這張圖。

這兩扇窗戶,每一扇單獨存在的時候都是完好的窗戶,可以正常開合。但是兩個都安裝到牆上後就沒有辦法正常開合。這就是我們不能只跑單元測試的原因了,不跑整合測試就無法發現一些問題。同理不跑單元測試也會有一些無法發現的問題,所以我們在跑測試的時候,整合測試和單元測試,一項都不能少。

那具體實踐的時候要如何做呢,我推薦大家分成兩步來進行:

  • 第一步是將底子打好:你需要對你的的微服務進行單元化測試,編寫單元化的測試用例,然後再強化整合測試。沒有好底子的微服務是不可靠的,任何時候都可能會出問題,而且出問題後的排查會非常費時。

  • 第二步是自動化的持續整合環境:將能夠自動化的部分全部進行自動化,減少人工的介入。

GitLab/CI

自動化整合環境這塊目前已經有很多的開源方案了,比如常見的 Jenkins,還有 GitLab。我們選擇的是 GitLab,或者說是選擇了 GitLab/CI,選擇它的原因有以下幾點:

  • 統一的 web 頁面

  • 可以再 MR 中跳轉檢視

  • Pipeline 編排直觀展示

  • 所有操作都在專案中搞定

  • GitLab 官方支援

GitLab WorkFlow

既然我們選擇使用了 GitLab,那我們內部就會嚴格遵守 GitLab 的 WorkFlow。WorkFlow 主要分為兩個部分。

第一部分是面向程式碼倉庫。程式碼倉庫中,我們一般會有三類分支,第一類分支是 master 分支,一般只會有一個,我們會定義 master 分支,並基於這個分支進行線上版本的釋出。第二類分支是 develop 分支,一般也只會有一個,develop 分支是從 master 分支中 checkout 出來的,功能比 master 領先,包含一些已經完成功能開發,但是還沒有釋出的功能。第三類分支是 feature 分支,特性分支,一般會有很多個,新功能都會在這個分支上進行開發,往往一個功能對應一個 feature 分支。最後一類是 hotfix 分支,這個就比較常見了,線上釋出後,如果發現了一個需要緊急修復的bug,這時你就可以在 master 分支上 checkout 出來一個 hotfix 分支,把程式碼改掉。不過進行這個操作時你需要注意,master 分支和 develop 分支都需要進行該 commit 合併,否則就不能算完成了 bug 修復。

第二部分與 CI/CD 有關。以我們的流程舉例,研發的同學提交程式碼到 GitLab 倉庫,之後 GitLab 會觸發事先約定好的 CI 的 pipeline 進行測試和構建。等待測試和構建成功後再進行 code review 的確認,確認無誤後會合併到 develop 分支並最終合併到 master 分支進行釋出。這就是 GitLabCI 的一個配置,總結來看可以劃分為下圖的四個階段。

下圖是配置檔案對應的 pipeline 的展示,大家可以看一下。

微服務下的場景變形

其實到目前為止的方案,已經是微服務沒有大熱前的完備方案了。如果你想要將方案運用到微服務的整合測試裡,你還需要做一些變形,不妨參考下圖中所示的又拍雲現在使用的整套流程。

從圖中可以看到,我們目前使用的整套流程相比標準的其實有做一些小的變形,變形主要集中在中間的整合測試環境這一塊,我們將每個伺服器都部署在了整合環境內,使整合環境變成一個準釋出環境。具體流程是,當我們的建立 projectA 後,由它來 push 程式碼,完成後觸發 CI,也就是在 GitLab runner 上進行測試。

在跑測試的過程中,因為 A 服務需要呼叫 B 和 C 服務,所以通過 API 去請求整合環境中的對應服務。如果測試完成後沒有問題,則合併到主線。再通過在 master 分支打 tag 的方式來觸發容器構建並推送到 Harbor 映象倉庫。最後我們會做一個線上 release。這個就是我們的大致流程了。

那麼接下來我們來具體看一下變形中會遇到的問題。

服務發現

在微服務場景下的變形中遇到了很多問題,我覺得其中值得注意的是“服務發現”。比如我們現在有這樣一個場景,A 服務在跑測試時需要依賴 B 服務和 C 服務,面對這個需求,在沒有引入 Kubernetes 之前,我們可以通過使用一臺共用機器,將服務都佈置到這臺機器上,並在測試程式碼裡寫死 IP 地址,讓每一次測試都在這個環境內跑。但這個方法會有下面四個無法忽視的問題:

  • 服務更新延遲

  • 環境許可權混亂

  • 人工操作容易出錯

  • 維護成本過高

因此我們引用了 Kubernetes Service 的方案進行優化。

Kubernetes Service 的流程大家可以大概看一下。我們先定義一個 Service, 我們這邊建立的 ClusterIP 型別的,定義了暴露埠 8000,目標埠 8000,協議是 TCP,標籤選擇器是 app=holdon。通過這種方式,我們可以把一組相同功能的服務,繫結在同一個 Service 下。在 Kubernetes 叢集內,定義好 Service 後,會提供內部的 DNS 解析,你可以通過一個固定的域名訪問指定的 Service。這樣當在跑測試的時候就可以通過這個域名加對應埠,呼叫到對應服務了。

持續交付

持續交付(英語:Continuous Delivery,縮寫為 CD)。每個項⽬都要有⼀個 Dockerfile,提供了服務運⾏所需的環境,以及服務對應的軟體包。當需要發版本的時候,我們會在主線上打上⼀個 tag,觸發映象構建,然後推送到 Harbor 映象倉庫。其中,這個 tag 也會對應到映象的版本號。

上圖是我們的 CD 流程大家可以參考看一下。需要提一下的是,我們引用 Harbor 的原因是因為它相對官方的 Registry 更安全。大家應該都知道,官方的 Registry 本身不帶許可權校驗,當你公司內部使用的時候,這個問題會導致你的映象有被其他部門的人覆蓋掉的可能性,所以我們引入了 Harbor。但這裡也有一個問題,使用同一個 tag 去推依然會被覆蓋的情況。不過好歹做到了小組和小組之間、部門和部門之間的隔離。

持續部署

持續部署(英語:Continuous deployment,縮寫為 CD),目前這塊,在實踐過程中,我們是隻針對整合測試環境,線上更新還是走常規的流程。給項⽬增加⼀個 k8s-deploy.yaml 的⽂件,⾥⾯包含了服務相關的配置、部署⽅式、訪問⽅式等等,等待映象構建完成後,apply 該⽂件就可以了 。

持續部署流程圖

回顧

現在我們再回到服務變形的流程圖來看一下,當我們有 A、B、C 三個服務,且 A 服務在測試時需要呼叫整合環境內的 B 服務與 C 服務時,可以通過 K8S 提供的內部域名進行訪問。等待整塊測試跑完後,我們在主線上打 tag,讓 CI 去幫執行 image build 構建映象並推送到 Harbor 倉庫。

其中涉及到的準釋出環境,可以通過 kubectl apply 的方式進行部署。由於線上環境更復雜,推薦大家通過自研的容器雲平臺來進行操作,我們就是這麼處理的,通過雲平臺釋出,功能更加全面安全,更加符合線上部署的流程。

成果展示

最後,跟大家分享下最近正在做的專案的情況。從 2019 年 12月 開始到現在,我們每天基本保持在一個較高的 commit 數上,而這其中一共進行了大約 4500 次的測試。想象下,如果沒有這套自動化持續整合環境,測試需要怎麼來進行,需要投入人力資源。

點選閱讀可直接跳轉,獲取現場分享視訊、下載 PPT。

推薦閱讀

【實戰分享】從選型到專案落地,漫談 gRPC

白話科普,10s 瞭解 API

相關文章