【问题标题】:How to run multiple stages on the same node with declarative Jenkins pipeline?如何使用声明性 Jenkins 管道在同一节点上运行多个阶段?
【发布时间】: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,但它仍然在我的两个测试节点之间随机分配(尽管变量或标签名称仍然没有错误) .

标签: jenkins jenkins-pipeline


【解决方案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 希望这些步骤在特定节点上运行。
    • 这是不正确的,在管道的开头指定一个代理会为整个脚本分配一个代理。
    猜你喜欢
    • 1970-01-01
    • 2018-05-21
    • 1970-01-01
    • 2018-04-28
    • 1970-01-01
    • 1970-01-01
    • 2017-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多