You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jenkins并行流水线:如何基于在线节点列表构建测试Stage?

Jenkins Pipeline 节点管理与并行执行问题

现有代码

def generateTestStage(job_name, nodes_label) {
    return {
        stage(job_name) {
            node(nodes_label) {
                /* stage stuff */
            }
        }
    }
}

pipeline
for(item in nodesByLabel(label: "myNodes", offline: true)) { // nodesByLabel requires "Pipeline Utility Steps" plugin
    stagesMap[ "${item}" ] = generatePrepareStage("${item}")
}

stagesMap += JOB_MAP.collectEntries() { key, val ->
    [ "$key" : generateTestStage(key, val) ]
}

parallel parallelStageMap

需求与现存问题

需求:先准备标签myNodes下的所有可用节点,再执行测试任务,任务耗时极长(8节点并行需4小时),但节点可能中途离线或进入维护状态。

现存两种问题:

  • 当nodesByLabel设置offline: false:任务启动前离线的节点,后续上线后不会触发generatePrepareStage,导致测试部署失败;
  • 当nodesByLabel设置offline: true:任务会一直等待离线节点上线,可能导致任务耗时远超预期(甚至数天)。

解决方案思路

思路1:动态获取在线节点+准备阶段延迟执行

核心是不在 pipeline 初始化阶段一次性获取所有节点,而是在每个测试阶段动态检查节点状态,仅对当前在线的节点执行准备和测试逻辑,同时跳过离线节点(或加入重试机制)。

修改generateTestStage,在阶段内先获取当前在线节点列表,匹配到目标节点后再执行准备+测试:

def generateTestStage(job_name, target_label) {
    return {
        stage(job_name) {
            // 动态获取当前在线的目标标签节点
            def online_nodes = nodesByLabel(label: target_label, offline: false)
            if (online_nodes.isEmpty()) {
                echo "⚠️ 标签${target_label}下无在线节点,跳过当前阶段"
                return
            }
            // 选第一个可用节点(或根据需求选择特定节点)
            def target_node = online_nodes[0]
            node(target_node) {
                // 先执行准备逻辑(复用原generatePrepareStage逻辑)
                generatePrepareStage(target_node)()
                /* 执行测试逻辑 */
                echo "开始在节点${target_node}执行测试任务"
                // 测试代码...
            }
        }
    }
}

// 构建并行阶段时,直接基于JOB_MAP生成,无需提前获取节点
def parallelStageMap = JOB_MAP.collectEntries() { key, val ->
    [ key : generateTestStage(key, val) ]
}

parallel parallelStageMap

思路2:调整并行结构,分离节点准备与测试的容错逻辑

把节点准备和测试拆分为两个独立的并行阶段,给准备阶段添加超时限制,超时则标记节点为不可用,测试阶段自动跳过该节点:

// 生成带超时和重试的准备阶段
def generatePrepareStageWithCheck(node_name, timeout_minutes = 30) {
    return {
        stage("Prepare_${node_name}") {
            timeout(time: timeout_minutes, unit: 'MINUTES') {
                // 轮询检查节点是否在线,每5秒查一次
                waitUntil {
                    def node_obj = Jenkins.instance.getNode(node_name)
                    return node_obj != null && node_obj.toComputer().isOnline()
                }
                node(node_name) {
                    /* 准备逻辑:部署依赖、环境初始化等 */
                    echo "完成节点${node_name}的准备工作"
                }
            }
        } catch (TimeoutException e) {
            echo "⏰ 节点${node_name}超时未上线,跳过准备阶段"
            // 标记该节点为不可用,后续测试阶段跳过
            env["NODE_${node_name}_AVAILABLE"] = "false"
        }
    }
}

// 生成测试阶段,仅执行可用节点的测试
def generateTestStage(job_name, node_name) {
    return {
        stage(job_name) {
            if (env["NODE_${node_name}_AVAILABLE"] == "false") {
                echo "⚠️ 节点${node_name}不可用,跳过测试"
                return
            }
            node(node_name) {
                /* 测试逻辑 */
                echo "在节点${node_name}执行测试任务"
                // 测试代码...
            }
        }
    }
}

pipeline {
    agent none
    stages {
        stage("Prepare Nodes") {
            steps {
                script {
                    def prepareStages = [:]
                    // 获取所有带目标标签的节点(不管离线与否)
                    def all_nodes = nodesByLabel(label: "myNodes", offline: true)
                    all_nodes.each { node ->
                        prepareStages["Prepare_${node}"] = generatePrepareStageWithCheck(node)
                    }
                    parallel prepareStages
                }
            }
        }
        stage("Run Tests") {
            steps {
                script {
                    def testStages = JOB_MAP.collectEntries() { key, node_name ->
                        [ key : generateTestStage(key, node_name) ]
                    }
                    parallel testStages
                }
            }
        }
    }
}

关键优化点

  • 动态节点检查:避免初始化阶段一次性获取节点列表,而是在执行阶段实时获取在线节点,适配节点状态变化;
  • 超时容错:给离线节点的准备阶段设置超时,超时则跳过,避免任务无限等待;
  • 状态传递:通过环境变量标记不可用节点,测试阶段自动跳过,保证整体任务能继续执行;
  • 逻辑内聚:把准备逻辑嵌入测试阶段,或通过函数复用,避免节点状态变化导致的准备遗漏。

内容的提问来源于stack exchange,提问作者BouinA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 19:35:00