如何使用声明性 Jenkins 管道在同一节点上运行多个阶段?

Posted

技术标签:

【中文标题】如何使用声明性 Jenkins 管道在同一节点上运行多个阶段?【英文标题】:How to run multiple stages on the same node with declarative Jenkins pipeline? 【发布时间】:2017-12-05 20:39:56 【问题描述】:

目标 在同一节点上运行声明式 Jenkins 管道的多个阶段。

设置 这只是显示问题的一个最小示例。有 2 个 Windows 节点“windows-slave1”和“windows-slave2”都标有“windows”标签。

注意:我真正的 Jenkinsfile 不能使用全局代理,因为有一组阶段需要在不同的节点上运行(例如 Windows 与 Linux)。

预期行为 Jenkins 根据标签选择“Stage 1”中的节点之一,并在“Stage 2”中使用相同的节点,因为变量 windowsNode 已更新为“Stage 1”中选择的节点。

实际行为 “Stage 2”有时在与“Stage 1”相同的节点上运行,有时在不同的节点上运行。请参阅下面的输出。

Jenkinsfile

#!groovy

windowsNode = 'windows'

pipeline 
  agent none
  stages 
    stage('Stage 1') 
      agent 
        label windowsNode
      
      steps 
        script 
          // all subsequent steps should be run on the same windows node
          windowsNode = NODE_NAME
        
        echo "windowsNode: $windowsNode, NODE_NAME: $NODE_NAME"
      
    
    stage('Stage 2') 
      agent 
        label windowsNode
      
      steps 
        echo "windowsNode: $windowsNode, NODE_NAME: $NODE_NAME"
      
    
  

输出

[Pipeline] stage
[Pipeline]  (Stage 1)
[Pipeline] node
Running on windows-slave2 in C:\Jenkins\workspace\test-agent-allocation@2
[Pipeline] 
[Pipeline] script
[Pipeline] 
[Pipeline] 
[Pipeline] // script
[Pipeline] echo
windowsNode: windows-slave2, NODE_NAME: windows-slave2
[Pipeline] 
[Pipeline] // node
[Pipeline] 
[Pipeline] // stage
[Pipeline] stage
[Pipeline]  (Stage 2)
[Pipeline] node
Running on windows-slave1 in C:\Jenkins\workspace\test-agent-allocation
[Pipeline] 
[Pipeline] echo
windowsNode: windows-slave2, NODE_NAME: windows-slave1
[Pipeline] 
[Pipeline] // node
[Pipeline] 
[Pipeline] // stage
[Pipeline] End of Pipeline
Finished: SUCCESS

您知道设置有什么问题吗?我猜这就是 Jenkinsfile 的解析和执行方式。

其他建议?最初设置 windowsNode 时,可能有一个 Jenkins API 可以根据“windows”标签选择一个节点。

【问题讨论】:

您的第 2 阶段没有拾取重命名的变量。如果您将windowsNode = 'windows' 更改为windowsNode = 'asdf',那么您应该会看到“没有带有标签‘asdf’的节点”错误,这意味着您的第2 阶段仍在运行label 'windows' 而不是label 'windows-slave2' 但是,我尝试了stage("Stage 2" ) environment someVariable = "$windowsNode" agent label env.someVariable ..,尽管它分配了一个节点并且没有给出“缺少属性”错误(这意味着agent 至少知道 someVariable 存在),但它不起作用。如果我打印env.someVariable 的值,它是节点名称,所以虽然它可以看到正确的windowsNode 变量,但我怀疑someVariableagent 指令中处于某种空白状态,导致它运行类似agent label '' 而不是 agent label 'windows-slave2' 我也尝试了environment someVariable = "\'$windowsNode\'" ,它使变量'windows-slave2' 而不仅仅是windows-slave2,但它仍然在我的两个测试节点之间随机分配(尽管变量或标签名称仍然没有错误) . 【参考方案1】:

从Declarative Pipeline 插件的1.3 版开始,正式支持此功能。 它被正式称为“顺序阶段”。

pipeline 
    agent none

    stages 
        stage("check code style") 
            agent 
                docker "code-style-check-image"
            
            steps 
                sh "./check-code-style.sh"
            
        

        stage("build and test the project") 
            agent 
                docker "build-tools-image"
            
            stages 
               stage("build") 
                   steps 
                       sh "./build.sh"
                   
               
               stage("test") 
                   steps 
                       sh "./test.sh"
                   
               
            
        
    

官方公告在这里:https://jenkins.io/blog/2018/07/02/whats-new-declarative-piepline-13x-sequential-stages/

【讨论】:

不错的功能,但是如果您必须在阶段序列中分配 2 个单独的节点,您会使用什么: stage1 - nodeA stage2 - nodeB stage3 - nodeA stage4 - nodeA ?您可以将 NODE_NAME 保存到变量 nodeA|B 中并在第 3 阶段和第 4 阶段在代理 node nodeA|B 中使用它吗? 如果在顺序块本身是并行阶段的一部分时尝试执行此操作,则会出现语法错误。我在引发这一发展的问题中看到了类似的抱怨。这一点也不罕见,但实现该功能的人似乎没有想到它。如果有办法让它发挥作用,我会很高兴听到。事实上,我不得不求助于管道环境中定义的变量,在第一个 seq 阶段的脚本块中设置,并在没有环境的情况下写入/读取它。前缀。【参考方案2】:

您可以在脚本块中定义阶段。这些阶段是在给定代理中运行的父阶段的子阶段。这是我不得不在与您类似的用例中使用的方法。

#!groovy

windowsNode = 'windows'

pipeline 
  agent none
  stages 
    stage('Stage A') 
      agent 
        label windowsNode
      
      steps 
        script 

          stage('Stage 1')         
            windowsNode = NODE_NAME
            echo "windowsNode: $windowsNode, NODE_NAME: $NODE_NAME"
          

          stage('Stage 2') 
            echo "windowsNode: $windowsNode, NODE_NAME: $NODE_NAME"
          
        
      
    
  

【讨论】:

如果从服务器也有两个执行器,并且我已经阻止了这两个执行器来完成这项工作,是否可以使用这种方法使用并行阶段?【参考方案3】:

我发现这正如你所期望的那样工作

#!groovy

windowsNode = 'windows'

pipeline 
    agent none
    stages 
        stage('Stage 1') 
            steps 
                node(windowsNode) 
                    script 
                        // all subsequent steps should be run on the same windows node
                        windowsNode = NODE_NAME
                    
                    echo "windowsNode: $windowsNode, NODE_NAME: $NODE_NAME"
                
            
        
        stage('Stage 2') 
            steps 
                node(windowsNode) 
                    echo "windowsNode: $windowsNode, NODE_NAME: $NODE_NAME"
                
            
        
    

【讨论】:

【参考方案4】:

agent none 替换为agent any

【讨论】:

这无济于事。使用agent any 意味着如果step 中未提供代理,那么它将尝试在any 可用节点上运行。 OP 希望这些步骤在特定节点上运行。 这是不正确的,在管道的开头指定一个代理会为整个脚本分配一个代理。

以上是关于如何使用声明性 Jenkins 管道在同一节点上运行多个阶段?的主要内容,如果未能解决你的问题,请参考以下文章

Jenkins 声明性管道 - 如果不满足某些条件而不是跳过一个阶段,如何中止整个构建?

Jenkins声明性管道:如何从输入步骤中读取选择?

如何防止相同类型的两个管道 jenkins 作业在同一节点上并行运行?

如何在同一个 Jenkins 节点中加载另一个 groovy 脚本?

如何使用变量名作为声明性Jenkins的参数运行ArtifactoryMavenBuild?

如何在 Jenkins 声明式管道中创建方法?