Jenkins流水线如何配置Stage任意环节失败仍继续执行作业
现有Jenkins声明式流水线Infracost阶段配置如下:
stage('Infracost'){ agent { docker { image 'infracost/infracost:ci-latest' args "--user=jenkins --entrypoint=''" alwaysPull true reuseNode true } } environment { INFRACOST_API_KEY = credentials('INFRACOST_API_KEY') USAGE_FILE = '/tmp/infracost-usage.yml' SYNC_USAGE_FILE = true } steps { dir('./services/' + serviceType + '/terraform'){ sh script:'infracost breakdown --path .', label: "Infracost output" sh script:'infracost diff --path plan.json', label: "Infracost diff" } } }
需求为该阶段任意环节(包括Docker镜像拉取、容器启动等agent准备环节,以及steps内脚本执行环节)失败时,整个流水线不终止,继续执行后续阶段。此前在steps块内使用catchError仅能覆盖steps内的脚本异常,Docker agent相关故障依然会导致流水线直接失败。
声明式流水线的stage执行顺序为:分配/启动agent -> 执行stage内部的options、steps、post等逻辑。stage级别的docker agent准备工作(拉镜像、启容器、挂载工作区)属于前置环节,早于steps块执行,因此steps内部的catchError根本无法捕获agent阶段抛出的异常,自然无法阻止流水线终止。
方案一(推荐,与原配置行为完全一致)
将Docker agent逻辑从stage的agent块下沉到steps内部,通过脚本式Docker调用执行任务,让镜像拉取、容器启动、命令执行全流程都处于catchError的捕获范围内。由于原配置已经开启reuseNode true,本身就是复用当前工作节点启动容器,不存在额外节点调度,调整后行为和原配置无差异。
调整后配置如下:
stage('Infracost'){ // 直接复用上层流水线分配的工作节点,和原reuseNode true效果一致 agent any environment { INFRACOST_API_KEY = credentials('INFRACOST_API_KEY') USAGE_FILE = '/tmp/infracost-usage.yml' SYNC_USAGE_FILE = true } steps { // 全链路捕获异常,覆盖从镜像拉取到命令执行的所有环节 catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE', message: 'Infracost阶段执行失败,流水线继续运行') { dir('./services/' + serviceType + '/terraform'){ // 对应原配置alwaysPull true,每次执行拉取最新镜像 docker.image('infracost/infracost:ci-latest').pull() // 传入原配置的docker启动参数,在容器内执行原有命令 docker.image('infracost/infracost:ci-latest').inside("--user=jenkins --entrypoint=''") { sh script:'infracost breakdown --path .', label: "Infracost output" sh script:'infracost diff --path plan.json', label: "Infracost diff" } } } } }
配置说明:
buildResult: 'UNSTABLE'表示该阶段失败时,仅将整个构建标记为不稳定(黄色告警状态),不会中断流水线执行;如果不需要保留失败标记,也可以设置为SUCCESS,但不推荐,会丢失故障排查记录。- 不要使用stage options内置的
catchError,该配置依然无法覆盖agent准备阶段的异常。
方案二(保留原stage结构,外层嵌套捕获)
如果不想改动原有Infracost stage的配置结构,可以在外层套一个使用基础常驻agent的父stage,在父stage的steps中用catchError包裹整个原有Infracost stage,这样子stage所有环节(包括agent启动)的异常都会被外层捕获。
配置示例:
stage('Infracost-Ignore-Failure') { // 使用常驻可用的基础agent,不存在docker启动类异常 agent any steps { catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE', message: 'Infracost阶段执行失败,流水线继续运行') { // 原有Infracost stage完整放在此处,所有异常都会被外层捕获 stage('Infracost'){ agent { docker { image 'infracost/infracost:ci-latest' args "--user=jenkins --entrypoint=''" alwaysPull true reuseNode true } } environment { INFRACOST_API_KEY = credentials('INFRACOST_API_KEY') USAGE_FILE = '/tmp/infracost-usage.yml' SYNC_USAGE_FILE = true } steps { dir('./services/' + serviceType + '/terraform'){ sh script:'infracost breakdown --path .', label: "Infracost output" sh script:'infracost diff --path plan.json', label: "Infracost diff" } } } } } }
内容的提问来源于stack exchange,提问作者fraserc182

