在看起来不是阶段或闭包的管道中运行并行线程

问题描述 投票:0回答:1

我有一个运行 50 个并行阶段的管道。在每个阶段,我都会启动一个 python 脚本(使用

sh
bat
步骤)来运行该阶段所需的所有逻辑。这个相同的 python 脚本创建一个
summary.json
文件,其中包含任务及其进度/状态。我想从 Jenkins 轮询此文件,以更新在 python 脚本运行时在构建页面上显示的报告。

可能的解决方案:

  1. 对于每个并行阶段,我可以生成一个额外的“监视”阶段,该阶段不断轮询工作区中的文件以获取其监视的阶段并更新报告。这里的问题是,我将把我拥有的阶段数量加倍,这在管道视图中看起来很糟糕。
  2. 对于每个并行阶段,我可以启动另一个“线程”,其行为与前一个选项完全相同,只是它不会在管道视图或 BlueOcean 中显示为阶段。
  3. 以异步模式运行主 python 脚本(使用 dont-kill-me 标志在后台启动进程),然后继续从同一阶段轮询文件。这里的问题是我需要监视进程 PID 或者有某种方法来检测原始进程是否已终止。另一个问题是我很可能不会得到日志。

有人有任何建议或可能的解决方案吗?谢谢。

multithreading jenkins continuous-integration jenkins-pipeline jenkins-groovy
1个回答
0
投票

对于每个并行阶段,我可以生成一个额外的“监视”阶段,该阶段不断轮询工作区中的文件以查找其监视的阶段并更新报告

 +----------------+                 +-------------------+
 |                |                 |                   |
 |   Parallel     |                 |   Monitor/Report  |
 |   Stages (50)  |---generates---> |   Update Thread   |
 |                |   summary.json  |                   |
 +----------------+                 +-------------------+
  • 作为每个阶段一部分的脚本在单独的节点上执行。因此线程需要在它监视的同一节点上运行。
  • 所有groovy代码(包括这个线程)都在内置节点上运行,所以它不会找到文件。也许我们可以为每个阶段调整这个和 1 个线程,但这会导致控制器速度非常慢,因为我们将消耗所有线程。

您可以使用 Jenkins 原生的

parallel
步骤 来并发运行任务,而不是手动创建线程。 Jenkins 将适当地处理执行线程。

由于每个阶段都在单独的节点上运行,因此您可以使用

agent
指令指定每个阶段或并行分支的节点。

同一个 python 脚本创建一个

summary.json
文件,其中包含任务及其进度/状态。我想从 Jenkins 轮询此文件,以更新在 python 脚本运行时在构建页面上显示的报告。

为了解决在 Python 脚本运行时轮询

summary.json
文件以获取更新的需要,在不加倍阶段数量且不使 Jenkins 控制器过载的情况下,我不应该使用
while
循环来连续轮询Python 脚本并检查
summary.json
文件。该操作是轻量级的,但当扩展到多个阶段时,如果管理不当,可能会消耗 Jenkins 控制器上的大量资源。
我最初做了一个带有
sleep(15)
暂停的池化,这应该可以防止可能导致高 CPU 利用率的紧密循环。然而,这仍然意味着每个阶段每 15 秒独立执行一次 I/O 操作,这会乘以您拥有的并行阶段的数量。

为了尽量减少对 Jenkins 控制器的影响,您可以考虑:

  • 如果不需要近实时监控,则增加每次轮询之间的轮询间隔以减少负载。
  • 确保用于轮询的文件 I/O 操作是使用异步 I/O(如果可用)完成的。
  • 实现限流机制,限制一定时间内各阶段的文件I/O操作数量。
  • 将监控进程移至 Jenkins 的外部到一个单独的系统,负责监视
    summary.json
    文件并更新报告。这意味着您的 Jenkins 作业需要与此外部系统通信以提供更新。
pipeline {
    agent any
    stages {
        stage('Execute and Monitor') {
            steps {
                script {
                    def parallelStagesMap = [:]
                    for(int i = 0; i < 50; i++) {
                        def stageName = "Stage-${i}"
                        parallelStagesMap[stageName] = {
                            node {
                                // Start the Python script in the background
                                sh "python ${stageName}.py &"
                                // Get the PID of the Python script
                                def pid = sh(script: "echo $!", returnStdout: true).trim()
                                // Poll the summary.json file for updates
                                while (sh(script: "ps -p ${pid}", returnStatus: true) == 0) {
                                    if (fileExists("${stageName}_summary.json")) {
                                        // Logic to update the report here
                                    }
                                    // Sleep for 30 seconds before polling again
                                    sleep(30)
                                }
                            }
                        }
                    }
                    // Execute all stages in parallel
                    parallel parallelStagesMap
                }
            }
        }
    }
    post {
        always {
            // Collect and archive reports from all stages
            // ...
        }
    }
}

如果这些更改后负载仍然过高,则可能需要外部监控解决方案来满足您的所有限制。

注意:我认为 Jenkins 本身不支持实时更新构建页面。如果您希望在 Jenkins 构建页面上显示实时更新,您可能需要使用支持此功能的 Jenkins 插件,或者写入一个文件,然后通过 HTTP 提供服务并使用 JavaScript 轮询该文件并实时更新网页。可以利用 HTML Publisher Plugin 等插件来显示报告,但对于实时更新,您可能需要自定义解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.