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

Posted java构架师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在Docker容器中运行Spring Boot GraalVM原生镜像相关的知识,希望对你有一定的参考价值。

将 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应用程序,Docker多阶段构建特性就很方便了。使用它,我们可以在第一个容器中编译GraalVM本机映像—并且只在第二个容器中使用生成的本机Spring引导应用程序并运行它。因此,我们需要稍微扩展Dockerfile:

FROM oracle/graalvm-ce:20.1.0-java11
 
ADD . /build
WORKDIR /build
 
# 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
 
RUN source "$HOME/.sdkman/bin/sdkman-init.sh" && ./compile.sh
 
 
# We use a Docker multi-stage build here so that we only take the compiled native Spring Boot app from the first build container
FROM oraclelinux:7-slim
 
MAINTAINER Jonas Hecht
 
# Add Spring Boot Native app spring-boot-graal to Container
COPY --from=0 "/build/target/native-image/spring-boot-graal" spring-boot-graal
 
# Fire up our Spring Boot Native app by default
CMD [ "sh", "-c", "./spring-boot-graal" ]

我们只需在这里通过 COPY --from=0 从第一个构建容器复制编译结果。然后我们定义应用程序的启动命令 ./spring-boot-graal ,就像我们在机器上一样。这里它只是包装在一个 CMD 语句中。你可能注意到了,我们换成了 oraclelinux:7-slim 作为我们第二次运行容器的基本图像。这节省了大量内存,因为生成的映像只需要大约 180MB ,而不是接近 2GB ,如果我们坚持使用 oracle/graalvm ce:20.1.0-java11 ,则需要 2GB

这已经是这里需要准备的全部内容,我们现在可以使用以下命令运行Docker多级构建:

docker build . --tag=spring-boot-graal

这还需要一段时间,你可以去喝杯咖啡。当您获得如下输出时,Docker构建成功完成:

[spring-boot-graal:289]   (typeflow): 114,554.33 ms,  6.58 GB
[spring-boot-graal:289]    (objects):  63,145.07 ms,  6.58 GB
[spring-boot-graal:289]   (features):   6,990.75 ms,  6.58 GB
[spring-boot-graal:289]     analysis: 190,400.92 ms,  6.58 GB
[spring-boot-graal:289]     (clinit):   1,970.98 ms,  6.67 GB
[spring-boot-graal:289]     universe:   6,263.93 ms,  6.67 GB
[spring-boot-graal:289]      (parse):  11,824.83 ms,  6.67 GB
[spring-boot-graal:289]     (inline):   7,216.63 ms,  6.73 GB
[spring-boot-graal:289]    (compile):  63,692.52 ms,  6.77 GB
[spring-boot-graal:289]      compile:  86,836.76 ms,  6.77 GB
[spring-boot-graal:289]        image:  10,050.63 ms,  6.77 GB
[spring-boot-graal:289]        write:   1,319.52 ms,  6.77 GB
[spring-boot-graal:289]      [total]: 313,644.65 ms,  6.77 GB
 
real  5m16.447s
user  16m32.096s
sys 1m34.441s
Removing intermediate container 151e1413ec2f
 ---> be671d4f237f
Step 10/13 : FROM oracle/graalvm-ce:20.0.0-java11
 ---> 364d0bb387bd
Step 11/13 : MAINTAINER Jonas Hecht
 ---> Using cache
 ---> 445833938b60
Step 12/13 : COPY --from=0 "/build/target/native-image/spring-boot-graal" spring-boot-graal
 ---> 2d717a0db703
Step 13/13 : CMD [ "sh", "-c", "./spring-boot-graal" ]
 ---> Running in 7fa931991d7e
Removing intermediate container 7fa931991d7e
 ---> a0afe30b3619
Successfully built a0afe30b3619
Successfully tagged spring-boot-graal:latest

有了这样的输出,我们可以简单地用 docker run -p 8080:8080 spring-boot-graal 运行我们的Spring Boot本机应用程序:

$ docker run -p 8080:8080 spring-boot-graal
 
  .   ____          _            __ _ _
 /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\
( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\
 \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::
 
2020-04-19 09:22:51.547  INFO 1 --- [           main] i.j.s.SpringBootHelloApplication         : Starting SpringBootHelloApplication on 06274db526b0 with PID 1 (/spring-boot-graal started by root in /)
2020-04-19 09:22:51.547  INFO 1 --- [           main] i.j.s.SpringBootHelloApplication         : No active profile set, falling back to default profiles: default
2020-04-19 09:22:51.591  WARN 1 --- [           main] io.netty.channel.DefaultChannelId        : Failed to find the current process ID from ''; using a random value: -949685832
2020-04-19 09:22:51.593  INFO 1 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2020-04-19 09:22:51.594  INFO 1 --- [           main] i.j.s.SpringBootHelloApplication         : Started SpringBootHelloApplication in 0.063 seconds (JVM running for 0.065)

哇,我想这又简单又快了。现在终于可以在浏览器中访问你的应用了 http://localhost:8080/hello

在Docker容器中动态配置Spring Boot本机应用程序的端口

能够在Docker容器中构建和运行本机编译的Spring Boot应用程序,我们现在的行动完全自由了!一些读者可能已经知道,我真的很喜欢Heroku。那么为什么不在那里运行我们的原生Spring启动应用程序呢?
img
对于大多数作为服务提供商的云平台来说,我们需要的一件事是在运行时动态配置Spring-Boot本机应用程序的端口。这仅仅是因为大多数云提供商在我们的应用程序前面放置了某种代理。Heroku也不例外。正如Heroku博士所说:

web进程必须侦听由Heroku设置的 $PORT 上的HTTP通信。Dockerfile中的 EXPOSE 不受尊重,但可以用于本地测试。只支持HTTP请求。

为了实现这一点,我们需要在运行时向Spring Boot本机应用程序传递一个端口变量。由于GraalVM支持还处于早期阶段,我们不能依赖大量的文档。但答案很简单!我们只需要将 -D 参数(如 -Dserver.port=8087 )传递给本机应用程序,就像我们已经习惯于从非本机Spring Boot应用程序传递一样:

./spring-boot-graal -Dserver.port=8087

完成后,我们的应用程序开始使用端口 8087 。考虑到这一点,我们还需要在 docker run 命令中定义端口。因此,我们需要再次对Dockerfile进行小的更改:

...
# Add Spring Boot Native app spring-boot-graal to Container
COPY --from=0 "/build/target/native-image/spring-boot-graal" spring-boot-graal
 
# Fire up our Spring Boot Native app by default
CMD [ "sh", "-c", "./spring-boot-graal -Dserver.port=$PORT" ]

有了它,我们就可以在命令行中使用动态端口设置运行我们的停靠本机SpringBoot应用程序,如下所示:

docker run -e "PORT=8087" -p 8087:8087 spring-boot-graal

我们的应用程序现在可以在 http://localhost:8087/hello

Travis CI&Heroku容器注册表和运行时将我们从“退出状态137”错误中解救出来

当我们继续在Heroku上部署我们的应用程序时,如果我们还没有:

heroku create spring-boot-graal

当我们计划在“Docker模式”中使用Heroku时,我们还需要将Heroku堆栈设置为container:

heroku stack:set container --app spring-boot-graal

遗憾的是,在我们这里的例子中,我们不能使用关于在Heroku上使用Docker、JDK 11和Maven 3.5.x运行Spring Boot的文章中的说明。使用它们时,我们会遇到以下错误:

Error: Image build request failed with exit status 137
real  2m51.946s
user  2m9.594s
sys 0m19.085s
The command '/bin/sh -c source "$HOME/.sdkman/bin/sdkman-init.sh" && ./compile.sh' returned a non-zero code: 137

当Docker没有足够的内存时,通常会出现此错误。因为免费的Heroku dyno只能保证512MB的RAM,我们将在这里完成GraalVM本机编译。

但正如docs所述,使用heroku.yml构建Docker映像的方法并不是在heroku上运行Docker容器的唯一选择。幸运的是,还有另一种方法可以使用容器注册表和运行时(Docker部署)。这使我们能够解耦Docker映像构建过程(这是如此的内存消耗!)从运行我们的Docker。

使用TravisCI编译Heroku ready Docker映像

因此,我们需要将Docker构建过程转移到另一个CI云平台上,如TravisCI。它已经被证明可以直接在Travis虚拟主机上工作,那么为什么不同时使用Travis Docker服务呢?

img

我们知道如何在Docker容器中对Spring Boot进行本机编译,因此所需的 native-image-compile.yml 变得非常简单:

dist: bionic
language: minimal
 
services:
  - docker
 
script:
  # Compile App with Docker
  - docker build . --tag=spring-boot-graal

示例项目 native-image-compile.yml 另外在Travis主机上实现了一个单独的构建作业 native-image-compile ,以展示如何在TravisCI上完成GraalVM native-image编译,而无需Docker。

但同样在Travis上,我们需要防范“ Error:Image build request failed with exit status 137 ”错误。这件事在我真正解决问题之前发生过很多次!

使用带有–no server选项和合适的-J-Xmx参数的本机映像

正如Spring文档中提到的,我们现在应该在使用Spring运行本机映像编译时使用 --no server 选项。但是这个参数对本机图像编译过程有什么影响呢?正如官方文件所说:

要考虑的另一个前提条件是最大堆大小。运行基于JVM的应用程序的物理内存可能不足以构建本机映像。对于基于服务器的映像构建,我们允许将80%的报告物理RAM用于所有服务器,但每台服务器的容量不得超过14 GB(有关确切的详细信息,请参阅本机映像源代码)。如果您使用 --no server 选项运行,您将得到报告为物理RAM的全部80%作为基线。此模式还考虑 -Xmx 参数。

我们可以省略 no server 选项,以减少本机映像编译所消耗的内存量。但是结合Spring还有一个开放的GraalVM问题,这使得没有服务器的图像构建有时是不可靠的。幸运的是,我在这个GitHub问题中发现了一个提示:我们可以配置 --no server 选项总共占用的内存量。这是在 Xmx 参数的帮助下完成的,比如 -J-Xmx4G

time native-image \\
  --no-server -J-Xmx4G \\
  --no-fallback \\
  --initialize-at-build-time \\
  -H:+TraceClassInitialization \\
  -H:Name=$ARTIFACT \\
  -H:+ReportExceptionStackTraces \\
  -Dspring.graal.remove-unused-autoconfig=true \\
  -Dspring.graal.remove-yaml-support=true \\
  -cp $CP $MAINCLASS;

native-image 命令中使用这样的选项,可以重复地将内存量减少到4GB的RAM。对于TravisCI来说,这已经足够了,因为它使用Docker服务为我们提供了超过6GB的容量(参见这个构建示例)。使用该选项将导致Spring Boot应用程序的本机映像编译的以下输出:

08:07:23.999 [ForkJoinPool-2-worker-3] DEBUG io.netty.util.internal.PlatformDependent - maxDirectMemory: 4294967296 bytes (maybe)
...
[spring-boot-graal:215]   (typeflow): 158,492.53 ms,  4.00 GB
[spring-boot-graal:215]    (objects):  94,986.72 ms,  4.00 GB
[spring-boot-graal:215]   (features): 104,518.36 ms,  4.00 GB
[spring-boot-graal:215]     analysis: 368,005.35 ms,  4.00 GB
[spring-boot-graal:215]     (clinit):   3,107.18 ms,  4.00 GB
[spring-boot-graal:215]     universe:  12,502.04 ms,  4.00 GB
[spring-boot-graal:215]      (parse):  22,617.13 ms,  4.00 GB
[spring-boot-graal:215]     (inline):  10,093.57 ms,  3.49 GB
[spring-boot-graal:215]    (compile):  82,256.99 ms,  3.59 GB
[spring-boot-graal:215]      compile: 119,502.78 ms,  3.59 GB
[spring-boot-graal:215]        image:  12,087.80 ms,  3.59 GB
[spring-boot-graal:215]        write:   3,573.06 ms,  3.59 GB
[spring-boot-graal:215]      [total]: 558,194.13 ms,  3.59 GB
 
real  9m22.984s
user  24m41.948s
sys 2m3.179s

需要考虑的一点是,本机映像编译现在会慢一点。因此,如果您在本地计算机上运行,并且有大量内存,可以随意删除 -J-Xmx4G 参数。

将我们停靠的本机Spring启动应用程序推送到Heroku容器注册表

现在我们终于可以将build Docker映像推送到Heroku的容器注册表中了,以后我们就可以从那里运行Spring Boot本机应用程序了。因此,我们需要配置一些环境变量,以便在TravisCI作业的设置中推送到Heroku的容器注册表。第一个HEROKU用户名应该保留您的HEROKU电子邮件,HEROKU密码将继承您的HEROKU API密钥。请确保避免在生成日志中显示值:

img

使用 native-image-compile.yml 中的以下配置,我们应该能够成功登录到Heroku容器注册表:

- script:
        # Login into Heroku Container Registry first, so that we can push our Image later
        - echo "$HEROKU_PASSWORD" | docker login -u "$HEROKU_USERNAME" --password-stdin registry.heroku.com

现在,在一个成功的Docker构建将我们的SpringBoot应用程序编译成一个本机可执行文件之后,我们最终需要将生成的Docker映像推送到Heroku容器注册表中。因此,我们需要为Docker图像构建使用正确的标记

docker build . --tag=registry.heroku.com/yourAppName/HerokuProcessType
docker push registry.heroku.com/yourAppName/HerokuProcessType

对于我们的示例应用程序, native-image-compile.yml 中的具体 docker builddocker push 命令如下所示:

- docker build . --tag=registry.heroku.com/spring-boot-graal/web
    - docker push registry.heroku.com/spring-boot-graal/web

在Heroku容器基础设施上发布我们的停靠本机Spring Boot应用程序

docker推送成功后的最后一步是在Heroku容器基础设施上发布我们的原生SpringBoot应用程序。自2018年5月以来,这始终是使用Docker在Heroku上真正运行应用程序的最后一步(在此之前,只需推送即可)。

有两种方法可以实现这一点,根据文件。通过heroku的 CLIcontainer:release web 或者使用API。第一个需要我们在TravisCI中安装herokucli,后者应该是现成的。因此,让我们创建所需的 curl 命令:

curl -X PATCH https://api.heroku.com/apps/spring-boot-graal/formation \\
          -d '{
                "updates": [
                {
                  "type": "web",
                  "docker_image": "'"$(docker inspect registry.heroku.com/spring-boot-graal/web --format={{.Id}})"'"
                }]
              }' \\
          -H "Content-Type: application/json" \\
          -H "Accept: application/vnd.heroku+json; version=3.docker-releases" \\
          -H "Authorization: Bearer $DOCKER_PASSWORD"

这个命令甚至比官方Heroku文档中记录的命令更好。它已经包含 docker inspect registry.heroku.com/spring-boot-graal/web--format={.Id}} 命令来检索所需的docker映像Id。此外,它还省略了事先登录heroku CLI来创建文档中提到的所需 ~/.netrc 的需要。这是因为我们只在这里使用 -H“Authorization:Bearer$DOCKER_PASSWORD” (其中 $DOCKER_PASSWORD 是我们的Heroku API密钥)。

Travis的问题是:它不理解我们的 curl 命令,因为它对它的解释是完全错误的。即使我们介意正确的多行用法。我想我们的Java用户组ü林根扬声器Kai Tödter已经知道某些CI系统的限制。这就是为什么他为这个目的编写了bash脚本。那时,我开始使用一个名为heroku-release.sh的简单脚本,以便在heroku上发布我们的Spring Boot应用程序:

#!/usr/bin/env bash
 
herokuAppName=$1
dockerImageId=$(docker inspect registry.heroku.com/$herokuAppName/web --format={{.Id}})
 
curl -X PATCH https://api.heroku.com/apps/$herokuAppName/formation \\
          -d '{
                "updates": [
                {
                  "type": "web",
                  "docker_image": "'"$dockerImageId"'"
                }]
              }' \\
          -H "Content-Type: application/json" \\
          -H "Accept: application/vnd.heroku+json; version=3.docker-releases" \\
          -H "Authorization: Bearer $DOCKER_PASSWORD"

使用此脚本,我们终于可以完全运行 native-image-compile.yml 了:

dist: bionic
language: minimal
 
services:
  - docker
 
- script:
    # Login into Heroku Container Registry first, so that we can push our Image later
    - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin registry.heroku.com
 
    # Compile App with Docker
    - docker build . --tag=registry.heroku.com/spring-boot-graal/web
 
    # Push to Heroku Container Registry
    - docker push registry.heroku.com/spring-boot-graal/web
 
    # Release Dockerized Native Spring Boot App on Heroku
    - ./heroku-release.sh spring-boot-graal

就这样!在下一个成功的TravisCI构建之后,我们应该能够看到在Heroku上运行的本机编译和停靠的Spring Boot应用程序 https://spring-boot-graal.herokuapp.com/hello

img

您甚至可以使用 heroku logs 命令查看幕后发生的事情:

$ heroku logs -a spring-boot-graal
 
2020-04-24T12:02:14.562471+00:00 heroku[web.1]: State changed from down to starting
2020-04-24T12:02:41.564599+00:00 heroku[web.1]: State changed from starting to up
2020-04-24T12:02:41.283549+00:00 app[web.1]:
2020-04-24T12:02:41.283574+00:00 app[web.1]: .   ____          _            __ _ _
2020-04-24T12:02:41.283575+00:00 app[web.1]: /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\
2020-04-24T12:02:41.283575+00:00 app[web.1]: ( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\
2020-04-24T12:02:41.283576+00:00 app[web.1]: \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )
2020-04-24T12:02:41.283576+00:00 app[web.1]: '  |____| .__|_| |_|_| |_\\__, | / / / /
2020-04-24T12:02:41.283578+00:00 app[web.1]: =========|_|==============|___/=/_/_/_/
2020-04-24T12:02:41.286498+00:00 app[web.1]: :: Spring Boot ::
2020-04-24T12:02:41.286499+00:00 app[web.1]:
2020-04-24T12:02:41.287774+00:00 app[web.1]: 2020-04-24 12:02:41.287  INFO 3 --- [           main] i.j.s.SpringBootHelloApplication         : Starting SpringBootHelloApplication on 1c7f1944-1f01-4284-8931-bc1a0a2d1fa5 with PID 3 (/spring-boot-graal started by u11658 in /)
2020-04-24T12:02:41.287859+00:00 app[web.1]: 2020-04-24 12:02:41.287  INFO 3 --- [           main] i.j.s.SpringBootHelloApplication         : No active profile set, falling back to default profiles: default
2020-04-24T12:02:41.425964+00:00 app[web.1]: 2020-04-24 12:02:41.425  WARN 3 --- [           main] io.netty.channel.DefaultChannelId        : Failed to find the current process ID from ''; using a random value: -36892848
2020-04-24T12:02:41.427326+00:00 app[web.1]: 2020-04-24 12:02:41.427  INFO 3 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 59884
2020-04-24T12:02:41.430874+00:00 app[web.1]: 2020-04-24 12:02:41.430  INFO 3 --- [           main] i.j.s.SpringBootHelloApplication         : Started SpringBootHelloApplication in 0.156 seconds (JVM running for 0.159)

用Docker运行SpringBoot应用程序作为GraalVM本机映像真的很酷!

能够利用容器的强大功能以及Spring-Boot和GraalVM原生映像的优点,真的将我们带到了一个新的水平!现在我们几乎可以在任何地方构建和运行我们的原生Spring Boot应用程序。如果我们记住一些基本条件,我们可以在几乎所有持续集成的云平台中构建本地应用程序。不管是TravisCI,CircleCI还是别的什么。在那里建造了它,我们可以在任何地方运行它。作为第一个例子,我们在本文中看到了如何在Heroku上运行本机应用程序,现在我们知道要注意什么了。有了持续的整合和交付。

但是等等!我们不是使用GraalVM原生镜像编译来真正受益于Kubernetes这样的云原生平台吗?由于我们极大地减少了Spring Boot应用程序的内存占用和启动时间,并且能够在Docker容器中运送这些本机应用程序,我们已经准备好了在Kubernetes集群中运行应用程序的一切!就像我们习惯于从所有那些时髦的Quarkus.io或Go应用程序。所以一如既往:继续关注后续帖子!

以上是关于在Docker容器中运行Spring Boot GraalVM原生镜像的主要内容,如果未能解决你的问题,请参考以下文章

在 Docker 容器内运行 Spring Boot 应用程序,无法连接 MySQL

在 docker 容器中几分钟后 Spring Boot 连接重置

Docker 容器整合 Spring Boot 应用

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

Spring Boot + MySQL docker 容器

Spring boot JDBC无法连接到docker容器中的mysql