把源码变成可运行的容器镜像,原本需要在本地装好 Docker、手动执行构建命令再登录远程仓库推送。团队多人协作时,每个人机器环境不同,打出的镜像还可能不一致。GitHub Actions 作为托管在代码仓库里的持续集成服务,能让我们在每次提交后自动完成这套流程,既省事又可靠。

一、GitHub Actions 的工作机制
GitHub Actions 的核心概念是 workflow、job 和 step。workflow 是一个自动化流程,以 YAML 文件形式存放在仓库的 .github/workflows 目录中。当仓库发生指定事件,例如 push 到 main 分支或打上 v 开头标签,GitHub 会自动拉起一台临时的运行器(runner),按照文件里的描述一步步执行。
每个 workflow 可以包含多个 job,job 之间默认并行,也可以通过 needs 关键字形成依赖。在构建镜像的场景里,我们通常只需要一个 job,里面顺序执行:检出代码、登录镜像仓库、构建、推送。这种声明式写法让整个过程透明且可审查,任何人都能从仓库历史中看到某次镜像是怎么产生的。
1.1 事件触发方式
最常见的触发条件是 push 和 pull_request。如果只想在发布版本时构建镜像,可以限定为 tags 匹配特定正则。例如下面这段配置表示只在推送形如 v1.0.0 的标签时才运行:
on:
push:
tags:
- 'v*'
另一种做法是结合 release 事件,当在 GitHub 页面上正式发布 Release 才触发,适合对生产镜像把关较严的团队。无论哪种,都建议在配置里写明分支或标签过滤,避免无关提交频繁消耗构建额度。
二、编写构建与推送的 workflow
下面给出一个可直接使用的完整示例,功能是在推送 tag 时构建镜像并推送到 Docker Hub。你需要先在仓库 Settings 的 Secrets 里配置 DOCKER_USERNAME 和 DOCKER_PASSWORD 两个密钥。
name: build-and-push
on:
push:
tags:
- 'v*'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/my-app:${{ github.ref_name }}
这个文件使用了三个官方 action。actions/checkout@v4 负责把仓库代码下载到运行器;docker/login-action@v3 完成镜像仓库认证;docker/build-push-action@v5 则封装了构建与推送。注意 github.ref_name 在 tag 触发时就是标签名,用它当镜像标签能直观对应版本。
如果你使用的是私有镜像仓库,只需把 login-action 的 registry 参数填上地址即可,例如 registry.ipipp.com。构建参数里也可以加入 platforms: linux/amd64,linux/arm64 来生成多架构镜像,方便在树莓派或云上不同机型部署。
2.1 使用缓存加速构建
每次从头构建会重复下载基础镜像和依赖,非常慢。build-push-action 支持通过 cache-from 和 cache-to 把层缓存到 GitHub Actions 缓存或镜像仓库。下面展示把缓存写入仓库自身缓存区的写法:
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/my-app:${{ github.ref_name }}
cache-from: type=gha
cache-to: type=gha,mode=max
开启后,第二次构建只会重新处理变动的层,速度通常能提升一半以上。需要注意免费账户的缓存空间有限,定期清理旧缓存可以避免额度浪费。
三、Dockerfile 的配合要点
workflow 只是调度者,真正决定镜像内容的还是 Dockerfile。为了让 GitHub Actions 构建顺利且产物小巧,推荐采用多阶段构建,把编译工具和运行环境分开。
FROM golang:1.22 AS builder WORKDIR /src COPY . . RUN go build -o app main.go FROM alpine:3.19 WORKDIR /app COPY --from=builder /src/app . CMD ["./app"]
上面的例子先用 Go 官方镜像编译出二进制,再拷贝到极简的 alpine 里。最终推送的镜像不包含编译器,体积可能从近 1GB 降到 10MB 出头。在 Actions 里构建这种 Dockerfile 和本地没有任何区别,但环境统一,不会因为某人本地装了旧版本 Go 而打出异常镜像。
另一个常见坑是忘记在 Dockerfile 里声明依赖安装步骤。由于运行器是干净环境,必须保证 COPY 之前用包管理器装齐所有系统库,否则构建会失败且报错信息不明显。建议在提交 workflow 前,先在本机用相同 Docker 版本跑一次 docker build 验证。
四、密钥与权限管理
镜像仓库账号密码绝对不能写进 YAML 明文。GitHub 的 Secrets 会在运行时以环境变量形式注入,日志中自动脱敏。除了账号密码,若推送到 GitHub 自身容器服务(GHCR),可以用内置的 GITHUB_TOKEN,并赋予 packages: write 权限。
permissions: contents: read packages: write
这样不需要额外配置密码,适合完全托管在 GitHub 生态内的项目。若是企业内部仓库,建议为 CI 单独建一个只读或只写账号,和开发者个人账号隔离,降低密钥泄露带来的影响范围。
五、常见问题排查
很多初次使用者会遇到登录失败,通常是因为 Secrets 名字拼错或密码含特殊字符未被正确解析。可以在 login 步骤前加一个只打印用户名的调试步骤(注意不要打印密码),确认变量传入正常。
另一个高频问题是构建超时,尤其是基础镜像来自境外仓库时。可以在 job 里加一个设置国内镜像加速的步骤,或者将常用基础镜像提前同步到私有仓库。此外,若项目含大量小文件,COPY . . 会较慢,可借助 .dockerignore 排除 node_modules、.git 等目录,显著减少上下文传输时间。
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| denied: requested access to the resource is denied | 未登录或标签名不含用户名 | 检查 login 步骤与 tags 格式 |
| COPY failed: file not found | 路径被 dockerignore 忽略 | 调整忽略规则或复制路径 |
| cache export failed | 缓存空间不足 | 清理旧缓存或关闭 cache-to |
整体来看,GitHub Actions 构建 Docker 镜像是一套成本低、收益高的自动化方案。只要理清触发条件、密钥管理和 Dockerfile 优化三点,就能让每次发布都变得轻松可控。
GitHub_ActionsDockerCI_CD修改时间:2026-08-11 08:39:45