前言
在上一篇中,对于CMD和ENTRYPOINT只是简单介绍了它们使用上的差别。即有两个方式书写。分别是exec模式和shell模式。具体格式如下:
- exec模式
CMD /ENTRYPOINT ["executable", "param1", "param2"], - 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  | ehco $0  | 
执行该文件
1  | $ /home/test.sh param1 param2  | 
实际执行的样子为:
1  | $ /bin/bash -c "/home/test.sh param1 param2"  | 
注意第一个参数是/home/test.sh,而不是param1。所以输出结果如下:
1  | /home/test.sh  | 
上面阐述了这么多,只是想让你们理解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  | Unrecognized option: -jar /app.  | 
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  | ENTRYPOINT ["echo", "Hello"]  | 
执行 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的参数了。