ENTRYPOINT 是“命令”:它设置了容器启动时必须执行的主程序,这个设定很难被改变。CMD 是“默认参数”:它为上面的“命令”提供默认的参数。如果用户在运行容器时提供了其他参数,CMD 提供的默认值就会被替换掉。下面这个表格可以让你更直观地看到它们的不同:
| 特性 | CMD | ENTRYPOINT |
| :— | :— | :— |
| 核心作用 | 提供默认的命令或参数,容易被覆盖 | 设定容器的主程序,确保其始终运行 |
| 参数覆盖 | 被 docker run 后面的全部参数覆盖 | 被 docker run 后面的参数追加到其命令末尾 |
| 覆盖方式 | docker run <image> <new_command> 完全替换 CMD | 除非使用 --entrypoint 标志,否则无法轻易覆盖 |
光看理论还不够直观,我们来看看具体例子。
假设 Dockerfile 里写着 CMD ["ls"]。
* 当你运行 docker run my-image 时,容器会执行 ls,列出目录内容。
* 如果你运行 docker run my-image -l,这时 docker run 后面的 -l 会覆盖掉 CMD 指定的 ls,导致容器尝试执行 -l 这个命令,从而报错。这是因为 ls 这个命令被完全替换掉了。
假设 Dockerfile 里写着 ENTRYPOINT ["ls"]。
* 当你运行 docker run my-image 时,效果和上面一样,执行 ls。
* 当你运行 docker run my-image -l 时,-l 会被追加到 ENTRYPOINT 命令的后面,最终执行的命令是 ls -l。这能成功实现你想要的详细列表输出。
这是一种很常用也推荐的做法,结合了二者的优点:用 ENTRYPOINT 固定主程序,用 CMD 提供灵活的默认参数。
ENTRYPOINT ["curl"] CMD ["https://example.com"]
在这个例子里:
* 运行 docker run my-image,实际执行 curl https://example.com。
* 运行 docker run my-image https://api.github.com,CMD 的默认 URL 被覆盖,实际执行 curl https://api.github.com。
虽然 ENTRYPOINT 被设计为不容易被覆盖,但确实有一个方法:在 docker run 时使用 --entrypoint 标志。
例如,无论 Dockerfile 里的 ENTRYPOINT 是什么,执行下面的命令都会让容器运行 sh 命令:
docker run --entrypoint sh my-image
这通常用于调试或紧急情况下,临时进入容器内部。
使用 CMD 和 ENTRYPOINT 时,有两种写法,强烈建议优先使用 Exec 格式(即带中括号的写法)。
CMD ["executable", "param1", "param2"] 或 ENTRYPOINT ["executable", "param1"]。这是推荐写法,它会直接执行程序,信号处理正常。CMD command param 或 ENTRYPOINT command param。这种写法会通过 /bin/sh -c 启动一个子进程来执行命令,可能导致容器无法正常收到退出信号,并且在 ENTRYPOINT 下,docker run 的追加参数可能失效。