GitLab Runner docker pull 慢:并行流水线镜像拉取排查
1. 问题现象团队并行提交多个 MR 后GitLab Runner 队列开始变长。业务测试本身没有明显变慢但 job 卡在环境准备阶段Preparing environment Using Docker executor with image node:20 ... Pulling docker image node:20 ... context deadline exceeded这类问题不要一上来就改测试脚本。先确认到底是测试执行慢还是 runner 在拉镜像时已经被拖住。本文记录一套排查顺序从 runner executor、基础镜像、service 镜像、pull policy、镜像源和本地缓存逐层检查。2. 环境示例GitLab Runner: Docker executor Runner 节点Linux / Docker Engine 并发场景多个分支同时跑单测和集成测试 常见镜像node:20、python:3.12、postgres:16-alpine、redis:7、ghcr.io/.../test-runner如果是 GitHub Actions 自建 runner也可以参考同样的排查思路。核心都是job 开始前要先把容器镜像拉到 runner 节点。3. 先区分 pending、preparing 和 script看 job 卡在哪一段阶段现象方向pending没有 runner 接任务runner 数量、tag、并发数preparing已分配 runner正在拉镜像Docker、镜像源、DNS、缓存script脚本已经执行测试耗时、依赖缓存、数据库初始化如果日志里出现Pulling docker image说明至少有一部分时间花在镜像拉取。4. 在 runner 节点手工验证进入 runner 节点不通过 CI直接拉关键镜像dockerpull node:20dockerpull python:3.12dockerpull postgres:16-alpinedockerpull redis:7-alpine如果团队使用 GHCR、MCR、Quaydockerpull ghcr.io/your-org/test-runner:latestdockerpull mcr.microsoft.com/playwright:v1.44.0-jammydockerpull quay.io/skopeo/stable:latest常见错误包括context deadline exceeded i/o timeout connection reset by peer TLS handshake timeout这时问题优先看 runner 节点的网络、DNS、镜像源和代理配置。5. 配置 Docker 镜像源可以先用1ms-helper处理 Docker 场景curl-sSLhttps://static.1ms.run/1ms-helper/install.sh|bashsudo1ms-helper config:dockerLinux Docker 的基础配置可以参考{registry-mirrors:[https://docker.1ms.run],dns:[8.8.8.8,114.114.114.114]}然后重启 Dockersystemctl daemon-reload systemctl restartdockersystemctl statusdocker再验证dockerpull docker.1ms.run/library/node:20dockerpull docker.1ms.run/library/postgres:16-alpinedockerpull ghcr.1ms.run/your-org/test-runner:latest说明毫秒镜像在这里解决的是多源镜像入口和拉取稳定性这一层不替代 runner 并发、测试拆分和依赖缓存。6. 检查 GitLab Runner pull policyGitLab Runner Docker executor 支持pull_policy。如果每次 job 都强制拉镜像runner 本地缓存就很难发挥作用。示例[[runners]] executor docker [runners.docker] image docker.1ms.run/library/node:20 pull_policy [if-not-present, always]实际生产里不要简单照抄。建议按镜像类型区分镜像类型建议高频基础镜像固定 tag配合本地缓存安全扫描镜像固定版本关键场景考虑 digest数据库 service 镜像固定小版本减少重复拉取团队自建镜像使用内部版本号不长期依赖 latest7. 检查.gitlab-ci.yml一个常见配置test:image:docker.1ms.run/library/node:20services:-name:docker.1ms.run/library/postgres:16-alpinealias:postgresscript:-npm ci-npm test如果每个 job 都启动独立数据库 service且并发很多镜像拉取、容器启动、数据库初始化都会叠加。需要结合缓存、测试拆分和服务复用一起看。8. 固定关键镜像版本近期 Docker 官方复盘过 Trivy 和 KICS 相关的供应链攻击事件。CI 里常见的扫描器、构建器、测试工具镜像不建议长期用模糊的latest。至少做到生产发布链路用明确 tag。关键扫描和构建镜像记录 digest。基础镜像升级走 MR而不是静默变化。runner 节点定期清理但不要把高频镜像缓存清得太干净。Docker 支持按 digest 拉取镜像例如dockerpull alpinesha256:digestDigest 不是为了让镜像拉得更快而是为了让 CI 结果更可复现。9. 排查清单检查项命令/位置runner 是否接任务GitLab job 状态、runner tag是否卡在镜像job 日志里的Pulling docker imageDocker 是否正常systemctl status dockerDNS 是否异常1ms-helper check:dns基础镜像能否拉取docker pull node:20多源镜像能否拉取docker pull ghcr.io/...pull policy 是否合适config.tomlservice 镜像是否过多.gitlab-ci.yml总结CI 并行构建变慢时不要只看测试脚本。runner 的镜像拉取阶段经常是隐藏瓶颈。我的处理顺序是看 job 卡在 pending、preparing 还是 script。在 runner 节点手工拉基础镜像和 service 镜像。配置 Docker 镜像源并验证多源镜像入口。检查pull_policy和本地缓存。固定关键工具镜像版本必要时记录 digest。这样才能把“测试慢”和“runner 拉镜像慢”分开处理。