作者 | Vishnu Vasudevan
译者| 赵青窕
审校 | Noe
根据ActiveState 2020年CI/CD状态调查结果,Jenkins是目前市场上使用最多的CI/CD工具。作为市场上发展较早的持续集成工具,它有足够的时间来进行推广,并一直是推动DevOps构建和软件交付方法的关键组成部分。
得益于逾1800个插件,使用正确的插件集就很容易对Jenkins进行扩展,通过扩展我们几乎无所不能。Jenkins用户可以基于不同的插件获得个性化体验。
插件可能是Jenkins的核心,但它们也很快成为使用Jenkins的团队的负担。当下管理Jenkins平台已慢慢成为成长型团队和公司的瓶颈,不会提高反而会降低敏捷性。
在本指南中,我将更多地介绍Jenkins的一些缺点,以及一些替代方案。
插件对于使用 Jenkins 非常重要,以至于在安装过程中会要求你从选择一个插件开始。默认安装附带大约 20 个插件,以及你在安装期间选择的任何插件。 这些插件负责将你的 CI/CD 同 GitHub 或 Bitbucket 等外部工具集成以进行版本控制。插件还扩展了 Jenkins 的功能及其工作方式。即使是编排构建的管道系统(pipeline system)也是一个插件,可以使用其他插件进行修改。

你可以通过市场向 Jenkins 添加新插件。市场中的所有插件都是基于社区且开源的,这意味着任何人都可以创建符合他们需求的自定义插件并将其释放到市场上。通常情况下,在选择插件时,你需要注意以下三个重要事项:
●插件的受欢迎程度可以通过安装次数来评估
●它的维护情况如何,可以通过查看最近一次更新的时间来估计
●所需的依赖关系,比如你是否需要安装其他的插件

虽然大量可用的插件曾经被视为是平台的优势,但现在经常被视为一个缺点,助长了许多人与 Jenkins 之间的爱恨情仇。当它第一次发布时,Jenkins 感觉就像一个美妙的自助服务环境,一个可靠的开发团队几乎可以使用 Jenkins 管道和正确的插件集做任何事情。然而,今天,你需要真正的 Jenkins 专业知识来维护 Jenkins 服务器,这是一个劣势,尤其是与市场上较新的 SaaS CI/CD相比较时。
有很多与 Jenkins 插件生态系统相关的常态问题。首当其冲的就是升级和依赖的无休止循环。一个简单的项目可能需要超过 25 个不同的插件,且你安装的每个插件又需要其他的插件来保证其可以正常工作。这会导致你陷入进退两难,比如你安装了两个插件,每个插件都需要相同的第三个插件才能工作,并且每个都依赖于第三个插件的不同版本,从而导致安装问题或错误。
当你发现插件存在安全漏洞时,就会出现更大的问题。如果你发现某个插件不安全或有漏洞,你可以在 GitHub 上提交问题并等待该插件被打补丁,但如果补丁永远不会到来怎么办?你可以忍受安全漏洞,可以找到替换插件并修改所有管道以适应新插件,或者复制插件并修补它,然后成为该插件的新维护者。鉴于每天都会发现新的安全漏洞并且插件需要经常修补,这很快就会触发大量的工作。
这也导致了 Jenkins 插件的另一个问题。即使是非常流行的插件也经常被原始维护者随意地维护或放弃。从维护者的角度来看,这是完全可以理解的。插件是为了解决问题而创建的,大多数维护者并不打算在他们的工作之外成为专业的插件维护者。但是在用户方面,这会导致插件的支持很差,因为维护者是社区成员,没有义务无限期地维护插件。
Jenkins 插件的最后一个问题是缺乏透明度。 如果不仔细检查代码,你将无法知道插件的范围,并且你无法根据需要来限制插件的一些访问和操作权限。因此,在选择插件时,信任因素非常重要,需要小心减少潜在的攻击面。
当你考虑在单个团队中使用 Jenkins 时,这些问题看起来可能不是一个大问题。他们可以使用他们最喜欢的插件集构建相应的管道,并且假设没有安全问题,一切都会工作地很好。但是随着公司的发展,更多的开发人员加入进来,创建了新的团队,并且有多个应用程序和服务,Jenkins 插件就不能很好地工作。如果你让每个人都使用他们想要的任何插件,你很快就会遇到依赖和升级问题。另一方面,如果你限制开发人员可以使用的插件,整个开发团队的士气可能都会变得低落。
Jenkins 只允许一个主节点,因此,在设计时根本没有考虑到高可用性。由于这些设计选择,必须重新启动服务器以更改配置或安装新插件,但这类操作会导致服务器停机并中断组织中依赖 Jenkins 的每个人的工作。目前这个困境没有简单的解决方案。为每个团队创建 Jenkins 服务器纯粹是浪费财力,并且会逐渐形成一个个的小团体。拥有独立 Jenkins 服务器的团队将基于不同的插件集构建不兼容的管道,从而无法为你的团队间彼此共享自动化工作的情况做好准备。
你不能让Jenkins成为一个真正的自助服务平台,因为它操作起来会很复杂,而且会减少团队之间的合作。但是反对Jenkins插件的最大论点是插件是安全隐患。如前所述,几乎所有插件都是社区创建和支持的。你需要相信维护人员使用了有利于安全的最佳方式,并针对新发现的漏洞都会对插件进行了修补,如果要正确地完成这些工作,可能需要每周进行一次。CI/CD平台通常可以访问许多系统,并拥有修改基础设施和与生产环境交互所需的凭证。这使得任何CI/CD平台都成为基础设施中非常敏感的一部分,而安全性应该是这些工具的首要任务。
到目前为止,你已经可以很好地理解Jenkins插件的一些问题。如果你正在考虑停止依赖,转向一个更安全的开发环境,你可以采取下面的方式。
创建一个精心挑选的插件列表,并坚持使用这些插件。这个列表应该同时考虑安全性和可维护性的问题。一般来说,最好坚持使用如GitHub或Amazon Web Services等云提供商集成的插件,因为它们通常会得到相应云提供商的社区和开发者的大力支持。例如,GitHub插件是一个维护良好的插件,你可以依赖它。在主服务器上运行升级并运行新插件之前,先在辅助Jenkins服务器上测试它们。
避免更改Jenkins管道工作方式的插件,因为如果这些插件工作异常,不再被维护,或者存在安全漏洞,你将需要重做整个管道。
通过检查插件的GitHub库,主动关注插件的健康状况,看看它们是否正在积极地被维护。如果发现异常,你应该寻找替代插件。这可以防止将来升级Jenkins服务器或发现安全漏洞时导致的停机。插件是具有潜在漏洞的独立软件,攻击者可以利用这些漏洞来访问你的构建系统,以及Jenkins出于必要时授予读写权限的其他部分,如代码存储库、云提供商和网络连接。正如在SolarWinds供应链攻击事件中,攻击者通过构建管道将恶意软件注入,不久后,受污染的软件又被交付给公司的数万个客户。这表明,CI/CD管道是构建流程中被信任的一部分,只需要一段恶意代码,不仅能让你的公司受到攻击,还可以让你的客户受到攻击。这一领域的极端敏感性使得插件的监控和管理至关重要。
容器技术使你能够对环境的所有依赖关系进行打包,多亏了Docker插件,你可以在Docker容器中运行Jenkins。Docker容器可以使用为任务创建的映像并充当Jenkins代理,完成构建所需的所有依赖项,从而无需在服务器上安装插件。
Jenkins插件问题的一个最简单的解决方案就是使用更少的插件。使用的插件越少,问题就越少。为了达到该目的,你可以选择脚本而不是插件。例如,发送Slack通知就像发送一个HTTP请求到一个API一样简单,并且可以避免依赖第三方插件。脚本可能比插件更可靠,因为你可以在本地使用它们来执行相同的操作。虽然这看起来比使用插件需要更多的工作,但你可以在公司内部共享你的自动化脚本,并在任何管道中使用它们,且不影响Jenkins服务器。
第二种方法是尽可能多地使用Jenkins模板(Jenkins templates)。模板允许你定义可重用的管道(任务)片段,并添加一个抽象层,使开发人员更容易配置和使用Jenkins。使用模板为你提供了一种识别所需插件的简单方法,因为你需要的插件是模板中引用的插件。这为你提供了一个协作构建管道的地方,并减少了跨组织使用的插件数量。
Jenkins及其插件生态系统对于寻找项目中CI/CD解决方案的人非常有吸引力。然而,不利的插件维护、多种不同的依赖关系和安全风险已经使许多人对该工具产生了反感。
Jenkins插件管理将会影响你的企业,而管理不善的插件会使你的企业处于危险之中。实施缓和策略非常重要,比如通过定义标准作业或管道来最小化插件数量,仔细管理插件,在单独的实例上测试插件和升级,以及使用基于容器的作业来减少对Jenkins插件的依赖。
如果你厌倦了管理你的CI/CD生态系统,包括你的Jenkins服务器,你可能会对一个无代码的DevOps平台感兴趣,它可以取代许多Jenkins插件。这样的平台还可以连接你现有的工具,提高你团队的敏捷性和响应性,并允许你专注于创建优秀的软件,而不是管理你的管道。
赵青窕,51CTO社区编辑,从事多年驱动开发。研究兴趣包含安全OS和网络安全领域,发表过网络相关专利。
原文标题:Why You Should Stop Relying on Jenkins Plug-ins
链接:https://dzone.com/articles/why-you-should-stop-relying-on-jenkins-plugins-2
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串