当前位置: 首页 > 工具软件 > curl-to-java > 使用案例 >

使用 run-java.sh 运行 Java 应用

郜玉石
2023-12-01

当需要运行Java应用时,不管在机器上还是容器里,一般的做法是运行 java 命令。

一些 Java 应用会被打包成单个可执行的 JAR 文件,如 Spring Boot 应用。对于这样的 JAR 文件,只需要使用 java -jar /opt/app.jar 即可。

另外一些应用会打包成多个 JAR 文件的形式。对于这样的目录结构,只需要用 java -cp lib/* com.example.Main 指定类路径(classpath)和入口 Java 类即可。

在构建容器镜像时,一般使用 Dockerfile 的 ENTRYPOINT 来指定运行的 java 命令。

ENTRYPOINT [ "java", "-jar", "/opt/app.jar" ]

直接使用 java 命令的做法,在容器运行时并不灵活。如果想使用 -D 参数来添加新的系统属性,就必须要修改 Dockerfile 中的ENTRYPOINT 定义。这就意味要创建新的镜像,使用起来非常的不方便。

更好的做法是使用本文中介绍的 run-java.sh。顾名思义,run-java.sh 是一个 shell 脚本。它的作用是运行Java程序。该脚本可以从 GitHub (fabric8io-images/run-java-sh) 或 Maven 仓库(io.fabric8:run-java-sh:1.3.8)下载。

基本用法

该脚本的特点是可以使用环境变量来配置调用 java 命令时的参数。这些环境变量大致可以分成如下几类:

第一类与Java应用相关。

  • JAVA_APP_DIR 设置应用文件的根目录,默认为 run-java.sh 所在的目录。

  • JAVA_APP_NAME 设置应用进程的名称。

  • JAVA_MAJOR_VERSION 设置 JVM 的版本号。默认从 java -version 命令的输出中解析。

第二类与 Java 应用的 classpath 相关。

  • JAVA_CLASSPATH 设置完整的 classpath。

  • JAVA_LIB_DIR 设置作为库的 JAR 文件的目录。默认为 JAVA_APP_DIR 的值。

  • JAVA_APP_JAR 设置可执行的 JAR 文件的路径。

  • JAVA_MAIN_CLASS 设置入口类的名称。

第三类与Java运行的参数相关。

  • JAVA_OPTIONS 设置额外的参数。

第四类与运行的内存和 CPU 相关。

  • JAVA_INIT_MEM_RATIO 设置起始的 heap 尺寸占全部内存的比例。转换成参数 -Xms。值 20 表示 20%

  • JAVA_MAX_MEM_RATIO 设置最大的 heap 尺寸占全部内存的比例。转换成参数 -Xmx。值 50 表示 50%

  • JAVA_MAX_CORE 设置最大的 CPU 核数。

第五类与调试相关。

  • JAVA_DEBUG 启用远程调试。

  • JAVA_DEBUG_SUSPEND 以暂停模式启用远程调试。

  • JAVA_DEBUG_PORT 设置远程调试的端口。默认为 5005

第六类与 HTTP 代理相关。

  • HTTP_PROXY 设置 HTTP 代理。转换成 Java 的系统属性 http.proxyHost 和 http.proxyPort

  • HTTPS_PROXY 设置 HTTPS 代理。转换成 Java 的系统属性 https.proxyHost 和 https.proxyPort

  • NO_PROXY 设置不使用代理的主机名。转换成 Java 的系统属性 http.nonProxyHosts

最后一类与应用诊断相关。

  • JAVA_DIAGNOSTICS 启用输出额外的诊断信息。

需要注意的是,JAVA_INIT_MEM_RATIO 和 JAVA_MAX_MEM_RATIO 要求设置容器的内存约束,也就是 Kubernetes 容器声明的 resources.limits.memory 的值。否则的话,这两个设置都无效。内存的约束值作为计算时的基础。

具体使用

下面我们看一下如何具体使用 run-java.sh

实际上,Quarkus 应用在以 JVM 模式运行时,默认的容器镜像使用的就是 run-java.sh。下面是 Dockerfile 的内容。

在构建镜像时,从 Maven 仓库下载 run-java.sh 脚本,并保存为 /deployments/run-java.sh 文件。ENTRYPOINT 指令直接运行 run-java.sh 脚本。额外的参数以环境变量 JAVA_OPTIONS 来传递。

FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4 

ARG JAVA_PACKAGE=java-11-openjdk-headless
ARG RUN_JAVA_VERSION=1.3.8
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
# Install java and the run-java script
# Also set up permissions for user `1001`
RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \
    && microdnf update \
    && microdnf clean all \
    && mkdir /deployments \
    && chown 1001 /deployments \
    && chmod "g+rwX" /deployments \
    && chown 1001:root /deployments \
    && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \
    && chown 1001 /deployments/run-java.sh \
    && chmod 540 /deployments/run-java.sh \
    && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/conf/security/java.security

# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
# We make four distinct layers so if there are application changes the library layers can be re-used
COPY --chown=1001 target/quarkus-app/lib/ /deployments/lib/
COPY --chown=1001 target/quarkus-app/*.jar /deployments/
COPY --chown=1001 target/quarkus-app/app/ /deployments/app/
COPY --chown=1001 target/quarkus-app/quarkus/ /deployments/quarkus/

EXPOSE 8080
USER 1001

ENTRYPOINT [ "/deployments/run-java.sh" ]

需要注意的是,这里并没有使用 JAVA_APP_JAR 或 JAVA_MAIN_CLASS 来指定应用的入口 JAR 文件或 Java 类。在这种情况下,run-java.sh 会查找应用根目录下的唯一 JAR 文件,作为 JAVA_APP_JAR 的值。这个时候,应用根目录下必须有且仅有一个可执行的 JAR 文件。否则,脚本会出错。

下面介绍一个 run-java.sh 脚本的使用场景。当 Java 应用在 Kubernetes 上运行时,发现了一个只在 Kubernetes 上可以重现的 bug。这个时候需要启用 Java 的远程调试功能来进行调试。此时,可以修改应用的 Deployment,添加一个名为 JAVA_DEBUG,值为 true 的环境变量。当新的 Pod 启动之后,应用已经启用了远程调试功能。用 kubectl port-forward 访问 Pod 的 5005 端口,就可以在 IDE 中进行调试。在我B站的视频中,有该场景的具体演示。

如果需要下载 run-java.sh,可以关注我的公众号,发送消息 “run-java” 即可,会给出网盘下载地址。

 类似资料: