本篇的主要内容是为了澄清docker容器的一些容易混淆的概念,主要分两部分, 一是容器端口的publishexpose,二是Dockerfile中ENTRYPOINTCMD的区分。

Expose or Publish

expose的作用是为了容器间通信,也就是说expose的端口只能被其他容器访问,但是 不能被docker之外访问,而publish的端口即可用于容器间通信也可被外界访问。所以从 逻辑上来说,publish包含了expose

有这样的区分主要是为了可移植性。我们知道在Dockerfile文件中只有Expose命令, expose暴露的端口只能用于容器间通信,不会跟主机上的端口冲突。如果在 Dockerfile中加入了publish,在其他的机器上构建时就有可能导致端口冲突。

在命令行里使用这两个参数时,二者是可以混用的,不会冲突。同时指定--expose--publish与单独使用--publish的效果一样。

ENTRYPOINT or CMD

Docker提供了一个默认的ENTRYPOINT:/bin/sh -c,但是没有默认的CMD。比如当我们 在命令行里执行docker run -it ubuntu bash的时候,entrypoint就是/bin/sh -c, command就是bash

在命令行里,所有镜像名字之后的参数都是作为command传给了entrypoint。在Dockerfile 中,我们可以指定默认的ENTRYPOINTCMD。比如我们执行docker run -it ubuntu的 时候,效果和docker run -it ubuntu bash是一样的,因为ubuntu的Dockerfile里指定了 CMDbash.

二者的分离主要也是应用在Dockerfile中,通过灵活的设置,我们可以做出来一些有趣的, 很方便使用的镜像。比如将ENTRYPOINT设为[/bin/cat],那么在执行docker run catimg /etc/password的时候就相当于在执行/bin/cat /etc/password,整个镜像此时变 成了一个二进制程序。这个例子可能显得有点无聊,但是既然任意程序都可以用做 ENTRYPOINT,自然是只有想不到,没有做不到了。如果将一个redis镜像的ENTRYPOINT设 为["redis", "-H", "something", "-u", "toto"],那么在执行docker run redisimg get key就相当于docker run redisimg redis -H something -u toto get key,这就是 一个简单明了的redis客户端,也不用输那么多参数了。

Dockerfile中与此相关的命令还有一个RUN,相关细节较为琐碎,下面将详细叙述:

RUN

RUN用来在上一层layer的基础上执行一些系统命令并且创建新的一层,可以说是 Dockerfile中最为常见的命令了,主要有两种形式

  • RUN <command> 通过/bin/sh -c执行
  • RUN ["executable", "param1", "param2"] (exec形式)

exec形式可以避免shell对参数的一些处理并且可以用在一些没有/bin/sh的基本镜像上, 如果想使用别的shell,也可以使用此种方式,比如RUN ["/bin/bash", "-c", "echo hello"]。 注意事项:

  1. exec形式是以json数组的形式来解析的,所以各参数必须用双引号"",不能用单引号''
  2. exec形式下如果不明确制定是不会调用shell的,也就意味着一些环境变量是无法解析的,

比如RUN [ "echo", "$HOME" ],如果需要必须自己明确指定所用的shell

CMD

CMD主要是为容器提供默认的运行程序,有三种形式:

  • CMD ["executable","param1","param2"] (exec形式)
  • CMD ["param1","param2"] (将参数传给ENTRYPOINT)
  • CMD command param1 param2 (shell形式,/bin/sh -c执行)

一个Dockerfile中只能有一个CMD,如果指定了多个,只有最后一个起作用。运行时的参 数会覆盖Dockerfile中CMD

注意事项可参考RUN的注意事项

ENTRYPOINT

ENTRYPOINT有两种形式:

  • ENTRYPOINT ["executable", "param1", "param2"] (exec形式)
  • ENTRYPOINT command param1 param2 (shell形式)

类似于CMD,如果指定了多个ENTRYPOINT,只有最后一个起作用。docker run --entrypoint可覆盖Dockerfile中的设置。

exec形式是最常用的,shell形式会阻止任何CMD或者run的参数的执行(因为已经在 ENTRYPOINT中指定了执行程序),但是不能传递信号。

## 参考链接

  1. Dockerfile Reference
  2. Difference between “expose” and “publish” in docker
  3. What is the difference between CMD and ENTRYPOINT in a Dockerfile?