Jenkins的核心问题是它的单体巨石。一切都耦合在一起,插件,配置,web ui,Jenkins核心,一切都在一个大型Web应用程序中。是时候我们开始将我们在自己的生产系统上学到的经验应用到Jenkins本身,Jenkins需要是一个云原生模块化系统。
Jenkins于2005年开始以Hudson的身份开始出现,2008年,作为Java One公爵选择奖的获得者,Jenkins获得了成功。当甲骨文收购Sun时,开源社区决定推出一个新项目Jenkins,以便将其作为一个真正的开源和自由的努力。从那时起,它一直继续存在。
在我看来,Jenkins在连续整合集成(CI)甚至在某种程度上持续部署(CD)方面确实是这个领域里唯一的选项。然而,经过几年的使用和Jenkins的战斗,我认为可能是继续前进的时候了。像许多其他项目一样,Jenkins已经成为自己成功的牺牲品。
我在职业生涯的大部分时间都和Jenkins在一起。首先作为Jenkins工件的消费者,当它仍然是Hudson时,现在作为Jenkins的开发管理员,在庞大的团队中创建和配置数千个工作。
Jenkins在那些年里已经成长和成熟。但一切都不顺利。我发现自己每天都在和Jenkins打架。虽然已经取得了一些进展以解决我的一些问题,但其他问题是难以解决的问题,这些问题深入探讨了Jenkins的工作方式。
构建配置
Jenkins核心是Jobs,这些构建配置告诉Jenkins什么以及如何做事。
Web UI
起初一切都是通过UI完成的。所有项目都是通过非常易于使用的Web表单进行配置的。起初这很棒。但很快就发现这很难备份和维护。随着对代码配置的不断增长的需求,这并不适合。配置大量作业非常繁琐且容易出错。维护这些配置更加困难。
工作DSL
接下来是 Jenkins Job DSL 。这是面向配置驱动开发的可喜步骤。Jenkins Job DSL允许您使用Groovy 编写工作代码。您可以创建可重用的类和方法来生成10,100,1000的作业,而作为管理员的工作很少。作为额外的好处,我们还可以控制作业配置。
def gitUrl = 'git:<font><i>//github.com/jenkinsci/job-dsl-plugin.git'</i></font><font> job('PROJ-unit-tests') { scm { git(gitUrl) } triggers { scm('*/15 * * * *') } steps { maven('-e clean test') maven('-B release:prepare release:perform') shell('cleanup.sh') } } </font>
但一切都不顺利。Job DSL有几个关键缺陷。
从头开始并不总是那么容易,寻找Job模式难以实现。
构建脚本的测试几乎是不可能的。这导致系统管理员在生产中测试配置。此外,尝试新功能非常困难。如果您希望一个分支的构建方式与其他分支不同,该怎么办 你可以做到这一点,但它不是很干净。
虽然Job DSL是集中维护的,但是SA很喜欢,但在配置工作时,它让开发人员感觉像是二等公民。并非所有乔布斯都陷入了完全相同的模式。这经常导致非常复杂的DSL脚本。
Jenkins管道
最近, Jenkins Pipeline 或许多人将它们称为Jenkinsfile,因为构建是从Jenkinsfile名称的存储库中的特殊文件驱动的。Jenkinsfile将控制权交还给开发人员。构建配置与代码一起存储。这允许不同分支的行为不同,具体取决于该分支中的Jenkinsfile配置。SA仍然可以维护一组核心功能,开发人员可以将这些功能导入到脚本中并根据需要进行自定义。
pipeline { agent any stages { stage('Build') { steps { <font><i>// </i></font><font> } } stage('Test') { steps { </font><font><i>// </i></font><font> } } stage('Deploy') { steps { </font><font><i>// </i></font><font> } } } } </font>
虽然这确实提供了在更加孤立的环境中进行实验的能力,但它仍然存在一些问题。虽然Jenkinsfile在技术上很时髦,但由于安全问题严重,你不能做很多你通常认为可以做的事情。
通常这会显示为奇怪的错误,例如无法为每个错误做错在列表上。我不能告诉你我多少次在Jenkinsfile中编码只是为了让安全脚本权限看起来好看。然后,这需要有人登录Jenkins,批准调用,然后再次运行作业,冲洗并重复,直到希望工作完成。
除此之外,无法在外部配置和存储白名单。
因此,当您使用库启动新的Jenkins实例时,您必须再次批准所有相同的脚本。在失败之前,您无法批准脚本。所以,祝你好运,确保一切都得到正确批准。
Jenkinsfile分为几个阶段。这样可以很好地隔离构建思想,例如构建,测试,发布等。但是,Jenkins的开源版本不支持从Jenkinsfile中的阶段重新启动构建。这是全有或全无。对于简短的小型构建,这不是什么大问题。对于较大的构建,这是一个事务破坏者。
插件
插件很棒。它们允许第三方将新功能写入Jenkins。我们将包含各种内容的页面合并在一起。例如,我可以拥有一个显示代码覆盖率,单元测试历史记录,静态代码分析趋势等的构建。很棒的权利。从表面上看是的。
但插件有一个黑暗的一面。
由于插件和插件版本,维护Jenkins是一场噩梦。插件是在INTO Jenkins中部署的,作为单个Jenkins Master Web容器的一部分,这意味着它们共享一个共同的类路径。
我怎么知道我是否会破坏了某些东西?
很多时候升级Jenkins也会更改底层配置文件。如果您使用Jenkins Job DSL,那么您必须确保它与您的Jenkins版本和所有已安装的插件保持同步,这会强制您拥有多个Jenkins实例。但是你不能轻易地用Jenkins做传统的金丝雀部署模式。
这最终导致新功能停滞不前并延迟升级。
文件数据
Jenkins生成并用于渲染所有可爱构建结果页面的所有数据都存储为文件。很多,很多很多文件。这使得Jenkins的表现非常出色。
大型管道视图花费几秒钟渲染并不罕见。这些大文件也使Jenkins内存密集。很多时候Jenkins将这些文件解析为整个DOM,将文件的全部内容加载到内存中。Jenkins使用几千兆字节的堆大小并不罕见。实际上,我们的生产Jenkins实例只有一个32 GB的堆来保持运行
内存
说到内存,因为所有插件实际上都是加载到Jenkins中的代码,所以看到大量内存泄漏是很常见的。这不是Jenkins的核心故障,但它仍然会取消服务器。我们现在安排定期重启Jenkins实例以减轻内存泄漏。
系统Groovy脚本
如果您每个人都想使用System Groovy脚本,请不要这样做!你会(将)把Jenkins搞得一团糟。这是因为脚本不是分叉进程,而是脚本运行在Jenkins内部。这是有目的的,因为作为系统groovy脚本,您希望访问Jenkins本身和实际运行的类。但这意味着您还可以访问运行Jenkins的直接JVM和进程。因此,你可以搞乱类路径,堆,甚至把整个事情搞砸(System.exit?)
Jenkins配置
虽然Job DSL和Jenkins文件有助于配置Jobs,但在配置Jenkins本身时却做得很少。您需要将Jenkins配置为连接到静态从属或Mesos群集,或者设置安全性或配置环境变量,构建工具等。作业DSL有一些可用于执行此操作的库,但它们很难理解且无法测试。
无法测试
我曾经说过一次,两次,数千次。Jenkins是不可能测试的。谁会使用无法测试的编程语言?除了在生产中,没有人!有些人可能会说你当然可以测试Jenkins。相信我,我试过了。你是如何真正测试它的?我们已经尝试启动Jenkins的本地实例,但是在实际运行构建时会很快崩溃。实际配置怎么样?您需要配置对群集的访问权限,模拟关键基础架构,如artifactory和DTR。这真的很难。最重要的是,您希望测试不会影响实际系统。有几次我们的“测试”由于缺乏隔离而导致生产问题。
升级
我在插件中简要提到了这一点,但升级是一个真正的问题。当你升级一个插件甚至Jenkins本身的全部或全部。如果您有一个需要较旧版本插件的作业,而另一个需要较新版本的作业则会被卡住。您将需要多个Jenkins实例。迁移到新版本并不总是可行,因为升级插件也会导致底层XML也发生变化。因此,回滚几乎是不可能的。
Jenkins Web UI
实际的Web UI在一个名为Jenkins Master的Web容器上运行。你只能拥有其中一个。它不支持任何类型的群集或故障转移。因此,如果你有一个非常庞大的开发团队,所有人都打到Jenkins,那么一个实例需要非常强大并且不断受到监控。
结论
詹金斯的核心问题是它是一个单体巨石,一切耦合在一起。
我理想的CI / CD系统需要:
值得庆幸的是,似乎有些东西可能适合该法案。