1. Introduction
1.导言
GraalVM compiles Java applications into machine executables using its Ahead-Of-Time (AOT) compiler. These executables execute directly into the target machine without using a Just-In-Time (JIT) compiler. The GraalVM-produced binaries are smaller, have a fast start-up time, and provide peak performance without any warm-up. Besides, these executables have a lower memory footprint and CPU than the applications running on JVM.
GraalVM使用其超前(AOT)编译器将 Java 应用程序编译为机器可执行文件。这些可执行文件可直接在目标计算机上执行,而无需使用即时 (JIT) 编译器。GraalVM 生成的二进制文件更小,启动时间更短,无需预热即可提供峰值性能。此外,这些可执行文件的内存占用和 CPU都低于在 JVM 上运行的应用程序。
Docker lets us package software components into a Docker Image and run as a Docker container. A Docker container contains everything the application needs to run including the application code, runtime, system tools, and libraries.
通过 Docker,我们可以将软件组件打包到 Docker Image 中,并作为 Docker 容器运行。Docker 容器包含应用程序运行所需的一切,包括应用程序代码、运行时、系统工具和库。
In this tutorial, we’ll discuss creating a GraalVM native image of a Java application. We’ll then talk about how to use this native image as a Docker image and run it as a Docker container.
在本教程中,我们将讨论如何创建 Java 应用程序的 GraalVM 本地镜像。然后,我们将讨论如何将该本地镜像用作 Docker 镜像,并将其作为 Docker 容器运行。
2. What Is a Native Image?
2.什么是原生图像?
Native Image is a technology that compiles Java code ahead of time into a native executable. This native executable includes only the code required to be executed at runtime. This includes application classes, standard library classes, the language runtime, and statically linked native code from JDK.
本地映像是一种将 Java 代码提前编译为本地可执行文件的技术。该本地可执行文件仅包含运行时需要执行的代码。这包括应用程序类、标准库类、语言运行时以及 JDK 中静态链接的本地代码。
The Native Image Builder (native-image) scans the application classes and other metadata to create a binary file specific to an operating system and architecture. The native-image tool performs a static application code analysis to determine the classes and methods reachable while the application runs. It then compiles the required classes, methods, and resources into a binary executable.
本地映像生成器(native-image)会扫描应用程序类和其他元数据,以创建一个特定于操作系统和体系结构的二进制文件。native-image 工具执行静态应用程序代码分析,以确定应用程序运行时可访问的类和方法。然后,它将所需的类、方法和资源编译成二进制可执行文件。
3. Benefits of Native Image
3.原生图像的好处
There are several benefits of a native image executable:
本地映像可执行文件有几个好处:
- As a native image builder compiles only the resources that are needed at runtime, the size of the executable is small
- Native executables have extremely fast start-up times as these are directly executed in the target machine without a JIT compiler
- Provides a lesser attack surface as it packages only the required application resources
- Useful to package in a lightweight container image such as Docker Image for fast and efficient deployment
4. Building a GraalVM Native Image
4.构建 GraalVM 本地镜像
In this section, we’ll build a GraalVM native image for a Spring Boot application. First, we need to install GraalVM and set the JAVA_HOME environment variable. Second, create a Spring Boot application with the Spring Web and GraalVM Native Support dependencies:
在本节中,我们将为 Spring Boot 应用程序构建 GraalVM 本地镜像。首先,我们需要安装 GraalVM 并设置 JAVA_HOME 环境变量。其次,使用 Spring Web 和 GraalVM Native Support 依赖项创建 Spring Boot 应用程序:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.1.5</version>
</dependency>
We also need to add the following plugin for the GraalVM native support:
我们还需要 添加以下插件,以获得 GraalVM 本机支持:
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.9.27</version>
</plugin>
</plugins>
</build>
This application contains a sample rest controller:
该应用程序包含一个休息控制器示例:
@RestController
class HelloController {
@GetMapping
public String hello() {
return "Hello GraalVM";
}
}
Let’s build the native executable using the Maven command:
让我们使用 Maven 命令构建本地可执行文件:
$mvn -Pnative native:compile
The native-maven-plugin builds the GraalVM native image. Since the GraalVM native image compiler performs static code analysis, the build time is high compared to the regular Java application compilation.
native-maven-plugin 可构建 GraalVM 本地镜像。由于 GraalVM 本地镜像编译器会执行静态代码分析,因此与普通 Java 应用程序编译相比,构建时间较长。
The following is the output of the GraalVM compilation:
以下是 GraalVM 编译的输出结果:
========================================================================================================================
GraalVM Native Image: Generating 'springboot-graalvm-docker' (executable)...
========================================================================================================================
<strong>[1/8] Initializing... (42.7s @ 0.15GB)>
Java version: 17.0.8+9-LTS, vendor version: Oracle GraalVM 17.0.8+9.1
Graal compiler: optimization level: 2, target machine: x86-64-v3, PGO: ML-inferred
C compiler: gcc (linux, x86_64, 11.3.0)
Garbage collector: Serial GC (max heap size: 80% of RAM)
// Omitted for clarity
<strong>[2/8] Performing analysis... [******] (234.6s @ 1.39GB)>
15,543 (90.25%) of 17,222 types reachable
25,854 (67.59%) of 38,251 fields reachable
84,701 (65.21%) of 129,883 methods reachable
4,906 types, 258 fields, and 4,984 methods registered for reflection
64 types, 70 fields, and 55 methods registered for JNI access
4 native libraries: dl, pthread, rt, z
[3/8] Building universe... (14.7s @ 2.03GB)
[4/8] Parsing methods... [*******] (55.6s @ 2.05GB)
[5/8] Inlining methods... [***] (4.9s @ 2.01GB)
[6/8] Compiling methods... [**********
[6/8] Compiling methods... [*******************] (385.2s @ 3.02GB)
[7/8] Layouting methods... [****] (14.0s @ 2.00GB)
[8/8] Creating image... [*****] (30.7s @ 2.72GB)
48.81MB (58.93%) for code area: 48,318 compilation units
30.92MB (37.33%) for image heap: 398,288 objects and 175 resources
3.10MB ( 3.75%) for other data
82.83MB in total
// Omitted for clarity
Finished generating 'springboot-graalvm-docker' in 13m 7s.
// Omitted for clarity
In the above compilation output, the following are a few key points:
在上述编译输出中,有以下几个关键点:
- The compilation uses the GraalVM Java compiler to compile the application
- The compiler does a reachability check for types, fields, and methods
- Next, it builds the native executed and shows the executable size and the time taken for compilation
Post the successful build, we can find the native executable available in the target directory. This executable can be executed in the command line.
构建成功后,我们可以在目标目录中找到本地可执行文件。该可执行文件可在命令行中执行。
5. Building a Docker Image
5.构建 Docker 映像
In this section, we’ll develop a Docker image for the native executable generated in the previous step.
在本节中,我们将为上一步生成的本地可执行文件开发一个 Docker 镜像。
Let us create the following Dockerfile:
让我们创建下面的 Dockerfile:
FROM ubuntu:jammy
COPY target/springboot-graalvm-docker /springboot-graalvm-docker
CMD ["/springboot-graalvm-docker"]
Next, let us build the Docker image using the following command:
接下来,让我们使用以下命令构建 Docker 映像:
$docker build -t springboot-graalvm-docker .
Post successful build, we can notice that springboot-graalvm-docker Docker image is available:
构建成功后,我们可以看到 springboot-graalvm-docker Docker 映像可用:
$docker images | grep springboot-graalvm-docker
We can execute this image using the following command:
我们可以使用以下命令执行该图像:
$docker run -p 8080:8080 springboot-graalvm-docker
The above command starts the container and we can notice the Spring Boot startup logs:
上述命令启动了容器,我们可以看到 Spring Boot 的启动日志:
// Ommited for clarity
*** INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization <strong>completed in 14 ms>
*** INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
*** INFO 1 --- [ main] c.b.g.GraalvmDockerImageApplication : Started GraalvmDockerImageApplication in 0.043 seconds (process running for 0.046)
The application starts in 43 milliseconds. We can access the REST endpoint by accessing the following command:
应用程序在 43 毫秒后启动。我们可以通过访问以下命令来访问 REST 端点:
$curl localhost:8080
It shows the following output:
输出结果如下
Hello GraalVM
6. Conclusion
6.结论
In this article, we build a Docker image for a GraalVM native executable.
在本文中,我们将为 GraalVM 本地可执行文件构建一个 Docker 镜像。
We started discussing the GraalVM native image and its advantages. It is useful for use cases requiring a first start-up and low memory footprint. Next, we generated the native executable of a Spring Boot application using the GraalVM native image compiler. Lastly, we developed a Docker Image with the native executable and started a Docker container with the image.
我们开始讨论 GraalVM 本机镜像及其优势。它适用于需要首次启动和低内存占用的用例。接下来,我们使用 GraalVM 本地镜像编译器生成了 Spring Boot 应用程序的本地可执行文件。最后,我们用原生可执行文件开发了一个 Docker 镜像,并用该镜像启动了一个 Docker 容器。
The source code for this application is available on over on GitHub.
该应用程序的源代码可在 over on GitHub 上获取。