1. 簡介
GraalVM 通過其 Ahead-Of-Time (AOT) 編譯器將 Java 應用程序編譯為機器可執行文件。這些可執行文件直接進入目標機器執行,而無需使用 Just-In-Time (JIT) 編譯器。GraalVM 生成的二進制文件更小,啓動時間更快,並且在沒有預熱的情況下提供最佳性能。此外,這些可執行文件相對於在 JVM 上運行的應用程序而言,內存佔用和 CPU 消耗更低。
Docker 允許我們將軟件組件打包為 Docker 鏡像並在 Docker 容器中運行。一個 Docker 容器包含應用程序運行所需的一切,包括應用程序代碼、運行時、系統工具和庫。
在本教程中,我們將討論如何為 Java 應用程序創建 GraalVM 本地映像。然後,我們將討論如何使用此本地映像作為 Docker 鏡像並在 Docker 容器中運行它。
2. 原生鏡像是什麼?
原生鏡像是一種技術,它將 Java 代碼提前編譯成原生可執行文件。該原生可執行文件僅包含在運行時所需代碼,包括應用程序類、標準庫類、語言運行時以及來自 JDK 的靜態鏈接的本機代碼。
原生鏡像構建器(native-image)掃描應用程序類和其他元數據,以創建特定於操作系統和架構的二進制文件。 native-image 工具執行靜態應用程序代碼分析,以確定在應用程序運行時可訪問的類和方法。然後,它將所需的類、方法和資源編譯成二進制可執行文件。
3. 原生圖像的優勢
以下是原生圖像可執行文件的幾個優勢:
- 作為原生圖像構建器,它僅編譯在運行時所需的資源,因此可執行文件的大小很小
- 原生可執行文件具有極快的啓動時間,因為它們直接在目標機器上執行,而無需 JIT 編譯器
- 它提供了一個更小的攻擊面,因為它只打包了所需的應用程序資源
- 非常適合打包在輕量級容器鏡像中,例如 Docker 鏡像,以便快速高效地部署
4. 構建 GraalVM 原生鏡像
本節將為 Spring Boot 應用程序構建 GraalVM 原生鏡像。首先,我們需要安裝 GraalVM 並設置 JAVA_HOME環境變量。其次,創建一個 Spring Boot 應用程序,並添加 Spring Web 和 GraalVM Native Support 依賴項:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.1.5</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.9.27</version>
</plugin>
</plugins>
</build>該應用程序包含一個示例 REST 控制器。
@RestController
class HelloController {
@GetMapping
public String hello() {
return "Hello GraalVM";
}
}
讓我們使用 Maven 命令構建原生可執行文件:
$mvn -Pnative native:compilenative-maven-plugin 構建 GraalVM 本地鏡像。由於 GraalVM 本地鏡像編譯器執行靜態代碼分析,因此與常規 Java 應用程序編譯相比,構建時間較高。
以下是 GraalVM 編譯的輸出:
========================================================================================================================
GraalVM Native Image: Generating 'springboot-graalvm-docker' (executable)...
========================================================================================================================
[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
[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在上述編譯輸出中,以下是一些關鍵要點:
- 編譯過程使用 GraalVM Java 編譯器編譯應用程序
- 編譯器會對類型、字段和方法進行可達性檢查
- 然後,它會構建原生可執行文件並顯示可執行文件大小和編譯時間
編譯成功後,可以在目標目錄下找到原生可執行文件,該可執行文件可以在命令行中執行。
5. 構建 Docker 鏡像
本節將為先前步驟生成的原生可執行文件構建 Docker 鏡像。
讓我們創建以下 Dockerfile:
FROM ubuntu:jammy
COPY target/springboot-graalvm-docker /springboot-graalvm-docker
CMD ["/springboot-graalvm-docker"]接下來,讓我們使用以下命令構建 Docker 鏡像:
$docker build -t springboot-graalvm-docker .構建成功後,我們可以注意到 springboot-graalvm-docker Docker 鏡像可用:
$docker images | grep springboot-graalvm-docker我們可以使用以下命令執行此圖像:
$docker run -p 8080:8080 springboot-graalvm-docker上述命令啓動容器,我們可以注意到 Spring Boot 的啓動日誌:
// Ommited for clarity
*** INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization 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)應用程序啓動時間為 43 毫秒。可以通過以下命令訪問 REST 端點:
$curl localhost:8080
它顯示了以下輸出:
Hello GraalVM6. 結論
在本文中,我們為 GraalVM 原生可執行文件構建了一個 Docker 鏡像。
我們討論了 GraalVM 原生鏡像及其優勢。它對於需要快速啓動和低內存佔用率的應用場景非常有用。接下來,我們使用 GraalVM 原生鏡像編譯器生成了 Spring Boot 應用程序的原生可執行文件。最後,我們使用該原生可執行文件和 Docker 鏡像,開發了一個 Docker 鏡像並啓動了 Docker 容器。