UCloud內部長期使用 Gitlab 來管理代碼。雖然Gitlab做爲一套開源平臺已很優秀,但咱們對於其能爲CI/CD提供的敏捷性並不十分滿意,內部實踐中的代碼發佈週期仍需按天計算。爲此,咱們打造了一個基於Kubernetes的內部容器服務平臺(名爲KUN),用於託管內部服務,並將Gitlab對接到KUN平臺,從而藉助Kubernetes的雲原生優點,得到更好的CI/CD效果。這套系統運行一年內,Gitlab的Pipeline一共觸發了994次,執行了約20000+次Job,在測試環境和正式環境一共進行了7000+次部署,即天天部署約20次。如下是該項目的一些實踐經驗。git
咱們對CI/CD的目標github
CI即Continuous Integration(持續集成),指代碼集成到主幹以前必須經過自動化測試,只要有一個測試用例失敗,就不能集成。目的是讓產品快速迭代的同時還能保持高質量。docker
CD有兩種意思:後端
Continuous Delivery,持續交付,指的是任何的修改都通過驗證,能夠隨時實施部署到生產環境。 Continuous Deployment,持續部署,是持續交付的更高階段,指的是任何修改後的內容都通過驗證,自動化的部署到生產環境。 二者的區別,在因而否自動部署到生產環境。對UCloud而言,確定要之後者,也就是持續部署爲目標。安全
Gitlab分支管理編輯器
咱們採用的Gitlab分支管理模型在接入KUN平臺先後並未發生變化,故簡述以下:工具
master:主分支,代碼通過驗證。從 master 建立 tag 進行 Release。 dev:研發主分支,用於合入特性分支和補丁分支,在此分支。單元測試
臨時分支: 特性分支:用於開發某個特性; 補丁分支:用於修復線上 bug。 下面以一個實例來介紹CI/CD開發流程。StepFlow是UCloud新近推出的一款可視化工做流產品,經過StepFlow用戶能夠靈活便捷地定義本身的工做流,快速實現業務功能。整套StepFlow系統由多個模塊組成,並所有部署在Kubernetes集羣上。測試
在實例中,咱們須要開發其中名爲optimize-allocate 的一個feature:ui
則開發流程爲:
首先,在 Gitlab 上建立了編號爲80的Issue,跟進這個optimize-allocate的feature;
從dev分支建立一個新分支,名爲feature/80-optimize-allocate,在該分支上進行開發;
在feature/80-optimize-allocate上開發完成,進行commit,此時會觸發靜態測試、單元測試、Review等Pipeline Job;
測試經過後,建立一個從feature/80-optimize-allocate到dev的merge request,由負責人進行審覈。審覈經過而且merge成功後,觸發靜態測試、單元測試、鏡像構建、鏡像部署、集成測試等Pipeline Job;
測試經過後,建立一個從dev到master的mergerequest,由負責人進行審覈。審覈經過而且merge成功後,負責人建立tag v1.1.1,而後觸發靜態測試、單元測試、鏡像構建、鏡像部署、集成測試等Pipeline Job;
注:版本號tag是有命令規範的,v{x}.{y}.{z}表明着v{主版本}.{次版本}.{小修訂版本}
Gitlab CI/CD Pipeline
Gitlab 8.0版本之後,默認集成並開啓了Gitlab-CI,是能夠提供必定CI能力的,咱們以此搭建持續集成的流水線,其中有Pipeline、Stage和Job三個層級須要設計。
Pipeline
Gitlab 會檢測一個項目的根目錄下的 .Gitlab-CI.yml 文件,用戶可在該文件中定義 CI/CD Pipeline,一個 Pipeline 表明了 CI/CD 的整個過程。代碼發生變化時(好比 push、tag、merge等),將觸發一個 Pipeline 的運行。
Stage
一個 Pipeline 包含多個 Stage,好比「靜態檢查」、「單元測試」、「構建鏡像」等等,這些 Stage 一個接一個順序執行。
Job
每個 Stage 能夠包含多個 Job,同一個 Stage 的全部 Job 同時執行。每一個Job需指定若干個重要屬性如image, stage, tags, service等。
在StepFlow例子中,針對要開發的feature,其Pipeline設計以下,包括靜態檢查、單元測試和最後的兩個手動 Code Review:
當其須要發佈到公共測試環境(相似預發佈環境),則設計另外一個Pipeline,包括:執行完整的靜態檢查, 單元測試, 預發佈鏡像 build, 預發佈部署, 預發佈集成測試。
而合併 master 以後觸發 prod 環境的另外一個 Pipeline,包括靜態檢查、生產環境鏡像的 build、生產環節的部署、最後集成測試:
Gitlab Runner
咱們將Gitlab Runner和Gitlab-CI配合使用,負責具體 Job 的運行。Gitlab Runner Kubernetes Executor 提供了在 Kubernetes 中運行 Job 的能力。運行原理以下:
Gitlab Runner 須要事先部署並註冊到 Gitlab 上; 當代碼發生更新時,Gitlab 根據用戶的配置,通知 runner 來運行 Job; Gitlab Runner 使用某個鏡像來建立一個 Pod,而後在其中運行某些命令; Gitlab Runner 將整個運行過程以及運行結果告訴 Gitlab。
Kaniko集成和改造:在容器中構建Docker鏡像
爲了使用 CI/CD 將代碼變成最終運行在 Kubernetes 中的服務,必不可少的一步就是容器鏡像的構建。因爲CI Job自己就是以容器的形式運行的,因此須要在容器中構建出 Docker 鏡像。
已有的方法,包括把 Host 上的 Docker Socket 掛載到 Pod 裏面去(Docker Socket Mounting),或者是在 Pod 再啓動一個 Docker Daemon(Docker-in-Docker),然而,前者有安全風險,後者須要 Pod 具有特權,兩種方法都不適合。
Kaniko(https://github.com/GoogleContainerTools/kaniko)是 Google開源的一個工具,能夠實如今 docker 容器裏面製做 docker 鏡像,而且不須要任何特權。
可是原生的 Kaniko 鏡像因爲缺乏一些必要的工具,沒法和Gitlab CI集成。爲此咱們修改了Kaniko鏡像,添加了整個busybox工具包進去,以支持其做爲一個CI Job來運行。而後就能夠把Gitlab往內部容器服務平臺KUN對接了。代碼示例以下:
KUN+Gitlab:基於Kubernetes的CI/CD流程
KUN中CI/CD的整個流程如上圖所示。從圖中咱們能夠看到,CI部分是一個單元測試,預發佈部署,集成測試,Debug,提交代碼的循環過程。在CI過程當中預發佈的部署是經過CD系統來實現的,CI其中一個步驟是生成部署文件,而後按照項目、資源集、版本等參數提交到CD後端系統。CD系統提供了頁面入口,當部署文件推送到CD系統後,用戶能夠在頁面查找對應的部署文件。若是須要部署,在頁面點擊「部署」按鈕便可實現部署。
YAML編輯器
爲方便用戶使用,咱們爲KUN開發了專門的YAML編輯器,相比用普通的文本編輯器寫YAML,能夠提供智能模板補全、搜索替換等能力。
上圖展現了一個使用案例,目前頁面編輯器支持的功能有:
Snippet:模版補全,用戶編輯文檔時,會提示相關模版,可選擇直接模版輸入
Hover:用戶鼠標放置關鍵字,提示關鍵字含義和官方文檔連接
搜索替換:使用⌘+F/Ctrl+F打開頁面搜索支持頁面直接查詢
部署系統
爲了在Gitlab CI Job中把服務部署到Kubernetes上,咱們研發了部署系統。在CI Pipeline的最後一步,用戶生成一個YAML文件,定義須要部署到Kubernetes上的資源,而後使用部署系統提供的一個工具鏡像,該鏡像會調用部署系統的API,將YAML提交給部署系統。接下來部署系統將使用用戶的權限,調用Kubernetes API實現真正的部署。
目前部署系統主要包含兩部分功能:
1. 資源集管理:
資源集是用戶項目下一個或多個資源的集合,資源指 Kubernetes 的 object 的描述文件,通常爲 YAML 文件; 資源集分爲多個版本,不一樣的版本對應資源的不一樣的描述文件; 用戶能夠選擇某個版本,而後指定集羣執行部署。
2. 部署任務
用戶每次部署都會生成一個 job,就是部署任務 執行部署後,用戶可在任務詳情頁直接查看部署任務日誌。
當完成上述全部工做,Gitlab能很好嵌入KUN平臺,運行在Kubernetes上的各模塊就可像齒輪同樣有序運轉,從而得到CI/CD上的良好效果。
前面提到的StepFlow項目,從一開始就實施了這樣一套 CI/CD,效率得到了很大提高,每一個編譯Job耗時約3分鐘,其餘Job耗時在1分鐘之內。
總結
CI/CD是提供高質量互聯網服務必不可少的一環。Gitlab和Kubernetes都是很是優秀的開源軟件,在此基礎上,咱們根據本身的實際狀況,結合二者打造了一套高效的CI/CD平臺,努力提高研發效率,爲用戶提供更優質的服務。