首發於 Jenkins 中文社區git
這是一個關於 dailymotion 從 Jenkins 到 Jenkins X 的旅程,咱們遇到的問題,以及咱們是如何解決它們的故事。github
在 dailymotion ,咱們堅信 devops 最佳實踐,而且在 Kubernetes 投入了大量投資。 咱們的部分產品已經部署在 Kubernetes 上,但並非所有。 所以,當遷移咱們的廣告技術平臺的時候,咱們想要徹底採用「 Kubernetes 式」——或者雲原生,以追隨技術趨勢! 這意味着要從新定義整個 CI/CD 流水線,從靜態/永久環境遷移,轉向動態按需分配環境。 咱們的目標是受權給咱們的開發人員,縮短咱們的上線時間以及下降咱們的運營成本。web
對於新的 CI/CD 平臺咱們的初始需求是:docker
在 CI/CD 生態系統中有至關多的參與者,可是隻有一個符合咱們的需求,Jenkins X ,它基於 Jenkins 和 Kubernetes ,原生支持預覽環境和 gitops安全
Jenkins X 的設置至關簡單,而且在他們的官方網站上已經有很好的文檔(譯註:譯者曾對 Jenkins X 文檔中文本地化作了一些貢獻,同時也期待更多的人蔘與以完善中文文檔)。 因爲咱們已經使用了 Google Kubernetes Engine (GKE),因此 jx
命令行工具本身建立了全部東西,包括 Kubernetes 集羣。 這裏有一個小小的哇哦效果,在幾分鐘內得到一個完整的工做系統是很是使人印象深入的。服務器
Jenkins X 提供了不少快速入門和模板來增長哇哦效果, 然而,在 dailymotion ,咱們已經有了帶有 Jenkins 流水線的倉庫,咱們想要重用它們。 咱們決定以"艱難的方式"作事情,並重構咱們的聲明式流水線,使它們與 Jenkins X 兼容。less
實際上,這一部分並不針對 Jenkins X ,而是基於 Kubernetes 插件在 Kubernetes 上運行 Jenkins 。 若是您習慣使用「經典的」 Jenkins ,即在裸金屬或虛擬機上運行靜態代理,那麼這裏的主要更改是,每次構建都將在本身的短暫的 pod 上執行。 流水線的每一個步驟均可以指定應該在 pod 的哪一個容器上執行。 在插件的源代碼中有一些流水線的例子。 在這裏,咱們的"挑戰"是定義容器的粒度,以及它們將包含哪些工具:須要足夠的容器,以便咱們能夠在不一樣流水線之間重用它們的鏡像,但也不能太多,以控制維護量——咱們不想花時間從新構建容器鏡像。工具
之前,咱們一般在 Docker 容器中運行大多數流水線步驟,當咱們須要自定義步驟時,咱們在運行中的流水線中構建它,就在運行它以前。 雖然它比較慢,可是易於維護,由於全部內容都是在源代碼中定義的。 例如,升級 Go 運行時的版本能夠在一個 pull-request 中完成。 所以,要預先構建容器鏡像聽起來像是給現有設置增長了更多的複雜性。 它還有幾個優勢:倉庫之間的重複更少,構建速度更快,而且不會由於某些第三方託管平臺宕機而出現更多構建錯誤。測試
這些天將給咱們帶來一個有趣的話題:在 Kubernetes 集羣中構建容器鏡像。網站
Jenkins X 附帶了一組"構建打包",使用 "Docker in Docker" 從容器內部構建鏡像。 可是隨着新的容器運行時的到來,Kubernetes 推出了它的容器運行時接口( CRI ),咱們想要探索其餘的選擇。 Kaniko 是最成熟的解決方案,符合咱們的需求/技術棧。 咱們很興奮……
……直到咱們遇到兩個問題:
咱們測試了一些解決方案來爲 CI 流水線構建定製的"工具"鏡像, 最後,咱們選擇使用一個單個倉庫,每一個分支對應一個 Dockerfile
和鏡像。 由於咱們將源代碼託管在 Github 上,而且使用 Jenkins Github 插件來構建咱們的倉庫, 因此它能夠構建咱們全部的分支,併爲 webhook 事件上的新分支建立新的任務,這使得管理起來很容易。 每一個分支都有本身的 Jenkinsfile 聲明式流水線,使用 Kaniko 構建鏡像,並將其推入咱們的容器註冊中心。 這對於快速添加新鏡像或編輯現有的鏡像很是有用,由於咱們知道 Jenkins 會處理好全部的事情。
咱們在以前的 Jenkins 平臺中遇到的一個主要問題來自靜態的代理/執行器,在高峯時間有時構建隊列很長。 Kubernetes 之上的 Jenkins 使這個問題很容易解決,主要是在 Kubernetes 集羣上運行時,它能支持集羣自動伸縮。 集羣將根據當前負載簡單地添加或刪除節點。 但這是基於所請求的資源,而不是基於所觀察到的使用的資源。 這意味着,做爲開發人員,咱們的工做是在構建 pod 模板中定義所需的資源( CPU 和內存)。 而後 Kubernetes 調度程序將使用此信息找到一個匹配的節點來運行 pod ——或者它可能決定建立一個新的節點。 這真是太好了,由於咱們再也不有長構建隊列。 但相反,咱們須要謹慎地定義咱們所需資源的恰當數量,並在更新流水線時更新它們。 因爲資源是在容器級別定義的,而不是在 pod 級別定義的,因此處理起來有點複雜。 可是咱們不關心限制,只關心請求。 pod 的請求只是全部容器請求的相加。 所以,咱們只是將整個 pod 的資源請求寫在第一個容器上——或者 jnlp 容器上——它是默認的容器。 下面是咱們使用的一個 Jenkinsfile 的例子, 也是咱們如何聲明請求的資源的例子:
pipeline {
agent {
kubernetes {
label 'xxx-builder'
yaml """ kind: Pod metadata: name: xxx-builder spec: containers: - name: jnlp 複製代碼
如今咱們已經擁有了全部的工具,而且可以爲咱們的應用程序構建一個鏡像, 咱們準備下一步:將它部署到"預覽環境"!
Jenkins X 經過重用現有的工具——主要是 Helm ,使得部署預覽環境變得很容易, 只要你遵循一些約定,例如用於鏡像標籤的值的名稱。 最好是從"包"中提供的 Helm charts 複製/粘貼。 若是你不熟悉 Helm ,它基本上是一個 Kubernetes 應用程序包管理器。 每一個應用程序都打包爲一個 "chart" ,而後能夠經過使用 helm 命令行工具做爲一個 "release" 被部署。 預覽環境是經過使用 jx 命令行工具進行部署的,該工具負責部署 Helm chart ,並以評論的形式,將所公開服務的 URL 添加到 Github pull-request 中。 這一切都很是好,並且對於咱們第一個使用純 http 的 POC 來講頗有效。 但如今是2018年(譯註:做者是在2018年寫的這篇文章),沒有人再使用 http 了。 讓咱們加密吧! 多虧了 cert-manager,當在 kubernetes 中建立 ingress 資源時,咱們能夠自動爲咱們的新域名得到一個 SSL 證書。 咱們試圖在咱們的設置中啓用 tls-acme
標誌——與 cert-manager 進行綁定,可是它不起做用。 這給了咱們一個機會來看看 Jenkins X 的源代碼——它也是用 Go 開發的。 稍做修復以後都好了, 如今咱們可使用 let's encrypt 提供的自動化證書來享受安全的預覽環境。
咱們在預覽環境中遇到的另外一個問題與上述環境的清理有關。 每一個打開一個 pull-request ,就建立一個預覽環境,所以在 pull-request 被合併或關閉時應該刪除預覽環境。 這由 Jenkins X 設置的 Kubernetes 任務來處理,它刪除了預覽環境所使用的名稱空間。 問題是這個任務不會刪除 Helm release ——因此,好比若是您運行 helm list,您仍然會看到一個很大的舊的預覽環境列表。 對於這個問題,咱們決定改變使用 Helm 部署預覽環境的方式。 Jenkins X 團隊已經寫過關於 Helm 和 Tiller ( Helm 的服務器端組件)的這些問題, 所以,咱們決定使用 helmTemplate
特性標誌,只使用 Helm 做爲模板渲染引擎,並使用 kubectl
處理生成的資源。 這樣,咱們就不會用臨時預覽環境"污染" Helm releases 列表。
在初始化 POC 的某個階段,咱們對咱們的設置和流水線感到滿意,並但願將咱們的 POC 平臺轉變爲準生產的平臺。 第一步是安裝 SAML 插件以設置 OKTA 集成——以容許內部用戶登陸。 它運行得很好,幾天後,我注意到咱們的 OKTA 集成已經再也不存在了。 我正忙着作其餘事情,因此我只是問個人同事他是否作了一些改變,而後繼續作其餘事情。 但幾天後再次發生時,我開始調查。 我注意到的第一件事是 Jenkins Pod 最近從新啓動過。 可是咱們有一個持久化的存儲,咱們的任務仍然在那裏,因此是時候仔細看看了! 事實證實,用於安裝 Jenkins 的 Helm chart 有一個啓動腳本, 它從 Kubernetes configmap
重置了 Jenkins 配置。 固然,咱們不能像在虛擬機上管理 Jenkins 那樣管理在 Kubernetes 中運行的 Jenkins !
所以,咱們沒有手動編輯 configmap
,而是後退一步,查看全局。 這個 configmap
自己由 jenkins-x-platform 管理, 所以升級平臺將重置咱們的自定義更改。 咱們須要將咱們的"定製"存儲在安全的地方並跟蹤咱們的更改。 咱們能夠用 Jenkins X 的方式,用一個 umbrella chart 來安裝/配置一切, 可是這種方法有一些缺點:它不支持 "secret" —— 咱們將一些敏感的值存儲在咱們的 Git 倉庫中—— 它"隱藏"了全部的 sub-charts 。 因此,若是咱們列出全部已安裝的 Helm releases ,咱們將只看到一個。 可是還有其餘基於 Helm 的工具,它們更對 Gitops 更友好。 Helmfile 就是其中之一,它經過 helm secrets 插件和 sops爲 secrets 提供了原生支持。 我如今不會詳細介紹咱們的設置,但別擔憂,這將是我下一篇博客文章的主題!
咱們故事的另外一個有趣的部分是從 Jenkins 到 Jenkins X 的實際遷移。 以及咱們如何使用兩個構建系統處理倉庫。 首先,咱們設置新的 Jenkins 來只構建 "jenkinsx" 分支, 而且更新了舊的 Jenkins 的配置來構建除 "jenkinsx" 分支以外的全部分支。 咱們計劃在 "jenkinsx" 分支中準備新的流水線,並將其合併以進行遷移。 對於咱們的初始化 POC ,它工做得很好,可是當咱們開始使用預覽環境時, 咱們必須建立新的 PR ,而這些 PR 不是基於新的 Jenkins 構建的,由於分支限制。 所以,咱們選擇在這兩個 Jenkins 實例上構建一切, 但對於舊的 Jenkins 使用 Jenkinsfile
文件名,對於新的 Jenkins 使用 Jenkinsxfile
文件名。 遷移以後,咱們將更新此配置並重命名文件,但這是值得的, 由於它使咱們可以在兩個系統之間進行平滑的轉換,而且每一個項目均可以本身遷移,而不會影響其餘項目。
因此,Jenkins X 爲你們準備好了嗎?老實說,我不這麼認爲。 並不是全部功能和所支持的平臺—— Git 託管平臺或 Kubernetes 託管平臺——都足夠穩定。 可是,若是您準備投入足夠的時間來深刻研究,並選擇適合您的使用場景的穩定特性和平臺, 那麼您將可以使用 CI/CD 等所需的一切來改進您的流水線。 這將縮短您的上線時間,下降您的成本,若是您對測試也很認真,那麼請對您的軟件質量充滿信心。
一開始,咱們說這是咱們從 Jenkins 到 Jenkins X 的旅程。但咱們的旅程並未結束,咱們還在旅行中。 部分緣由是咱們的目標仍在移動:Jenkins X 仍處於大的發展階段,並且它自己正在朝着 Serverless 的方向前進, 目前正在使用 Knative 構建 的路上。 它的目的地是雲原生 Jenkins 。 它尚未準備好,可是您已經能夠預覽它的外觀了。
咱們的旅程還將繼續,由於咱們不但願它結束。 咱們如今的目的地並非咱們的最終目的地,而是咱們不斷進化的一個步驟。 這就是咱們喜歡 Jenkins X 的緣由:由於它遵循相同的模式。 那麼,你在等待什麼來開始你本身的旅程呢?
譯註:譯者曾對 Jenkins X 文檔中文本地化作了一些貢獻,同時也期待更多的人在 Jenkins X 旅程中, 可以參與到 Jenkins 中文社區以完善 Jenkins X 的中文文檔。
譯者:王冬輝