【DevOps实战】使用 GitHub Actions 自动构建镜像并双推至 Docker Hub 和 GHCR

在现代软件开发中,CI/CD(持续集成/持续部署) 已经成为标配。对于容器化项目来说,最爽的体验莫过于:代码一提交,镜像自动打好并推送到仓库,随时可以拉取部署。

本文将以一个前后端分离项目(Go + Vue)为例,详细介绍如何使用 GitHub Actions 搭建一套自动化的 Docker 构建流水线,并实现 "一次构建,双重推送" ------同时推送到 GitHub Container Registry (ghcr.io) 和 Docker Hub。

🚀 为什么要做双重推送?

  1. GitHub Container Registry (GHCR)
    • 优势:与 GitHub 源码仓库无缝集成,拉取速度快,权限管理方便(使用 GitHub Token 即可)。
    • 场景:适合内部开发、CI 流程中间产物。
  2. Docker Hub
    • 优势:全球最大的容器镜像库,也是默认的 Docker 拉取源。
    • 场景:适合对外发布正式版本,用户 docker pull 时不需要输入长长的域名。

🛠️ 准备工作

在开始编写流水线之前,我们需要准备好 Dockerfile 并配置好安全凭证。

1. 准备 Dockerfile(多阶段构建)

为了减小镜像体积,强烈建议使用多阶段构建 (Multi-stage Build)。以下是一个典型的 Go + Vue 项目的 Dockerfile 示例:

bash 复制代码
# Stage 1: 构建前端  
FROM node:20-alpine AS frontend-builder  
WORKDIR /web  
COPY frontend/package.json ./  
RUN npm install  
COPY frontend/ .  
RUN npm run build 

# Stage 2: 构建后端  
FROM golang:1.24-alpine AS backend-builder  
WORKDIR /server  
COPY backend/go.mod backend/go.sum ./  
RUN go mod download  
COPY backend/ .  
# 编译为静态二进制文件  
RUN CGO\_ENABLED=1 GOOS=linux go build -ldflags="-s -w" -o app-server main.go

# Stage 3: 最终运行时 (极简 Alpine)  
FROM alpine:latest  
WORKDIR /app  
# 复制后端二进制  
COPY --from=backend-builder /server/app-server .  
# 复制前端静态资源  
COPY --from=frontend-builder /web/dist ./dist  
# 暴露端口  
EXPOSE 3000  
CMD ["./app-server"]

2. 配置 Docker Hub Access Token

⚠️ 警告:千万不要在 CI/CD 中直接使用你的 Docker Hub 登录密码!

  1. 登录 Docker Hub
  2. 点击头像 -> Account Settings -> Security -> New Access Token
  3. 设置描述(如 "GitHub Actions"),权限选 "Read & Write",生成并复制 Token

3. 配置 GitHub Secrets

为了让 GitHub Actions 能登录你的 Docker Hub,需要将用户名和 Token 存入仓库密钥。

  1. 进入 GitHub 项目仓库 -> Settings -> Secrets and variables -> Actions
  2. 点击 New repository secret ,添加以下两个变量:
    • DOCKERHUB_USERNAME: 你的 Docker Hub 用户名
    • DOCKERHUB_TOKEN: 刚才复制的 Token

⚙️ 编写 GitHub Actions Workflow

在项目根目录下创建文件:.github/workflows/docker-publish.yml。

这个 Workflow 实现了以下核心逻辑:

  1. 触发机制:代码推送到 main 分支或打上 v* 标签(如 v1.0.0)时触发。
  2. 权限设置:允许 Action 写入 GitHub Packages。
  3. 多源登录:同时登录 GHCR 和 Docker Hub。
  4. 智能 Tag:自动根据 Git 分支名或 Tag 名生成 Docker Tag(例如自动生成 latest)。
yaml 复制代码
name: Build and Publish Docker Image

on:  
  push:  
    branches: [ "main" ]    # 推送到 main 分支时触发  
    tags: [ 'v*.*.*' ]   # 推送 v1.0.0 格式 tag 时触发  
  pull\_request:  
    branches: [ "main" ]   # PR 时只构建不推送

env:  
  # GitHub Container Registry 配置  
  REGISTRY_GHCR: ghcr.io  
  IMAGE_NAME_GHCR: ${{ github.repository }}  
  # Docker Hub 配置 (格式:用户名/项目名)  
  IMAGE_NAME_DOCKERHUB: ${{ secrets.DOCKERHUB_USERNAME }}/nanodoc 

jobs:  
  build-and-push:  
    runs-on: ubuntu-latest  
    permissions:  
      contents: read  
      packages: write # 允许写入 GitHub Packages

    steps:  
      # 1. 检出代码  
      - name: Checkout repository  
        uses: actions/checkout@v4

      # 2. 设置 Docker 构建环境  
      - name: Set up Docker Buildx  
        uses: docker/setup-buildx-action@v3

      # 3. 登录 GitHub Container Registry  
      - name: Log into ghcr.io  
        if: github.event_name != 'pull_request'  
        uses: docker/login-action@v3  
        with:  
          registry: ${{ env.REGISTRY_GHCR }}  
          username: ${{ github.actor }}  
          password: ${{ secrets.GITHUB_TOKEN }} # GitHub 自带 Token

      # 4. 登录 Docker Hub (使用 Secrets)  
      - name: Log into Docker Hub  
        if: github.event_name != 'pull_request'  
        uses: docker/login-action@v3  
        with:  
          username: ${{ secrets.DOCKERHUB_USERNAME }}  
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      # 5. 提取元数据 (自动生成 Tags)  
      # 这步非常关键,它会同时为两个 Registry 生成对应的 tags  
      - name: Extract Docker metadata  
        id: meta  
        uses: docker/metadata-action@v5  
        with:  
          images: |  
            ${{ env.REGISTRY_GHCR }}/${{ env.IMAGE_NAME_GHCR }}  
            ${{ env.IMAGE_NAME_DOCKERHUB }}  
          tags: |  
            type=semver,pattern={{version}}  
            type=ref,event=branch  
            type=raw,value=latest,enable={{is_default_branch}}

      # 6. 构建并推送  
      - name: Build and push Docker image  
        id: build-and-push  
        uses: docker/build-push-action@v5  
        with:  
          context: .  
          file: ./Dockerfile  
          push: ${{ github.event_name != 'pull_request' }}  
          tags: ${{ steps.meta.outputs.tags }}  
          labels: ${{ steps.meta.outputs.labels }}  
          cache-from: type=gha # 使用 GitHub Actions 缓存加速  
          cache-to: type=gha,mode=max

✅ 验证与使用

配置完成后,当你下一次推送代码时,Action 就会自动运行。

场景一:日常开发推送

当你执行 git push origin main 时,Action 会构建并推送:

场景二:发布版本

当你打上 Tag 并推送时:

bash 复制代码
git tag v1.0.0  
git push origin v1.0.0

Action 会构建并推送以下 Tag,非常适合生产环境使用:

用户如何拉取?

用户现在可以自由选择源:

bash 复制代码
# 从 Docker Hub 拉取 (默认,短路径)  
docker pull yourname/nanodoc:latest
bash 复制代码
# 从 GitHub Packages 拉取  
docker pull ghcr.io/yourname/nanodoc:latest

📝 总结

通过这套配置,我们实现了一个专业的开源项目发布流程:

  1. 安全性:不暴露密码,使用 Token。
  2. 自动化:完全由 Git 操作触发,无需人工干预。
  3. 多源分发:同时覆盖了 GitHub 生态用户和 Docker Hub 大众用户。
  4. 高效:利用 Docker Layer 缓存和 GitHub Actions 缓存,构建速度飞快。

希望这篇实战教程能帮到正在折腾 CI/CD 的你!🚀

相关推荐
MicrosoftReactor2 小时前
技术速递|GitHub Copilot SDK 与云原生的完美融合
云原生·github·copilot
梁萌2 小时前
docker部署gitlab和gitlab runner
docker·eureka·gitlab
赛博云推-Twitter热门霸屏工具2 小时前
Twitter 自动化与热门霸屏实战:以赛博云推为例的技术解析
运维·自动化·twitter
研发小能2 小时前
主流DevOps平台对比分析:嘉为蓝鲸 vs GitLab vs Azure DevOps vs Jenkins
devops·devops产品·devops平台·devops厂商·国产devops厂商
代码方舟2 小时前
Java后端实战:对接天远车辆过户查询API打造自动化车况评估系统
java·开发语言·自动化
手动阀行2 小时前
守护发布的最后一道防线:将自动化红队测试深度嵌入 CI/CD 流水线,筑牢 MCP 应用持续交付的安全底座
安全·ci/cd·自动化
kabcko2 小时前
Windows10安装Docker
运维·docker·容器
梦想的旅途22 小时前
基于 RPA 模拟驱动的企业微信外部群自动化架构解析
机器人·自动化·rpa
爱内卷的学霸一枚2 小时前
现代DevOps实践:从CI/CD到GitOps的深度技术解析
运维·ci/cd·devops