Jenkins多分支流水线:在构建结束时清理工作区/删除目录

4
我们正在使用Jenkins多分支(基于脚本)管道来运行多个构建过程,以并行方式进行。每个构建过程将创建一个新的、独特命名的目录以检出代码并运行测试。这些目录是唯一命名的,因为它们包含了分支名称,还有一个随机部分,比如“JAA2A5HUZB7TRE4OTVXU7S...”。这意味着,在每个构建过程后,会有X个目录保留在Jenkins节点上(其中X=并行作业数*分支数),占满了磁盘空间。
我想知道如何自动删除这些目录。我的初始管道的简化版本如下:
// Initialize the matrix
def matrix = [
  'foo',
  'bar',
]

// Initialize empty tasks map
def tasks = [:]

// Job status
successful = true

// Fill our tasks map from the Matrix data
for (x in matrix) {

  def job_name = x

  tasks[job_name] = {
    node('libvirt') {
        // Checkout repository first
        stage("$job_name - Checkout") {
          checkout scm
        }

        // Then build the machine
        gitlabCommitStatus('build') {
            stage("$job_name - Build") {
                  sh "./bin/build.sh ${job_name}"
            }
        }
      }
    }
  }
}


//// Pipeline ////

notifyBuild('STARTED')

// Run tasks in parallel
try {
  parallel tasks
} catch(e) {
  throw e
} finally {
  if (successful) {
    notifyBuild('SUCCESS')
  } else {
    notifyBuild('FAILED')
  }
}

// Methods
def notifyBuild(String buildStatus = 'STARTED') {
   // used to send formatted e-mails 
}

我首先像这样添加了deleteDir()
} finally {
  if (successful) {
    notifyBuild('SUCCESS')
  } else {
    notifyBuild('FAILED')
  }
  node('libvirt') {
    deleteDir()
  }
}

如果出现错误,构建将失败。

然后我添加了一个像这样的cleanWs()

} finally {
  if (successful) {
    notifyBuild('SUCCESS')
  } else {
    notifyBuild('FAILED')
  }
  node('libvirt') {
    cleanWs()
  }
}

输出结果为:
Running on node in /srv/jenkins/workspace/pipeline-deletedir-DRD7EKBEMMWJQZW6KKMQVVBTJTPTKLRAE2ITDK7V7IB5PTFXZUZA
[Pipeline] {
[Pipeline] step
[WS-CLEANUP] Deleting project workspace...[WS-CLEANUP] done
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

但是目录/srv/jenkins/workspace/pipeline-deletedir-DRD7EKBEMMWJQZW6KKMQVVBTJTPTKLRAE2ITDK7V7IB5PTFXZUZA仍然存在。
我正在寻找任何解决方案,以使这个脚本流水线(如果您知道如何做到声明性流水线)能够删除构建创建的所有目录。

部分解决方案是利用“孤立项策略”和激进值。这不是回答 OP 的问题,因为它仅适用于已删除的分支(以及关闭的 PR 的 PR 分支)。但它应该有助于在一定程度上限制磁盘空间的使用。 - Patrice M.
“孤立项策略”能否删除跨越远程代理群集的废弃分支的旧工作区(包括在发现分支删除时处于离线状态的代理)。 - simon.watts
4个回答

3
解决方案是在stage("$job_name - Build")步骤中添加try/catch/finally,类似于:
gitlabCommitStatus('build') {
    stage("$job_name - Build") {
        try {
            sh "./bin/build.sh ${job_name}"
         } catch (Exception e) {
            raise e
         } finally {
            deleteDir()
         }
    }
}

由于Jenkins问题(https://issues.jenkins-ci.org/browse/JENKINS-41805),这不会删除所有涉及的目录,但它会删除所有庞大的目录(包含代码/构建/工件...)。

您需要添加“raise e”,否则即使其中一个失败,构建也将始终返回成功。


2

首先,你不能删除正在运行构建的目录deleteDir() 步骤只会删除工作区的内容。

你可以触发一个在构建完成后运行的作业来删除这些目录。为此,你可以使用类似于 pipeline-deletedir-* 的模式。


但是下游作业需要:1.在与作业完全相同的节点上运行,2.在该节点上具有完全相同的目录名称。 - daks
此外,如果我理解正确的话 https://issues.jenkins-ci.org/browse/JENKINS-41805,deleteDir() 应该可以工作。 - daks

0

这并没有直接回答你的问题(我个人使用deleteDir(),对我来说它有效),但作为在多分支管道中节省磁盘空间的替代方案,你可以考虑丢弃“旧”的构建:

stage ('Remove old builds') {
    //keep 10 builds per branch
    properties([[$class: 'BuildDiscarderProperty', strategy: [$class: 'LogRotator', artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10']]]); 
}

1
我知道BuildDiscard会删除所有日志、构件以及UI上的列表,但我相当确定它不会删除旧目录,因为我们已将此属性设置为5天,而目录(位于节点上而非主节点)仍然存在。 - daks
很抱歉这并没有什么帮助,我知道构建丢弃器正在删除主节点上的作业目录。另外,顺便说一下,在某些情况下,我会在流水线的开头执行deleteDir()。我希望我能指向文档(有时候在Jenkins中很难找到东西),但我查看了我的一个执行此操作的构建,并且Jenkins似乎总是针对每个分支使用相同的工作区(除非存在并行构建-因此我不会获得工作区目录的增殖),但这只是我在我的环境中注意到的行为,您的情况可能会有所不同。 - Brad Albright
1
事实上,在简单的流水线中,它使用相同的目录,因此在作业开始时使用deleteDir()是可以的。我们最初就是这样做的,所以问题没有出现。当转移到多分支流水线时,这个随机命名的目录特性出现了,而这个deleteDir()提示不再起作用。事实上,流水线的完整文档很难找到,而且并没有实现所有功能。 - daks

0

我认为最好的方法是在所有阶段之后添加文章部分:

    stages {
         stage("$job_name - Build") {
             sh "./bin/build.sh ${job_name}"
         }
    }
        
    post {
         always {
             echo 'One way or another, I have finished'
             deleteDir() /* clean up our workspace */
         }
    }

1
“post”指令只在一台机器上运行。如果您有两个或更多阶段并行运行在不同的机器上,那么其中一个或多个机器将无法执行“post”阶段。 - Ari Sweedler

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