与Jenkins声明性管道中的矩阵并行运行非矩阵阶段



我正在转换我们的企业构建,以便它使用"matrix"指令(https://www.jenkins.io/blog/2019/11/22/welcome-to-the-matrix/),但我在优化它时遇到了困难。在矩阵前的世界中,我们有一个构建步骤(生成jar(,然后是并行的"包"步骤(生成linux和windows发行版(,基本上是"系统测试"阶段(在各种JVM上执行windows和linux发行版(。我们在包和系统测试阶段的同时进行一些"代码质量"检查。

这在很大程度上似乎有助于使用"矩阵"。因此,"打包"显然是一个矩阵,用于构建windows和linux平台。"系统测试"是平台和jvm的另一个双轴矩阵。我可以很容易地使一个矩阵跟随另一个矩阵。到目前为止,一切都很好。

然而,我一直把"代码质量"作为一个异类。有没有一种方法可以使这些阶段与矩阵并行运行。一旦创建了构建,它们就独立了(不需要等待打包(。它们也很耗时,所以在两个矩阵阶段连续运行它们会使构建时间更长。如果你把一个矩阵放在一个平行的阶段里,詹金斯会抱怨。

关于如何将非矩阵阶段与矩阵并行运行,有什么想法吗?

我想出了一个有点IMHO的";"更好";解决方案:

stage {
matrix {
axes {
axis {
name 'ORIGAXIS'
values 'ALPHA','BETA','BAR','BAZ'
}
}
stages {
stage ("alpha") {
when { expression { env.ORIGAXIS == "ALPHA" } }
steps {
alpha()
}           
stage ("beta") {
when { expression { env.ORIGAXIS == "BETA" } }
steps {
beta()
}
}
stage ("Tests") {
when { allOf
expression { env.ORIGAXIS != "ALPHA" }
expression { env.ORIGAXIS != "BETA" }
}
stages {
stage("First") {
originalFirst( ...)
}
stage("Second") {
originalSecond(...)
}
}
}
}
}

当然,最终的布局并不完美。但是,它是有效的,没有那么麻烦,而且仍然很容易维护。

我有一个解决自己问题的方法。

根据文件(https://www.jenkins.io/doc/book/pipeline/syntax/#declarative-matrix(,我所寻找的是不可能的"矩阵中的每个单元可以包括一个或多个阶段,以使用该单元的配置顺序运行。请注意,一个阶段必须有并且只有一个步骤、阶段、并行或矩阵。如果阶段指令嵌套在并行块或矩阵块本身中,则不可能在该阶段指令中嵌套并行块或阵列块">

但是。。。。你可以作弊。我能够将矩阵转换为一个通用的调度队列。即每个组合调用N个阶段——对我来说是2;准备";以及";执行";。我通过一个附加的";矩阵型";中的参数。matrixtype为矩阵本身获取1个值,以及为非矩阵的每一行获取额外值。然后,我使用矩阵"excludes"来确保非矩阵行只执行一次。实际上,这将非矩阵折叠为按矩阵类型区分的矩阵。

Original (parallel-stages in series with a subsequent matrix)
stage {
parallel {
stage("alpha") {
alpha(..)
}
stage("beta") {
beta(..)
}
// etc
}
}
stage {
matrix {
axes {
axis {
name 'ORIGAXIS'
values 'FOO','BAR','BAZ'
}
}
stages {
stage("First") {
originalFirst( ...)
}
stage("Second") {
originalSecond(...)
}
}
}
}
Replacement (parallel folded into matrix)
stage {
matrix {
axes {
axis {
name 'MATRIXTYPE
values 'ORIGINAL', 'ALPHA', 'BETA'
axis {
name 'ORIGAXIS'
values 'FOO','BAR','BAZ'
}
excludes {
// Execute Alpha and Beta only once (during 'FOO')
exclude {
axis {
name 'MATRIXTYPE'
values 'ALPHA', 'BETA'
}
axis {
name 'ORIGAXIS'
values 'BAR','BAZ'
}
}
}
}
stages {
stage("First") {
dispatchFirst( "${MATRIXTYPE}", ...)
}
stage("Second") {
dispatchSecond( "${MATRIXTYPE}", ...)
}
}
}
}

dispatchFirst(..(和dispatchSecond(..(是共享库中的简单调度方法,它们检查matrixtype,并根据需要调用originalFirst(。它有点笨拙,相当于把平行阶段硬塞进矩阵中,但它很有效。并且,您可以获得并行化(构建速度优化(的好处

希望在未来,会有更优雅的东西。


更新(2022-09-22(:"替换"所做的是暴力破解。它创建了一个有9个排列的矩阵:ORIGINAL-FOOORIGINAL-BARORIGINAL-BAZALPHA-FO。排除然后去掉ALPHA-BAR、ALPHA-BAZ、BETA-BAR和BETA-BAZ,在矩阵中留下5个可执行元素(上面强调(。对于5个元素中的每一个;dispatchFirst";那么";dispatchSecond";方法,将矩阵变量传递给它。

然后执行dispatchFirst,以便:如果matrixType==ALPHA,则调用ALPHA(([只调用一次]如果matrixType==BETA,则调用BETA(([只调用一次]否则,使用中传递的FOO/BAR/BAZ调用originalFirst(..(

dispatchSecond仅在matrixType=ORIGINAL时操作,并且它使用.中传递的FOO/BAR/BAZ调用originalSecond(..(

最新更新