每个Dockerfile写到最后,都需要指定一个需要执行的命令,及其参数。
一个Docker容器,需要有且仅有一个前台进程在运行。
一旦这个进程结束了,那么容器本身的运行也就终结了。
而Dockerfile最后指定的这个命令,就是Docker容器的前台进程。
指定的方法,一般有两种,
ENTRYPOINT
与
CMD
。
二者表面上看不出区别,实际上却有巨大差异,也有内在联系。
每个Docker容器,都必须要有一个可执行(executable)文件或命令、及相关参数,作为执行入口(entrypoint)。
所以,
ENTRYPOINT
和
CMD
,二者在Dockerfile与
docker run
里,至少指定一个。
在执行
docker run args... <image> cmd...
时,镜像名称
<image>
后的所有内容,都被看做
CMD
。
它会覆盖镜像中写好的
CMD
(如果有的话)。
如果使用
docker run --entrypoint /bin/bash <image> cmd
,可以复写ENTRYPOINT为
/bin/bash
。
使用区别
¶
使用ENTRYPOINT和CMD的方式,大概分两种。
一是Dockerfile,二是
docker run
。
还有docker-compose.yml,其实和
docker run
差不多。
Dockerfile
¶
ENTRYPOINT和CMD至少要有一个。
使用
ENTRYPOINT entry arg0
形式,与CMD将没有任何配合。因此,除非特定需求,否则不推荐这种使用方式。
右下角
entry arg0 /bin/sh -c cmd arg1
这种形式,几乎没有什么使用场景,反而是常见错误,应该尽量避免。
本质上,其实可以理解为ENTRYPOINT是真正的Docker可执行入口,而CMD则是可选参数。
之所以在很多情况下直接写CMD也能生效,是因为ENTRYPOINT就相当于是指定Shell,而CMD则是指定Shell中执行的命令。
注意,只是『相当于』。
docker run与docker-compose.yml
¶
在
docker run <image> cmd arg1
时,总是相当于CMD的
["cmd", "arg1"]
形式。
此外,docker-compose.yml的情况与
docker run
类似。
在
docker run
中使用
--entrypoint
参数时,限制非常大。
仅仅只能指定一个命令、或可执行文件,不能指定一个参数列表。
$ docker run --entrypoint ls alpine /etc/passwd
/etc/passwd
$ docker run --entrypoint ls alpine -l /etc/passwd
-rw-r--r-- 1 root root 1224 Apr 24 2017 /etc/passwd
这时,如果希望把-l
参数放到--entrypoint
中,则无法做到。
(一些早期版本似乎是可以的。)
$ docker run --entrypoint 'ls -l' alpine /etc/passwd
container_linux.go:262: starting container process caused "exec: \"ls -l\": executable file not found in $PATH"
docker: Error response from daemon: oci runtime error: container_linux.go:262: starting container process caused "exec: \"ls -l\": executable file not found in $PATH".
ERRO[0001] error waiting for container: context canceled
在--entrypoint
中,无法指定一个列表。
不过,docker-compose.yml文件中,倒是可以。
image: alpine
entrypoint:
command: /etc/passwd
注意PID ¶
原则上,一个Docker容器里应该只有一个进程,其PID为1。
Docker外部的操作,比如docker stop
,就是向这个进程发送信号。
如果那个唯一的前台进程PID不为1,那么就会收不到信号,只能在超时(默认约10秒)后被kill。
在Dockerfile中使用ENTRYPOINT entry arg0
这种形式时,entry
的位置总是应该使用exec
,后面再接其它内容。
比如,ENTRYPOINT exec top
,这可以确保top
命令是PID为1的进程。
否则,ENTRYPOINT top
的形式,PID为1的进程就是/bin/sh -c top
,而top
则被挤到了另外一个进程。
例如,对一个如下的Dockerfile:
FROM alpine
ENTRYPOINT top
执行以下的build与run操作:
docker build -t top .
docker run --rm -it --name test top
在另一个Shell中,可以查看到以下结果:
$ docker exec test ps
PID USER TIME COMMAND
1 root 0:00 /bin/sh -c top
7 root 0:00 top
8 root 0:00 ps
$ time docker stop test
real 0m10.774s
user 0m0.008s
sys 0m0.004s
如果换成ENTRYPOINT exec top
,或者ENTRYPOINT ["top"]
,就可以避免这个问题。
Docker版本 ¶
本文中描述的部分内容,在早期的Docker版本中并不正确。
撰写本文时,测试用的Docker版本信息如下。
$ docker version
Client:
Version: 17.10.0-ce
API version: 1.33
Go version: go1.8.3
Git commit: f4ffd25
Built: Tue Oct 17 19:02:43 2017
OS/Arch: linux/amd64
Server:
Version: 17.10.0-ce
API version: 1.33 (minimum version 1.12)
Go version: go1.8.3
Git commit: f4ffd25
Built: Tue Oct 17 19:01:22 2017
OS/Arch: linux/amd64
Experimental: false