GitHub Actions 部署拉取 ghcr.io 镜像失败的坑,我靠南京大学代理搞定了

做个人项目部署的时候,最烦的不是代码写不出来,而是 CI/CD 跑了一半,卡在拉镜像这一步。

最近我在给「极速PDF转换器」配 GitHub Actions 自动部署流程,后端用 Node,前端用 Vue,数据库是 Postgres,缓存用 Redis,还搭了一个 Umami 做数据统计。整个流程本来很顺:本地 push 代码 → GitHub Actions 触发 → SSH 到阿里云服务器 → docker compose up -d --build。但每次都在拉 ghcr.io/umami-software/umami:3.4 的时候卡死,报错 unexpected EOF,重试几次还是断。


问题定位

一开始以为是 Docker Hub 镜像加速器没配好,毕竟国内服务器直连 docker.io 本来就慢。我在服务器上配了 DaoCloud 的 registry-mirrors:

{
  "registry-mirrors": [
    "https://docker.m.daocloud.io"
  ]
}

配完重启 Docker,docker pull redis:7-alpine 秒拉成功,docker pull postgres 也没问题。但一拉 ghcr.io/umami-software/umami:3.4,还是老样子——下载进度条走到一半就断了。

查了一圈才发现:registry-mirrors 只对 docker.io 生效,ghcr.io 根本不走这个加速器。ghcr.io 是 GitHub Container Registry,和 Docker Hub 是两个完全不同的仓库,国内直连 GitHub 的 CDN 本身就不稳定,大镜像层下载到一半被掐断是常态。


踩过的坑

在找到最终方案之前,我试了好几种思路:

  1. 换不同的 Docker Hub 镜像源
    换了 docker.1ms.run、huecker.io、dockerhub.timeweb.cloud 等等,全部无效——原因同上,这些只加速 docker.io。

  2. 给 ghcr.io 单独配镜像加速
    尝试在 daemon.json 里加 registry-config 字段给 ghcr.io 指定代理,但 Docker 版本不支持这个配置,折腾半天没生效。

  3. GitHub Actions 里加重试逻辑
    在 workflow 里写了 bash 循环重试,最多重试 5 次,每次间隔 30 秒。结果是:重试 5 次,失败 5 次。网络层的问题,重试解决不了。

  4. 考虑上 Dokploy / Coolify
    这类自托管 PaaS 确实能简化部署流程,但它们底层还是调 docker compose pull,不解决 ghcr.io 拉取问题,换了个壳而已。


最终方案:南京大学 ghcr 代理

偶然在 GitHub 上看到一个国内高校维护的 ghcr 代理列表,试了南京大学的,居然一次成功:

docker pull ghcr.nju.edu.cn/umami-software/umami:3.4

52MB 的大层几秒钟就下完了,完全没有断流。拉取成功后,打回原名 tag:

docker tag ghcr.nju.edu.cn/umami-software/umami:3.4 ghcr.io/umami-software/umami:3.4

这样 docker-compose.yml 里写的 image: ghcr.io/umami-software/umami:3.4 完全不用改,Docker 启动时会直接命中本地缓存,不再去远程拉取。


集成到 GitHub Actions

验证成功后,我把这套流程写进了 workflow,拆成两步:

- name: Pull images via NJU proxy
  uses: appleboy/ssh-action@v1.0.3
  with:
    host: ${{ secrets.SSH_HOST }}
    username: ${{ secrets.SSH_USER }}
    key: ${{ secrets.SSH_PRIVATE_KEY }}
    script: |
      cd /opt/codeblank/deploy
      docker pull ghcr.nju.edu.cn/umami-software/umami:3.4
      docker tag ghcr.nju.edu.cn/umami-software/umami:3.4 ghcr.io/umami-software/umami:3.4
      docker compose pull --quiet

- name: Build and deploy
  uses: appleboy/ssh-action@v1.0.3
  if: success()
  with:
    host: ${{ secrets.SSH_HOST }}
    username: ${{ secrets.SSH_USER }}
    key: ${{ secrets.SSH_PRIVATE_KEY }}
    script: |
      cd /opt/codeblank/deploy
      docker compose up -d --build

第一步只负责拉镜像(用 NJU 代理 + tag 回原名),第二步才构建部署。if: success() 保证了只有镜像拉成功了才会执行部署,避免半残服务启动。


小结

国内服务器拉 ghcr.io 镜像,registry-mirrors 帮不上忙,必须用 ghcr 专用代理。南京大学这个目前稳定可用,如果你也遇到类似问题,可以直接替换域名试试。

另外提醒一句:这类公益代理的可用性取决于维护方,建议部署前先在服务器上手动验证一下。如果后续不可用了,可以关注国内其他高校或社区维护的 ghcr 镜像列表。

posted on 2026-10-20 17:08  码间留白  阅读(56)  评论(0)    收藏  举报

导航