文章目录
  1. 1. docker-compose传参问题
    1. 1.1. Dockerfile中CMD和ENTRYPOINT命令详解
      1. 1.1.1. exec 模式和 shell 模式
        1. 1.1.1.1. CMD 指令
          1. 1.1.1.1.1. exec 模式
          2. 1.1.1.1.2. shell 模式
      2. 1.1.2. ENTRYPOINT 指令
        1. 1.1.2.1. exec 模式
        2. 1.1.2.2. shell 模式
    2. 1.2. 总结

docker-compose传参问题

Dockerfile中CMD和ENTRYPOINT命令详解

CMD 和 ENTRYPOINT 指令都是用来指定容器启动时运行的命令。

单从功能上来看,这两个命令几乎是重复的。单独使用其中的一个就可以实现绝大多数的用例。

但是还是有些许区别,我们来一起看看。

exec 模式和 shell 模式

CMD 指令

CMD 指令的目的是:为容器提供默认的执行命令。
CMD 指令有三种使用方式,其中的一种是为 ENTRYPOINT 提供默认的参数:

CMD ["param1","param2"]

另外两种使用方式分别是 exec 模式和 shell 模式:

exec 模式
CMD ["executable","param1","param2"] // 这是 exec 模式的写法,注意需要使用双引号。

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

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

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

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

![paste image][image-1]

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

FROM ubuntu
CMD [ "top" ]

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

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

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

$ docker exec testcon ps aux

在启动容器时我们通过命令行指定参数 ps aux 覆盖默认的 top 命令:

![paste image][image-2]

从上图可以看到,命令行上指定的 ps aux 命令覆盖了 Dockerfile 中的 CMD [ “top” ]。实际上,命令行上的命令同样会覆盖 shell 模式的 CMD 指令。

shell 模式
CMD command param1 param2 // 这是 shell 模式的写法。

注意命令行参数可以覆盖 CMD 指令的设置,但是只能是重写,却不能给 CMD 中的命令通过命令行传递参数。

使用 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

![paste image][image-3]

ENTRYPOINT 指令

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

exec 模式和 shell 模式的基本用法和 CMD 指令是一样的,下面我们介绍一些比较特殊的用法。

exec 模式

ENTRYPOINT ["executable", "param1", "param2"] // 这是 exec 模式的写法,注意需要使用双引号。

指定 ENTRYPOINT 指令为 exec 模式时,命令行上指定的参数会作为参数添加到 ENTRYPOINT 指定命令的参数列表中。用下面的代码构建镜像 test1:
FROM ubuntu
ENTRYPOINT [ “top”, “-b” ]

运行下面的命令:

$ docker run --rm test1 -c

![paste image][image-4]

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

由 CMD 指令指定默认的可选参数:

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

使用这段代码构建镜像 test2 并不带命令行参数启动容器:

$ docker run --rm test2

这时容器中运行的命令为:top -b -c

如果我们指定命令行参数:

$ docker run --rm test2 -n 1

-n 1 会覆盖 通过 CMD [ “-c” ] 指定的参数,容器执行的命令为:top -b -n 1

![paste image][image-5]

shell 模式

ENTRYPOINT command param1 param2 // 这是 shell 模式的写法。

总结

同时使用 CMD 和 ENTRYPOINT 的情况

对于 CMD 和 ENTRYPOINT 的设计而言,多数情况下它们应该是单独使用的。当然,有一个例外是 CMD 为 ENTRYPOINT 提供默认的可选参数。
我们大概可以总结出下面几条规律:

 • 如果 ENTRYPOINT 使用了 shell 模式,CMD 指令会被忽略。
 • 如果 ENTRYPOINT 使用了 exec 模式,CMD 指定的内容被追加为 ENTRYPOINT 指定命令的参数。
 • 如果 ENTRYPOINT 使用了 exec 模式,CMD 也应该使用 exec 模式。

真实的情况要远比这三条规律复杂,好在 docker 给出了官方的解释,如下图所示:

![paste image][image-6]

当我们无法理解容器中运行命令的行为时,说不定通过这个表格可以解开疑惑!

对于 Dockerfile 来说,CMD 和 ENTRYPOINT 是非常重要的指令。它们不是在构建镜像的过程中执行,而是在启动容器时执行,所以主要用来指定容器默认执行的命令。但是提供两个功能类似的指令,必然会给用户带来理解上的困惑和使用中的混淆。希望本文能够帮助大家理解二者的区别与联系,并更好的使用二者。

[参考文章][1]

[1]: https://www.jb51.net/article/136264.htm

[image-1]: http://clockcoder.com/images/1533481840969knusv9p6.png?imageslim
[image-2]: http://clockcoder.com/images/1533481465709ykhuxhyz.png?imageslim
[image-3]: http://clockcoder.com/images/1533482454180c60gwk9k.png?imageslim
[image-4]: http://clockcoder.com/images/1533482744721dhf9h6m5.png?imageslim
[image-5]: http://clockcoder.com/images/1533482884412gf1hetn0.png?imageslim
[image-6]: http://clockcoder.com/images/1533483041334y9x98iy8.png?imageslim

文章目录
  1. 1. docker-compose传参问题
    1. 1.1. Dockerfile中CMD和ENTRYPOINT命令详解
      1. 1.1.1. exec 模式和 shell 模式
        1. 1.1.1.1. CMD 指令
          1. 1.1.1.1.1. exec 模式
          2. 1.1.1.1.2. shell 模式
      2. 1.1.2. ENTRYPOINT 指令
        1. 1.1.2.1. exec 模式
        2. 1.1.2.2. shell 模式
    2. 1.2. 总结