研发协同平台持续集成Jenkins作业设计演进

源宝导读:Jenkins作为一个开源的持续集成工具,被大家广泛使用。本文将分享,Jenkins在明源云研发协同平台中的运用,以及在其作业设计方面的演进历程。

一、作业设计1.0

    起初,为了尽快推出研发协同平台v1.0,我们运用Jenkins工具,快速实现了站点的部署、配套服务的安装、以及文件打包等功能。

    使用过程包括以下简单的几个步骤:

1、定义Jenkins Job模板

    参照Jenkins Job的config.xml结构,定义出Job模板,以及相关业务对应的子模板。

2、替换业务参数

    依据业务场景,选择使用不同的子模板,替换对应的业务参数与子模板,生成最终的Jenkins Job配置文件。

3、调用Jenkins API

    通过直接调用Jenkins Api完成Job的创建与Build。

4、轮询监控job状态

    通过不停的轮询监控已出发build的job,获取其当前执行状态,得到最终执行结果。

    刚开始为了快速实现业务需求,采用最简单直接的方式引入了Jenkins,并快速的完成了相关业务功能的实现。

二、作业设计1.x

    随着相关业务需求的不断递增,起初的代码结构也慢慢显露出其弊端,主要体现在:

  • 子模板难以维护:数量不断增加,子模板的相似度也在不断增加;

  • 参数变量追溯困难:为应对各种不同场景,使用了多种多样的业务参数变量;

  • Job状态不一致问题:业务数据状态与job实际执行状态出现不一致;

  • 接入新作业代码冗余:每接入一个新的Job作业,都需要定义过多的与Jenkins相关的内容。

    为解决上述问题,我们对Jenkins作业的设计进行了不断的改进,改进主要分为2个方向。

2.1、业务抽象

    我们将不同的业务进行了划分,站点部署、配套服务、打包等,并且提取统一的Job构造器,避免业务代码与功能代码相混淆。

    除此之外还对Job的执行、状态监控、失败重试等功能性业务托管到Jenkins调度服务,并通过消息通知实现了解耦。

2.2、Jenkins SDK

    为了更方便的操作Jenkins相关功能,提取了针对Jenkins的SDK,避免直接与Jenkins进行交互,该SDK也是Jenkins调度服务的底层工具。

    最终设计结构如下图所示:

三、作业设计2.0

    在完成作业设计1.x的落地后,已经能很好的支撑当时的持续集成业务,但是在后续的业务发展中,不足之处也逐渐显现:

  • Job作业的模板组装不够灵活,当原有业务发生变化时,不太容易灵活的调整。

  • 当有新产品类型的持续构建需求时,需要重新定义新的模板和作业。

  • Job和业务的耦合度较高。

    从设计的角度来看,主要是扩展性和配置的灵活性不足。

    一次持续集成,也就是一组Jenkins作业按照一定的顺序组成,而一个作业里面实际上都是在执行一个一个的命令,基于此, 我们设计了作业2.0的基础模型:管道、作业、命令。

    本次作业设计的演进充分利用了Jenkins的插件功能,并且取代了之前的Jenkins调度服务

    总体设计图如下:

  • 管道:一个持续集成管道由一系列持续集成作业组成; 不同功能的作业组合成不同功能的管道; 持续集成管道中的作业可以是串行,也可以是并行

  • 作业: 管道中的作业由一组命令组成; 不同的命令组合成不同功能的作业

  • 命令:命令是持续集成中的最小功能单元;研发协同平台内置了一批命令集

  • Jenkins执行器:专门负责执行管道作业,利用Jenkins SDK与Jenkins进行交互;

  • Job状态回调管道:这里运用了Jenkins的Notification插件,Jenkins Job执行过程中的状态会通过该插件即时回调;

3.1、类设计图

服务

  • ServiceDomainService:服务调用的场景类,负责统一调度执行服务。

  • BaseService:服务基类,所有服务都继承与该类进行扩展。

    若有新的服务,只需通过继承BaseService进行扩展即可。

/// <summary>
/// 服务
/// </summary>
public abstract class BaseService : ITransientDependency
{/// <summary>/// 服务类型/// </summary>public virtual ServiceType ServiceType { get; set; } = ServiceType.Other;/// <summary>/// 执行操作/// </summary>/// <param name="args"></param>/// <returns></returns>public abstract Task Execute(BaseServiceArgs args);/// <summary>/// 执行器/// </summary>internal ICiExecutor CiExecutor { get; }/// <summary>/// 构造函数/// </summary>/// <param name="ciExecutor"></param>public BaseService(ICiExecutor ciExecutor){CiExecutor = ciExecutor;}
}

管道与作业

  • BaseCiPipeline:管道抽象类,其中定义了管道作业的的构建动作。

  • Job:单个作业对象,包含Jenkins Job 必须的属性。

    若新服务需要构造新的执行管道,需要通过BaseCiPipeline抽象类进行派生。

/// <summary>
/// CI流水线
/// </summary>
public abstract class BaseCiPipeline : ITransientDependency
{/// <summary>/// 构建Jenkins Job 集合/// </summary>/// <param name="args">CI流水线参数</param>/// <returns></returns>internal abstract Task<List<Job>> StructureJobs(CiPipelineArgs args);
}
/// <summary>
/// JenkinsJob定义
/// </summary>
public class Job
{/// <summary>/// Job执行顺序编号/// </summary>public int JobIndex { get; set; }/// <summary>/// 执行步骤/// </summary>public List<CiAction> ActionList { get; set; }/// <summary>/// Job参数/// </summary>public Dictionary<string, string> BuildParams { get; set; } = new Dictionary<string, string>();/// <summary>/// 执行节点/// </summary>public string AssignedNode { get; set; }/// <summary>/// 工作空间/// </summary>public string BaseJobName { get; set; }/// <summary>/// 当前JobName/// </summary>public string JobName { get; set; }/// <summary>/// Job目录地址/// </summary>public string JobPath { get; set; }/// <summary>/// 业务回调参数/// </summary>public string Notes { get; set; }
}

命令

  • CiAction:命令基类,定义了命令的基本属性与动作。

    每个新增的命令只需要实现自己的变化点即可。

/// <summary>
/// 操作
/// </summary>
public abstract class CiAction
{/// <summary>/// 操作类型/// </summary>public virtual ActionTypeEnum ActionType { get; set; } = ActionTypeEnum.BatchFile;/// <summary>/// 操作说明/// </summary>internal string _actionName { get; set; }/// <summary>/// 模板/// </summary>/// <returns></returns>internal abstract string Template();/// <summary>/// 参数/// </summary>/// <returns></returns>internal abstract Dictionary<string, string> BuildParams();/// <summary>/// 生成Action内容/// </summary>/// <returns></returns>public string Build(){var result = this.Template();var pars = this.BuildParams();if (pars != null && pars.Count > 0){foreach (var item in pars){result = result.Replace(item.Key, item.Value, StringComparison.OrdinalIgnoreCase);}}return result;}
}

执行器

  • ICiExecutor:执行器接口,该接口定义了执行器的动作以及对外扩展的回调事件。

  • JenkinsExecutor:是基于Jenkins 实现的执行器,通过解析管道作业,获得最终的Job并与Jenkins进行交互。

    这里只实现了Jenkins的执行器,若将来扩展了新的持续集成工具可以直接扩展新的执行器。

/// <summary>
/// 执行器
/// </summary>
public interface ICiExecutor
{/// <summary>/// 开始构建/// </summary>event EventHandler<CiPipelineArgs> Started;/// <summary>/// 构建失败/// </summary>event EventHandler<CiPipelineArgs> Failed;/// <summary>/// 构建成功/// </summary>event EventHandler<CiPipelineArgs> Succeed;/// <summary>/// 执行流水线/// </summary>/// <param name="ciPipeline">流水线</param>/// <param name="args">参数对象</param>Task Execute(BaseCiPipeline ciPipeline, CiPipelineArgs args);/// <summary>/// 触发构建事件/// </summary>/// <param name="args">业务参数对象</param>/// <returns></returns>Task InvokeStarted(CiPipelineArgs args);/// <summary>/// 触发构建失败事件/// </summary>/// <param name="args">业务参数对象</param>/// <returns></returns>Task InvokeFailed(CiPipelineArgs args);/// <summary>/// 触发构建成功事件/// </summary>/// <param name="args">业务参数对象</param>/// <returns></returns>Task InvokeSucceed(CiPipelineArgs args);
}

回调管道

  • BaseServiceCallbackHandler:回调处理基类,所有的回调处理都应集成与该类。

    每个回调处理,可以通过定义 ServiceTypes去侦听自己所关心的回调事件,并做相应的业务处理。

/// <summary>
/// 服务回调处理
/// </summary>
public abstract class BaseServiceCallbackHandler
{/// <summary>/// 处理的服务类型/// </summary>public virtual ServiceType[] ServiceTypes { get; set; }/// <summary>/// 执行器/// </summary>public ICiExecutor CiExecutor { get; }public BaseServiceCallbackHandler(ICiExecutor ciExecutor){CiExecutor = ciExecutor; }
}

作业运行流程

  • 执行流程:客户端通过调用服务场景,指定需要执行的具体服务,服务定义了其对应的管道作业,最终交由执行器进行执行。

  • 回调流程:回调场景主要用于接收Jenkins的回调请求,通过分析请求类型进而触发相应的处理事件。

3.2、本次演进主要带来的好处包括:

1、可扩展性

  • 实现个性化功能,只需自定义命令。

  • 对于不同的业务场景,只需定义不同的持续集成管道。

2、灵活性

    通过原子性的命令定义,可以自由组合出各种想要的作业,进一步定义出各种功能的持续集成管道,从而满足业务多样化的需求。

3、解耦

    通过抽象出Jenkins执行器,来实现业务和Jenkins管道之间的解耦,从而较小业务对Jenkins管道的影响。

四、展望未来

    目前的作业设计已经具备了一定的灵活性和扩展性,在下阶段我们还会对作业设计做更进一步的优化,通过封装标准的作业步骤(Action)、统一的CI流程,进而提供可视化的流水线定制功能。由之前的代码业务驱动改为数据驱动,最终实现作业的全面可定制与可配置。

------ END ------

作者简介

朱同学: 研发工程师,目前负责RDC研发协同平台的设计与开发工作。

也许您还想看

研发协同平台持续交付之代理服务实践

研发协同平台持续集成2.0架构演进

研发协同平台持续集成实践

招商城科走进武汉研发中心,现场编码解锁平台内核技术

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/310679.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一切都要从华为云 CloudIDE 酷似 VS Code 说起

前不久&#xff0c;有个关于华为云 CloudIDE 的问题在知乎、朋友圈、微博等圈子引起了广泛的讨论&#xff0c;甚至上了知乎热榜。那么&#xff0c;背后的真实情况到底是如何的&#xff1f;且听韩老师娓娓道来。华为云 CloudIDE 酷似 VS Code&#xff1f;首先要明确一点&#xf…

C++ class实现双亲表示法

#include <iostream> using namespace std; typedef char ElemType; const int MAXSIZE 100;class TreeNode {friend class Tree; private:ElemType data;int parent; };class Tree { private:TreeNode elem[MAXSIZE];int n;//树中当前的节点个数 }Tree;

单元测试:如何编写可测试的代码及其重要性

原文来自互联网&#xff0c;由长沙DotNET技术社区编译。如译文侵犯您的署名权或版权&#xff0c;请联系小编&#xff0c;小编将在24小时内删除。限于译者的能力有限&#xff0c;个别语句翻译略显生硬&#xff0c;还请见谅。作者&#xff1a;谢尔盖科洛迪&#xff08;SERGEY KOL…

C++ class实现孩子表示法

代码如下: #include <iostream> using namespace std; typedef char ElemType; const int MAXSIZE 100;class link {friend class Node; private:int child;link *next; };class Node {friend class Tree; private:ElemType data;link *first; };class Tree { private:…

IdentityServer 部署踩坑记

IdentityServer 部署踩坑记Intro周末终于部署了 IdentityServer 以及 IdentityServerAdmin 项目&#xff0c;踩了几个坑&#xff0c;在此记录分享一下。部署架构项目是基于 IdentityServerAdmin 项目修改的&#xff0c;感谢作者的开源付出&#xff0c;有需要 IdentityServer 管…

.Net Core微服务架构技术栈的那些事

一、前言大家一直都在谈论微服务架构&#xff0c;园子里面也有很多关于微服务的文章&#xff0c;前几天也有一些园子的朋友问我微服务架构的一些技术&#xff0c;我这里就整理了微服务架构的技术栈路线图&#xff0c;这里就分享出来和大家一起探讨学习&#xff0c;同时让新手对…

C++ class实现Huffman树(完整代码)

代码如下: #include <iostream> #include <string> using namespace std; const unsigned int n 8;//字符数NUM&#xff0c;这里的字符数为8 const unsigned int m 2 * n - 1;//结点总数 const float MAX 1e8;class HTNode {friend class HuffmanTree; private…

二叉树的遍历(堆栈)

二叉树的遍历&#xff08;堆栈&#xff09; 如何理解用堆栈方式代替递归去遍历二叉树&#xff0c;关键点在于了解每个结点输出时的顺序&#xff0c;以及理解前序中序后序是如何遍历的&#xff0c;这点很重要&#xff0c;可以自己画一个树图&#xff0c;熟练写出遍历的结果 以…

ASP.NET Core分布式项目实战(第三方ClientCredential模式调用)--学习笔记

任务10&#xff1a;第三方ClientCredential模式调用创建一个控制台程序dotnet new console --name ThirdPartyDemo添加 Nuget 包&#xff1a;IdentityModel添加之后还原dotnet restoreClientusing System; using System.Net.Http; using System.Threading.Tasks; using Identit…

C++ class实现邻接矩阵存储的图(完整代码)

代码如下: #include <iostream> #include <queue> using namespace std; typedef int VertexType; typedef int EdgeType; const int MaxVertexNum 30;class MGraph { public:MGraph(){CreatGraph();};void CreatGraph();void Visit(int v);void BFS(int v);void…

二叉搜索树(创建,插入,删除):基础篇,适合新手观看。

1.1 二叉搜索树的插入 二叉搜索树的概念相信大家都很清楚&#xff0c;无非就是左小右大 创建二叉搜索树&#xff0c;其实就是多次调用二叉搜索树的插入方法&#xff0c;所以首先我们来讲讲如何插入节点到二叉搜索树里&#xff0c;假设一颗二叉搜索树如下&#xff0c;现在要插入…

.NET 的过去、现在和未来

作为一名使用了十多年.NET 开发平台的开发者来说&#xff0c;多年来笔者鲜少在各类博客媒体推广布道 .NET平台&#xff0c;但也默默地关注着 .NET 平台的发展变化&#xff0c;为每一次新技术的出现欢呼&#xff0c;为近年来 .NET 的沉默感到惋惜&#xff0c;也为开放开源的 .NE…

AVL树(平衡二叉树)讲解,入门篇,适合新手观看

1.1 概念 平衡二叉树就是为了让二叉搜索树的平均查找长度更短&#xff0c;时间复杂度更靠近logN,如果一个二叉搜索树不平衡了就会出现图1情况&#xff0c;完全变成一个数组&#xff0c;时间复杂度也变为了O(N)。 平衡因子&#xff1a;平衡因子就是针对于树中某一结点&#xff…

读源码,对开发者重要吗?

.NET 5.0 Peview 2发布4月2日&#xff0c;微软发布了.NET 5.0 Preview 2&#xff0c;带来功能和性能方面的改进。这一版本包括.NET 5.0.0 Preview 2与.NET SDK 5.0.100 Preview 2。.NET 5是.NET Framework和.NET Core的未来&#xff0c;最终将成为一个统一平台&#xff0c;.NET…

Redis和DB数据一致性解决方案

问题出现原因 并发时候无法保证读写的先后顺序&#xff0c;如果删掉了缓存还没来得及写库&#xff0c;另外一个县城就多来读取&#xff0c;发现缓存为空就去读取数据库并且写入缓存&#xff0c;这时候缓存中就是脏数据如果先写库&#xff0c;在删除缓存前&#xff0c;写库的线…

数据结构堆的时间复杂度(最大堆,最小堆)

创建堆的方式有两种&#xff0c;一种是一边插入结点&#xff0c;一边调用堆的插入方法调整堆&#xff0c;这样的时间复杂度就是 O(NlogN)&#xff0c;而第二种方式就把时间复杂度缩减到了O(N)&#xff0c;它是采用先把结点插入好了&#xff0c;然后再来调整堆&#xff0c;并不是…

中国速度之二神山建设(3):有力的技术保障,基建世界里的云原生缩影 | IDCF DevOps案例研究...

内容来源&#xff1a;DevOps案例深度研究第4期 – 火神山雷神山 DevOps实践研究战队&#xff08;本文只展示部分PPT及研究成果&#xff0c;全程视频请移步文末&#xff09;本案例内容贡献者&#xff1a;赖泽薇、张扬、邓茜芸、韦一、刘德权、候利涛、冯利娟、常相宇、张力、韩丰…

Rx2.0后台开发分享

Rxjava2.x 微软的一个函数库&#xff0c;Rx是一个编程模型&#xff0c;模板是提供一致的编程接口&#xff0c;帮助开发者更方便的处理异步数据流&#xff0c;现在Rx已经支持几乎全部的流行编程语言。比较流行的有Rxjava&#xff0c;RxJs&#xff0c;Rx.NET&#xff0c;社区网站…

中国速度之二神山建设(4):全能运维,召之即来,来之即战 | IDCF DevOps案例研究...

内容来源&#xff1a;DevOps案例深度研究第4期 – 火神山雷神山 DevOps实践研究战队&#xff08;本文只展示部分PPT及研究成果&#xff0c;全程视频请移步文末&#xff09;本案例内容贡献者&#xff1a;赖泽薇、张扬、邓茜芸、韦一、刘德权、候利涛、冯利娟、常相宇、张力、韩丰…

Spring Cloud部分源码分析Eureka,Ribbon,Feign,Zuul

Eureka SpringCloud Eureka使用NetFlix Eureka来实现的&#xff0c;它包括了服务端组件和客户端组件&#xff0c;并且都是用java 编写的。 Eureka服务端就是服务注册中心&#xff0c;Eureka客户端主要处理服务的注册发现&#xff0c;通过注解和参数配置的方式&#xff0c;客户…