Learn Jenkins the hard way (3) - Jenkins的存储模型

摘要:一篇文章来看Jenkins的存储模型并讨论高可用的可行性。

前言

在上篇文章中咱们主要讲解了Jenkins的页面与路由,在本章中咱们要讲解下Jenkins的数据持久化机制。在Jenkins中数据的持久化是经过文件进行存储的,你们平时使用Hibernate进行持久化的时候,咱们只须要关心哪些地方是须要存储的,哪些位置是不须要储存的,而且在不须要存储的位置添加transient关键字便可,持久化的框架会自动帮我作好Java Object与数据库存储之间的序列化与反序列化的过程,而在Jenkins中因为数据的存储都是经过文件的方式进行存储的,有必要让你们了解下一个Jenkins是如何将数据进行组织与存储的。node

从JENKINS_HOME讲起

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的数据读取和存储模型就会变成内存读异步写,也就是说一旦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的架构很古老,这种存储模型也有他避免不了的问题。可是就目前来看,Jenkins是惟一的选择,不管是GitLab CI、Spinnaker仍是Travis CI等等,相比Jenkins都在功能或者生态上有所欠缺,并且若是不是超大型的企业对于目前Jenkins的性能问题基本上也是无感知的,能够说Jenkins目前是刚恰好的状态。对于CI/CD来说,咱们须要的并非多么超前的技术或者多么炫的页面,更多的是如何经过DevOps来保证质量和集成交付流程,对于不一样场景和不一样业务的开发者而言,上千个Jenkins插件和多余牛毛的介绍文章能够企业的DevOps快速进行实施。优化

在今年7月,阿里云推出了CodePipeline服务,CodePipeline是基于Jenkins进行二次开发的,在CodePipeline中,咱们经过对存储模型的改造实现了一个高可用的方案,对于Jenkins高可用有需求的开发者或者公司能够在公有云或者私有云中尝试使用CodePipeline来替代Jenkins,对Jenkins的全兼容可让开发者快速上手完成本身的持续交付流程。

相关文章
相关标签/搜索