本文系雲原生應用最佳實踐杭州站活動演講稿整理。杭州站活動邀請了 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。