在Docker容器中运行Spring Boot GraalVM原生镜像

本文介绍了如何在Docker容器中编译和运行Spring Boot应用的GraalVM原生镜像,以及如何在Heroku上部署。首先,通过Dockerfile使用Oracle的GraalVM镜像并添加Maven,然后解决编译时的内存错误。接着,利用Docker多阶段构建运行本机应用,并动态配置端口以适应Heroku。最后,使用TravisCI构建Heroku ready Docker镜像,并将其推送到Heroku容器注册表。
摘要由CSDN通过智能技术生成

将 Spring Boot与GraalVM原生映像的优点结合起来真的很酷。但是在Docker容器里也做这些魔术怎么样?在Heroku这样的云基础设施上运行那些本地应用程序怎么样?

再说一次“在我的机器上工作”!

在写第一篇关于 [将Spring Boot应用程序作为GraalVM本机映像运行]的文章时,我对今天我们尝试将Spring Boot与GraalVM本机映像一起使用的可能性感到非常兴奋。但是,无论我是在客户的网站上,还是在Fachhochschule Erfurt给我的学生讲课,我都会努力避免这种在我的机器上工作的困境。到目前为止,我们只在本地工作站上将Spring Boot应用程序编译成GraalVM本机映像。

既然我们已经到了2021年,我们不应该就此止步,而应该尝试使用某种容器来构建和运行我们的应用程序,对吧?我们应该借助一些持续集成的云平台来不断地做到这一点。最后,我们需要在某种云平台上部署和运行本机应用程序!
img
所以,首先要做的是——让我们弄清楚如何使用Docker将Spring Boot应用程序编译成GraalVM本机映像!

使用Docker将Spring Boot应用程序编译为GraalVM本机映像

在这里使用Docker最简单的方法是依赖Oracle的GraalVM Docker官方镜像。有趣的是,这个镜像既缺少Maven,也缺少原生镜像GraalVM插件。因此,让我们简单地将它们添加到镜像中,创建我们自己的Dockerfile。同样,所有代码示例都可以在GitHub上的示例项目中获得。

在这个博客文章系列的第一篇文章中,我们已经习惯了利用SDKMAN安装Maven。正如甲骨文官方的GraalVM Docker镜像是基于 oraclelinux:7-slim ,我们需要先安装 unzipzip 。为了正常工作,SDKMAN需要两者:

FROM oracle/graalvm-ce:20.0.0-java11
 
# For SDKMAN to work we need unzip & zip
RUN yum install -y unzip zip
 
RUN \
    # Install SDKMAN
    curl -s "https://get.sdkman.io" | bash; \
    source "$HOME/.sdkman/bin/sdkman-init.sh"; \
    # Install Maven
    sdk install maven; \
    # Install GraalVM Native Image
    gu install native-image;
 
RUN source "$HOME/.sdkman/bin/sdkman-init.sh" && mvn --version
 
RUN native-image --version
 
# Always use source sdkman-init.sh before any command, so that we will be able to use 'mvn' command
ENTRYPOINT bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && $0"

我们不应该忘记为Docker映像的用户启用 mvn 命令。因此,我们创建了一个稍微有趣的入口点,它总是在命令前面加上 source$HOME/.sdkman/bin/sdkman-init.sh 。定义了Dockerfile之后,我们应该用以下内容构建我们的镜像:

docker build . --tag=graalvm-ce:20.0.0-java11-mvn-native-image

构建完成后,我们可以在Docker容器中启动GraalVM本机映像编译。但是请稍候,下面的命令继承了第二个Docker卷定义 --volume“$HOME”/.m2:/root/.m2 。为什么?因为我真的想避免每次启动Docker容器时一遍又一遍地下载所有SpringMaven依赖项。通过这种装载,我们只需使用机器上缓存的Maven存储库:

docker run -it --rm \
    --volume $(pwd):/build \
    --workdir /build \
    --volume "$HOME"/.m2:/root/.m2 \
    graalvm-ce:20.0.0-java11-mvn-native-image ./compile.sh

第一个列 --volume$(pwd):/build 只是将Spring Boot应用程序的源代码(包括用于GraalVM本机映像编译的 .compile.sh 脚本)装载到Docker容器中。运行这个Docker构建,经过几分钟的繁重编译后,生成的 spring-boot-graal 本机应用程序应该已经准备好了。

防止java.lang.OutOfMemoryError错误

当我开始尝试Spring Boot应用程序的GraalVM本机映像编译时,我经常体验到 docker run 命令似乎需要很长时间才能完成。最后,一个 java.lang.OutOfMemoryError 错误被抛出到日志中,如下所示:

14:06:34.609 [ForkJoinPool-2-worker-3] DEBUG io.netty.handler.codec.compression.ZlibCodecFactory - -Dio.netty.noJdkZlibEncoder: false
Exception in thread "native-image pid watcher"
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "native-image pid watcher"

在这种情况下,Docker引擎很可能无法使用足够的内存。在Mac的Docker安装中,默认值只有2.00GB。正如stackoverflow q&a的评论所述,您必须给Docker更多的内存,因为GraalVM本机映像编译过程实际上是RAM密集型的。分配Docker引擎大约9到12GB的RAM,我可以在Docker容器中进行编译:

img

如果一切正常,您应该在 /target/native-image 目录中找到本机编译的Spring Boot应用程序 spring-boot-graal 。因此,要运行我们的应用程序,只需使用 ./target/native-image/spring-boot-graal :

$ ./spring-boot-graal
zsh: exec format error: ./spring-boot-graal

噢!原来这不管用!为什么?我们真的需要记住,我们是从Java应用程序编译本机可执行文件! 所以他们现在完全依赖平台了! 我们的Docker容器的基本映像将与我们的主机操作系统大不相同。我想这对所有的Java人来说都是新鲜事!从一开始我们就被告知Java是独立于平台的,这要归功于它的虚拟机。这个问题只有在这一点上我们才真正明白,我们开始在Docker容器中编译我们的应用程序。

这个问题的解决方案很简单:我们不仅在Docker容器中编译应用程序,而且在其中运行它们。

在Docker中运行本机Spring Boot应用程序

如果我们想在一个容器中运行本机Spring Boot

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值