将轻量级执行器用于声明性管道阶段(代理 none)

Use a lightweight executor for a declarative pipeline stage (agent none)

我正在使用声明式语法的 Jenkins Pipeline,目前有以下阶段:

  1. 准备
  2. 构建(两组平行的步骤)
  3. 测试(也是两组平行的步骤)
  4. 要求 if/where 部署
  5. 部署

对于第 1、2、3 和 5 步,我需要代理(执行者),因为他们在工作区上进行实际工作。对于第 4 步,我不需要一个,并且我不想在等待用户输入时阻止可用的执行程序。这似乎被称为经典脚本语法的 "flyweight" 或 "lightweight" 执行程序,但我找不到有关如何使用声明性语法实现此目的的任何信息。

到目前为止我已经尝试过:

  1. 直接在管道选项中设置代理,然后在舞台上设置agent none。这没有任何效果,并且管道 运行s 正常,在等待输入时阻塞执行程序。文档中也提到它不会有任何效果,但我想我还是试一试。
  2. 在管道选项中设置agent none,然后为除#4 之外的每个阶段设置代理。不幸的是,但意料之中的是,这会为每个阶段分配一个新的工作空间,这反过来又需要我存储和取消存储。这既混乱又给我带来了并行阶段(2 和 3)的更多问题,因为我不能在 parallel 构造之外编写代码。我假设在同一个工作区中的并行步骤 运行,因此两者中的 stashing/unstashing 都会有不幸的结果。

这是我的 Jenkinsfile 的大纲:

pipeline {
    agent {
        label 'build-slave'
    }
    stages {
        stage("Prepare build") {
            steps {
                // ...
            }
        }
        stage("Build") {
            steps {
                parallel(
                    frontend: {
                        // ...
                    },
                    backend: {
                        // ...
                    }
                )
            }
        }
        stage("Test") {
            steps {
                parallel(
                    jslint: {
                        // ...
                    },
                    phpcs: {
                        // ...
                    },
                )
            }
            post {
                // ...
            }
        }
        stage("Select deploy target") {
            steps {
                script {
                    // ... code that determines choiceParameterDefinition based on branch name ...
                    try {
                        timeout(time: 5, unit: 'MINUTES') {
                            deployEnvironment = input message: 'Deploy target', parameters: [choiceParameterDefinition]
                        }
                    } catch(ex) {
                        deployEnvironment = null
                    }
                }
            }
        }
        stage("Deploy") {
            when {
                expression {
                    return binding.variables.get("deployEnvironment")
                }
            }
            steps {
                // ...
            }
        }
    }
    post {
        // ...
    }
}

我是不是遗漏了什么,或者在当前版本中是不可能的?

在顶层设置 agent none,然后在每个阶段设置 agent { label 'foo' },在 input 阶段再次设置 agent none 似乎符合我的预期。

即每个执行某些工作的阶段都在同一个代理上运行,而 input 阶段不消耗任何代理上的执行程序。

pipeline {
    agent none
    stages {
        stage("Prepare build") {
            agent { label 'some-agent' }
            steps {
                echo "prepare: ${pwd()}"
            }
        }
        stage("Build") {
            agent { label 'some-agent' }
            steps {
                parallel(
                    frontend: {
                        echo "frontend: ${pwd()}"
                    },
                    backend: {
                        echo "backend: ${pwd()}"
                    }
                )
            }
        }
        stage("Test") {
            agent { label 'some-agent' }
            steps {
                parallel(
                    jslint: {
                        echo "jslint: ${pwd()}"
                    },
                    phpcs: {
                        echo "phpcs: ${pwd()}"
                    },
                )
            }
        }
        stage("Select deploy target") {
            agent none
            steps {
                input message: 'Deploy?'
            }
        }
        stage("Deploy") {
            agent { label 'some-agent' }
            steps {
                echo "deploy: ${pwd()}"
            }
        }
    }
}

但是,不能保证在 Pipeline 中使用相同的代理标签将始终使用相同的工作区,例如作为同一作业的另一个构建,而第一个构建正在等待 input.

您必须在构建步骤后使用 stash。正如您所注意到的,目前无法使用 parallel 正常完成此操作,因此您必须另外使用 script 块,以便为 [=28= 编写一段脚本化管道] after/before并行步骤。

有一种解决方法可以在其他阶段使用相同的构建从属。 您可以使用节点名称设置一个变量并在其他变量中使用它。

即:

pipeline {
    agent none
    stages {
        stage('First Stage Gets Agent Dynamically') {
            agent {
                node {
                    label "some-agent"
                }
            }
            steps {
                echo "first stage running on ${NODE_NAME}"
                script {
                  BUILD_AGENT = NODE_NAME
                }
            }
        }
        stage('Second Stage Setting Node by Name') {
            agent {
                node {
                    label "${BUILD_AGENT}"
                }
            }
            steps {
                echo "Second stage using ${NODE_NAME}"
            }
        }
    }
}

从今天(2021 年)开始,您可以使用嵌套阶段 (https://www.jenkins.io/doc/book/pipeline/syntax/#sequential-stages) 将输入步骤之前必须 运行 在同一工作区中的所有阶段分组,以及输入步骤后必须在同一工作区中 运行。当然,您需要在输入步骤之前将工件隐藏或存储在某个外部存储库中,因为第二个工作区可能与第一个不同:

pipeline {
    agent none
    stages {
        stage('Deployment to Preproduction') { 
            agent any 
            stages {
                stage('Stage PRE.1') { 
                    steps {
                        echo "StagePRE.1"
                        sleep(10)
                    }
                }
                stage('Stage PRE.2') { 
                    steps {
                        echo "Stage PRE.2"
                        sleep(10)
                    }
                }
            }
        }
        stage('Stage Ask Deploy') { 
            steps {
                input message: 'Deploy to production?'
            }
        }
        stage('Deployment to Production') { 
            agent any 
            stages {
                stage('Stage PRO.1') { 
                    steps {
                        echo "Stage PRO.1"
                        sleep(10)
                    }
                }
                stage('Stage PRO.2') { 
                    steps {
                        echo "Stage PRO.2"
                        sleep(10)
                    }
                }
            }
        }
    }
}