如何在Jenkins流水线中为失败的阶段实现重试选项?

78

我有一个包含多个阶段的Jenkinsfile,其中一个阶段实际上是另一个作业(部署作业),在某些情况下可能会失败。

我知道可以使用Jenkinsfile制作提示,但我不知道如何为此作业实现重试机制。

我希望能够点击失败的阶段并选择重试。

jenkins-pipelines-with-stages


2
这个整体功能请求在[JENKINS-33846]上。令人失望的是,它只适用于[JENKINS-45455]中的声明性流水线。 - mkobit
3个回答

57

你应该能够结合“重试”和“输入”来实现那个功能。 大概就是这样。

stage('deploy-test') {
   try {
     build 'yourJob'
   } catch(error) {
     echo "First build failed, let's retry if accepted"
     retry(2) {
        input "Retry the job ?"
        build 'yourJob'
     }
   }
}

如果您希望在没有验证者的情况下完成输入,也可以使用超时功能。还有一个可能有用但我还没有使用过的选项是waitUntil。

编辑: WaitUntil 似乎是最好的选择,您应该稍微尝试一下,但像这样的代码会更加简洁:

stage('deploy-test') {
   waitUntil {
     try {
       build 'yourJob'
     } catch(error) {
        input "Retry the job ?"
        false
     }
   }
}

顺便提一下,这里有所有步骤的文档 https://jenkins.io/doc/pipeline/steps


它会添加重试提示吗?我有些怀疑。 - sorin
哦,你说得对。我会为此更新我的回答! - fchaillou
1
是否可以只为重试部分启用超时?我可能希望作业具有不同的超时时间。我尚未接受答案,因为我不认为阻塞作业是一个好的解决方案。理想情况下,重试选项应在作业已经完成后出现。假设此作业由 GitHub 钩子触发,并且针对 PR。如果出现错误,我更希望在 GitHub 上看到失败而不是无响应。 - sorin
在我的等待waitUntil{}和构建流水线步骤的测试中,我发现需要显式地返回true,因为该步骤不返回布尔类型。 - Slack Flag
1
无法按照发布的方式工作,出现错误:未知的阶段部分“waitUntil”。从0.5版本开始,阶段中的步骤必须在“steps”块中。 - Peter McIntyre
@fchaillou 在第一个代码片段中,当作业第一次失败时,它将重试2次,无论作业在第二次尝试中是否通过,是吗?是否有可能在作业通过后立即停止重试? - blackreaper

14

这个带有漂亮的递增等待时间

stage('deploy-test') {
 def retryAttempt = 0
 retry(2) {
    if (retryAttempt > 0) {
       sleep(1000 * 2 + 2000 * retryAttempt)
    }

    retryAttempt = retryAttempt + 1
    input "Retry the job ?"
    build 'yourJob'
 }
}

2
sleep()默认的时间单位是秒,所以除非你想要等待超过一小时,否则请指定sleep(..., unit:"MILLISECONDS")或使用更少的秒数。 - Carl Walsh
1
我认为你不能再在一个stage块的顶部放置一个retry期望在第423行,第17列的阶段“代码覆盖率”中有“steps”,“stages”或“parallel”之一。 - jcollum
1
只有当我把它放在“steps”声明之后时,它才对我起作用。 - jcollum
1
我相信如果你在声明式流水线中使用stage('test') { options { retry(2) } steps { echo "hello" } },你可以将它放在步骤之外。 - Chris Maggiulli

10

这个Gist(不是我的)是我在尝试实现这个功能时找到的较好选项之一。https://gist.github.com/beercan1989/b66b7643b48434f5bdf7e1c87094acb9

我将其改为了一个共享库中的方法,只需重试或中止满足我的需求。还添加了最大重试次数,并使超时时间变量化,以便我们可以根据需要更改它。

package com.foo.bar.jenkins

def class PipelineHelper {
    def steps

    PipelineHelper(steps) {
        this.steps = steps
    }

    void retryOrAbort(final Closure<?> action, int maxAttempts, int timeoutSeconds, final int count = 0) {
        steps.echo "Trying action, attempt count is: ${count}"
        try {
            action.call();
        } catch (final exception) {
            steps.echo "${exception.toString()}"
            steps.timeout(time: timeoutSeconds, unit: 'SECONDS') {
                def userChoice = false
                try {
                    userChoice = steps.input(message: 'Retry?', ok: 'Ok', parameters: [
                            [$class: 'BooleanParameterDefinition', defaultValue: true, description: '', name: 'Check to retry from failed stage']])
                } catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException e) {
                    userChoice = false
                }
                if (userChoice) {
                    if (count <= maxAttempts) {
                        steps.echo "Retrying from failed stage."
                        return retryOrAbort(action, maxAttempts, timeoutMinutes, count + 1)
                    } else {
                        steps.echo "Max attempts reached. Will not retry."
                        throw exception
                    }
                } else {
                    steps.echo 'Aborting'
                    throw exception;
                }
            }
        }
    }
}

使用示例,最多重试2次,等待60秒以获取输入。

def pipelineHelper = new PipelineHelper(this)

stage ('Retry Example'){
    pipelineHelper.retryOrAbort({
        node{
            echo 'Here is an example'
            throw new RuntimeException('This example will fail.')
        }
    }, 2, 60)
}

只需记得将节点放在闭包内,以便等待输入不会阻塞执行器。

如果您使用付费版的Jenkins Enterprise Cloudbees,则可以使用Checkpoint插件更好地处理此问题,但不打算为开源Jenkins发布该插件(JENKINS-33846)。


一般来说,最好利用Jenkins构建SDL功能,正如其他答案所示。 - xpmatteo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接