小團隊的 微服務 + CI/CD + Kubernetes 實踐


公司的應用架構一開始就選定了微服務+Kubernetes,整個開發環境都在內網,使用 Jenkins 做半自動化的 CI/CD.

整個前后端都拆分得很細,分了很多層次:

  1. 腳手架層:封裝了開源的各種庫(mysql/redis/es 的 CRUD 庫,log/tracing/config 庫等)。
  2. 基礎層(BaseXxx):基於腳手架層,實現了如下幾類應用的基礎層:測試器、數據庫升級器、微服務、網關、通用工具、一次性任務等。
  3. 中台:在基礎服務上,按功能實現了一些比較通用的中台服務。比如權限認證服務、用戶服務、訂單服務等。
  4. 應用層:每個倉庫對應一個具體的微服務,也可能包含測試器、數據庫升級器等。可能會在 CI 中被打包成多個鏡像。

最終只對外暴露出幾個對外網關。

這種結構的目的,就是提升代碼的復用能力,把應用層能復用的東西,都抽到下面兩層去了。
但這要求我們的基礎層API一開始就設計得足夠好,因為越到后期,API 的影響面就越大,幾乎無法修改。

一、CI/CD

1. Continuous Integration 方案

目前我們是使用 Jenkins 作為 CI/CD 工具,層次結構也完全對應前面講到的代碼層次結構。

通過一套 BatchJob (批量構建任務,串行或並行地調用相關的子任務)來按依賴順序,自下向上地層層更新 csharp/python/golang 依賴,構建 nuget 包,最后打包成 docker 鏡像。

更新過程中會通過如下幾個檢測項來判斷是否需要構建 nuget 包/docker 鏡像:

  1. 更新私有依賴,返回值:是否更新了依賴
  2. 更新第三方依賴的小版本,返回值:是否更新了依賴、是否有主版本變更(主版本需要手動升級)
  3. 與上次構建相比,代碼倉庫是否存在變更(目的是加速構建)

每一個倉庫的依賴更新與版本自增都對應一個 jenkins 任務,由每一層的 BatchJob 按預先定義好的順序啟動這些小任務。(相互獨立的任務會被並行調用,以加速構建)

而在應用層,是通過 batchjob 並行構建所有的 docker 鏡像。

有一個專門的 job_config 倉庫(Python 模塊),存儲着:

  1. Git 倉庫、Jenkinsfile 與 Jenkins 任務的對應關系,以 yaml 格式保存
    • 通過 python 代碼提供 api,動態地從上述配置中查詢出各層的 Git 倉庫、Jenkins 任務的各種信息。
    • 提供命令從上述 yaml 配置中生成出所有的 jenkins jobs 配置(xml文檔),這樣就不需要通過 UI 一個個添加 Jenkins 任務。
  2. 上述的任務分層、任務之間的優先級(相同優先級的任務是相互獨立的,可並行構建)

2. Continuous Deployment 方案

構建完成后,需要通過一個“鏡像快照”的功能,將所有鏡像的版本號、掃描出來,然后生成它們的 k8s yaml 配置文件,保存到 git 倉庫中,並打上 tag(時間戳)。方便隨時回退。

k8s 配置生成方面,我們目前是使用的自定義模板,通過字符串替換的方式進行填充。以后可能會考慮使用 kustomize。

最后通過一個部署的任務將指定的版本的 yaml 應用到集群中。

另外現在正在考察 jenkins-x,以后可能會將應用層的鏡像構建到 k8s 部署,從 jenkins 移出來。

3. CI/CD 目前存在的問題

3.1 Kubernetes 配置生成

先說說 k8s 配置生成,試用了 kustomize,發現它功能還是比較弱,匹配不上我們現在的 yaml 配置生成的需求。也可能是我們目前的使用姿勢不對吧。

3.2 GitOps

另外也查了很多 Jenkins-X 的資料,它遵從 GitOps 開發流程,能檢測 Git 倉庫變更/Pull Request,直接生成 Docker 鏡像並部署到 Preview - Stageing - Production 環境。

但是我 GitOps 和公司目前的這套構建體系不太契合:這種以 Git 倉庫為中心的方式,好像只面向能生成最終的 Docker 鏡像/K8s Pod 的 Git 代碼,而不適合用於構建底層依賴包。

比如說我更新了一個基礎層的依賴包 A,現在需要讓所有的應用層項目都引用這個新版本依賴。應用層可能有幾十上百個倉庫,手動更新幾乎不可能。
通過 GitOps 做不到讓應用層的這上百個倉庫自己更新一下底層依賴。只能借助一個 Jenkins 的 BatchJob,調用一下所有應用層依賴更新的子任務。

P.S. dotnet-nuget/java-maven 目前沒有很多現代現代語言都有的 動態依賴(如 pyproject.toml/package.json/pubspec.yml/go.mod/Cargo.toml,可以通過指定范圍之類的方法靈活配置依賴) + 依賴快照(如 pyproject.lock/yarn.lock/pubspec.lock/go.sum/Cargo.lock,所有依賴的完整快照,保證當前依賴環境可復現) 這類的依賴管理方法,以及 poetry update/yarn upgrade/flutter packages upgrade/go get -u/cargo update 之類的依賴升級命令。
只有一個 xxx.csproj/xxx.xml 記錄所有直接依賴的精確版本。要做自動化依賴管理,只能自己寫腳本去訪問 nuget api,讀取並更新 csproj 文件。
查詢資料 nuget versions 發現 nuget/maven 確實也支持基於范圍的版本依管理,但問題是它們沒有 xxx.lock 文件作為環境快照!這意味着使用靈活的依賴管理,可能導致歷史環境無法復現。

另外 GitOps 自動化部署的流程也和公司目前的部署方法不切合。我們每個開發/測試人員都有一套自己的開發/測試環境,有的測試會需要使用特定版本的一套后端微服務。也就是說測試人員需要能夠控制自己測試環境整套微服務的版本,比如回退到某個時間點、將版本固定在某個時間點。而且不能在工作時間自動更新測試環境的后端微服務。(否則測試到一半,后端滾動更新了微服務,那大半天的測試就作廢了。)

GitOps 只適合一些扁平的應用,而對公司這種分層結構的代碼就有點無所適從。

解決方法
  1. 已有的 Jenkins 分層更新構建流程不變,只在應用層進行 GitOps 方式的 CI/CD。因為應用層倉庫相互之間是獨立的。
  2. 給 GitOps 的構建部署提供專用環境:Preview - Stageing - Production,個人服務器的部署仍然使用現有流程。

舊的分層更新任務會修改應用層的 csproj 文件,這樣就會觸發 GitOps。

以這種方式進行結合是比較好的,GitOps 和現有的分層結構不會沖突。

3.3 網絡問題/緩存問題

每次構建 dotnet 程序時,都需要從公網拉取依賴,使用靜態 Jenkins Slave 時,拉了第一遍后本地就有緩存了,不需要再拉第二第三遍。
可用 Serverless Jenkins (Jenkins-X)的話,每次都是啟動新容器,豈不是每次都要拉依賴?這個挺費時間的。
暫時想到的解決方案是使用 Baget 的緩存功能,讓私有 nuget 倉庫來緩存這些依賴。

雲上生產環境部署

開發人員需要一個直觀的 UI 界面,進行生產環境的灰度部署、監控分析。我們調研了很多管理平台:Rancher/Rainbond/KubeSphere,以及這些工具與 OAM/Istio 的契合度。

目前想到的比較好的一個方案,就是 flux+flagger+istio(istio 可換成 linkerd2),
此方案使用 github.com/gitee.com/coding.net 私有倉庫保存生產環境的 k8s 配置,在內網通過 jenkins 生成 k8s 配置,在生產環境中通過 flux 監控該倉庫的更新,然后 flux 使用 flagger 對其中的 k8s deployment 變更進行自動的灰度發布。

另外 Jenkins-X 有 Preview-Staging-Production 的一套 GitOps 部署流程,也可以一試。只是 jx 目前只提供了 CLI,沒有 UI。上手可能有點難。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM