Please enable Javascript to view the contents

Docker易错集锦-1: Dockerfile的ENTRYPOINT中的环境变量无法替换

 ·  ☕ 3 分钟

1.问题描述

开发同事编写Dockerfile时遇到问题,ENTRYPOINT ["java", "$JAVA_OPTS", "-jar", "/app.jar"]无法实现变量替换。自己工作中涉及变量替换等操作时,都是将shell命令放到可执行文件中,虽然对同事的这种写法奇怪,但当时也确实没发现错误的原因,深入了解过EXEC格式后,吃透了这个问题。

2.说明

Dockerfile中的ENTRYPOINT\RUN\CMD具备两种格式:EXEC格式SHELL格式

2.1 exec格式:

示例:
ENTRYPOINT ["sh", "-c", "/start.sh"]

实现:
直接使用命令本身来执行,不需要通过Shell解释器。实际程序PID=1,可以接收、处理信号,实现优雅启停。

优点:

  • 高效
  • 安全

缺点:

  • 不能使用Shell语法,灵活性差

2.2 shell格式:

写法示例:

1
2
3
4
5
RUN apt-get update && apt-get install -y --no-install-recommends \
    package-A \
    package-B \
    package-C  \
    && rm -rf /var/lib/apt/lists/*

实现:
此格式是使用Shell解释器来执行命令,最终程序被执行时,类似于/bin/sh -c的方式运行了我们的程序,这样会导致/bin/shPID为1的进程运行,
而实际程序是它fork/execs出来的子进程。

优点:
可以使用Shell语法,例如管道、重定向等,这增加了Dockerfile的灵活性;

缺点:

  • sh父进程带来额外开销;
  • 存在安全隐患,如shell注入攻击;
  • 无法实现优雅启停,docker stopSIGTERM信号只是发送给容器中PID为1的进程,无法传递给子进程,实际进程实际程序无法接收、处理信号;

3.解决

想要在EXEC格式ENTRYPOINT中引用环境变量,需要编写shell脚本。在脚本中,变量替换与反斜线转义都是生效的。然后ENTRYPOINT使用exec格式执行。

即脚本/start.sh内容为

1
java $JAVA_OPTS -jar /app.jar

Dockerfile中的内容为

1
ENTRYPOINT ["/bin/sh", "-c", "/start.sh"]

也可以通过 ENTRYPOINT ["/bin/sh", "-c", "java $JAVA_OPTS -jar /app.jar"]实现变量替换,但java进程无法接收、处理信号,不推荐

3.总结

一般来说,除非需要 shell 功能,否则应使用exec格式;如果需要ENTRYPOINTCMD中的 shell 功能,则应考虑编写shell脚本,然后使用exec格式执行。

shell格式:使用Shell解释器来执行命令,更灵活但也更复杂、更容易受到攻击。
exec格式:直接使用命令本身来执行,更简单、更高效、更安全,推荐使用。

名称最佳使用场景shell功能是否能处理信号程序ID
shell格式RUN支持(可引用变量、反斜线转义)1进程的子进程
exec格式ENTRYPOINT\CMD不支持是(可将信号传递给程序处理,实现优雅启停)1

4.参考

exec-form 变量替换
优雅的终止docker容器
关于 Dockerfile ENTRYPOINT 需要知道的一切

分享

Hex
作者
Hex
CloudNative Developer

目录