本文原文链接:https://blog.csdn.net/xzk9381/article/details/114635083
在编写 Dockerfile 过程中,需要使用 CMD 或 ENTRYPOINT 来指定容器运行时的命令。单从功能上来看,这两个命令的功能几乎是重复的,一般情况下使用其中一个命令就可以满足大部分的需求。接下来说一下 CMD 和 ENTRYPOINT 的不同点。
一、exec 和 shell 模式
首先要明确的一点就是 CMD 和 ENTRYPOINT 指令都可以使用 exec 和 shell 模式。这两种模式主要是用来指定容器中的不同进程作为 1 号进程。
1. exec 模式
前面提到了,exec 和 shell 模式会指定不同的进程作为容器内的 1 号进程。使用 exec 模式时,Dockerfile 中指定的命令就是容器内的 1 号进程,例如:
FROM ubuntu
CMD ["top"]
使用该 Dockerfile 构建镜像并运行,进入到容器内部使用 ps 命令可以发现 1 号进程就是 top 命令。
由于 exec 模式不会通过 shell 执行指令,所以使用该模式时无法获取到环境变量:
FROM ubuntu
CMD ["echo","${HOSTNAME}"]
构建镜像并运行后,可以发现在日志输出中的内容还是 ${HOSTNAME},并没有转换为对应的值。
但是如果使用 exec 模式执行 shell 就可以获得环境变量了:
FROM ubuntu
CMD ["/bin/bash","-c","echo ${HOSTNAME}"]
需要注意的是,使用 exec 模式需要使用双引号将参数包裹。
2. shell 模式
使用 shell 模式时,docker 会以 /bin/sh -c “task command” 的方式执行任务命令。也就是说容器中的 1 号进程不是任务进程而是 bash 进程:
FROM ubuntu
CMD top
构建镜像并运行后,使用 ps 命令可以发现 1 号进程是 /bin/sh -c top。
二、CMD 命令
CMD 命令用于为容器提供默认的启动命令。该命令有三种使用方式,其中最常用的一种是为 ENTRYPOINT 提供默认的参数,另外两种则是上面提到的 shell 和 exec 模式:
CMD ['param1','param2']
需要注意的是,在使用 docker run 命令时,如果手动指定了启动的命令,那么该命令会覆盖 Dockerfile 中的 CMD 指令。而且通过命令行指定的参数并不会传递到 CMD 命令中。
三、ENTRYPOINT 命令
ENTRYPOINT 命令同样用于为容器提供默认的启动命令。该命令有两种使用方式,也就是前面提到的 shell 模式和 exec 模式。基本用法是和 CMD 命令一样的,但是它也包含了一些特殊用法。
1. 示例一:指定 ENTRYPOINT 使用 exec 模式时,命令行上指定的参数会作为添加到 ENTRYPOINT 指定命令的参数列表中
FROM ubuntu
ENTRYPOINT ["top","-b"]
制作镜像后,使用如下命令启动容器:
docker run --rm test:v1 -c
进入容器中查看 1 号进程,可以发现进程为 top -b -c
,这就说明命令行中的参数被添加到参数列表中了。
2. 示例二:使用 CMD 命令指定默认的参数列表
FROM ubuntu
ENTRYPOINT ["top","-b"]
CMD ["-c"]
制作镜像后,使用如下命令启动容器(不带有命令行参数):
docker run --rm test:v1
进入容器中查看 1 号进程,可以发现进程为 top -b -c
。如果在启动容器时指定了参数:
docker run --rm test:v1 -n 1
那么 -n 1 参数会覆盖 CMD 中的参数,容器中实际执行的命令为 top -b -n 1
。
3. 示例三:指定 ENTRYPOINT 使用 shell 模式时,会完全忽略命令行参数
FROM ubuntu
ENTRYPOINT echo ${HOSTNAME}
制作镜像后,使用如下命令启动容器:
docker run --rm test:v1 ls
可以发现输出的内容是主机名称,而不是 ls 命令执行的结果。
4. 示例四:覆盖默认的 ENTRYPOINT 命令
在启动容器时,显示地指定 --entrypoint 参数可以覆盖默认的 ENTRYPOINT 命令:
docker run --rm test:v1 --entrypoint echo ${HOME}
四、了解 CMD 和 ENTRYPOINT 如何搭配使用
关于 CMD 和 ENTRYPOINT 如何搭配使用,官网给出了如下说明:
- Dockerfile should specify at least one of
CMD
orENTRYPOINT
commands. ENTRYPOINT
should be defined when using the container as an executable.CMD
should be used as a way of defining default arguments for anENTRYPOINT
command or for executing an ad-hoc command in a container.CMD
will be overridden when running the container with alternative arguments.
The table below shows what command is executed for different ENTRYPOINT
/ CMD
combinations:
No ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”] | |
---|---|---|---|
No CMD | error, not allowed | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
CMD [“exec_cmd”, “p1_cmd”] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry exec_cmd p1_cmd |
CMD [“p1_cmd”, “p2_cmd”] | p1_cmd p2_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry p1_cmd p2_cmd |
CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
总结起来的话,就是如下几点:
- 如果 ENTRYPOINT 使用了 shell 模式,CMD 指令会被忽略。
- 如果 ENTRYPOINT 使用了 exec 模式,CMD 指定的内容被追加为 ENTRYPOINT 指定命令的参数。
- 如果 ENTRYPOINT 使用了 exec 模式,CMD 也应该使用 exec 模式。
本文原文链接:https://blog.csdn.net/xzk9381/article/details/114635083