Dockerfile 命令书写exec和shell

前言

在上一篇中,对于CMDENTRYPOINT只是简单介绍了它们使用上的差别。即有两个方式书写。分别是exec模式和shell模式。具体格式如下:

  1. exec模式CMD /ENTRYPOINT ["executable", "param1", "param2"]
  2. shell模式 CMD /ENTRYPOINT command param1 param2

想要了解清楚这两个命令的区别,需要先明白这两个模式有什么区别。

shell模式

先来看看什么是shell。shell可以理解为与内核程序沟通的组软件,类似于Windows下的cmd.exe命令行窗口。在Linux中的终端就是一种shell,默认是bash(Bourne-Again Shell)。

现在我在Linux终端输入以下命令:

1
$ echo abc

这个命令就是一种shell模式,实际上它执行的样子是如下样子:

1
$ /bin/bash -c "echo abc"

大概意思就是将字符串”echo abc”丢到执行器bash去解析。-c参数的意思是第一个字符串是命令的路径,后面跟着的为命令的参数。

”echo abc“,第一个字符串是echo,默认情况下会到/bin下查找有没有echo这个文件。可以看到/bin的确存在echo的文件。

你想输入的替代文字

为了更加了解/bin/bash -c 的含义,我们可以自己新建一个test.sh验证一下,test.sh的内容如下,路径为/home/test.sh。$0表示第一个参数。

1
2
ehco $0
echo $1

执行该文件

1
$ /home/test.sh param1 param2

实际执行的样子为:

1
$ /bin/bash -c  "/home/test.sh param1 param2"

注意第一个参数是/home/test.sh,而不是param1。所以输出结果如下:

1
2
/home/test.sh 
param1

上面阐述了这么多,只是想让你们理解shell命令模式下,命令实际的执行样子是/bin/bash -c “command param1 param2”

exec模式

1
ENTRYPOINT ["executable", "param1", "param2"]

必须清楚了解命令 “executable” 的每一个参数,一个萝卜一个坑,不能随便乱拆与合并。例如执行 jar 文件的命令

1
$ java -Xmx256M -jar /app.jar

写成 exec 格式就是

1
ENTRYPOINT ["java", "-Xmx256M", "-jar", "/app.jar"]

而不能写成

1
ENTRYPOINT ["java", "-Xmx256M", "-jar /app.jar"]

否则 docker run 运行它时出错, “-jar” 和 “/app.jar” 分别是两个参数。

1
2
3
Unrecognized option: -jar /app.
jarError: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

exec 格式下的ENTRYPOINT才能 接收 CMD 或 dock run <image> 后的参数作为附加参数,相当于是往这个命令中附加元素。例如 Dockerfile 中写成:

1
ENTRYPOINT ["echo", "Hello"]

假设置构建出的镜像名(repository) 是 test(以下都以 test 作为镜像名称), 那么执行下面 docker 命令:

1
$ docker run test World and China

输出是:

1
Hello World and China

使用 exec 格式的 ENTRYPOINT 与 CMD 同在时还能接收 CMD 送过来的参数,如 Dockerfile:

1
2
ENTRYPOINT ["echo", "Hello"]
CMD ["World"]

执行 docker run 命令

1
$ docker run test

输出

1
Hello World

另外,如果执行如下 docker 命令

1
$ docker run test China Haha

输出就是

1
Hello China Haha

原因是 CMD 在运行容器时由 docker run <image> 后的命令覆盖的了,所以 World 不见了。

shell 格式的下ENTRYPOINT不能接受参数

有了上面 shell 到 exec 格式的映射关系之后,我们就不难理解为什么 shell 格式的 ENTRYPOINT 不能接收 CMD 或 docker run 传过来的参数。因为参数将作为 “/bin/sh” 的参数而非 shell 的参数,举例说明:

对于 shell 格式

1
ENTRYPOINT java -Xmx256M -jar /app.jar

从 CMD 或 docker run 而来的参数( 比如 Hello World) 最终将会组成下面完整的 ENTRYPOINT

1
ENTRYPOINT ["/bin/sh", "-c", "java -Xmx256M -jar /app.jar", "Hello", "World"]

“Hello”, “World” 在这里是作为/bin/sh的参数的,而 “/bin/sh” 因为没有对应的参数,所以会忽略掉它们。而当采用了exec格式,就变成如下格式

1
ENTRYPOINT ["java", "-Xmx256M", "-jar", "/app.jar","Hello","World"]

这个时候从CMD或者docker run接受到的参数表示的就是java的参数了。