动态给容器指定 Java 启动参数

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: 在做 Java 程序容器化时都会遇到一个问题,ENTRYPOINT ["java", "$JAVA_OPTS", "-jar", ...] 这样的写法 $JAVA_OPTS 就是个字符串无法在运行时展开。为了不把参数硬编码到容器里,每次调整参数重新构建镜像,可以有多种方案。

在做 Java 程序容器化时都会遇到一个问题,ENTRYPOINT ["java", "$JAVA_OPTS", "-jar", ...] 这样的写法 $JAVA_OPTS 就是个字符串无法在运行时展开。为了不把参数硬编码到容器里,每次调整参数重新构建镜像,可以有多种方案,先介绍几种不够好的方案。

  1. ENTRYPOINT java $JAVA_OPTS -jar ...,这种方式的问题是 java 不是容器主进程(至于为什么要保证 java 是主进程,又是一个话题,是容器化基本最佳实践之一);
  2. ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar ..."],这种写法其实等价于上面一种方式,上面一种方式在运行时就是以 /bin/sh -c "java $JAVA_OPTS -jar ..." 方式运行的,所以缺陷也是相同的;
  3. ENTRYPOINT ["entrypoint.sh"] 然后在脚本中启动 java,使用脚本对于需要在启动时做复杂操作的容器比较有用,但是对启动 java 来说未免小题大作,并且同样有 java 不是容器主进程的问题。

从 shell 角度出发,解决非主进程问题的方案是使用 exec 命令,exec 在启动其后参数中的指令时,不会创建子进程而是用指令进程替换自身,使指令进程占用自身的 PID(exec 其后的第一个指令替换了自身之后,后续的其它指令自然也不会被执行了)。于是上面

这三个方案可以改为:

  1. ENTRYPOINT exce java $JAVA_OPTS -jar ...
  2. ENTRYPOINT ["sh", "-c", "exce java $JAVA_OPTS -jar ..."]
  3. 脚本里写 exec java $JAVA_OPTS -jar ...

使用 exec 可以解决之前的问题,但是随之而来的问题是……丑,任何额外的命令都会破坏整洁,对于追求 clean code 的程序员来说 Dockerfile 也必须是整洁的。还好 java 是一个成熟的生态,其实本身提供了相应的环境变量 JDK_JAVA_OPTIONSJAVA_TOOL_OPTIONS

JDK_JAVA_OPTIONS 是在 Java 9 引入的,java 程序启动时不需要在命令行指定就会自动读取的环境变量,它略微有些限制,主要是为了防止滥用不允许使用可能改变主类或者让主类不执行的参数,通常需要指定的内存、GC 等参数都可以使用。遇到不允许使用的参数时 java 会直接报错并退出,所以只要程序顺利启动就不用担心使用了不允许使用的参数。在这里指定的参数无法覆盖命令行的相同参数,需要锁定的配置可以直接指定在 ENTRYPOINT中。

JAVA_TOOL_OPTIONS 是存在很久的环境变量,这个环境变量同样不需要在命令行显式指定。它名字中的 TOOL 提示了除了 java 命令之外其它 java 工具命令例如 javac 之类的也会去读取这个变量的值。在这里指定的参数既不能覆盖命令行的相同参数,也不能覆盖 JDK_JAVA_OPTIONS 中的相同参数,优先级最低。

除此之外,还有各家专用的一些环境变量,比如 Oracle 家的 _JAVA_OPTIONS、IBM 家的 IBM_JAVA_OPTIONS,它们通常提供了覆盖命令行上相同参数的能力,但是环境变量名却不可移植,在 Xxx as Code 的时代并不是个好选择。

综上所述,可以得出这样的决策路径:

  1. Java 9 及以上的 java 命令使用 JDK_JAVA_OPTIONS
  2. CI/CD 或者打包工具之类的非 java 命令时使用 JAVA_TOOL_OPTIONS
  3. Java 9 以下的 java 命令使用 JAVA_TOOL_OPTIONS

极特殊情况下需要覆盖命令行上的参数时,先反思自己,再反思自己,最后找各家自己定义的环境变量。
最后一个问题,看到这里可能会有疑问,设置环境变量会不会影响到其它 java 进程?如果遵循了容器化的最佳实践,那答案显然是不会,而且即使在主机上,要想多个进程间环境变量互不影响也是很简单的事情不是吗?

目录
相关文章
|
3月前
|
Kubernetes Cloud Native Java
云原生之旅:从容器到微服务的演进之路Java 内存管理:垃圾收集器与性能调优
【8月更文挑战第30天】在数字化时代的浪潮中,企业如何乘风破浪?云原生技术提供了一个强有力的桨。本文将带你从容器技术的基石出发,探索微服务架构的奥秘,最终实现在云端自由翱翔的梦想。我们将一起见证代码如何转化为业务的翅膀,让你的应用在云海中高飞。
|
3月前
|
Java Linux Maven
java依赖冲突解决问题之容器加载依赖jar包如何解决
java依赖冲突解决问题之容器加载依赖jar包如何解决
|
26天前
|
存储 算法 Java
java制作海报六:Graphics2D的RenderingHints方法参数详解,包括解决文字不清晰,抗锯齿问题
这篇文章是关于如何在Java中使用Graphics2D的RenderingHints方法来提高海报制作的图像质量和文字清晰度,包括抗锯齿和解决文字不清晰问题的技术详解。
30 0
java制作海报六:Graphics2D的RenderingHints方法参数详解,包括解决文字不清晰,抗锯齿问题
|
1月前
|
消息中间件 NoSQL Kafka
Flink-10 Flink Java 3分钟上手 Docker容器化部署 JobManager TaskManager Kafka Redis Dockerfile docker-compose
Flink-10 Flink Java 3分钟上手 Docker容器化部署 JobManager TaskManager Kafka Redis Dockerfile docker-compose
33 4
|
1月前
|
Java
java构造方法时对象初始化,实例化,参数赋值
java构造方法时对象初始化,实例化,参数赋值
43 1
|
1月前
|
Kubernetes Cloud Native 流计算
Flink-12 Flink Java 3分钟上手 Kubernetes云原生下的Flink集群 Rancher Stateful Set yaml详细 扩容缩容部署 Docker容器编排
Flink-12 Flink Java 3分钟上手 Kubernetes云原生下的Flink集群 Rancher Stateful Set yaml详细 扩容缩容部署 Docker容器编排
68 0
|
3月前
|
Java
【Azure 应用服务】如何查看App Service Java堆栈JVM相关的参数默认配置值?
【Azure 应用服务】如何查看App Service Java堆栈JVM相关的参数默认配置值?
【Azure 应用服务】如何查看App Service Java堆栈JVM相关的参数默认配置值?
|
3月前
|
安全 算法 Java
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
这篇文章讨论了Java集合类的线程安全性,列举了线程不安全的集合类(如HashSet、ArrayList、HashMap)和线程安全的集合类(如Vector、Hashtable),同时介绍了Java 5之后提供的java.util.concurrent包中的高效并发集合类,如ConcurrentHashMap和CopyOnWriteArrayList。
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
|
3月前
|
Java 容器
【Java集合类面试一】、 Java中有哪些容器(集合类)?
这篇文章列出了Java中的四大类集合接口:Set、List、Queue和Map,以及它们的常用实现类,如HashSet、TreeSet、ArrayList、LinkedList、ArrayDeque、HashMap和TreeMap。
【Java集合类面试一】、 Java中有哪些容器(集合类)?
|
3月前
|
消息中间件 Java 大数据
"深入理解Kafka单线程Consumer:核心参数配置、Java实现与实战指南"
【8月更文挑战第10天】在大数据领域,Apache Kafka以高吞吐和可扩展性成为主流数据流处理平台。Kafka的单线程Consumer因其实现简单且易于管理而在多种场景中受到欢迎。本文解析单线程Consumer的工作机制,强调其在错误处理和状态管理方面的优势,并通过详细参数说明及示例代码展示如何有效地使用KafkaConsumer类。了解这些内容将帮助开发者优化实时数据处理系统的性能与可靠性。
86 7

热门文章

最新文章