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

📅 2026/8/16 8:49:32
CI Runner 越跑越慢:巡检缓存、磁盘和僵尸进程
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 或 killimport os import shutil import subprocess import logging import time logging.basicConfig(levellogging.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(fFailed 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_outputTrue, textTrue, timeout30, checkFalse) logger.info(command%s exit%s\n%s, command, result.returncode, result.stdout) def run_inspection(self): 执行全量巡检主逻辑 usage self.get_disk_usage(/) logger.info(fCurrent 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_percentfloat(os.environ[RUNNER_DISK_ALERT_PERCENT])) inspector.run_inspection()脚本可由 Cron 或现有监控调度输出只读证据。空间是否充足仍取决于清理策略、活跃 Job 和缓存增长速度。3. 自动化交付重构构建增量缓存机制与智能并行 Build 任务。除了进行环境清理还应从构建架构层面重构 CI 流水线。通过引入远程对象存储如 S3 或 MinIO缓存依赖包并结合 DockerBuildKit的cache-from机制实现跨 Runner 节点的增量镜像构建。优化后的构建步骤配置示例如下# Docker BuildKit 增量构建配置示例 export DOCKER_BUILDKIT1 docker buildx build \ --build-arg BUILDKIT_INLINE_CACHE1 \ --cache-fromcr.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 --progressplain . 21 | grep CACHED # 检查私有镜像仓库中 Cache Tag 的更新时间 skopeo inspect docker://cr.internal/ci/app-cache:master | jq .Created部署巡检与 BuildKit 缓存后应用相同的依赖、网络和并发条件复测构建时间与缓存命中率再决定是否推广。4. 巡检边界只报告不自动清理在 CI/CD 自动化交付流水线的生产优化中校验规则是防止构建节点锁死与依赖污染的核心保障。在 Dockerfile 编写中优化 Layer Cache 命中的首要检查为优先复制依赖描述文件如package.json、go.mod执行依赖下载后再复制业务源代码。避免由于无关文件改动如README.md导致整个依赖安装阶段 Cache 失效。Runner 清理应在低峰期、互斥锁或独立节点中执行。对docker system prune使用时间过滤能降低风险但卷的保留策略需要单独设计不能把清理命令直接用于共享的活跃构建节点。Runner 的concurrent不能按固定 CPU 比例套用编译、测试和镜像构建的 CPU、内存与 I/O 特征不同。用队列时间、Load、内存和磁盘等待逐步调参并给暂停接收新 Job 设置可解释的恢复条件。