Dockerfile中Entrypoint指令使用方法分析
ENTRYPOINT的作用是设置容器运行时的执行命令,即容器的默认入口 ENTRYPOINT有两种使用格式
- shell格式: ENTRYPOINT command param1 param2
- exec格式: ENTRYPOINT [“executable”, “param1”, “param2”] (推荐优先使用改格式)
1、shell格式
命令和参数没有被中括号括起来,命令/参数之间用空格隔开,而不是逗号。
++在制作镜像过程中,如果使用shell格式,则ENTRYPOINT
会自动忽略通过CMD
指令和docker run
命令输入的参数。++使用该方式制作的镜像,容器运行是的命令就固定死了,在运行时无法更改,即使在运行时添加了其他的命令,也会被或略。
shell格式有一个缺点,即ENTRYPOINT将以“/bin/sh -c”作为入口命令,也就是说用户指定执行实体不是作为容器的PID 1
来运行。该命令不能传递信号量到子进程,因此当执行docker stop [container]
命令时,执行实体无法接收到SIGTERM
信号。这种情况下,docker结束执行实体的方法是,等待超时(即发送SIGTERM
没响应)后,再发一条SIGKILL
信号来强制kill掉用户指定执行实体。
下面举例说明使用shell格式的缺点,以及应对策略:
- Dockerfile文件内容如下,制作镜像
shell:v1
|
|
- 运行容器,我们发现top并不是
PID 1
,而是PID 6
|
|
- 计算结束容器的时间
|
|
++通过上述时间可以看出,通过docker stop
停止该容器用了 10s 多的时间++
为了在使用shell格式的情况下,用户进程也能通过docker stop
命令正常结束,我们可以Dockerfile文件做一点微调,即在执行实体的前面加上exec
命令
- Dockerfile文件内容
|
|
- 运行容器,发现top变成了
PID 1
|
|
- 计算结束容器的时间
|
|
发现docker stop
命令的执行时间明显减少(0.54s),这里的头号功臣是exec
命令。
系统调用exec
是以新的进程去代替原来的进程,但进程的PID保持不变。因此,可以这样认为,exec系统调用并没有创建新的进程,只是替换了原来进程上下文的内容。原进程的代码段,数据段,堆栈段被新的进程所代替。因此在容器运行起来后,top
进程会替换/bin/sh -c
进程,成为PID 1
进程。
2、exec格式
命令和参数都有"
,且之间用,
隔开,整体用中括号括起来。
在制作镜像是,使用了exec格式,则系统会组合ENTRYPOINT
和CMD
两个命令,作为容器运行时的默认执行命令,即ENTRYPOINT
+CMD
。该方式下制作的镜像ENTRYPOINT
指令指定的内容是固定不变的,运行时无法修改,而CMD
指定的部分是可变的,在docker run
的时候,增加的参数会覆盖CMD
指定的参数。
这里的使用场景,应该也比较好理解。镜像中不变的命令放在ENTRYPOINT
指令中,可能变化的指令或者参数放在CMD
指令中。
另外,用exec格式制作的镜像,容器运行时的PID 1
一定是ENTRYPOINT
指定的运行实体,而不是/bin/sh -c
下面距离说明:
- 镜像制作的Dockerfile文件内容,制作exec:v1镜像,exec格式的ENTRYPOINT举例:
|
|
- 默认命令运行容器,我们发现top是
PID 1
,且命令为top -b -c
(即ENTRYPOINT
+CMD
)
|
|
docker run
命令中增加参数-H
,发现PID 1
的命令变为top -v -H
,即运行时输入的参数把CMD
指令指定的参数覆盖了
|
|
3、不设置ENTRYPOINT
的情况
通过上面两种情况的介绍,可能有人会问,上面两种情况,当默认命令设置好后,都不能修改,那如果我们需要把可执行实体和参数都设置为运行时变的,该怎么办呢?这时候我们可以不设置ENTRYPOINT
指令,而把执行的命令和参数都设置在CMD
指令中,这样就可以修改了。
4、总结ENTRYPOINT
和CMD
指令不同组合的用法
CMD
指令也分exec
和shell
两种格式,跟ENTRYPOINT
类似,这里就不再赘述了。
CMD
和ENTRYPOINT
都可以用来指定容器运行时的默认执行命令,下面是使用这两个字段的几条规则:
- 在镜像制作中,docker文件中至少需要这只
CMD
和ENTRYPOINT
中的一个 CMD
可以作为ENTRYPOINT
字段的补充命令,为ENTRYPOINT
中的可执行命令设置默认参数;也可以单独设置容器执行的命令和参数。- 当容器运行时设置了命令参数,则
CMD
字段中的内容将被覆盖。 - 使用shell格式时,
ENTRYPOINT
指令会忽略任何CMD
和docker run
命令的参数,并会运行在/bin/sh -c
中(即用户进程的父进程是/bin/sh -c
)
下表给出了ENTRYPOINT
和CMD
配合使用时各种场景:
No ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”] | |
---|---|---|---|
No CMD | error, not allowed | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
CMD [“exec_cmd”, “p1_cmd”] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry exec_cmd p1_cmd |
CMD [“p1_cmd”, “p2_cmd”] | p1_cmd p2_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry p1_cmd p2_cmd |
CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |