Blog chevron_right 未分类

配置 Spring Boot 以使用 Azul Zulu 和调试选项构建 Docker 映像

借助 Spring Boot Maven 插件,您可以从应用程序中轻松创建 Docker 映像。本文将介绍配置 Spring Boot 的更多技巧和示例,以定义此类 Spring Boot Docker 映像中使用的 Java 运行时,并解释如何添加其他环境选项来简化调试。


本文的撰写得到 Spring 开发技术推广工程师 DaShaun Carter 的悉心帮助,他在我提出有关这些配置选项的问题时立即给予解答。在此向 DaShaun 表示感谢!


Spring Boot Petclinic 项目

为了说明我们的方法,文中将运用 Spring Boot Petclinic 项目(一款利用 Maven 或 Gradle 构建的 Spring Boot 应用程序)来演示如何全面设置 Spring Boot 项目,以及如何将代码结构化并执行必要的封装。

您可从 GitHub 获取完整项目,包括针对各种用例的丰富配置:

$ git clone https://github.com/spring-projects/spring-petclinic
$ cd spring-petclinic

修改和扩展默认设置

我们只需在项目的 pom.xml 文件中的 spring-boot-maven-plugin 部分稍稍进行额外配置,即可修改所生成 Docker 映像中的 OpenJDK 发行版。请勿更改执行过程,仅插入配置部分即可。此 Docker 映像的目标是纳入所有可能使用的调试和测试工具,因此包含以下所有选项。当然,使用哪些工具取决于您的用例。

    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <image>
                <buildpacks>
                    <buildpack>paketobuildpacks/azul-zulu</buildpack>
                    <buildpack>paketobuildpacks/java</buildpack>
                </buildpacks>
                
                <env>
                    <BP_JVM_VERSION>17</BP_JVM_VERSION>
                    <BP_JVM_TYPE>JDK</BP_JVM_TYPE>

                    <BPE_DELIM_JAVA_TOOL_OPTIONS xml:space="preserve"> </BPE_DELIM_JAVA_TOOL_OPTIONS>
                    <BPE_APPEND_JAVA_TOOL_OPTIONS>-Xlog:gc:/tmp/gc.log</BPE_APPEND_JAVA_TOOL_OPTIONS>

                    <BPE_DEFAULT_BPL_DEBUG_ENABLED>true</BPL_DEBUG_ENABLED>
                    <BPE_DEFAULT_BPL_DEBUG_PORT>8000</BPL_DEBUG_PORT> <!-- This is the default value -->
                    <BPE_DEFAULT_BPL_JMX_ENABLED>true</BPL_JMX_ENABLED>
                    <BPE_DEFAULT_BPL_JMX_PORT>5000</BPL_JMX_PORT> <!-- This is the default value -->
                    <BPE_DEFAULT_BPL_JAVA_NMT_ENABLED>true</BPL_JAVA_NMT_ENABLED> <!-- This is the default value -->
                    <BPE_DEFAULT_BPL_JFR_ENABLED>true</BPL_JFR_ENABLED> 
                    <BPE_DEFAULT_BPL_JFR_ARGS>dumponexit=true,filename=/tmp/rec.jfr,duration=600s</BPL_JFR_ARGS>
                </env>
            </image>
        </configuration>
        <executions>
            ...
        </executions>
    </plugin>

为了更好地理解这一部分,我们分别来看看所有不同选项:

BPL_:应用程序映像的运行时特性,在应用程序容器中被设置为环境变量。必须为这些特性附加 BPE_DEFAULT_,才可将其通过“bake”命令构建至容器映像以供自动使用

构建 Docker 映像

此处的构建 Docker 映像操作采用单行命令,输出将显示对 Azul JDK 的一些引用以及我们在不同测试周期之后的设置:

$ ./mvnw spring-boot:build-image

[INFO] Scanning for projects...
[INFO] 
[INFO] ------------< org.springframework.samples:spring-petclinic >------------
[INFO] Building petclinic 3.1.0-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 

...

[INFO] --- spring-boot:3.1.1:build-image (default-cli) @ spring-petclinic ---
[INFO] Building image 'docker.io/library/spring-petclinic:3.1.0-SNAPSHOT'
[INFO] 
[INFO]  > Pulling builder image 'docker.io/paketobuildpacks/builder:base' 100%

...

[INFO]  > Pulling buildpack image 'docker.io/paketobuildpacks/azul-zulu:latest' 100%
[INFO]  > Pulled buildpack image 'paketobuildpacks/azul-zulu@sha256:79419af00c95f85c088e68808f61b2486c39a7e12a0033995970c97e95408069'

...

[INFO]  > Running creator
[INFO]     [creator]     ===> ANALYZING
[INFO]     [creator]     Image with name "docker.io/library/spring-petclinic:3.1.0-SNAPSHOT" not found
[INFO]     [creator]     ===> DETECTING
[INFO]     [creator]     8 of 27 buildpacks participating
[INFO]     [creator]     paketo-buildpacks/azul-zulu             10.1.5

...

[INFO]     [creator]     ===> BUILDING
[INFO]     [creator]     
[INFO]     [creator]     Paketo Buildpack for Azul Zulu 10.1.5
[INFO]     [creator]       https://github.com/paketo-buildpacks/azul-zulu
[INFO]     [creator]       Build Configuration:
[INFO]     [creator]         $BP_JVM_JLINK_ARGS           --no-man-pages --no-header-files --strip-debug --compress=1  configure custom link arguments (--output must be omitted)
[INFO]     [creator]         $BP_JVM_JLINK_ENABLED        false                                                        enables running jlink tool to generate custom JRE
[INFO]     [creator]         $BP_JVM_TYPE                 JDK                                                          the JVM type - JDK or JRE
[INFO]     [creator]         $BP_JVM_VERSION              17                                                           the Java version
[INFO]     [creator]       Launch Configuration:
[INFO]     [creator]         $BPL_DEBUG_ENABLED           true                                                         enables Java remote debugging support
[INFO]     [creator]         $BPL_DEBUG_PORT              8000                                                         configure the remote debugging port
[INFO]     [creator]         $BPL_DEBUG_SUSPEND           false                                                        configure whether to suspend execution until a debugger has attached
[INFO]     [creator]         $BPL_HEAP_DUMP_PATH                                                                       write heap dumps on error to this path
[INFO]     [creator]         $BPL_JAVA_NMT_ENABLED        true                                                         enables Java Native Memory Tracking (NMT)
[INFO]     [creator]         $BPL_JAVA_NMT_LEVEL          summary                                                      configure level of NMT, summary or detail
[INFO]     [creator]         $BPL_JFR_ARGS                dumponexit=true,filename=/tmp/rec.jfr,duration=600s          configure custom Java Flight Recording (JFR) arguments
[INFO]     [creator]         $BPL_JFR_ENABLED             true                                                         enables Java Flight Recording (JFR)
[INFO]     [creator]         $BPL_JMX_ENABLED             true                                                         enables Java Management Extensions (JMX)
[INFO]     [creator]         $BPL_JMX_PORT                5000                                                         configure the JMX port
[INFO]     [creator]         $BPL_JVM_HEAD_ROOM           0                                                            the headroom in memory calculation
[INFO]     [creator]         $BPL_JVM_LOADED_CLASS_COUNT  35% of classes                                               the number of loaded classes in memory calculation
[INFO]     [creator]         $BPL_JVM_THREAD_COUNT        250                                                          the number of threads in memory calculation
[INFO]     [creator]         $JAVA_TOOL_OPTIONS                                                                        the JVM launch flags
[INFO]     [creator]         Using Java version 17 from BP_JVM_VERSION
[INFO]     [creator]       A JDK was specifically requested by the user, however a JRE is available. Using a JDK at runtime has security implications.
[INFO]     [creator]       Azul Zulu JDK 17.0.7: Contributing to layer

...

[INFO] Successfully built image 'docker.io/library/spring-petclinic:3.1.0-SNAPSHOT'
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:29 min
[INFO] Finished at: 2023-07-03T11:26:39+02:00
[INFO] ------------------------------------------------------------------------

检查创建的 Docker 映像

在构建过程中下载并创建了各种映像,如您所见,数量多于预期,但其中一些映像在单元测试期间使用。

$ docker images

REPOSITORY                   TAG              IMAGE ID       CREATED        SIZE
paketobuildpacks/run         base-cnb         f2e5000af0cb   3 days ago     87.1MB
postgres                     15.3             1921dda0e2c5   2 weeks ago    412MB
mysql                        5.7              2be84dd575ee   2 weeks ago    569MB
testcontainers/ryuk          0.5.1            ec913eeff75a   6 weeks ago    12.7MB
paketobuildpacks/builder     base             99ec7fb86b9d   43 years ago   1.34GB
paketobuildpacks/azul-zulu   latest           276db25e20db   43 years ago   10.4MB
paketobuildpacks/java        latest           2ddc6cc7d346   43 years ago   207MB
spring-petclinic             3.1.0-SNAPSHOT   c05d70c78109   43 years ago   496MB

运行 Docker 映像

现在可以启动 Docker 映像。针对不同用例配置了三个端口:

  • 8080:用于访问网站
  • 8000:调试端口
  • 5000:JMX 端口

通过终端启动 Docker 映像。

$ docker run -p 8080:8080 -p 8000:8000 -p 5000:5000 --name petclinic spring-petclinic:3.1.0-SNAPSHOT

Setting Active Processor Count to 16
Debugging enabled on port *:8000
Enabling Java Flight Recorder with args: dumponexit=true,filename=/tmp/rec.jfr,duration=600s
JMX enabled on port 5000
...
[0.349s][info][jfr,startup] Started recording 1. The result will be written to:
[0.349s][info][jfr,startup] 
[0.349s][info][jfr,startup] /tmp/rec.jfr


              |\      _,,,--,,_
             /,`.-'`'   ._  \-;;,_
  _______ __|,4-  ) )_   .;.(__`'-'__     ___ __    _ ___ _______
 |       | '---''(_/._)-'(_\_)   |   |   |   |  |  | |   |       |
 |    _  |    ___|_     _|       |   |   |   |   |_| |   |       | __ _ _
 |   |_| |   |___  |   | |       |   |   |   |       |   |       | \ \ \ \
 |    ___|    ___| |   | |      _|   |___|   |  _    |   |      _|  \ \ \ \
 |   |   |   |___  |   | |     |_|       |   | | |   |   |     |_    ) ) ) )
 |___|   |_______| |___| |_______|_______|___|_|  |__|___|_______|  / / / /
 ==================================================================/_/_/_/

:: Built with Spring Boot :: 3.1.1


2023-07-06T10:55:23.329Z  INFO 1 --- [           main] o.s.s.petclinic.PetClinicApplication     : Starting PetClinicApplication v3.1.0-SNAPSHOT using Java 17.0.7 with PID 1 (/workspace/BOOT-INF/classes started by cnb in /workspace)
...

验证 Docker 映像

此时可以执行不同步骤来验证我们使用附加设置创建的 Docker 映像:

  • 打开浏览器并将其指向 localhost:8080。Petclinic Web 界面可用于浏览宠物主人和兽医。
  • 打开第二个终端并在运行的 Docker 内部执行以下检查:
$ docker exec -it petclinic sh

# Check the Java version
$ /layers/paketo-buildpacks_azul-zulu/jdk/bin/java -version
openjdk version "17.0.7" 2023-04-18 LTS
OpenJDK Runtime Environment Zulu17.42+19-CA (build 17.0.7+7-LTS)
OpenJDK 64-Bit Server VM Zulu17.42+19-CA (build 17.0.7+7-LTS, mixed mode, sharing)

# Check if we can execute JCMD which is not included in the default build with a JRE
$ /layers/paketo-buildpacks_azul-zulu/jdk/bin/jcmd
1 org.springframework.boot.loader.JarLauncher
181 jdk.jcmd/sun.tools.jcmd.JCmd

# Check the generated files we can use later for further investigation
# The application must be running for a longer time, before data is saved in rec.jfr
$ ls -l /tmp
total 20
drwxr-xr-x 2 cnb cnb 4096 Jul 10 06:44 2023_07_10_06_44_57_1
-rw-r--r-- 1 cnb cnb 1819 Jul 10 06:45 gc.log
drwxr-xr-x 2 cnb cnb 4096 Jul 10 06:44 hsperfdata_cnb
-rw-r--r-- 1 cnb cnb    0 Jul 10 06:44 rec.jfr
drwx------ 2 cnb cnb 4096 Jul 10 06:44 tomcat-docbase.8080.16382390067193381204
drwx------ 3 cnb cnb 4096 Jul 10 06:44 tomcat.8080.7527925084169386730

# Check the Java info, using Process ID 1 we found with jcmd
$ /layers/paketo-buildpacks_azul-zulu/jdk/bin/jinfo 1

Java System Properties:
#Mon Jul 10 06:46:46 UTC 2023
com.sun.management.jmxremote.rmi.port=5000
java.specification.version=17
sun.jnu.encoding=ANSI_X3.4-1968
com.sun.management.jmxremote.authenticate=false
java.class.path=/workspace
java.vm.vendor=Azul Systems, Inc.
...
java.library.path=/layers/paketo-buildpacks_azul-zulu/jdk/lib\:/usr/java/packages/lib\:/usr/lib64\:/lib64\:/lib\:/usr/lib
...

VM Flags:
-XX:ActiveProcessorCount=16 -XX:CICompilerCount=12 -XX:CompressedClassSpaceSize=117440512 -XX:ConcGCThreads=3 -XX:+ExitOnOutOfMemoryError -XX:+FlightRecorder -XX:G1ConcRefinementThreads=13 -XX:G1EagerReclaimRemSetThreshold=128 -XX:G1HeapRegionSize=16777216 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=536870912 -XX:+ManagementServer -XX:MarkStackSize=4194304 -XX:MaxDirectMemorySize=10485760 -XX:MaxHeapSize=23857201152 -XX:MaxMetaspaceSize=133729280 -XX:MaxNewSize=14310965248 -XX:MinHeapDeltaBytes=16777216 -XX:MinHeapSize=16777216 -XX:NativeMemoryTracking=summary -XX:NonNMethodCodeHeapSize=7602480 -XX:NonProfiledCodeHeapSize=122027880 -XX:+PrintNMTStatistics -XX:ProfiledCodeHeapSize=122027880 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:SoftMaxHeapSize=23857201152 -XX:StartFlightRecording=dumponexit=true,filename=/tmp/rec.jfr,duration=600s -XX:ThreadStackSize=1024 -XX:+UnlockDiagnosticVMOptions -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC 

VM Arguments:
jvm_args: -Djava.security.properties=/layers/paketo-buildpacks_azul-zulu/java-security-properties/java-security.properties -XX:+ExitOnOutOfMemoryError -Xlog:gc:/tmp/gc.log -XX:ActiveProcessorCount=16 -agentlib:jdwp=transport=dt_socket,server=y,address=*:8000,suspend=n -XX:StartFlightRecording=dumponexit=true,filename=/tmp/rec.jfr,duration=600s -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=5000 -Dcom.sun.management.jmxremote.rmi.port=5000 -XX:MaxDirectMemorySize=10M -Xmx23297904K -XX:MaxMetaspaceSize=130595K -XX:ReservedCodeCacheSize=240M -Xss1M -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics -Dorg.springframework.cloud.bindings.boot.enable=true 
java_command: org.springframework.boot.loader.JarLauncher
java_class_path (initial): /workspace
Launcher Type: SUN_STANDARD

利用日志和调试配置

有关借助各种工具和配置来分析和调试 Docker 内部应用程序,本文将不做讨论,不过我们可对所有这些项目进行快速验证,确认其配置是否正确以及如何使用。

从 Docker 中取出文件

您可将 Docker 映像内部的 JFR 记录和垃圾回收器文件复制到您的 PC。

$ docker cp petclinic:/tmp/rec.jfr rec.jfr
$ docker cp petclinic:/tmp/gc.log gc.log

连接与调试

在 IDE(例如 IntelliJ IDEA)中,您可以通过端口 8000 启动与 Docker 内部正在运行的应用程序的调试连接,并在代码中设置断点。

Connected to the target VM, address: 'localhost:8000', transport: 'socket'

连接到 JMX

端口 5000 上配置的 JMX 连接允许您使用 VisualVM 或 Azul Mission Control 连接到 Docker 内部正在运行的应用程序。例如,您可以在给定的持续时间内启动 JFR 记录,并立即将结果可视化。

点击下方三幅图片中的任意一幅可查看相应大图。

结语

本文的扩展 pom.xml 配置包括启用远程调试和生成日志文件的各种方法。切勿同时使用这些方法,并且绝对不可将其用于生产环境。我们的目标只是确定 Spring Boot Docker 中的默认 Java 发行版是否可由其他设置进行替换和扩展。

那么,大功告成!