CI Runner 越跑越慢:巡检缓存、磁盘和僵尸进程
2026/8/16 8:49:30 网站建设 项目流程

CI Runner 越跑越慢:巡检缓存、磁盘和僵尸进程

CI 构建变慢时,分别记录候选改动两侧的构建耗时、缓存命中率和磁盘等待。超时上限由流水线配置与正常构建分布决定。

先盘点/var/lib/docker、构建缓存、残留容器和依赖目录,再从构建日志确认是否真的发生了重复下载。磁盘占用与 CPU Load 是线索,不能在检查前就把根因归给缓存。

1. 流水线性能瓶颈定位:网络拉取与 Docker Layer Cache 失效。

在自动化交付优化中,可用诊断命令查看 CI Runner 的磁盘、CPU、网络 I/O 与构建日志,再判断阻塞来自下载、缓存失效还是资源争用。

执行以下诊断命令可以查看 Docker 资源占用与 Runner 节点的构建空间明细:

# 检查 Docker Daemon 占用的磁盘空间分布(镜像、容器、数据卷、Build Cache) docker system df -v # 查看当前 CI Runner 节点上的残留构建进程与任务 ps aux | grep -E 'docker-buildx|git-clone|npm install' | grep -v grep # 提取 Runner 宿主机的磁盘 I/O 阻塞与等待统计 iostat -xz 1 10 # 先预览 Git 工作区中可清理的未跟踪文件;确认后再执行清理 git clean -fdxn

如果构建日志显示依赖安装层在无关代码改动后重新执行,就检查 Dockerfile 中COPY . .与依赖文件的顺序;若iostat同时显示等待升高,再结合内存和 Swap 判断是否存在磁盘压力。两类证据可以同时出现,但不应在采集前写成既定根因。

2. 日常巡检设计:报告 Runner 磁盘与长时间进程

可以定时采集磁盘使用率、残留进程与 Docker Build Cache,达到阈值后生成报告并暂停接收新 Job。清理是有状态操作,应在确认无活跃任务、保留策略和互斥条件后由独立流程执行。

以下 Python 脚本只采集并报告,不执行 prune 或 kill:

import os import shutil import subprocess import logging import time logging.basicConfig(level=logging.INFO, format='%(asctime)s - [CI-INSPECTOR] - %(message)s') logger = logging.getLogger("RunnerInspector") class CIRunnerInspector: """采集 Runner 磁盘与 Docker 占用,只报告,不删除资源。""" def __init__(self, disk_threshold_percent: float): self.threshold = disk_threshold_percent self.build_dir = "/var/lib/gitlab-runner/builds" def get_disk_usage(self, path: str = "/") -> float: """获取指定挂载路径的磁盘使用率百分比""" try: total, used, free = shutil.disk_usage(path) return (used / total) * 100.0 except Exception as e: logger.error(f"Failed to check disk usage for path {path}: {e}") return 0.0 def collect_diagnostics(self): """输出 Docker 空间与长时间运行进程,供 Runner 调度系统核对 Job 归属。""" commands = [ ["docker", "system", "df", "-v"], ["ps", "-eo", "pid,etime,args"], ] for command in commands: result = subprocess.run(command, capture_output=True, text=True, timeout=30, check=False) logger.info("command=%s exit=%s\n%s", command, result.returncode, result.stdout) def run_inspection(self): """执行全量巡检主逻辑""" usage = self.get_disk_usage("/") logger.info(f"Current root disk usage: {usage:.2f}% (Threshold: {self.threshold}%)") if usage > self.threshold: logger.warning("Disk usage exceeded the configured threshold; collecting diagnostics") self.collect_diagnostics() if __name__ == "__main__": inspector = CIRunnerInspector(disk_threshold_percent=float(os.environ["RUNNER_DISK_ALERT_PERCENT"])) inspector.run_inspection()

脚本可由 Cron 或现有监控调度,输出只读证据。空间是否充足仍取决于清理策略、活跃 Job 和缓存增长速度。

3. 自动化交付重构:构建增量缓存机制与智能并行 Build 任务。

除了进行环境清理,还应从构建架构层面重构 CI 流水线。通过引入远程对象存储(如 S3 或 MinIO)缓存依赖包,并结合 DockerBuildKitcache-from机制,实现跨 Runner 节点的增量镜像构建。

优化后的构建步骤配置示例如下:

# Docker BuildKit 增量构建配置示例 export DOCKER_BUILDKIT=1 docker buildx build \ --build-arg BUILDKIT_INLINE_CACHE=1 \ --cache-from=cr.internal/ci/app-cache:master \ --tag cr.internal/ci/app:v2.4.0 \ --tag cr.internal/ci/app-cache:master \ --push .

在命令行下运行并检验 BuildKit 缓存命中情况:

# 验证 BuildKit 编译日志中 CACHED 缓存命中步数占比 docker buildx build --progress=plain . 2>&1 | grep CACHED # 检查私有镜像仓库中 Cache Tag 的更新时间 skopeo inspect docker://cr.internal/ci/app-cache:master | jq .Created

部署巡检与 BuildKit 缓存后,应用相同的依赖、网络和并发条件复测构建时间与缓存命中率,再决定是否推广。

4. 巡检边界:只报告,不自动清理

在 CI/CD 自动化交付流水线的生产优化中,校验规则是防止构建节点锁死与依赖污染的核心保障。在 Dockerfile 编写中,优化 Layer Cache 命中的首要检查为:优先复制依赖描述文件(如package.jsongo.mod),执行依赖下载后,再复制业务源代码。避免由于无关文件改动(如README.md)导致整个依赖安装阶段 Cache 失效。

Runner 清理应在低峰期、互斥锁或独立节点中执行。对docker system prune使用时间过滤能降低风险,但卷的保留策略需要单独设计,不能把清理命令直接用于共享的活跃构建节点。

Runner 的concurrent不能按固定 CPU 比例套用;编译、测试和镜像构建的 CPU、内存与 I/O 特征不同。用队列时间、Load、内存和磁盘等待逐步调参,并给暂停接收新 Job 设置可解释的恢复条件。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询