Modern Architecture
& Coding Solutions

GitHub Actions 构建 Java 项目并推送 Docker 镜像

前言

每当在本地 docker builddocker push 时,你是否想过:要是代码提交后,镜像能自动构建、自动推送,该多好?GitHub Actions 让这一切变为现实——只要写好 .yml 配置文件,每次 git push 都能自动化完成打包与部署。

本文将从零开始,教你配置一套完整的 GitHub Actions 流水线:从 Maven 编译、单元测试,到 Docker 多阶段构建,再到镜像推送到 Docker Hub 或 GitHub 私有仓库。包含完整的 YAML 配置、缓存策略和安全性最佳实践。

本文属于 MACS Dev Hub“现代架构与编码解决方案”系列。前文已介绍 WSL2+Docker 开发环境搭建Spring AI+Ollama 本地生成 SQL,建议结合阅读。

一、核心 YAML 配置解析

1.1 GitHub Actions 架构原理

GitHub Actions 的本质是事件驱动的无服务器执行平台。其核心组件包括:

  • Workflow(工作流):存放在 .github/workflows/ 目录下的 YAML 文件,定义了完整的 CI/CD 流程。
  • Event(事件):触发工作流的 Git 行为,如 pushpull_requestrelease 等。
  • Job(任务):工作流中的一个执行单元,包含多个 Step。
  • Step(步骤):具体的执行动作,可以是 Shell 命令或预置 Action。
流程图

1.2 完整工作流配置

以下是一个面向生产环境的完整配置文件 .github/workflows/docker-build.yml

name: Java CI/CD with Docker

on:
  push:
    branches: [ main ]
    paths-ignore:
      - '**.md'
      - '.gitignore'
  pull_request:
    branches: [ main ]
  workflow_dispatch:  # 支持手动触发

env:
  REGISTRY: docker.io
  IMAGE_NAME: ${{ secrets.DOCKER_USERNAME }}/my-java-app

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write  # 用于 GHCR 推送

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

      # 2. 设置 JDK 17 并启用 Maven 缓存
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: maven  # 一键启用 Maven 依赖缓存

      # 3. 运行单元测试
      - name: Run tests
        run: mvn clean test

      # 4. 编译打包
      - name: Build with Maven
        run: mvn package -DskipTests

      # 5. 登录 Docker Hub
      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}

      # 6. 构建并推送 Docker 镜像
      - name: Build and push Docker image
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: |
            ${{ env.IMAGE_NAME }}:latest
            ${{ env.IMAGE_NAME }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

1.3 关键步骤详解

Step 1:检出代码(actions/checkout@v4)

- uses: actions/checkout@v4

将 GitHub 仓库代码拉取到 Runner 的工作目录中。@v4 是当前最新稳定版本。

Step 2:设置 JDK 17(actions/setup-java@v4)

- uses: actions/setup-java@v4
  with:
    java-version: '17'
    distribution: 'temurin'
    cache: maven  # 自动缓存 ~/.m2/repository

cache: maven 是 setup-java v4 引入的内置缓存功能,相比旧版本需要单独配置 actions/cache 更简洁。它会自动缓存 Maven 依赖,将下载时间从分钟级降到秒级。

Step 3:运行测试与编译

- run: mvn clean test      # 单元测试
- run: mvn package -DskipTests  # 跳过测试加快构建

-DskipTests 跳过测试执行(但仍会编译测试代码),适合快速构建镜像的场景。生产环境建议保留测试阶段。

Step 4:登录镜像仓库(docker/login-action@v3)

- uses: docker/login-action@v3
  with:
    username: ${{ secrets.DOCKER_USERNAME }}
    password: ${{ secrets.DOCKER_TOKEN }}
  • Docker Hub 账号密码需存放在 GitHub Secrets 中(Settings → Secrets and variables → Actions)。
  • 若使用 GitHub Container Registry,则将 username: ${{ github.actor }}password: ${{ secrets.GITHUB_TOKEN }},并添加 registry: ghcr.io

Step 5:构建并推送镜像(docker/build-push-action@v6)

- uses: docker/build-push-action@v6
  with:
    context: .          # 构建上下文目录
    push: true          # 构建后立即推送
    tags: |             # 镜像标签
      user/app:latest
      user/app:${{ github.sha }}
    cache-from: type=gha
    cache-to: type=gha,mode=max

build-push-action 是 GitHub Actions 生态中构建 Docker 镜像的终极工具,深度集成了 Docker Buildx,支持多平台构建、密钥管理和远程缓存等高级功能。

cache-from: type=ghacache-to: type=gha 利用 GitHub Actions 的内置缓存来加速镜像层构建。mode=max 会缓存所有构建层,最大化命中率。

参数说明
contextDocker 构建上下文目录
push是否推送镜像
tags镜像标签,支持多标签
cache-from从指定源读取缓存
cache-to保存缓存到指定源

二、Dockerfile 多阶段构建

为了让流水线真正高效,Dockerfile 的写法至关重要。推荐使用多阶段构建:用 JDK 编译,用 JRE 运行,显著减小最终镜像体积。

# 阶段 1:构建阶段(使用完整 JDK)
FROM eclipse-temurin:21-jdk-alpine AS builder

WORKDIR /app

# 先复制依赖文件,利用 Docker 层缓存
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
RUN ./mvnw dependency:go-offline -B

# 再复制源代码并打包
COPY src src
RUN ./mvnw package -DskipTests

# 阶段 2:运行阶段(使用精简 JRE)
FROM eclipse-temurin:21-jre-alpine

# 创建非 root 用户运行应用
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

WORKDIR /app

# 从构建阶段复制 JAR 包
COPY --from=builder /app/target/*.jar app.jar

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

多阶段构建的两大优势

  • 最终镜像只包含 JRE 和 JAR 包,不含编译工具、源码等冗余文件,镜像体积可减小约 70%
  • 先复制 pom.xml 并下载依赖,再复制源代码,能最大化利用 Docker 的层缓存——只要依赖不变,下载步骤不会重复执行。

三、缓存策略详解

3.1 Maven 依赖缓存

actions/setup-java@v4 中设置 cache: maven 即可自动处理:

- uses: actions/setup-java@v4
  with:
    java-version: '17'
    distribution: 'temurin'
    cache: maven

这会在 ~/.m2/repository 路径缓存 Maven 依赖,后续流水线命中缓存后,依赖下载步骤直接跳过。

3.2 Docker 层缓存

build-push-action 支持两种层缓存策略:

策略适用场景配置方式
GitHub Actions 缓存(gha)同仓库、同分支的多次构建cache-from: type=gha
Registry 缓存跨仓库或自托管 Runnercache-from: type=registry,ref=user/app:cache

3.3 判断缓存命中

actions/cache 提供了 cache-hit 输出,可用于条件判断:

- name: Cache Maven dependencies
  id: cache-maven
  uses: actions/cache@v4
  with:
    path: ~/.m2/repository
    key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
    restore-keys: ${{ runner.os }}-m2-

- name: Install dependencies
  if: steps.cache-maven.outputs.cache-hit != 'true'
  run: mvn dependency:go-offline

3.4 各缓存策略对比

缓存类型命中率配置复杂度关键参数
Maven 依赖cache: maven
Docker 层 (gha)type=gha
Docker 层 (registry)type=registry,ref=镜像:缓存标签

四、安全实践

4.1 敏感信息管理

所有密码、Token 都应存放在 GitHub Secrets 中,严禁硬编码。访问路径:Settings → Secrets and variables → Actions。

Secrets 分为三个层级:

  • 仓库级:仅当前仓库可用,适合 DOCKER_USERNAME 等通用凭证。
  • 环境级:分环境(dev/prod)隔离,适合多环境密钥管理。
  • 组织级:组织下多个仓库共享,适合团队统一管理。

4.2 OIDC 替代长期令牌

对于云平台(AWS/GCP/Azure),推荐使用 OpenID Connect(OIDC) 替代长期密码。OIDC 让 GitHub Actions 可以请求短期访问令牌,密码不会暴露在 Secrets 中,安全性更高。

4.3 最小权限原则

permissions:
  contents: read      # 只读代码
  packages: write     # 仅 GHCR 场景需要
  id-token: write     # OIDC 场景需要

不要滥用 permissions,只给予任务所需的最小权限。切勿在日志中打印或硬编码 Secrets。

4.4 Action 版本固定

使用 uses: actions/checkout@v4 而非 @main@latest,避免第三方 Action 被篡改带来的供应链攻击风险。

五、进阶:推送至 GHCR

GitHub Container Registry(GHCR)是与 GitHub 仓库深度集成的镜像仓库,权限直接继承 GitHub 的团队管理模型。

- name: Log in to GHCR
  uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: ${{ github.actor }}
    password: ${{ secrets.GITHUB_TOKEN }}  # 自动注入的临时 Token

- name: Build and push to GHCR
  uses: docker/build-push-action@v6
  with:
    push: true
    tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

secrets.GITHUB_TOKEN 是 GitHub Actions 运行时自动注入的临时 Token,无需手动配置。github.repository 的格式为 owner/repo-name

GHCR 与 Docker Hub 对比

对比项GHCRDocker Hub
与代码仓库关联极强一般
权限继承GitHub 权限体系独立 Docker Hub 权限
Actions 集成原生支持需额外配置

六、完整 YAML 示例(生产级)

以下是融合了多阶段构建、缓存优化和安全实践的完整配置文件:

name: Production CI/CD Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
  workflow_dispatch:

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: maven
      - name: Run tests
        run: mvn clean test

  build:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      id-token: write

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: maven

      - name: Build JAR
        run: mvn package -DskipTests

      - name: Log in to GHCR
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=semver,pattern={{version}}
            type=sha,format=long
            type=raw,value=latest

      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

七、常见问题

问题原因解决方法
permission denied while trying to connect to Docker daemonRunner 无 Docker 权限使用官方 Runner(已预装 Docker)
Maven 依赖一直重新下载cache: maven 未生效确认 setup-java 版本为 v4,检查 pom.xml 哈希是否变化
Docker 镜像未推送push: true 漏配或登录失败检查登录步骤是否成功,确认 Secrets 正确
denied: requested access to the resource is deniedToken 权限不足检查 Token 是否包含 write:packages 权限
构建时间过长缓存未命中检查 pom.xml 和 Dockerfile 层顺序优化

八、总结

本文从零开始配置了一套完整的 GitHub Actions 流水线,涵盖了以下核心要点:

  • 核心 YAML 配置:checkout、setup-java、Maven 构建、Docker 登录与推送
  • 多阶段 Dockerfile:构建阶段用 JDK,运行阶段用 JRE,镜像体积减小约 70%
  • 缓存策略:Maven 依赖缓存 + Docker 层缓存,构建时间从数分钟降至秒级
  • 安全实践:Secrets 管理、最小权限原则、Action 版本固定
  • 镜像仓库:Docker Hub 与 GHCR 两种方案对比

GitHub Actions 的免费配额(每月 2000 分钟构建时间)足以支撑中小项目的日常需求。将这套流水线配置好,你就能真正享受 “代码提交,镜像自动就绪” 的现代化开发体验。

本文全部代码示例已在 Windows 11 + WSL2 环境下验证。若配置中遇到问题,欢迎访问 MACS Dev Hub 留言交流。后续文章将深入探讨 Java 应用接入 Prometheus + Grafana 监控,敬请期待。


赞(1) 打赏
未经允许不得转载:MACS Dev Hub » GitHub Actions 构建 Java 项目并推送 Docker 镜像

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续提供更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫

微信扫一扫

登录

找回密码

注册