使用Docker环境在GraalVM本地镜像中启动Spring Boot Web服务

在Docker环境中启动Spring Boot Web服务:使用GraalVM本地镜像。

你好,我是株式会社船井総研デジタル的 @fsdg-adachi_h。今天我来介绍一下如何在WSL Ubuntu的Docker环境中,将GraalVM原生镜像的Spring Boot Web应用程序作为容器来启动。

spring-boot_graalvm_on_docker.png

目标

在Windows 11上通过Linux进行云开发。

您可以通过点击这里查看文章的列表。

 

实现

在本地环境中的WSL Ubuntu的Docker环境中,使用Dockerfile构建的GraalVM本地映像Spring Boot Web服务的自定义容器映像来启动。

以原生形式启动应用作为容器。

执行环境

要素概要terminalターミナルUbuntuOSDockerコンテナ実行環境

网络服务容器

要素概要app-hello-spring-boot-nativeカスタムコンテナappネイティブイメージ アプリケーションtomcatWeb サーバー

技术话题

在Java中,什么是本地镜像构建?

可以展开查看这里。关键词 内容
Native Image Build 将Java代码编译成本地机器码。通常,Java代码被编译为一种中间语言称为Java字节码,并在Java虚拟机(JVM)上运行。然而,通过Native Image Build,可以直接将Java代码转换为可直接执行的本地机器码,从而实现更快的执行速度和更低的内存使用量。

Native Image Build的需求来自以下方面:

关键词 内容
性能提升 Java通常被认为是一种高级编程语言,并且由于在JVM上运行,执行速度可能较慢。通过Native Image Build,可以实现高速的执行速度。

内存优化 使用JVM执行Java代码通常需要大量内存。通过Native Image Build,可以使用更少的内存来运行程序。

本地程序集成 Java可以与其他编写的本地程序(如C语言或C++)集成。但是,为了进行集成,需要本地代码。通过Native Image Build,可以将这些本地程序和Java代码无缝集成在一起。

Dockerfile 是什么?

以下内容只需一种翻译版本:这是一台可以展开的设备。

Dockerfile

Dockerfile是一个用于构建Docker容器的文本文件。Docker容器是包含运行应用程序和服务所需要的环境的轻量级可移植虚拟化单元。

关键词 内容
脚本格式 Dockerfile以简单的脚本格式编写,因此容器的构建过程可以轻松自动化。
层级结构 Docker镜像在执行Dockerfile的每个命令时以图层的形式生成,并且可以进行重用和缓存。
版本控制 Dockerfile以文本为基础,因此与代码一样容易使用版本控制系统进行管理。
可移植性 通过Dockerfile,应用程序及其依赖可以打包到一个容器镜像中,从而提高在不同环境之间的可移植性。
自动化与效率 通过使用Dockerfile,可以自动化应用程序的构建和环境设置。这样可以减少手动操作时间和人为错误,提高开发和部署过程的效率。
可重现性 Dockerfile完全定义了构建步骤,因此可以在不同环境中复制相同的应用程序。这样可以确保在开发、测试和生产环境之间的一致性。
环境隔离 Docker容器与主机系统分离,以避免应用程序的依赖关系和库的冲突,并在更安全的环境中运行。
可扩展性 通过使用Dockerfile,可以创建自定义镜像。因此,可以轻松创建根据特定需求定制的容器镜像。

开发环境

    Windows 11 Home 22H2 を使用しています。
由于操作 WSL 上的 Ubuntu,你也可以参考 macOS 的方式。
在Windows的Microsoft Store应用程序版本中安装WSL。请从相关文章中查看安装方法。
> wsl –version
WSL版本:1.0.3.0
内核版本:5.15.79.1
WSLg版本:1.0.47安装Ubuntu。请从相关文章中查看安装方法。
$ lsb_release -a
没有可用的LSB模块。
发行商ID:Ubuntu
描述:Ubuntu 22.04.1 LTS
发布:22.04

安装Java JDK。请从相关文章中查看安装方法。
$ java -version
openjdk版本:“17.0.6” 2023-01-17
OpenJDK Runtime环境 GraalVM CE 22.3.1(构建17.0.6+10-jvmci-22.3-b13)
OpenJDK 64位服务器VM GraalVM CE 22.3.1(构建17.0.6+10-jvmci-22.3-b13,混合模式,共享)

安装Maven。请从相关文章中查看安装方法。
$ mvn -version
Apache Maven 3.6.3
Maven目录:/usr/share/maven
Java版本:11.0.18,供应商:Ubuntu,运行时:/usr/lib/jvm/java-11-openjdk-amd64

安装Docker。请从相关文章中查看安装方法。
$ docker –version
Docker版本23.0.1,构建a5ee5b1

本文主要介绍了如何在Ubuntu终端上进行操作。对于初次学习使用Vim进行复制粘贴的人来说,在下面的文章中介绍了步骤。请务必尝试一下。

 

制作网络应用的规格.

NoエンドポイントHTTPメソッドMIME タイプ1/api/dataGETapplication/json
当向 “/api/data” 接口发送 HTTP GET 请求时,将实现一个简单的 Web 服务,该服务会返回JSON数据。
{“message”:”你好,世界!”}

显示 Hello World 的步骤

GraalVM的环境设置。

您可以在这篇相关文章中查看步骤确认。

 

确认JDK的版本

切换Java的版本。

$ sudo update-alternatives --config java
$ sudo update-alternatives --config javac
请确认是否选择了GraalVM。
$ java -version
openjdk version "17.0.6" 2023-01-17
OpenJDK Runtime Environment GraalVM CE 22.3.1 (build 17.0.6+10-jvmci-22.3-b13)
OpenJDK 64-Bit Server VM GraalVM CE 22.3.1 (build 17.0.6+10-jvmci-22.3-b13, mixed mode, sharing)

创建Spring Boot Web服务。

您可以在这篇相关文章中确认步骤。

 

转到项目文件夹中

切换到项目文件夹。
※ 我们将~/tmp/hello-spring-boot定义为项目文件夹。

$ cd ~/tmp/hello-spring-boot
为了简化结构,将所有元素都写在应用程序类中的例子的修改。
删除控制器类,修改应用程序类。
$ rm src/main/java/com/example/springboot/controller/HelloController.java
$ vim src/main/java/com/example/springboot/Application.java文件内容

Application.java
package com.example.springboot;

import java.util.Map;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
@RequestMapping(“/api”)
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

@GetMapping(“/data”)
public Map<String, String> getData() {
Map<String, String> map = Map.of(“message”, “你好世界!”);
return map;
}
}

修改 pom.xml

创建pom.xml文件。

$ vim pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>hello-spring-boot</artifactId>
    <version>1.0</version>
    <name>hello-spring-boot</name>

    <properties>
        <java.version>17</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>app</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
                <configuration>
                    <imageName>app</imageName>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

构建一个以母语为基础的形象

构建一个本地镜像。
※将在target/app创建一个本地镜像。

$ mvn -Pnative native:compile
通过这些步骤,您可以在本地环境的Ubuntu上构建应用程序的本机映像。

构建本地镜像容器。

修改Dockerfile。

$ vim Dockerfile

文件的内容 de

# because the native image app was built by Ubuntu 22.04.
FROM ubuntu:22.04

# create a work dir.
WORKDIR /app

# copy a native image app.
COPY target/app app

# open port 8080 for a native image app.
EXPOSE 8080

# startup a native image app.
ENTRYPOINT ["./app"]

启动 Docker 守护进程。

$ sudo service docker start
 * Starting Docker: docker    [ OK ]
如果您没有Docker环境,请在以下相关文章中查看Docker Engine的安装步骤。

 

构建容器镜像。

$ docker build \
    --no-cache \
    --tag app-hello-spring-boot-native:latest .

我要确认容器镜像。

$ docker images | grep app-hello-spring-boot-native
app-hello-spring-boot-native   latest     f0794df4d28c   23 minutes ago   149MB
在此步骤中,可以在本地 Docker 环境中构建应用的自定义容器镜像作为本地镜像。

启动容器

在本地启动容器。
※ 停止容器时,请按下 ctrl + C。

$ docker run --rm \
    --publish 8080:8080 \
    --name app-local \
    app-hello-spring-boot-native:latest

与传统的JVM容器相比,启动时间大大缩短。

2023-03-14T11:09:55.330Z  INFO 1 --- [main] com.example.springboot.Application : Started Application in 0.073 seconds (process running for 0.08)
显示详细的输出。
$ docker run –rm –name app-local -p 8080:8080 app-hello-spring-boot-native:latest. ____ _ __ _ _
/\\ / ___’_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | ‘_ | ‘_| | ‘_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
‘ |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.0.4)

2023-03-14T11:09:55.275Z INFO 1 — [ main] com.example.springboot.Application : 使用Java 17.0.6和PID 1启动AOT-处理的应用程序(由cnb在/workspace中启动)
2023-03-14T11:09:55.275Z INFO 1 — [ main] com.example.springboot.Application : 未设置活动配置文件,回退到默认配置文件: “default”
2023-03-14T11:09:55.290Z INFO 1 — [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat使用8080端口初始化(http)
2023-03-14T11:09:55.291Z INFO 1 — [ main] o.apache.catalina.core.StandardService : 启动服务 [Tomcat]
2023-03-14T11:09:55.291Z INFO 1 — [ main] o.apache.catalina.core.StandardEngine : 启动Servlet引擎:[Apache Tomcat/10.1.5]
2023-03-14T11:09:55.298Z INFO 1 — [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : 初始化Spring嵌入式WebApplicationContext
2023-03-14T11:09:55.298Z INFO 1 — [ main] w.s.c.ServletWebServerApplicationContext : 根WebApplicationContext:初始化完成,耗时22毫秒
2023-03-14T11:09:55.329Z INFO 1 — [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat已启动,端口:8080(http),上下文路径:”
2023-03-14T11:09:55.330Z INFO 1 — [ main] com.example.springboot.Application : 应用程序已启动,耗时0.073秒(进程运行0.08秒)

通过前面的步骤,我们能在本地的Docker环境中启动应用的自定义容器的本地镜像。

容器的运行确认

使用curl命令从另一个终端进行确认。

$ curl -v http://localhost:8080/api/data -w '\n'

发力

*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /api/data HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Fri, 04 Aug 2023 01:52:30 GMT
<
* Connection #0 to host localhost left intact
{"message":"Hello World!"}
在之前的步骤中,终端显示了 {“message”:”你好世界!”} 并成功获取了 JSON 数据。

连接到容器

从另一个终端连接到容器。

$ docker exec -it app-local /bin/bash

连接到容器后,请确认目录。
※ 当您要离开容器时,请按下ctrl + D。

# pwd
/app
# ls -lah
total 68M
drwxr-xr-x 1 root root 4.0K Mar 17 14:53 .
drwxr-xr-x 1 root root 4.0K Mar 17 14:53 ..
-rwxr-xr-x 1 root root  68M Mar 17 14:29 app

使用top命令来检查情况。

top - 14:58:43 up 10:17,  0 users,  load average: 0.00, 0.00, 0.22
Tasks:   3 total,   1 running,   2 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.4 us,  0.2 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   7763.1 total,   4489.9 free,   1395.1 used,   1878.1 buff/cache
MiB Swap:   2048.0 total,   2046.5 free,      1.5 used.   6110.2 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
    1 root      20   0 1478396 108644  48388 S   0.0   1.4   0:00.21 app
   35 root      20   0    4624   3748   3236 S   0.0   0.0   0:00.02 bash
   43 root      20   0    7312   3424   2860 R   0.0   0.0   0:00.02 top
虽然以 Spring Boot 的形式运行,但可以确认 java 命令未启动,也就是不需要 JVM。

我尝试显示容器的信息。

# cat /etc/*-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.2 LTS"
PRETTY_NAME="Ubuntu 22.04.2 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.2 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
这个容器是基于Ubuntu创建的。换句话说,您可以像使用Ubuntu一样处理它。

总结

在 WSL Ubuntu 的 Docker 环境中,可以启动通过 Dockerfile 构建的 GraalVM 原生映像 Spring Boot Web 应用的自定义容器。

理解 Dockerfile 是很重要的。尽管有自动构建工具存在,但有时也需要手动编写。使用 Ubuntu 还能了解 Linux 知识。虽然一开始可能感觉难,但随着逐步掌握,你会逐渐有自信地编写。

请问使用 WSL Ubuntu,在 Docker 环境中作为容器轻松启动 GraalVM 原生映像的 Spring Boot Web 应用程序的体验如何?请务必尝试一下。我们也会继续介绍 Java 的开发环境等相关内容,请敬请期待。

推荐内容

 

相关文章

让我们将其与常规的JAR文件格式构建进行比较吧!