夏潤澤 分布式實驗室

雲原生時代,應用子產品不斷被拆分,子產品的數量不斷上漲并且子產品之間關系也愈加複雜。企業在落地雲原生技術的時,同時也需要有強大的 DevOps 手段,沒有 DevOps 的雲原生不可能是成功的。Jenkins X 是 CDF(持續傳遞基金會)與 Jenkins 社群在雲原生時代的全新 DevOps 産品,本文我們将介紹 Jenkins X 以及 Jenkins X 背後的技術。
背景
Jenkins 在2004年誕生。根據官網的資料統計(截止2019年3月)有 250,000 的 Jenkins 伺服器正在運作、15,000,000+ Jenkins 使用者、1000+ Jenkins插件。Jenkins 在 DevOps 領域取得了巨大的成功,但随着技術的不斷發展與使用者數量的不斷上升,傳統 Jenkins 所暴露出來的問題也越來越多。在這裡我們将介紹傳統 Jenkins 所遇到的挑戰。
Jenkins 所遇到的挑戰 - 單點故障
在傳統的 Jenkins 當中,我們首先會遇到的問題就是 Jenkins 的單點故障問題。
Jenkins 的曆史非常悠久,在當時大多數程式都是單機程式,Jenkins 也不例外。
對比其他系統,Jenkins 的單點故障問題會更加凸顯,熟悉 Jenkins 的使用者都知道,它是一個基于插件的系統,而我們會經常安裝插件,這時候我們就需要重新開機 Jenkins 伺服器。這将導緻共用這個平台的所有使用者都無法使用。
Jenkins 所遇到的挑戰 - JVM消耗資源多
Jenkins 是 Java 系的程式,這使得 Jenkins 需要使用 JVM,而 JVM 将會消耗大量的記憶體。
CI/CD 任務往往都是在代碼送出時被觸發,在非工作時間,這些資源消耗是可以大大降低的。
Jenkins 所遇到的挑戰 - Job 的排程方式使 CI/CD 變得困難
在 Jenkins 誕生的年代,機器資源并沒有像現在一樣豐富、可排程,導緻 Jenkins 的排程模式使得不适合現代的環境。
Jenkins 的排程模式是一種盡量能夠節省資源的方式進行排程的。在一般的排程過程中 Jenkins 需要經曆以下幾個階段:
檢查有沒有可用的 agent -> 如果沒有的可用的 agent,計算是否有 agent 預計将要運作完任務 -> 等待一段時間-> 啟動動态的 agent -> agent 與 master 建立連接配接。
這種方式使 CI/CD 任務被執行的太慢,我們往往都需要等待幾十秒甚至更長時間來準備 CI/CD 的執行環境。
Jenkins X 的誕生與理念
Jenkins X 是起源于 Jenkins 的一個項目,在2018 年 2 月從 JEP(Jenkins Enhancement Proposals)中誕生。目前已經成為一個獨立的項目,并且有了自己獨立的 Logo。
Jenkins X 的設計目标是使用 Kubernetes 的力量來建構現代化的 CI/CD 平台,為使用者提供自動化的 CI/CD。
在現代化方面我認為主要有兩個部分,其中一部分為使用者體驗的現代化,另外一部分則是架構方面的現代化。我們這裡将首先介紹有關使用者體驗的現代化,稍後再介紹架構方面的現代化。
重新思考雲原生時代的 CI/CD
使用者在使用傳統的 Jenkins 的 時候往往都在思考一個問題,我們應該怎樣建立一個流水線來完成的工作。而使用者想要的并不是如何建立一個流水線,而是想要:
更快的上市時間
更高的部署頻率
更快的修複時間
更低釋出故障率
更短的恢複時間
其實就是通過自動化「軟體傳遞」和「架構變更」的流程,來使得建構、測試、釋出軟體能夠更加地快捷、頻繁和可靠。
Jenkins X 在設計之初目标就在思考如何讓使用者如何落地雲原生的 CI/CD,如何讓使用者成為“高性能” 的團隊,在《加速度》一書當中 Jenkins X 團隊總結出了一些高性能團隊所有的特點:
版本化所有制品
自動執行部署過程
使用基于主幹的開發
實施持續內建
實施持續傳遞
使用松耦合架構
讓團隊成員獲得動力
于是 Jenkins X 團隊将這些特性都融入到了 Jenkins X 當中,讓使用者更容易落地高性能的 DevOps。
以應用為視角的 CI/CD 産品
Jenkins X 與其他 CI/CD 産品的最大的不同,在于 Jenkins X 切換了使用者在使用 CI/CD 産品時候的視角。在前面我們提到過在傳統 Jenkins 當中,使用者往往思考的是如何去建立一個流水線,而在全新的 Jenkins X 當中,使用者是建立一個應用,而 Jenkins X 将自動的為這個應用建立CI/CD 流水線。
為應用建立自動化的 CI/CD 流水線其中就包括 CD 也就是持續傳遞/部署過程,是以 Jenkins X當中擁有了環境管理的功能。
在 Jenkins X 當中,所有操作将是 GitOps 的,GitOps 就是指将所有對基礎設施的操作,全部轉換為 Git 操作。
在上面的圖中上面為使用者的 Git 倉庫,中間為 Jenkins X,最下方為 Kubernetes 當中的 namespace,每一個 namespace 都對應一個環境。當使用者對我們的 Git 倉庫操作時,Jenkins X 會将這些操作轉換為對 Kubernetes 叢集的操作,以産生預覽環境、預釋出環境等。
在 Jenkins X 當中建立一個應用,預設将擁有三個環境,也就是有三個 Git 倉庫。其中第一個為應用的源代碼倉庫,它對應使用者的預覽環境,當使用者建立一個PR(Pull Request)時,Jenkins X 将為這個 PR 生成一個預覽環境,讓使用者更容易進行代碼 review。同時這裡還有 staging 和 production 倉庫,在這兩個倉庫當中并沒有應用的源代碼,而是應用 helm chart 的索引資訊。如果倉庫當中擁有某一個版本 chart 的索引,那麼 Jenkins X 就會把這些應用自動的部署到這個環境當中。
環境管理與 GitOps 當中還有一個非常有意思的設計,那就是随時釋出一個 release 版本。這裡具體行為是這樣的,當使用者合并一個 PR 到應用的倉庫的時候,這時候 Jenkins X 将會自動釋出一個小版本的 release,并且自動建立并一個 PR 到 staging 環境的倉庫當中,保持應用的 master 分支與 staging 環境當中的部署的應用是對應的。讓使用者習慣基于主幹的開發和主幹随時可釋出的 DevOps 實踐。
Jenkins X 的演進
Jenkins X 在不斷的疊代演進,在最初版本中 Jenkins X 使用傳統 Jenkins 完成大部分工作。
随後 Jenkins X 引入了 Prow 作為 webhook 事件的接收者,來避免大量的倉庫掃描,并且彌補傳統倉庫掃描描述資訊不足的情況。
之後 Jenkins X 開始使用一次性的 Jenkins Server 運作 CI/CD 任務。一次性的 Jenkins Server 實際上是對傳統的 Jenkins 進行了改造,将傳統的 Jenkins 打包成為一個可以執行 Jenkinsfile 的執行器。當需要執行 CI/CD 任務的時候去動态的啟動 Jenkins Server 完成任務。這種方式雖然解決了長時間運作 Jenkins Server 所帶來的資源消耗,但是這種方式還仍然使用 JVM,并且流水線啟動的也很慢。
後來 Jenkins X 就将底層的流水線引擎從 Jenkins 切換到了 Tekton。Tekton 谷歌開源的 CI/CD 流水線引擎,這部分我們在後面還會有更詳細的介紹。
在 2019 年 Jenkins X 引入了自己的流水線定義語言 Jenkins-x.yaml,讓使用者可以使用 yaml 的形式來定義自己的流水線。
現在 Jenkins X 社群在努力開發自己的 webhook 響應器 lighthouse 以彌補 prow 隻支援 github scm 的問題。
Jenkins X 背後的技術
在上文當中,我們主要了解了 Jenkins X 的誕生以及全新的使用者體驗。在這一章當中我們将主要介紹 Jenkins X 背後的技術,這包括為了使用者體驗所使用的子產品,也包括 Jenkins X 的現代化架構。
應用打包 Draft 與 Build Packs
在前面我們提到過 Jenkins X 将是以應用為視角的 CI/CD 平台。平台首先要解決的一個問題就是如何進行應用标準化打包的問題。Jenkins X 的應用打包借鑒了很多 Draft 的思想。
Draft 是雲原生社群的一個項目,由 Azure 維護,目标是簡化使用者建立雲原生應用的過程。
我們在将應用雲原生化的時候需要進行很多工作,包括但不僅限于以下步驟:編寫 Dockerfile 将應用容器化->編寫 Kubernetes 相關的部署檔案->以 Helm 的形式對應用進行打包->……
這個過程對大部分傳統使用者來說都擁有很高的學習成本,于是 Draft 誕生了。在使用 Draft 時使用者不再需要去學習 Docker、Helm ……使用者隻需要輸入 draft create 并且選擇語言,那麼 draft 就會自動建立好包括 Dockerfile、deploy.yaml、helm chart、項目基本結構 …… 當需要部署時使用者隻需要輸入 draft up 就可以一鍵進行建構并且打包部署。
與 Draft 類似 Jenkins X 建立了自己的 buildpacks,其中包括 Dockerfile、Helm Chart、jenkins-x.yaml……使用者隻需要 jx create quickstart 就可以輕松的建立好這一系列的資源,并且 Jenkins X 還将在 scm 上建立好倉庫,并且設定好完整的 CI/CD 流水線。這時使用者可以根據項目生成的檔案,直接開始編寫自己的業務代碼。
現代化架構初探
在上文當中我們介紹了有關 Jenkins X 産品方面的現代化,現在來介紹一下 Jenkins X 架構上面的現代化。在 Jenkins X 當中是采用的一種 Serverless 的服務架構。Serverless 是一種雲計算執行模型,其中雲提供商(Kubernetes)運作伺服器,并動态管理機器資源的配置設定(From WIKI)。
在以 CI/CD 為業務的 Serverless架構,我們将主要需要以下三個子產品:
Webhook響應器(類似API Gateway,可以動态啟動服務)
動态 CI/CD 引擎(類似 Func,支援動态加載執行)
資料的持久化存儲
可靠、高性能的流水線引擎 - Tekton
Tekton 是誕生于 Knative 的一個項目,Knavie 是 Google 開源的一個 Serverless 平台。在 Serverless 平台當中非常重要的一環就是将應用打包,于是 Knavie Build 項目誕生了。在 Knavie Build 誕生之後,大家發現以雲原生的方式運作建構有很多優勢,例如運作速度很快、可以輕松利用池化的資源。于是大家開始嘗試用這種方式來做更多事情,例如執行測試、執行部署等,這個時候 Knavie Pipeline 項目也就誕生了。現在的 Tekton 的前身也就是 Knavie Pipeline,是一個純正的雲原生 CI/CD 引擎。
對 Jenkins X 來說 Tekton有非常大的優勢就是避免了 JVM 的使用,并且可以動态的啟動、高效利用資源,同時基于 CRD 開發的 Tekton也有非常易用的聲明式 API。
高性能、高可用的事件響應器 - Prow
Prow 是誕生于 Kubernetes 測試基礎架構的一個元件。目前在 Kubernetes 社群當中每天有上百個倉庫的上千個PR/Issue 都通過一個Prow 來處理。
Prow 是經過考驗的高性能事件響應器,并且基于 Kubernetes controller方式開發的 Prow 可以直接實作高可用,以保證我們事件入口随時可用。
Prow 同時還提供了一個非常重要的功能就是 ChatOps,使用這種方式我們隻需要在 Issue/PR 中進行回複就可以完成我們日常的大部分工作。
插件模型的 Prow 也可以很容易的插入我們想要自定義的功能。
CRD + Controller 粘合各個子產品
CRD 是 Kubernetes 當中的一等公民,是擴充 Kubernetes 的标準方式。使用這種模式開發,我們可以容易的實作聲明式 API 并且子產品之間是松耦合的。Kubernetes 的 watch 機制也可以讓我們的請求被快速的響應,并且達到請求的最終一緻性。這種開發模式同時擁有非常豐富的社群資源,我們可以利用社群資源很容易的實作例如高可用等特性。
這裡簡單介紹 Jenkins X 響應一個事件的流程:
首先 SCM 會發送一個 WebHook 事件到 Prow,告訴 Prow 有一個事件發生(例如有人送出PR)
Prow 在收到消息之後會在 Kubernetes 的 apiserver 建立一個 ProwJob,将 WebHook 消息轉換為CRD 資源。
這時 Kubernetes apiserver 會通知正在 watch ProwJob 的 Jenkins X Controller
Jenkins X Controller 擷取 ProwJob 的詳細資訊,并且建立 Tekton 相關的 Pipeline 資源
這裡我們利用 Kubernetes apiserver 做到了非常好的解耦。
于是我們就可以得到一個相對完整的圖,在上圖當中:
SCM 會發送消息到 Prow。在 Prow 當中有多個子產品,其中 Hook子產品用于處理 WebHook 消息。
Pipeline Operator(Jx Controller)發現有 ProwJob 被建立之後就會建立 Tekton 的一系列資源包括 Pipeline、Task、Pipeline Run 等。
在 Pipeline Run 當中會執行建構制品、推送制品以及部署制品到環境當中等工作。
環境清理與資料持久化
在介紹完我們的工作流程之後,我們再來介紹以下 Jenkins X 的環境清理和資料持久化。
在使用 Jenkins X 的過程中會産生一系列的資源,大量的資源被寫入到 Kubernetes 的 etcd,當資料量較大時就會影響我們 Kubernetes 的正常使用。Jenkins X 利用 Kubernetes 的核心資源 CronJob 來清理一些無用的資料,包括無用的 Preview環境、過期的Pod、過期的其他資料等等。
對于我們真正想要持久化的資料,Jenkins X 提供了外部對接的能力。我們可以去對接外部的存儲,例如AWS的S3、青雲的對象存儲……這樣我們就可以把想要持久化的資料很容易的保留下來。
在上面的圖中就展現了這一點,對于 Artifacts、Secrets、Build logs……我們都可以借助雲廠商的力量,進行資料的可靠存儲,這種方式同樣使我們的服務變得無狀态、高可用、更穩定。
總結
Jenkins X 是一個非常現代化的産品,包括現代化的使用者體驗與現代化的架構設計。
對于社群使用者來說,現在可以開始嘗試使用 Jenkins X(Jenkins X 對 Github的支援相對完善,并且相對傳統 Jenkins 來說更節省資源、容易落地)。
大型企業落地仍然難度比較高,需要社群、使用者以及企業的共同努力,建設下一代的 CI/CD 産品和社群。
大型企業落地 Kubernetes 之上的 CI/CD,使用傳統 Jenkins 仍然是首選方案。在使用這種方案時可以結合 Kubernetes、kubernetes-cd、casc 等插件使傳統 Jenkins 可以與 Kubernetes 互相結合。
目前社群中的開源 Kubernetes 發行版 KuberSphere 已經将這一方案內建到産品當中,使使用者可以獲得開箱即用的雲原生 CI/CD 功能,有興趣的同學可以試用、體驗。