一些背景
一直想把项目迁移到使用GraalVM构建出的原生应用上,但是在前段时间的一次尝试后,发现很难做到,其中一个最主要原因就在于我目前手头上没有X86架构的电脑。平时我使用的是一个M1处理器的MacBook,编译出的Docker镜像架构指令集也是Arm64的,无法在我的X86服务器启动。原本想着就这样算了,但是实际上我最近在和一个朋友做一个node的项目,这个项目上朋友计划配合GitHub Action进行发布流水线的操作,我就去查了一个,没想到还真查到Graal官方推出的一个Github Action插件,基于这里,我就去尝试探索了下,发现还真可以编译成功,最终也把项目在生产环境启动起来了。但是在这个过程中踩了不少坑。
代码变更
这里代码变更主要分为3类,分别是Gradle配置,GitHub Action配置文件和手动补充一些需要被A OT进去的配置。
tasks.named<BootBuildImage>("bootBuildImage") {environment = mapOf("BP_NATIVE_IMAGE_BUILD_ARGUMENTS" to"""-march=compatibility""")docker {publish.set(true)publishRegistry {imageName.set("docker.io/mingchiuli/${rootProject.name}:${version}")url.set("https://docker.io")val un = System.getenv("DOCKER_USERNAME")val pwd = System.getenv("DOCKER_PWD")username.set(un)password.set(pwd)}}
}
基于Spring boot的初始化配置,还需要对BootBuildImage这个任务进行一定配置,因为需要使用Spring官方提供的这个打包任务进行镜像构建,故配置DockerHub的用户名和密码用来推送镜像。在这里用户名和密码是通过环境变量注入的,需要和GitHub Action配置文件搭配。配置里的-march=compatibility
主要作用是禁止CPU指令优化,不加的话有可能启动镜像报错CPU指令集不支持的问题。
name: GraalVM build
on: [push, pull_request]
jobs:build:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v4- uses: graalvm/setup-graalvm@v1with:java-version: '21' # See 'Options' section below for all supported versionsdistribution: 'graalvm' # See 'Options' section below for all available distributionsgithub-token: ${{ secrets.GITHUB_TOKEN }}- name: Example steprun: |echo "GRAALVM_HOME: $GRAALVM_HOME"echo "JAVA_HOME: $JAVA_HOME"java --versionnative-image --version- name: Example step using Gradle pluginrun: ./gradlew bootBuildImageenv:DOCKER_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}DOCKER_PWD: ${{ secrets.DOCKERHUB_PWD }}
GitHub Action配置文件里对环境变量进行定义,就是项目里Gradle配置的DOCKER_USERNAME和DOCKER_PWD。这里的关键在于需要把gradle-wrapper.jar和gradle-wrapper.properties提交到GitHub仓库里,否则会报错找不到gradlew这个命令。
另外,像caffeine这样的库,可能它在程序运行的时候根据开发者缓存的配置动态加载了一些类进来,所以需要增加一些配置,如:
hints.reflection().registerType(TypeReference.of("com.github.benmanes.caffeine.cache.SSMSA"),MemberCategory.PUBLIC_FIELDS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS);
上线
上线我使用的是Docker Compose,关于生产环境的服务配置,只需要通过Docker容器启动的时候环境变量注入就可以了。在Docker Compose文件的environment这个下面写就OK。
之后的计划
这次成功改造为native化可以说久旱逢甘霖,由于native化以后占用内存大大降低,使在有限的经济条件下项目有了微服务化的可能,以前后端应用需要600M的内存占用,现在优化到200M:
启动时间也有了大提升,之前启动需要25秒左右,现在只需要4秒上下:
目前这个服务器内存节省出来很多内存,可以把它拆分出微服务了。之后这个项目的改造方向是应该是这个。