首先明确一点:
不管是ENTRYPOINT指令还是CMD指令,虽然都是在Dockerfile中就已经定义完成了。但是在构建镜像的时候并不会被执行,只有在执行docker run命令启动容器时才会起作用。
同类覆盖原则
Dockerfile中只能有一个ENTRYPOINT命令,如果有很多个,那么最后一条会覆盖掉前面所有的ENTRYPOINT命令。
同理,如果在启动的时候使用了 --entrypoint ,也将会覆盖Dockerfile中定义好的ENTRYPOINT命令。
再同理,
Dockerfile中只能有一个CMD命令,如果有很多个,那么最后一条会覆盖掉前面所有的CMD命令。
启动时候的CMD命令,例如run -d containerID
/bin/bash 的CMD /bin/bash也会覆盖Dockerfile中定义好的CMD命令。
ENTRYPOINT优先原则
执行docker run如果带有其他命令参数,如run -d $containerID
/bin/bash ,那么将不会覆盖ENTRYPOINT指令,而是以参数的形式追加至ENTRYPOINT指令之后。
ENTRYPOINT
- 用法一:用CMD来给ENTRYPOINT追加参数
当我们再Dockerfile文件中定义了入下的ENTRYPOINT命令
ENTRYPOINT ["java","-jar", "demo.jar"]
那么当我们运行此镜像执行docker run命令时,设置的任何命令参数或CMD指令的命令,都将作为ENTRYPOINT指令的命令参数,追加到ENTRYPOINT指令的命令之后。
如,在Dockerfile中有如上ENTRYPOINT指令的情况下,执行docker run $containerId
&启动容器。则容器启动后执行的第一条完整命令如下:
java -jar demo.jar &
- 用法二:屏蔽其他命令用法
可以发现,和上一种用法的区别在于少了中括号和引号,更加接近于我们执行bash命令的写法。
而事实上这种方法的本质也是通过在容器内部调用Shell来执行我们定义的ENTRYPOINT命令。也就是说下面的命令会被自动执行为:/bin/bash -c java -jar demo.jar
java -jar demo.jar
而通过这种方式定义的ENTRYPOINT不接受任何参数,所有的命令行或CMD命令都会被屏蔽掉。
这种方式,除了使用新的ENTRYPOINT命令,谁都无法影响其执行。
这样,ENTRYPOINT指令设置的top命令就不是容器中的第一个进程PID 1,这样在容器停止的时候就无法收到系统的SIGTERM信号。要想收到SIGTERM信号,务必使用Bash的内置exec命令使得top的PID 1,定义ENTRYPOINT指令如下:
ENTRYPOINT exec java -jar demo.jar
CMD
- 用法一:作为ENTRYPOINT命令参数
格式:
CMD ["-p","-v"]
这种情况下,要求ENTRYPOINT命令和CMD命令同时存在,且都使用此种用法(中括号加双引号进行分隔),因为如果ENTRYPOINT使用的是上述bash的用法,CMD命令将不生效(无意义)。
CMD的命令会作为参数追加至ENTRYPOINT命令后。
- 用法二:作为Shell脚本执行
CMD echo "lingg"
同ENTRYPOINT命令的第二种用法一样,会先自动调用shell脚本,然后再shell脚本中执行CMD命令。
上述命令将被转化为:
/bin/bash -c echo "lingg"