你是否也经历过这样的场景:某天早上,你正悠闲地喝着咖啡,准备开始一天的工作,突然收到告警邮件或监控提示:“Jenkins主节点磁盘使用率超过90%”!顿时心头一紧。登录服务器一看,$JENKINS_HOME/jobs/your-project/builds/ 目录下,密密麻麻全是构建历史的文件夹,从 #1 到 #10086,每个都占据着宝贵的磁盘空间。这些历史记录,有些是几周甚至几个月前的,除了占地方,似乎已经失去了大部分参考价值。

这就像你家的储物间,东西只进不出,总有一天会塞满。今天,我们就来当一次“整理大师”,系统地探讨如何为Jenkins制定一套优雅的“大扫除”方案,既释放磁盘空间,又能保留有价值的历史信息。

一、问题根源:为什么Jenkins会“吃”掉那么多空间?

在动手清理之前,我们得先明白这些空间都被谁占用了。Jenkins的构建历史不仅仅是控制台日志文本那么简单,它是一个完整的“构建档案”。一次典型的构建可能会产生:

  1. 构建产物:比如你编译生成的JAR包、WAR包、Docker镜像tar文件等。这是通常的“空间大户”。
  2. 工作空间快照:如果你配置了“归档工作空间”,Jenkins会把整个构建时的工作目录打包保存下来,以便后续排查问题。这个操作非常消耗空间。
  3. 控制台日志:每一次构建的完整输出日志。日积月累,体积也不容小觑。
  4. 测试报告与覆盖率文件:生成的HTML、XML格式的报告。
  5. 插件数据:一些插件(如Pipeline历史、凭证记录等)也会将数据存储在构建目录下。

默认情况下,Jenkins会永久保留所有构建历史,因为它认为每一次构建都可能有回溯价值。但在资源有限的实际生产环境中,我们必须做出取舍,制定一个自动化的清理策略。

二、核心武器:Jenkins自带的“磁盘空间清理”插件

Jenkins社区提供了强大的官方插件来应对这个问题,最核心的就是 Disk-Clean Plugin。它允许我们为每个Job配置灵活的清理策略,决定保留什么、删除什么。

技术栈声明:本文所有示例均基于 Jenkins Pipeline (Declarative Syntax)。

这个插件主要通过 cleanWs()deleteDir() 等指令,以及在Job配置中设置策略来工作。让我们先看一个最基础的在Pipeline脚本中清理工作空间的例子。

// 技术栈:Jenkins Pipeline (Declarative Syntax)
pipeline {
    agent any
    stages {
        stage('构建') {
            steps {
                // 模拟构建过程,生成一些文件
                sh '''
                    echo "模拟编译..."
                    mkdir -p target
                    dd if=/dev/zero of=target/big-dummy-file.bin bs=1M count=50
                    echo "构建产物已生成。"
                '''
            }
        }
        stage('清理工作空间') {
            steps {
                // 使用 cleanWs 插件清理当前构建的工作空间
                cleanWs(
                    cleanWhenAborted: true,    // 当构建被中止时也清理
                    cleanWhenFailure: true,    // 当构建失败时也清理
                    cleanWhenNotBuilt: true,   // 当构建未执行时也清理
                    cleanWhenSuccess: true,    // 当构建成功时也清理
                    cleanWhenUnstable: true,   // 当构建不稳定时也清理
                    deleteDirs: true,          // 删除整个目录,而不仅仅是内容
                    // 可以设置排除模式,不删除某些重要文件
                    patterns: [
                        [pattern: 'target/*.jar', type: 'INCLUDE'] // 例如,保留所有的JAR包
                    ]
                )
                // 注意:cleanWs() 主要清理的是本次构建的工作区(workspace)。
                // 它不会删除构建历史(即 /builds/ 目录下的内容)。
            }
        }
    }
}

注释:这个示例展示了在Pipeline的某个阶段结束后,立即清理工作空间。cleanWs() 功能强大,可以精细控制在不同构建状态下的清理行为,并支持通配符模式来保留关键文件。但请记住,它清理的是 workspace@tmp 之类的临时工作目录,对于已经归档到构建历史目录里的产物,它无能为力。

三、构建历史管理:制定你的保留策略

清理工作空间是“节流”,而管理构建历史才是“开源”的关键。我们需要告诉Jenkins:“嘿,别把所有东西都当宝贝存着,按我的规则来。”

这主要通过在Pipeline中配置 options 块,或者直接在自由风格Job的配置页面里设置。

策略一:按数量保留

这是最直接的方式,比如“只保留最近30次构建”。

// 技术栈:Jenkins Pipeline (Declarative Syntax)
pipeline {
    agent any
    // 在options中定义构建历史保留策略
    options {
        // 保留最近10次的构建历史记录
        buildDiscarder(logRotator(numToKeepStr: '10'))
        // 你也可以同时设置按天数保留,Jenkins会取两者中最严格的条件执行
        // buildDiscarder(logRotator(numToKeepStr: '10', daysToKeepStr: '7'))
    }
    stages {
        stage('示例') {
            steps {
                echo "这个Job只会保留最近10次构建的历史记录和产物。"
            }
        }
    }
}

注释:logRotator 是核心参数。numToKeepStr 定义了保留的构建个数。Jenkins会自动删除更早的构建,包括其对应的控制台日志、归档产物等所有数据。

策略二:按天数保留

适合那些构建频率不高,但需要按周期清理的场景,比如“只保留过去30天内的构建”。

// 技术栈:Jenkins Pipeline (Declarative Syntax)
pipeline {
    agent any
    options {
        // 只保留最近7天内的构建历史
        buildDiscarder(logRotator(daysToKeepStr: '7'))
        // 同时,最多只保留20个,即使都在7天内
        // buildDiscarder(logRotator(daysToKeepStr: '7', artifactDaysToKeepStr: '3', artifactNumToKeepStr: '5'))
    }
    stages {
        stage('示例') {
            steps {
                echo "这个Job只会保留7天内的构建记录。"
            }
        }
    }
}

注释:daysToKeepStr 指定了构建记录本身保留的天数。更有用的是 artifactDaysToKeepStrartifactNumToKeepStr,它们可以单独控制构建产物的保留策略。例如,你可以让构建记录保留30天,但构建产物只保留7天或最新的5个,这能极大节省空间,因为产物通常是最大的。

策略三:高级清理与脚本化策略

对于更复杂的需求,比如只想清理特定分支的构建,或者根据构建状态(仅成功/仅失败)来清理,我们可以结合 script 块和Jenkins的API。

// 技术栈:Jenkins Pipeline (Declarative Syntax)
pipeline {
    agent any
    parameters {
        choice(name: 'BRANCH_TO_CLEAN', choices: ['feature/*', 'hotfix/*', 'develop'], description: '选择要清理历史的分支模式')
    }
    stages {
        stage('清理旧分支构建') {
            steps {
                script {
                    // 这是一个示例性脚本,实际使用可能需要根据你的多分支Pipeline结构调整
                    def jobName = env.JOB_NAME // 当前任务名
                    def maxAge = 7 // 保留天数
                    def branchPattern = params.BANCH_TO_CLEAN

                    echo "开始清理任务 ${jobName} 中,分支模式为 ${branchPattern},超过 ${maxAge} 天的构建..."

                    // 注意:直接操作Jenkins内部API删除构建需要管理员权限,且需谨慎。
                    // 更常见的做法是通过调用 Jenkins CLI 或使用 `buildDiscarder` 在Job层面配置。
                    // 此处示意逻辑:
                    // 1. 获取该Job下所有构建。
                    // 2. 遍历,检查构建的显示名称(通常包含分支信息)和创建时间。
                    // 3. 如果匹配分支模式且超过maxAge,则调用 build.delete()。
                    // 由于安全性和复杂性,生产环境建议使用下一节介绍的“文件夹级”或“全局”策略。
                }
            }
        }
    }
}

注释:这个示例展示了通过参数化Pipeline来触发定向清理的想法。但在生产环境中,直接在Pipeline脚本中删除历史构建需要极高的权限,且容易出错。对于跨Job、跨分支的复杂清理,更好的方式是使用Jenkins的“视图(View)”结合脚本控制台,或者使用后续提到的外部工具。

四、全局优化与进阶方案

除了针对单个Job的优化,我们还需要从全局视角看问题。

1. 为整个文件夹(Folder)设置默认策略 如果你使用“CloudBees Folders”插件来组织Job,可以为一个文件夹下的所有Job设置统一的构建历史保留策略。这非常高效,避免了逐个Job配置的繁琐。在文件夹的配置页面中,找到“Pipeline Libraries / Default Properties”,可以添加一个“构建丢弃策略”属性。

2. 定期清理全局的Workspace和临时文件 Jenkins主目录下,除了 jobs,还有 workspaces(所有Job的工作空间副本)和大量插件生成的临时文件、缓存。可以编写一个简单的系统定时任务(如Linux的cron job),定期清理这些目录中的老旧文件。

# 示例:一个简单的Bash清理脚本(需在Jenkins服务器上运行)
#!/bin/bash
JENKINS_HOME="/var/lib/jenkins"

# 清理所有工作空间中,超过7天未修改的目录
find $JENKINS_HOME/workspace -mindepth 1 -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \;

# 清理插件产生的临时文件(具体路径因插件而异,请谨慎操作)
find $JENKINS_HOME -name "*.tmp" -mtime +1 -delete
find $JENKINS_HOME -name "*.log" -path "*/logs/*" -mtime +30 -delete

echo "全局临时文件清理完成。"

注释:此脚本需要根据你的实际目录和需求进行调整,尤其是 rm -rf 操作,务必先在测试环境验证。建议先使用 ls 命令替换 rm 来确认要删除的文件列表。

3. 监控与告警 预防胜于治疗。使用服务器监控工具(如Prometheus+Grafana,或Zabbix)对Jenkins主节点的磁盘使用率进行持续监控,并设置预警阈值(如80%)。这样你可以在问题爆发前,从容地启动清理计划。

4. 考虑分布式构建架构 将构建任务分散到多个代理节点(Agent)上执行。确保主节点(Master)只承担调度和存储核心数据(配置、构建记录元数据)的角色,而将产生大量中间文件和产物的构建过程放到Agent上。并配置Agent在工作完成后自动清理自己的本地工作空间。

五、应用场景、优缺点与注意事项

应用场景:

  • 磁盘空间持续增长:Jenkins服务器磁盘使用率不断攀升,频繁告警。
  • 构建速度变慢:磁盘IO成为瓶颈,因为需要从海量小文件中读取数据。
  • 项目生命周期管理:需要清理已下线项目或长期不活跃分支的构建数据。
  • 合规与审计:需要按照公司政策,只保留特定时长内的构建记录。

技术优缺点:

  • 优点
    • 成本极低:主要利用Jenkins现有插件和功能,无需额外投入。
    • 自动化程度高:一旦配置好策略,无需人工干预。
    • 灵活精细:可以按Job、按文件夹、按天数、按数量、甚至按构建状态进行控制。
    • 非侵入式:清理过程通常不会影响正在进行的构建。
  • 缺点
    • 策略配置分散:如果Job数量多且最初没有统一规范,逐个配置会非常耗时。
    • 清理非即时buildDiscarder 策略通常在构建完成后触发,不会立即释放空间。
    • 无法处理“僵尸”文件:对于插件异常、构建异常终止留下的孤立文件,可能需要手动或通过外部脚本清理。
    • 历史数据丢失风险:策略设置过于激进可能导致需要回溯问题时找不到历史构建。

注意事项(非常重要!):

  1. 先备份,后操作:在执行任何全局性、批量性的删除脚本前,确保你有完整的Jenkins主目录备份。
  2. 循序渐进:不要一次性将保留策略从“永久”改为“保留1个”。可以先设置为保留30天或50次,观察一段时间。
  3. 区分环境:生产环境的Jenkins构建历史可能具有更高的审计价值,保留策略应比测试环境更保守。
  4. 沟通与通知:如果清理策略会影响团队其他成员(例如测试需要下载旧版本产物),务必提前沟通。
  5. 关注插件兼容性:某些插件(如Build Pipeline, Delivery Pipeline)可能会引用旧的构建号,过于激进的清理可能导致这些视图显示异常。
  6. 使用“立即丢弃旧构建”:在Job配置页面,配置好策略后,可以点击这个按钮手动触发一次清理,立即查看效果并释放空间。

六、总结

给Jenkins做磁盘空间优化,就像管理一个数字仓库,核心思路是 “自动化策略”“精细化管理” 相结合。

  • 对于日常构建:首要任务是为每个Job配置合理的 buildDiscarder 策略,特别是利用 artifactDaysToKeepStrartifactNumToKeepStr 来严格控制构建产物——这个空间消耗的主力军。
  • 对于临时空间:在Pipeline中适时使用 cleanWs(),确保单次构建不留下垃圾。
  • 对于全局管理:善用文件夹级策略和操作系统级的定时清理任务,处理散落的“边角料”。
  • 对于长期健康:建立监控告警,并考虑通过增加Agent节点来分散存储压力。

记住,没有一劳永逸的万能配置。最好的策略是根据你团队的构建频率、产物大小、回溯需求以及服务器资源情况,量身定制一套组合拳。定期审视和调整这些策略,才能让你的Jenkins服务器始终保持轻盈、高效的状态,稳稳地托住团队的快速交付流程。

希望这篇指南能帮助你解决磁盘空间的困扰,让你的CI/CD流水线运行得更加顺畅!