cmd和entrypoint指令都是用来指定容器启动时运行的命令。
单从功能上看,这两个命令几乎是重复的。但是使用其中一个命令就可以实现绝大多数的用例。但是既然docker同时提供了它们,为了在使用中不至于混淆,我们在这里看看能不能理清楚。

exec模式和shell模式

cmd和entrypoint指令都支持exec模式和shell模式的写法。这两种模式的最主要用来指定容器中的不同进程为1号进程。我们在前面的文章中已经说明了,1号进程在linux系统中是非常重要的。

exec模式

使用exec模式时,容器中的任务进程就是容器中的1号进程,看下面的例子:

  1. FROM ubuntu
  2. CMD [ "top" ]

把上面的代码保存到test1目录的Dockerfile中,然后进入test1目录构建镜像并启动一个容器:

$ docker build -t test1 .
$ docker run -idt --name testcon test1

然后查看容器中的进程ID:

$ docker exec testcon ps aux

image.png
从图中我们看到运行top命令的进程ID为1,exec模式是建议的使用模式,因为当运行任务的进程作为容器中的1号进程时,我们可以通过docker的stop命令优雅地结束容器。

exec模式的特点是不会通过shell执行相关命令,所以像¥HOME这样的环境变量是取不到的:

FROM ubuntu
CMD [ "echo", "$HOME" ]

把上面的代码保存到test目录的Dockerfile中,然后进入test1目录构建镜像并启动一个容器:

$ docker build --no-cache -t test1 .
$ docker run --rm test1

image.png
通过exec模式执行shell可以获得环境变量:

FROM ubuntu
CMD [ "sh", "-c", "echo $HOME" ]

把上面的代码保存到test1目录的Dockerfile中,然后进入test1目录结构构建镜像并启动一个容器:

$ docker build --no-cache -t test1 .
$ docker run --rm test1

image.png
这次正确取到了$HOME环境变量的值。

shell模式

使用shell模式时,docker会以/bin/sh -c “task command”的方式执行任务命令。也就是说容器中1号进程不是任务进程而是bash进程,看下面的例子:

FROM ubuntu
CMD top

把上面代码保存到test2目录的Dockerfile中,然后进入test2目录构建镜像并启动一个容器:

$ docker build -t test2 .
$ docker run -itd --name testcon2 test2

然后查看容器中的进程ID:

$ docker exec testcon2 ps aux

image.png
1号进程执行的命令居然是/bin/sh -c top, 而我们指定的top命令的进程ID为7。这是由docker内部决定的,目的是让我们执行的命令或者脚本可以取到环境变量。

CMD指令

cmd指令的目的是,为容器提供默认的执行命令。cmd指令有三种使用方式:

  • CMD[“param1”, “param2”]
  • CMD[“executable”, “param1”, “param2”]
  • CMD command param1 param2

注意,命令行参数可以覆盖CMD指令的设置,但是只能是重写。却不能给CMD中的命令通过命令行传递参数。一般的镜像都会提供容器启动时的默认命令,但是有些场景中用户不想执行默认的命令。用户可以通过命令行参数的方式覆盖CMD指令提供的默认命令。比如通过下面的命令创建镜像:

FROM ubuntu
CMD [ "top" ]

在启动容器时我们通过命令行指定参数ps aux覆盖默认的top命令:
image.png
从上面的图可以看到,命令行上指定的ps aux命令覆盖了Dockerfile中CMD[“top”]。

ENTRYPOINT指令

ENTRYPOINT指令的目的也是为容器指定默认的执行任务,它也有两种使用方式,就是我们前面介绍的exec模式和shell模式:

  • ENTRYPOINT[“executable”, “param1”, “param2”]
  • ENTRYPOINT command param1 param2

其实ENTRYPOINT和CMD的基本用法是一致的,下面介绍一种特殊的用法:

FROM ubuntu
ENTRYPOINT [ "top", "-b" ]

运行下面的命令:

$ docker run --rm test1 -c

image.png
我们在命令行上添加的参数被追加到了top命令的参数列表中。

参考:
Dockerfile 中的 CMD 与 ENTRYPOINT
entrypoint和cmd的区别