小团队 CI runner 排队:从镜像拉取到缓存策略的排查记录

最近遇到一个很有迷惑性的 CI 问题:小团队自建了两台 runner,平时单个分支跑测试没问题,但一到几个人同时提 MR,队列就越来越长。

一开始大家以为是测试变慢了。看完日志才发现,很多 job 的脚本还没开始执行,时间先花在这里:

text 复制代码
Preparing environment
Pulling docker image node:20 ...
Pulling docker image postgres:16-alpine ...
context deadline exceeded

这篇不讲大而全的 CI 优化,只记录我会怎么排"runner 排队"和"镜像拉取慢"这两件事。

先拆流水线阶段

我先把 job 耗时拆成三段:

阶段 表现 常见原因
pending 等 runner 接任务 runner 少、tag 不匹配、并发限制
preparing runner 接了,但在准备环境 拉基础镜像、service 镜像、DNS、网络
script 脚本开始执行 测试慢、缓存没命中、数据库初始化慢

这次真正慢的是 preparing,不是 script。

一个典型 .gitlab-ci.yml

示例配置大概这样:

yaml 复制代码
test:
  image: node:20
  services:
    - name: postgres:16-alpine
      alias: postgres
    - name: redis:7-alpine
      alias: redis
  script:
    - npm ci
    - npm test

并发一上来,runner 要同时拉 nodepostgresredis。如果还有 e2e、扫描、构建 job,镜像来源会更多:

  • Docker Hub:基础镜像、数据库镜像。
  • GHCR:团队工具镜像。
  • MCR:Playwright / 浏览器测试镜像。
  • Quay:安全扫描或云原生工具镜像。

单个 job 慢 30 秒不明显,6 个 job 一起慢,队列就会明显变长。

先在 runner 节点复现

不要只盯着 CI 页面。我会直接到 runner 节点跑:

bash 复制代码
docker pull node:20
docker pull postgres:16-alpine
docker pull redis:7-alpine
docker pull mcr.microsoft.com/playwright:v1.44.0-jammy

如果这里就卡住,说明不是 GitLab 本身的问题,而是 runner 节点拉镜像这层不稳定。

配镜像源和多源入口

runner 是 Docker executor 时,可以先处理 Docker 镜像源:

bash 复制代码
curl -sSL https://static.1ms.run/1ms-helper/install.sh | bash
sudo 1ms-helper config:docker

然后验证常用镜像:

bash 复制代码
docker pull docker.1ms.run/library/node:20
docker pull docker.1ms.run/library/postgres:16-alpine
docker pull docker.1ms.run/library/redis:7-alpine

如果有 GHCR:

bash 复制代码
docker pull ghcr.1ms.run/your-org/e2e-runner:2026.05

我会把这组验证命令放进 runner 节点初始化文档。这样新加 runner 时,不用等第一条流水线失败才发现镜像入口没跑通。

pull policy 和缓存别乱配

GitLab Runner Docker executor 支持 pull_policy。但这不是一个"越不拉越好"的选项。

我一般这样分:

镜像 策略
高频基础镜像 固定 tag,允许本地缓存复用
团队自建测试镜像 使用版本号,不长期用 latest
安全扫描镜像 固定版本,关键链路记录 digest
临时实验镜像 可以每次拉,但不要进主干发布链路

示例:

toml 复制代码
[[runners]]
  executor = "docker"
  [runners.docker]
    image = "docker.1ms.run/library/node:20"
    pull_policy = ["if-not-present", "always"]

这个配置不是万能答案,重点是让团队知道:哪些镜像需要新鲜,哪些镜像应该稳定。

安全扫描镜像别随手 latest

最近几个月,安全扫描、IaC 扫描这类工具镜像也出现过供应链事件。CI 里这些镜像权限往往不低,还能接触代码和构建产物,所以不要把它们当普通工具随便拉。

我现在会对扫描镜像做三件事:

  • 固定版本 tag。
  • 升级走 MR。
  • 发布链路记录 digest。

这和镜像源加速不是同一件事:前者解决可控性,后者解决可达性。runner 想跑得稳,两层都要看。

最后复盘

这次真正的瓶颈不是测试脚本,而是 runner 公共入口太脆弱。每个人都在提 MR,但每条流水线都要重复拉镜像,镜像入口不稳定时,就会把团队并行效率拖下来。

我会把 runner 初始化分成四步:

  1. 配 Docker 镜像源。
  2. 预拉高频基础镜像。
  3. 固定工具镜像版本。
  4. 用一组 docker pull 验证多源入口。

这个动作不花哨,但对小团队很实用。测试优化之前,先让 runner 稳定把环境拉起来。

相关推荐
用户713874229007 小时前
深入理解 ASP.NET Core 中的 IActionResult
后端
用户713874229008 小时前
ASP.NET Core Results<T1, T2>深度解析
后端
TYKJ0238 小时前
Java服务器:8核16G真的适合你吗
后端
砍材农夫8 小时前
物联网 基于netty构建mqtt协议规范(轻量级二进制协议)
后端
Cache技术分享8 小时前
412. Java 文件操作基础 - 用装饰者模式定制 BufferedReader 实现结构化文本读取
前端·后端
逍遥德8 小时前
SpringBoot自带TaskScheduler 接口使用详解:(02)微服务多实例模式下,爆发任务重复执行问题
spring boot·分布式·后端·微服务·中间件
考虑考虑8 小时前
JDK26中的LazyConstant
java·后端·java ee
Gauss松鼠会8 小时前
【GaussDB】基于SpringBoot实现操作GaussDB(DWS)的项目实战
java·数据库·经验分享·spring boot·后端·sql·gaussdb
用户4099322502128 小时前
Composable的命名规矩和参数约定,别再瞎写了
前端·javascript·后端