CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
GitHub Actions CI/CD/课时/构建推送镜像
RawBlame
课时目录12 节

GitHub Actions CI/CD

  1. 01CI/CD 与 GitHub Actions
  2. 02Workflow 文件结构
  3. 03Jobs 与运行器
  4. 04Steps 与 Actions
  5. 05Secrets 与环境
  6. 06依赖缓存
  7. 07构建 Spring Boot
  8. 08构建 Next.js platform
  9. 09构建推送镜像
  10. 10部署策略
  11. 11PR 检查与分支保护
  12. 12综合练习

构建推送镜像

docker/build-push-action、GHCR、标签策略。

35 分钟

构建推送镜像

这一课你将能做到

  1. 写出使用 docker/login-action + docker/build-push-action 的 job,把镜像推到 GHCR(ghcr.io)
  2. 配置推包所需的 permissions: packages: write,并用 github.sha / latest 说明 tag 策略的取舍
  3. 结合仓库已有的 backend/Dockerfile、platform/Dockerfile,说明 CI 镜像构建与本地 docker build 的关系

为什么要学

测绿了还不够:K8s / Compose / 单机 Docker 跑的是镜像,不是 runner 上的临时目录。CI 负责「从 Dockerfile 构建并推到镜像仓库」,部署侧才有稳定可拉的引用。

GitHub 生态里 GHCR 与仓库同账号:常用 GITHUB_TOKEN 登录,少维护一套 Docker Hub 密码(仍要管好权限与可见性)。

本课聚焦 build-push;「谁把新 tag 写进集群」交给下一课部署策略。

先建立图像

CI(已测过,或同一流水线里 needs: [测试] 的后续 job)
  checkout
  docker login ghcr.io(用 GITHUB_TOKEN)
  docker build(context: backend 或 platform)
  docker push
       └─ ghcr.io/<owner>/coderoadmap-backend:<sha>
       └─ ghcr.io/<owner>/coderoadmap-backend:latest   (可选)

之后
  Helm values 的 image.tag = <sha>,或 Compose image: …:<sha>

人话:镜像是打包好的「可运行盒饭」;GHCR 是食堂冰箱。CI 负责炒好推进冰箱,部署侧负责写「今天吃哪一盒」的标签。

Tag 策略用途注意
完整 github.sha每次 commit 可追溯名字长,回滚最精确
短 sha(7 位)好读仍建议保留完整 sha 可查
semver 1.2.3正式发布需要发布流程配合
latest方便试玩可变,生产慎用

跟着做

步骤 1:本地先验证 Dockerfile

# 在仓库根,按项目文档构建
docker build -t coderoadmap-backend:local ./backend
# 若有 platform Dockerfile:
docker build -t coderoadmap-platform:local ./platform

本地构建失败,不要指望 Actions 变绿。记下 build context 目录与是否需要 build-args。

步骤 2:写 backend 推送 job

name: Docker

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  packages: write   # 推 GHCR 必需

jobs:
  docker-backend:
    runs-on: ubuntu-latest
    steps:
      - name: 拉取代码
        uses: actions/checkout@v4

      - name: 登录 GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          # 内置 token;配合 packages: write
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: 构建并推送
        uses: docker/build-push-action@v6
        with:
          context: backend
          push: true
          tags: |
            ghcr.io/${{ github.repository_owner }}/coderoadmap-backend:${{ github.sha }}
            ghcr.io/${{ github.repository_owner }}/coderoadmap-backend:latest

推 main 或手动运行后:到 GitHub Packages 页确认镜像出现;首次推送后记得检查包可见性设置。

步骤 3:为 platform 复制对称 job

把 context: platform、镜像名改为 coderoadmap-platform,可作第二个并行 job,也可用 matrix 复用步骤。仓库若已有 docker.yml,对照本课骨架读差异,而不是盲目覆盖。

步骤 4:核对权限与可见性

  • workflow 缺 packages: write 时,push 常 403。
  • Package 可见性 private/public 按项目策略设置;改了可见性再验证拉取权限。
  • 组织仓库还可能受组织级「Actions 写 packages」策略约束。

步骤 5:定 tag 纪律

笔记写三条本站约定(可按团队调整):

  1. 部署引用 sha tag,可回滚到精确 commit
  2. latest 仅开发便利,生产 values 不盯 latest
  3. 正式发版另打 semver(可与 GitHub Release 联动,进阶)

步骤 6:与 CI 的衔接方式

两种常见:

  • 同 workflow:build-push job 写 needs: [backend, platform]——测试红就不推镜像
  • 分 workflow:ci.yml 管测试,docker.yml 仅在 main push(已受保护)时推

入门推荐:测与推分离或强 needs,避免红测仍推镜像污染 registry。

讲清楚要点

  1. 因为运行环境吃镜像,所以 CD 链条通常以「可拉取的 image:tag」为界——不是把 runner 目录 scp 到生产(小站有意为之除外)。
  2. 因为 GHCR 与 GitHub 一体,所以常用 GITHUB_TOKEN + packages: write——少维护第二套密码,权限随 workflow 声明走。
  3. 因为 tag 决定回滚粒度,所以生产偏向不可变 sha,而不是漂浮的 latest——出问题能精确指回 commit。
  4. 因为 Dockerfile 定义构建上下文,所以 context 必须指向含 Dockerfile 的正确目录——monorepo 里 backend/platform 各自独立。
  5. 因为推送是写操作,所以权限与包可见性要显式设计——尤其开源仓库要防滥用写权限。
  6. 因为镜像构建耗时,所以用 buildx 层缓存(cache-from/cache-to)提速——先保证正确推送,再优化速度。

你可能会懵的地方

  1. 「GITHUB_TOKEN 从哪来?要放进 Secrets 吗?」 每个 job 自动注入,不必再存一份。要做的是在 workflow 顶层用 permissions 声明它需要的最小能力。

  2. 「镜像名里的 owner 大小写?」 GHCR 对路径大小写敏感。github.repository_owner 原样输出;若实际包路径是小写,用表达式函数统一(如 lower() 场景可改用 metadata-action 自动规整)。

  3. 「PR 里也 push 镜像吗?」 不要。fork PR 的 token 能力受限且 PR 数量多,容易产生垃圾 tag 甚至被滥用。常见做法:仅 main push 或 tag 发布时推。

  4. 「build-push-action 和手写 docker CLI?」 action 封装了 buildx、tags、多平台构建与缓存参数;手写 docker build && docker push 也可,但多平台与层缓存要自己管。入门用官方 action。

  5. 「推了镜像就等于部署了吗?」 不是。镜像只是产物;集群/服务器要更新引用(image.tag)并拉取后,环境才变化。这是下一课的核心。

错法对照

错法问题正解
无 packages: write 就 push403 denied顶层 permissions 显式声明
生产 values 只跟 latest不可追溯、易被覆盖钉 sha 或 semver
测试失败仍推镜像污染 registry、坏镜像可被拉取needs 挂检查 job 或分 workflow
context 指仓库根但 Dockerfile 在子目录找不到 Dockerfile 或上下文错乱context: backend 并确认 Dockerfile 路径
每次全量构建不缓存镜像层重复下载编译buildx cache-from/cache-to(type=gha)

卡点急救

  1. push 报 denied / 403:检查 workflow 顶层是否有 packages: write;组织仓库确认未被策略拦截;首次推包后到 Packages 页确认可见性与仓库关联(Connect repository)。

  2. 构建很慢:加 buildx 缓存——先 uses: docker/setup-buildx-action@v3,再给 build-push-action 加:

    cache-from: type=gha
    cache-to: type=gha,mode=max
    

    同时确认 Dockerfile 分层合理(依赖层在源码层之前,lock 未变就命中缓存)。

  3. 镜像推上去了但 tag 不对/少推:tags: 是多行列表,逐行核对表达式展开结果;不确定时先 push: false 跑一次看输出,或在 step 前用 echo ${{ github.sha }} 确认值。

面试常问

  • 镜像 tag 策略怎么设计?为什么生产不建议用 latest? 生产引用不可变 tag(commit sha 或语义化版本),latest 只作开发便利。latest 是可变指针:今天指向 A 版,明天被覆盖成 B 版,回滚与审计都失去锚点;出问题时「集群里跑的到底是哪次提交」都答不上来。tag 纪律的本质是把「镜像」与「代码版本」一一对应。

  • Docker 层缓存原理是什么?CI 里怎么利用? Docker 按层缓存:指令与输入不变则命中。Dockerfile 应把「少变层在前、多变层在后」——先 COPY lock 装依赖,再 COPY 源码。CI 里用 buildx 的 cache-from/cache-to: type=gha 把层缓存挂到 GitHub 缓存后端,跨 run 复用;lock 未变时依赖层直接命中,构建只剩源码编译段。

  • 多阶段构建对 CI 有什么价值? 编译阶段(Maven/Node 全量工具链)与运行阶段(只有 JRE/standalone 运行时)分离,最终镜像更小、攻击面更小;CI 里 build-push 一次完成「编译+打包」,部署侧拉到的就是精简产物。结合本站:backend 镜像只含 JRE + jar,platform 镜像只含 node + standalone 产物。

自测三问

  1. 推 GHCR 通常需要哪个 permission?在 workflow 哪里声明?
  2. 为什么生产更推荐 sha tag 而不是 latest?
  3. monorepo 里 backend 与 platform 镜像的 context 分别是什么?

小结一句话

build-push-action + GHCR 把 Dockerfile 变成可部署的镜像交付物;用 sha tag 保可追溯。

延伸

  • Working with the Container registry
  • docker/build-push-action
  • 下一课:部署策略——镜像进了仓库之后,谁把它送到环境。
加载本节练习…
← 上一课构建 Next.js platform下一课 →部署策略