[每日短篇] 23 - 动态给容器指定 Java 启动参数

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

  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 其后的第一个指令替换了自身以后,后续的其它指令天然也不会被执行了)。因而上面 3 个方案能够改成程序员

  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_OPTIONSshell

  1. JDK_JAVA_OPTIONS 是在 Java 9 引入的,java 程序启动时不须要在命令行指定就会自动读取的环境变量,它略微有些限制,主要是为了防止滥用不容许使用可能改变主类或者让主类不执行的参数,一般须要指定的内存、GC 等参数均可以使用。遇到不容许使用的参数时 java 会直接报错并退出,因此只要程序顺利启动就不用担忧使用了不容许使用的参数。在这里指定的参数没法覆盖命令行的相同参数,须要锁定的配置能够直接指定在 ENTRYPOINT中。
  2. JAVA_TOOL_OPTIONS 是存在好久的环境变量,这个环境变量一样不须要在命令行显式指定。它名字中的 TOOL 提示了除了 java 命令以外其它 java 工具命令例如 javac 之类的也会去读取这个变量的值。在这里指定的参数既不能覆盖命令行的相同参数,也不能覆盖 JDK_JAVA_OPTIONS 中的相同参数,优先级最低。

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

综上所述,能够得出这样的决策路径:编码

  1. Java 9 及以上(呃,话说如今还有 Java 9 如下?)的 java 命令使用 JDK_JAVA_OPTIONS
  2. CI/CD 或者打包工具之类的非 java 命令时使用 JAVA_TOOL_OPTIONS
  3. Java 9 如下(囧)的 java 命令使用 JAVA_TOOL_OPTIONS
  4. 极特殊状况下须要覆盖命令行上的参数时,先反思本身,再反思本身,最后找各家本身定义的环境变量

最后一个问题,看到这里可能会有疑问,设置环境变量会不会影响到其它 java 进程?若是遵循了容器化的最佳实践,那答案显然是不会,并且即便在主机上,要想多个进程间环境变量互不影响也是很简单的事情不是吗?命令行

相关文章
相关标签/搜索