一、问题背景

节后第一轮发布前,建议先跑一次 CI/CD 镜像预检。

很多流水线失败看起来像构建失败,实际卡在更前面的镜像阶段。典型现象是 runner 已经拿到任务,但日志停在 Pulling docker imagedocker pullImagePullBackOff

Pulling docker image node:20-alpine ...
ERROR: Job failed: failed to pull image "node:20-alpine": context deadline exceeded

如果流水线还没进入 npm cigo testdocker build,就不要先排查业务代码。先确认基础镜像、服务镜像和部署镜像能不能被稳定拉下来。

二、先区分流水线失败发生在哪一段

可以把一次发布流水线拆成五段:

阶段 检查点 常见日志
拉基础镜像 runner 启动构建容器 Pulling docker image
拉服务镜像 redis、postgres、minio 等依赖服务 Starting service ...
构建应用 安装依赖、编译、测试 npm cigo testmvn package
构建发布镜像 docker buildbuildx load metadatapull base image
部署到集群 Deployment、Job、Helm ImagePullBackOffErrImagePull

如果失败发生在前两段或最后一段,优先处理镜像来源和节点拉取路径。

三、列一份镜像账本

节后返工前,不建议只看 .gitlab-ci.yml 或 Jenkinsfile 里的主镜像。真实流水线里还会有 service、Dockerfile base image、Helm values、K8s initContainer。

可以按下面格式整理:

类型 示例 用途
构建镜像 node:20-alpinepython:3.12-slim 编译、测试
服务镜像 redis:7-alpinepostgres:16-alpine 集成测试依赖
发布镜像 业务应用镜像 部署到测试或生产
基础镜像 alpinedebiannginx Dockerfile FROM
K8s 基础组件 pause、sidecar、initContainer 集群运行
GHCR 镜像 内部工具、release helper 版本处理、制品上传

整理完成后,先在发布机或 runner 所在网络里做预检:

docker pull docker.1ms.run/node:20-alpine
docker pull docker.1ms.run/python:3.12-slim
docker pull docker.1ms.run/redis:7-alpine
docker pull docker.1ms.run/postgres:16-alpine
# 按实际 GHCR 镜像替换下面这一行
docker pull ghcr.1ms.run/your-org/release-helper:2026.05.05
docker pull k8s.1ms.run/pause:3.10

毫秒镜像(1ms.run)在这里解决的是多源镜像入口和拉取稳定性问题。它不是 CI 平台,也不替代制品仓库;它适合放在流水线启动前,把 Docker Hub、GHCR、K8s 等来源先过一遍。

四、写成一个预检脚本

预检脚本不要太复杂,目标是让失败位置更清楚。

下面的 your-org/release-helper 是占位示例,落地时替换成自己的 GHCR 镜像名:

#!/usr/bin/env bash
set -euo pipefail

images=(
  "docker.1ms.run/node:20-alpine"
  "docker.1ms.run/python:3.12-slim"
  "docker.1ms.run/redis:7-alpine"
  "docker.1ms.run/postgres:16-alpine"
  "ghcr.1ms.run/your-org/release-helper:2026.05.05"
  "k8s.1ms.run/pause:3.10"
)

for image in "${images[@]}"; do
  echo "checking ${image}"
  docker pull "${image}"
done

如果这个脚本失败,流水线后面的测试和部署暂时不用看。先排查:

  1. runner 节点 DNS 是否正常。
  2. Docker 或 containerd 是否配置了镜像源。
  3. 目标镜像 tag 是否存在。
  4. 私有镜像是否缺少登录凭证。
  5. 节点磁盘空间是否不足。
  6. 网络出口和代理配置是否和本地不同。

五、把预检放到 CI 前置阶段

预检通过后,再跑正式构建。下面是一个简化的 GitLab CI 示例,其他 CI 系统可以用同样思路迁移:

stages:
  - preflight
  - test
  - build
  - deploy

image_preflight:
  stage: preflight
  image: docker.1ms.run/docker:27-cli
  services:
    - name: docker.1ms.run/docker:27-dind
      command: ["--registry-mirror=https://docker.1ms.run"]
  script:
    - docker pull docker.1ms.run/node:20-alpine
    - docker pull docker.1ms.run/python:3.12-slim
    - docker pull docker.1ms.run/redis:7-alpine
    - docker pull docker.1ms.run/postgres:16-alpine
    - docker pull ghcr.1ms.run/your-org/release-helper:2026.05.05

这个阶段的作用不是替代测试,而是把“镜像是否可拉取”提前暴露出来。这样测试失败就是测试失败,构建失败就是构建失败,不会和镜像超时混在同一段日志里。

六、Docker 和 containerd 都要看

如果 CI runner 用 Docker,可以检查 daemon.json

{
  "registry-mirrors": ["https://docker.1ms.run"]
}

修改后重启 Docker,再验证:

docker info | grep -A 5 "Registry Mirrors"
docker pull docker.1ms.run/nginx:stable-alpine

如果部署目标是 Kubernetes,节点侧还要用 crictl 验证:

crictl pull docker.1ms.run/nginx:stable-alpine
crictl pull k8s.1ms.run/pause:3.10
crictl images | grep -E "nginx|pause"

Docker 能拉,不代表 containerd 节点一定能拉。CI 和集群的网络路径不同,这一步很容易被忽略。

七、节后发布前检查表

发布前可以按下面顺序过一遍:

  1. 从 CI 配置、Dockerfile、compose、Helm values、K8s YAML 里列出全部镜像。
  2. 把镜像按 Docker Hub、GHCR、K8s、私有仓库分组。
  3. 对基础镜像、service 镜像、发布镜像分别做 docker pull 预检。
  4. 对目标 K8s 节点做 crictl pull 验证。
  5. 生产发布尽量固定 tag 或 digest。
  6. runner 和生产节点分开检查,不默认两边网络一致。
  7. 把镜像预检放到正式 test/build/deploy 之前。
  8. 预检失败时先处理镜像来源、DNS、凭证和节点缓存。

八、总结

节后第一轮发布,不要等流水线跑到一半才发现基础镜像拉不下来。

先把镜像列成账本,再用预检脚本把 Docker Hub、GHCR、K8s、私有仓库这些来源过一遍。镜像阶段稳定以后,再看测试、构建、部署,问题边界会清楚很多。

毫秒镜像适合放在这个前置环节:用 docker.1ms.runghcr.1ms.runk8s.1ms.run 这类入口减少多源镜像拉取的不确定性,让 CI/CD 失败更容易定位到真正的阶段。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐