如何优化GitHub Actions中Spring Boot Native Image编译速度?
GitHub Actions中Spring Boot Native Image编译耗时过长的优化方案探讨
我尝试通过GitHub Actions将一个小型Spring Boot应用编译为Native Image,但编译耗时超20分钟,几乎无法使用。应用本身体量很小,我无法理解耗时过长的原因。当前使用的GitHub Actions工作流如下:
name: Native Image Compile and Deploy on: workflow_dispatch: inputs: app: description: 'Choose the application to build and deploy' required: true default: 'app-one' options: - app-one - app-two jobs: native-image-compile: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # Cache Gradle dependencies to speed up build times - name: Cache Gradle Dependencies uses: actions/cache@v3 with: path: | ~/.gradle/caches ~/.gradle/wrapper build/ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/native-image.properties') }} restore-keys: | ${{ runner.os }}-gradle # Setup GraalVM using the GitHub Action for GraalVM - uses: graalvm/setup-graalvm@v1 with: java-version: '21' distribution: 'graalvm' components: 'native-image' # Compile the application to a native image - name: Compile Application to Native Image run: | APP_NAME=${{ github.event.inputs.app }} # Use Gradle Daemon and configure Gradle to be faster ./gradlew --no-daemon :applications:$APP_NAME:nativeCompile --parallel --max-workers=2 # Build and tag the Docker image - name: Build and Tag Docker Image run: | APP_NAME=${{ github.event.inputs.app }} BRANCH_NAME=${{ github.ref_name }} docker build -t $APP_NAME:$BRANCH_NAME . docker save $APP_NAME:$BRANCH_NAME -o $APP_NAME-$BRANCH_NAME.tar # Tag the GitHub Branch with a Timestamp - name: Tag the GitHub Branch env: APP_NAME: ${{ github.event.inputs.app }} BRANCH_NAME: ${{ github.ref_name }} run: | TIMESTAMP=$(date +"%Y%m%d%H%M%S") TAG_NAME="$BRANCH_NAME-$APP_NAME-$TIMESTAMP" git tag $TAG_NAME git push origin $TAG_NAME
我已尝试缓存Gradle依赖并启用并行编译,但未带来明显改善,现寻求以下问题的解答:
- 是否有特定策略或配置可缩短Native Image编译时间?
- 有无优化GitHub Actions中Spring Boot应用Native Image编译工作流的替代方案?
- GraalVM或GitHub Actions是否存在已知限制或问题导致编译耗时过长?
问题1:缩短Native Image编译时间的策略与配置
- 扩展缓存范围:当前缓存仅覆盖Gradle相关目录,需新增GraalVM的缓存目录
~/.cache/graalvm,同时更新缓存key,加入Native Image配置文件的哈希值,确保编译中间产物能被复用。 - 升级Runner资源:默认
ubuntu-latestRunner为2核CPU、7GB内存,Native Image编译是CPU密集型任务,切换至ubuntu-latest-xl(8核CPU、14GB内存)可大幅压缩编译时间。 - 优化编译参数:
- 开发环境可使用
-O1编译级别替代默认的-O2,以少量运行性能损耗换取更快的编译速度; - 添加
--no-fallback参数跳过备用镜像生成,减少编译步骤; - 确保Spring Boot启用
spring.aot.enabled=true,提前生成AOT元数据,避免编译阶段重复分析。
- 开发环境可使用
- 启用Gradle Daemon:移除
--no-daemon参数,复用Gradle守护进程减少启动开销,同时将--max-workers设置为Runner的CPU核心数(如xl Runner设为8)。
问题2:GitHub Actions工作流的优化替代方案
- 使用预构建镜像:放弃
graalvm/setup-graalvm安装步骤,直接使用Docker官方的GraalVM镜像(如graalvm/graalvm-ce:21),减少环境初始化时间。 - 拆分工作流:将Native Image编译与Docker构建拆分为两个独立Job,编译Job使用高配Runner,构建Job使用普通Runner,通过Job间Artifact传递编译好的可执行文件,避免重复编译。
- 缓存Docker基础镜像:将包含GraalVM和Spring Boot依赖的基础镜像推送到GitHub Packages,后续构建直接复用,减少依赖下载耗时。
- 使用自托管Runner:对于频繁编译的场景,部署自托管Runner并保留本地缓存,避免每次远程Runner的环境初始化开销。
问题3:GraalVM与GitHub Actions的已知限制
- Runner资源瓶颈:默认Runner的CPU和内存不足以支撑Native Image的高效编译,资源不足会导致编译时间急剧拉长。
- 版本兼容性问题:部分旧版GraalVM处理Spring Boot AOT元数据时存在性能缺陷,建议使用最新稳定版(如GraalVM 21.0.2+)。
- 字节码分析开销:即使是小型Spring Boot应用,Native Image仍需分析大量依赖类库的字节码,首次无缓存时这个阶段本身耗时较长。
内容的提问来源于stack exchange,提问作者Dasma
相关产品推荐
相关产品推荐

