在上篇文章中咱們主要講解了Jenkins的頁面與路由,在本章中咱們要講解下Jenkins的數據持久化機制。在Jenkins中數據的持久化是經過文件進行存儲的,你們平時使用Hibernate進行持久化的時候,咱們只須要關心哪些地方是須要存儲的,哪些位置是不須要儲存的,而且在不須要存儲的位置添加transient
關鍵字便可,持久化的框架會自動幫我作好Java Object與數據庫存儲之間的序列化與反序列化的過程,而在Jenkins中因爲數據的存儲都是經過文件的方式進行存儲的,有必要讓你們瞭解下一個Jenkins是如何將數據進行組織與存儲的。node
JENKINS_HOME
是Jenkins啓動的時候會識別的一個環境變量,這個環境變量的做用是設定Jenkins的持久化根目錄,全部的Jenkins持久化文件會以此目錄爲根進行建立與存儲,而Jenkins中任務、構建、帳戶等信息都會以文件的方式存儲,所以這個文件目錄會在Jenkins的使用過程當中容量快速膨脹,對於須要維護Jenkins的同窗建議單獨掛一塊盤。
在JENKINS_HOME
這個目錄下的結構大體以下數據庫
能夠發現JENKINS_HOME
下有兩種命名的規則,一種是標準的命名,例如config.xml或者nodes的文件夾;還有一種相似類名的文件名,例如hudson.model.UpdateCenter.xml這種的文件。這兩種文件的命名能夠推測出一種是Jenkins內部實現的一些存儲,而另外一種是Jenkins提供出來的通用文件存儲機制。那麼咱們打開一個文件來看下存儲的內容是什麼樣子的,好比hudson.model.UpdateCenter.xml,裏面的內容以下
安全
文件是以標準的XML的格式進行持久化的,熟悉JAVA序列化與反序列化的同窗已經能夠猜想到,這個文件應該是對應着Jenkins中的一個對象,Jenkins經過將一個Java對象序列化和反序列化來進行存儲。對於上面兩種不一樣命名方式的文件,能夠發現文件內容都是XML格式的Java對象序列化格式,惟一的區別在於存儲路徑上會有所不一樣,這是爲何呢。架構
標準命名的一些路徑和文件大部分是Jenkins Master的一些實現,好比job、nodes等等,是Jenkins Master直接使用的一些內部實現的存儲,由於這些存儲的信息一般會有特殊的意義或者行爲,好比任務或者節點,若是存儲在單文件中會形成難以查詢、隔離等等的問題,所以Jenkins對於一些對象的存儲進行了優化。併發
通用格式的存儲例如com.aliyun.www.cos.DeployBuilder.xml
則大部分是插件的存儲文件,開發過Jenkins插件的開發者可能知道,在Jenkins的插件中並無讀取配置文件的動做,只有一個save方法,開發者須要進行配置存儲的時候,只須要調用父類中繼承下來的save方法便可。而這個save的方法的實現會自動以上面的這種類名爲文件名的方式存儲在JENKINS_HOME目錄下,所以,這也就是爲何不建議你們直接存儲配置的祕鑰到本身的插件中,而是須要調用credentials插件來實現,由於Jenkins在默認存儲的時候不會直接對你的祕鑰信息進行加密,會有很高的安全風險。那麼Jenkins在什麼時候會反序列化數據的呢?答案是Jenkins啓動時。Jenkins在啓動的時候,首先會啓動主進程server,而後加載全部的插件,再加載數據。在加載插件的時候,會經過類名查找相應的配置文件,再將配置中的信息反序列化Java的對象,所以若是在Jenkins的插件中存儲了大量的數據,會形成Jenkins重啓加載異常緩慢的問題,若是在插件中有大量的存儲需求,建議你們將存儲代理給Jenkins的Job,雖然Job在加載的時候也是在Jenkins啓動時經過文件的方式加載的,可是每一個Job的配置是隔離的,不會形成單次大文件的讀取效率瓶頸。負載均衡
不少開發者一直在思考如何將Jenkins作成高可用的,畢竟單節點的Jenkins從某種意義上來說總像一個定時炸彈。可是很遺憾的告訴你們,標準的Jenkins很難作到完整的高可用方案。那麼問題的根源在什麼?這要從咱們剛纔談到的存儲模型談起,上面咱們提到了Jenkins的啓動順序爲:主程序啓動 ——> 插件加載 ——> 數據加載。而當數據加載到內存後,Jenkins的數據讀取和存儲模型就會變成內存讀異步寫,也就是說一旦Jenkins啓動,就不再會嘗試從磁盤從新加載這幾個任務了。假設咱們從磁盤中讀取了10個任務,若是有從Jenkins UI頁面或者API提交修改任務的請求,那麼Jenkins會去去讀內存中的任務配置信息,而後修改內存數據,再異步刷新到磁盤上。換言之,Jenkins是有狀態的,並且是單機有狀態的,若是想經過簡單的共享存儲與負載均衡或者反向代理的方式實現Jenkins的高可用基本是不行的。框架
那麼有的開發者在想是否能夠經過通知而後異步更新內存數據的方式進行數據的共享呢。好比一個Jenkins Master更新了任務信息,而後經過開發一個插件通知另外一個Jenkins Master在內存中更新任務的配置呢。這種方式看樣子是可行的,可是實際操做中咱們會發現以下問題。首先若是是單純單個文件的變動或者變化理論上是可行的,可是須要這個插件在全部涉及存儲的擴展點上fire出事件,並進行處理,而且部分信息的變動還涉及相關插件的從新加載,而這些信息是沒法從單純的事件信息中得出的。其次若是涉及文件夾或者多個文件的變動,那麼此時須要進行全目錄的掃描或者加載,會觸發全量的數據加載,一旦這個操做涉及任務或者構建,那麼延時將是秒級以上的,若是在這個階段有任何的更新操做,極有可能形成數據的腦裂。最後,因爲Jenkins沒有公共的cache,會形成登陸態共享等等問題。異步
所以,目前標準的Jenkins尚未很好的高可用方案。可是,你們對於Jenkins的可靠性不用過於擔憂,一個4C8G的VM,進行JVM調優後能夠輕鬆應對上百的構建併發,對於大多數中小型公司而言是足夠了,對於更大型的公司會更傾向於使用多個獨立的Jenkins Master來分擔風險。性能
Jenkins沒有辦法作到高可用,是否意味着Jenkins的架構已通過於陳舊或者老邁了,咱們是否還繼續選擇Jenkins。誠然Jenkins的架構很古老,這種存儲模型也有他避免不了的問題。可是就目前來看,Jenkins是惟一的選擇,不管是GitLab CI、Spinnaker仍是Travis CI等等,相比Jenkins都在功能或者生態上有所欠缺,並且若是不是超大型的企業對於目前Jenkins的性能問題基本上也是無感知的,能夠說Jenkins目前是剛恰好的狀態。對於CI/CD來說,咱們須要的並非多麼超前的技術或者多麼炫的頁面,更多的是如何經過DevOps來保證質量和集成交付流程,對於不一樣場景和不一樣業務的開發者而言,上千個Jenkins插件和多餘牛毛的介紹文章能夠企業的DevOps快速進行實施。優化
在今年7月,阿里雲推出了CodePipeline服務,CodePipeline是基於Jenkins進行二次開發的,在CodePipeline中,咱們經過對存儲模型的改造實現了一個高可用的方案,對於Jenkins高可用有需求的開發者或者公司能夠在公有云或者私有云中嘗試使用CodePipeline來替代Jenkins,對Jenkins的全兼容可讓開發者快速上手完成本身的持續交付流程。