Docker 中 RUN/CMD/ENTRYPOINT 详解
又有一段时间没有写博客了。这一次我在 AWS 的 SageMaker 上去部署训练算法。SageMaker 的 train 和 host 都是在容器内实现的。我个人认为训练的任务放在容器里执行还是很科学的。在较低配置的机器上启动 Juypter Notebook 用于开发,然后在较高配置的集群上使用 Docker 训练算法,计算资源不闲置,用户也省钱。
本文参(chao)考(xi)了 Docker — 从入门到实践
RUN 指令
这个指令用于构建镜像,其实是在 Dockerfile 里面执行命令行命令。有两种写法
- shell 格式
RUN <COMMAND>。例如
1 | RUN apt-get update |
- exec 格式
RUN ["executable", "param1", "param2"]。例如
1 | RUN ["apt-get", "update"] |
但要注意,我们最好将命令写在同一个 RUN 命令里面,否则会造成镜像臃肿(因为每运行一次 RUN 就会新建一层)。
所以最好这么写
1 | RUN buildDeps='gcc libc6-dev make' \ |
RUN 指令在容器构建过程中起作用,和容器的运行无关。
CMD 指令
CMD 指令用于指定默认的容器主建成的启动命令。在一个镜像里面仅有一个 CMD 指令会起作用。比如下面这个 Dockerfile
1 | FROM ubuntu:16.04 |
第一个 CMD 指令会被忽略。如果运行这个 image,只会输出 second
1 | $ docker build -t test . |
ENTRYPOINT 指令
如果我们需要在容器运行时传一些参数,就需要 ENTRYPOINT 指令了。当存在 ENTRYPOINT 时,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际执行时,将变为
1 | <ENTRYPOINT> "<CMD>" |
举个栗子,下面这个镜像可以得知当前的公网 IP。
1 | FROM ubuntu:16.04 |
我们可以直接运行
1 | $ docker build -t myip . |
但如果我们希望显示 HTTP 头信息,就需要加上 -i 参数。但我们不可以直接加 -i 参数给 docker run myip 。因为 docker run myip -i 实际上会用 -i 来覆盖原有的默认 CMD 命令 curl -s http://ip.cn,而 -i 完全不是个命令。因此我们只能这么用
1 | $ docker run myip curl -s http://ip.cn -i |
很蠢,所以 ENTRYPOINT 指令就派上用场了。现在我们将 Dockerfile 改写如下
1 | FROM ubuntu:16.04 |
这样,当我们运行 docker run myip -i 时, CMD 命令为 -i。再加上 ENTRYPOINT,本质上执行的命令就变成了 curl -s http://ip.cn -i
P.S. 上面栗子来自Docker — 从入门到实践
总结
- 使用 RUN 指令安装应用和软件包,构建镜像。
- 如果 Docker 镜像的用途是运行应用程序或服务,比如运行一个 MySQL,应该优先使用 Exec 格式的 ENTRYPOINT 指令。CMD 可为 ENTRYPOINT 提供额外的默认参数,同时可利用 docker run 命令行替换默认参数。
- 如果想为容器设置默认的启动命令,可使用 CMD 指令。用户可在 docker run 命令行中替换此默认命令。