雲原生時代高性能Java框架—Quarkus(二)


——— *構建Quarkus本地鏡像、容器化部署Quarkus項目*


Quarkus系列博文


概覽

上一篇文章主要介紹了Quarkus以及給Quarkus提供“神力”的Java虛擬機GraalVM,並演示了如何安裝GraalVM以及Quarkus的初步用法。本文將主要指向Quarkus的“亮點”——本地化應用程序。

以下是本文的兩個目標:

  • 將Quarkus開發的Java應用程序編譯成本地可執行文件。
  • 將本地可執行文件打包到容器中。

注:在本文中本地可執行文件又稱本地鏡像,二者意思相同。

環境准備

以下為本文所演示時的環境配置

  • Intellij IDEA
  • Maven
  • GraalVM 20.1.0
  • Docker

接下來需要安裝GraalVM的一個擴展——“native-image“,此擴展用於將Java程序編譯成本地可執行文件,我們執行以下命令:

gu install native-image

運行以下命令,查看擴展是否已安裝:

$ native-image --version

img

生成本地可執行文件

生成本地可執行文件的步驟如下圖:

img

IDEA打開上一篇文章創建的項目,並打開控制台,執行maven命令:

./mvnw package -Pnative

控制台輸出以下內容:

[INFO] Scanning for projects...
...
[INFO] Building untitled 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
... 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.ExampleResourceTest
2020-07-19 22:24:08,962 INFO  [io.quarkus] (main) Quarkus 1.6.0.Final on JVM started in 1.085s. Listening on: http://0.0.0.0:8081
...
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
...
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Running Quarkus native-image plugin on GraalVM Version 20.1.0 (Java Version 11.0.7)
...
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 93802ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:45 min
[INFO] Finished at: 2020-07-19T22:25:44+08:00
[INFO] ------------------------------------------------------------------------

打開項目中的target文件夾

img

可以看到其中有個重要的文件:XXX-runner,它是一個對JVM不依賴的本地可執行文件,我們可以運行他來啟動應用程序。

$ ./target/untitled-1.0-SNAPSHOT-runner

img

成功啟動應用程序,並且啟動速度非常快🚀!

對比

在這里我們可以對比本地可執行文件與傳統基於jvm啟動速度的對比

運行如下命令,生成傳統應用程序的jar文件:

./mvnw package

分別運行本地可執行文件和jar文件:

img

速度差異非常的懸殊!

相關配置

打開項目根目錄的pom.xml,可以看到如下配置:

<profiles>
    <profile>
        <id>native</id>
        <properties>
            <quarkus.package.type>native</quarkus.package.type>
        </properties>
    </profile>
</profiles>

我們可以在id為native的profile中配置具體的配置項參數來自定義本地鏡像(本地可執行文件)的生成。

如下為quarkus提供的具體配置列表:

Quarkus提供了許多生成本地鏡像(native-image即本地可執行文件)的配置項,點擊查看(可左右滑動)。


容器化本地可執行文件

我們可以很輕松的將Java應用程序的jar包進行容器化,當然我們也可以很輕松的將上一步生成的本地可執行文件進行容器化。

容器化本地可執行文件的步驟如下:

img容器化本地可執行文件

添加配置

我們要將生成的本地可執行文件進行容器化,所以需要考慮到本地可執行文件對環境的兼容問題,在這里所生成的本地可執行文件的格式應該和docker鏡像中的環境兼容了,而不是我們的本機環境(MacOS,Linux,Windows等等)。因為不同的操作系統支持的本地可執行文件的格式並不一樣,quarkus在生成本地可執行文件的時候會根據不同的操作系統環境而選擇不同的可執行文件格式。

首先我們在項目的src/main/resources/application.properties文件中添加配置:

quarkus.native.container-runtime=docker

上面配置表明在容器化本地可執行文件時將基於docker環境,我們也可以基於其他的容器環境,比如podman。

執行以下命令生成兼容docker容器環境的本地可執行文件:

./mvnw package -Pnative -Dquarkus.native.container-build=true

執行以下命令,將本地可執行文件打包成docker鏡像:

docker build -f src/main/docker/Dockerfile.native -t quarkus-quickstart/getting-started .

生成完畢,運行以下命令即可啟動該容器:

docker run -i --rm -p 8080:8080 quarkus-quickstart/getting-started

可以看到通過容器方式啟動應用程序速度也很快

img

我們可以看一下這背后的Dockerfile,打開src/main/docker/Dockerfile.native

FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1
WORKDIR /work/
RUN chown 1001 /work \
    && chmod "g+rwX" /work \
    && chown 1001:root /work
COPY --chown=1001:root target/*-runner /work/application

EXPOSE 8080
USER 1001

CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

Quarkus使用ubi-minimal鏡像作為容器的基礎鏡像,它是一個通用基本鏡像,Dockerfiles使用基本鏡像的最小版本來減小生成的鏡像的大小。

無GraalVM環境下的鏡像生成

當我們處理一個CI/CD的環境或其他本地無GraalVM的環境時,此時就不能在本地生成本地可執行文件了。我們可以通過在docker中處理這些操作,在項目的src/main/docker中添加文件Dockerfile.multistage,並在文件中添加下面內容:

## Stage 1 : build with maven builder image with native capabilities
FROM quay.io/quarkus/centos-quarkus-maven:20.1.0-java11 AS build
COPY pom.xml /usr/src/app/
RUN mvn -f /usr/src/app/pom.xml -B de.qaware.maven:go-offline-maven-plugin:1.2.5:resolve-dependencies
COPY src /usr/src/app/src
USER root
RUN chown -R quarkus /usr/src/app
USER quarkus
RUN mvn -f /usr/src/app/pom.xml -Pnative clean package

## Stage 2 : create the docker final image
FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /work/
COPY --from=build /usr/src/app/target/*-runner /work/application

# set up permissions for user `1001`
RUN chmod 775 /work /work/application \
  && chown -R 1001 /work \
  && chmod -R "g+rwX" /work \
  && chown -R 1001:root /work

EXPOSE 8080
USER 1001

CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

這是一個多階段的鏡像打包過程,第一階段我們在docekr中構建本地可執行文件,第二階段再將本地可執行文件打包成鏡像。

運行如下命令:

docker build -f src/main/docker/Dockerfile.multistage -t quarkus-quickstart/getting-started .

如上操作將兩個階段的操作整合在一起,並生成了最終的鏡像。

運行如下命令啟動容器:

docker run -i --rm -p 8080:8080 quarkus-quickstart/getting-started

測試本地可執行文件

打開項目中的測試文件夾,可以看到有如下兩個件

img

其中ExampleResourceTest類為普通的Java測試類,他的運行基於JVM。

@QuarkusTest
public class ExampleResourceTest {

    @Test
    public void testHelloEndpoint() {
        given()
            .when().get("/hello")
            .then()
            .statusCode(200)
            .body(is("hello"));
    }

}

上述測試類使用了@QuarkusTest注解,這個注解類似於Spring Boot中的@SpringBootTest,用來在測試前啟動上下文。

NativeExampleResourceIT則不同,該測試類的代碼如下:

package com.example;

import io.quarkus.test.junit.NativeImageTest;

@NativeImageTest 1️⃣
public class NativeExampleResourceIT extends ExampleResourceTest 2️⃣{

    // Execute the same tests but in native mode.
}

1️⃣:@NativeImageTest 注解表示此測試類是一個基於本地鏡像的測試類,在測試之前,從本地可執行文件啟動應用程序。可執行文件位置可在Maven的pom.xml中配置(maven-failsafe-pluginnative.image.path屬性)。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>${surefire-plugin.version}</version>
    <executions>
        <execution>
            <goals>
                <goal>integration-test</goal>
                <goal>verify</goal>
            </goals>
            <configuration>
                <systemPropertyVariables>
                    <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
                    <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                    <maven.home>${maven.home}</maven.home>
                </systemPropertyVariables>
            </configuration>
        </execution>
    </executions>
</plugin>

2️⃣:這里的代碼表示我們擴展了之前的測試,但是您也可以自定義實現您自己的測試。

運行本地鏡像測試和普通測試的方式有差異,本地鏡像測試需要使用Maven命令來啟動,我們在IDEA控制台中運行./mvnw verify -Pnative即可啟動本地鏡像測試。注意:由於我們上一步中在項目的配置文件中添加了quarkus.native.container-runtime=docker,現在我們需要去掉,否則生成的可執行文件格式可能和你本機的格式不兼容。

[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------< com.example:untitled >------------------------
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.NativeExampleResourceIT
Executing [/Users/chengang/IdeaProjects/quarkus-demo/target/untitled-1.0-SNAPSHOT-runner, -Dquarkus.http.port=8081, -Dquarkus.http.ssl-port=8444, -Dtest.url=http://localhost:8081, -Dquarkus.log.file.path=target/target/quarkus.log]
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2020-07-23 22:21:09,626 INFO  [io.quarkus] (main) untitled 1.0-SNAPSHOT native (powered by Quarkus 1.6.0.Final) started in 0.019s. Listening on: http://0.0.0.0:8081
2020-07-23 22:21:09,626 INFO  [io.quarkus] (main) Profile prod activated. 
2020-07-23 22:21:09,626 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.3 s - in com.example.NativeExampleResourceIT
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] 
[INFO] --- maven-failsafe-plugin:2.22.1:verify (default) @ untitled ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  02:17 min
[INFO] Finished at: 2020-07-23T22:21:11+08:00
[INFO] ------------------------------------------------------------------------
chengang@chengangdeMacBook-Pro quarkus-demo % 

測試通過!

總結

本文主要介紹了Quarkus框架的本地化相關操作,我們具體介紹了如何將Quarkus項目編譯成本地可執行文件,隨后又演示了如何將生成的可執行文件打包成Docker鏡像,最后我們演示了如何以本地可執行文件的形式測試業務代碼。隨着將Java應用程序編譯成本地鏡像,Java的性能優勢有了極大的提升。

本文參考:https://quarkus.io/guides/building-native-image


🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟

歡迎訪問筆者博客:blog.dongxishaonian.tech

關注筆者公眾號,推送各類原創/優質技術文章 ⬇️

WechatIMG6


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM