今天学习到了一个好东西,分享一下。
springboot从3.0开始支持native编译了,现在已经3.4了,native支持的更加完善了。本文主要介绍以下SpringBoot Gradle Plugin得使用方法,不想看我啰嗦得直接点击查看原文看官方文档。
基于graalvm打包的native镜像有着快速启动,资源占用少的特性,性能上虽然比不上经过jit优化后的程序,但性能输出稳定,与jvm的应用极限性能相差不到10%。
默认情况,如果使用gradle作为构建工具,在装有docker的设备上直接执行bootBuildImage命令就可以打包成native镜像,一般来说最小的spring boot native镜像差不多120M左右,随着业务复杂,引入了更多的依赖,执行文件会略微膨胀,即便如此也比jar包要小很多。
在不做特别的配置性情况,springboot会使用paketobuildpacks/builder-jammy-java-tiny
作为构建工具,并打包出docker.io/libray/projectname:version类似的镜像,由于过程中需要下载以下依赖:
[creator] paketo-buildpacks/ca-certificates 3.9.0
[creator] paketo-buildpacks/bellsoft-liberica 11.0.1
[creator] paketo-buildpacks/syft 2.6.1
[creator] paketo-buildpacks/executable-jar 6.12.0
[creator] paketo-buildpacks/spring-boot 5.32.0
[creator] paketo-buildpacks/native-image 5.15.0
特别时bellsoft 和syft,体积较大,且需要从github上下载,由于国内各种网络原因,基本上下载不下来,因此咱们还需要一些科学上网的手段。不过由于是在容器中进行下载,不好直接设置代理,因此我对宿主机设置了系统级别的代理。
这样确实可以正常编译了,但是,还真有但是啊。科学上网一般都是按流量收费的,不可能每次编译都去下载吧,这一套依赖下来就是1G以上了,编译不了几次就出不去了。
springboot 的Gradle Plugin默认提供了基于volume的缓存,这个缓存默认是与镜像版本相关的,可以保证构建相同版本镜像时只下载依次依赖。但是随着版本变更,这个下载流量也是有些吃不消。对此,Gradle提供了缓存相关的配置,只需要在bootBuildImage任务中增加以下配置即可实现根据应用级的缓存:
bootBuildImage {
buildWorkspace {
volume {
name = "cache-{rootProject.name}.work"
}
}
buildCache {
volume {
name = "cache-{rootProject.name}.build"
}
}
launchCache {
volume {
name = "cache-${rootProject.name}.launch"
}
}
}
上述配置,只要应用名称不变,缓存就一直有效。有没有担心代码变更了,无法正常将新代码打包到镜像中去?
不必担心,我已经替大家验证过了,只要代码发生变更,就会触发graalvm的native-image编译过程。如果未修改代码,则不会执行编译,会直接从缓存中获取上一次编译结果,进行重新打包镜像。
以为这就完了?看看下面这个镜像,能看出什么问题吗?
是不是发现创建时间不对劲了?默认情况下,springboot的native镜像不会设置创建时间,因此打包后的时间就是一个默认的值,没有任何意义。因此,咱们还需要在bootBuildImage中添加配置:
bootBuildImage {
createdDate = "now"
}
这个时间格式为ISO 8601格式,但是“now"例外,使用now就是设置当前时间。
还没完,上面说了,Springboot Gradle Plugin打的镜像名称默认为:docker.io/library/{project.name}:{project.version}
如果我们需要将其推送到私有的制品库,还需要手动打tag。别担心,Spring已经考虑到了,咱们可以对镜像进行一些列的定制。我们可以设置iamgeName属性:
bootBuildImage {
imageName = "registryhost/doifor/{project.name}:{version}"
}
另外,还可以配置打包完成后进行推送及其认证信息,如下:
bootBuildImage {
publish = true
docker {
publishRegistry {
url = "registry.cn-chengdu.aliyuncs.com"
username = "xxxxxxx"
password = "xxxxxx"
}
}
}
不知道各位在做springboot native编译的时候有没有考虑过使用更高性能的设备来加速打包过程?如果代码又不在高性能设备上,该怎么办?将代码拷贝过去,然后安装gradle等环境?springboot默认使用 sock连接本机的docker进行打包,这让我十分的头痛,之前的处理方案就是在开发环境安装一个docker,并且由于默认使用非root用户执行命令,导致构建过程十分痛苦。我记得之前使用maven的docker插件编译时可以设置docker的tcp地址,今天也在这里找到了答案。可以设置docker的host,如下:
bootBuildImage {
docker {
host = "tcp://127.0.0.1:2375"
}
}
完成上述配置就可以痛快的享受Springboot native带来的乐趣了。
不过还是留了个坑没解决,我看官方文档中说可以为构建工具设置代理,但是经过尝试无效,也不知道是我的代理问题还是设置问题,反正就没有正常的从github上把依赖下来下载,官方说可以使用以下配置来进行设置代理:
tasks.named("bootBuildImage") {
environment["HTTP_PROXY"] = "http://proxy.example.com"
environment["HTTPS_PROXY"] = "https://proxy.example.com"
}
我配置了,但是没有解决问题,跪求原因,这个问题不解决,以后还是需要首次打包开全局代理,这个还是有些扫兴。