docker

关注公众号 jb51net

关闭
首页 > 网站技巧 > 服务器 > 云和虚拟化 > docker > dockerfile中CMD和ENTRYPOINT指令

dockerfile中CMD和ENTRYPOINT指令使用及说明

作者:赶路人儿

介绍了Dockerfile中的CMD和ENTRYPOINT指令以及它们的执行方式,包括exec形式和shell形式,CMD提供默认命令,支持覆盖;ENTRYPOINT定义固定命令,不支持覆盖,但可以通过--entrypoint参数实现覆盖

1、exec和shell两种风格

无论是CMD还是ENTRYPOINT指令,都支持exec和shell两种风格:

语法

示例

解析方式

Exec 形式

JSON 数组

CMD ["sleep", "60"]

直接执行二进制程序,docker实际运行execve("sleep", ["sleep", "60"])

Shell 形式

字符串

CMD sleep 60

通过 shell 执行命令,相当于 /bin/sh -c "sleep 60"

说明:execve时Linux内核的一个系统调用,作用事用于一个新程序替换当前进程(把新程序加载到当前进程的内存空间,当前的进程会被丢弃

注意:生产环境中推荐使用Exec形式,更安全、可控。

1)shell风格和exec风格解析方式:

shell风格是通过shell执行的命令,

CMD sleep 60 
#Docker实际运行时会变成
/bin/sh -c "sleep 60"

也就是说,它先启动一个 Shell 进程(/bin/sh),再让这个 shell 去执行 sleep 60。所以:

• 容器的 PID 1 进程 实际是 /bin/sh,不是 sleep

• Shell 风格中,可以解释变量、重定向符、管道符等(比如 CMD echo $PATH > /tmp/p)

而对于exec风格是直接执行命令,

CMD ["sleep", "60"]
#DOcker不会提懂/bin/sh,而是直接执行命令
execve("sleep", ["sleep", "60"])

所以,

2)环境变量展开:(CDM和ENTRYPOINT是一样的)

FROM busybox
ENV NAME=KnowLiu

# shell 形式
CMD echo "Hello $NAME"
#运行
docker run myimage
# 输出:Hello KnowLiu

#-------------
FROM busybox
ENV NAME=KnowLiu
CMD ["echo", "Hello $NAME"]

#运行
docker run myimage
# 输出:Hello $NAME

结论:

3)信号转发:(CDM和ENTRYPOINT是一样的)

FROM busybox
CMD ["sleep", "1000"]

#运行,然后停止容器会立即停止
docker run --name test mini
docker stop test

#-----------
FROM busybox
CMD sleep 1000

#运行,然后停止容器,容器停止会超时
docker run --name test mini
docker stop test

说明:由于/bin/sh是1号进程,SIGTERM 发给 shell,而 shell 默认不会转发信号,导致 sleep 不退出 → 容器停止会超时。

4)参数传递:

#docker file
FROM alpine:3.20
ENTRYPOINT ["echo", "hahaha!"]

#构建、运行
docker build -t my-test .
docker run my-test 
输出 hahaha!

docker run my-test hi
输出 hahaha! hi

docker run my-test echo hi
输出 hahaha! echo hi 

#------------------------
#docker file
FROM alpine:3.20
ENTRYPOINT echo hahaha

#构建、运行
docker build -t my-test .
docker run my-test 
输出 hahaha

docker run my-test hi
输出 hahaha

docker run my-test echo hi
输出 hahaha

可以看到,ENTRYPOINT指令的exec风格支持传递参数,shell风格不支持!关于命令覆盖,见下面!

5)总结二者的区别:

特性

Exec 形式

Shell 形式

写法

CMD ["cmd", "arg1", "arg2"]

ENTRYPOINT ["cmd", "arg1", "arg2"]

CMD cmd arg1 arg2

ENTRYPOINT cmd arg1 arg2

是否通过 /bin/sh

❌ 否

✅ 是

是否支持环境变量展开

❌ 否

✅ 是

是否支持管道、重定向等

❌ 否

✅ 是

信号传递(SIGTERM 等)

✅ 正常

⚠️ 可能被拦截

运行时参数(docker run 参数)

✅ 支持,仅针对对ENTRYPOINT指令,CMD指令是命令覆盖

❌ 否,仅针对对ENTRYPOINT指令,CMD指令是命令覆盖

PID 1 是谁

应用进程

/bin/sh

推荐场景

长期运行的服务、生产环境

临时命令、调试脚本

性能

更快、更干净

稍慢

2、CMD、ENTRYPOINT指令

1)基本:

二者都是用来提供容器启动时的命令,都支持exec和shell两种书写风格:

ENTRYPOINT虽然默认不能命令的覆盖,但是可以通过--entrypoint参数来实现:

docker run --entrypoint <新命令> <镜像名> [参数...]

看个例子

#dockerfile
FROM alpine:3.20
ENTRYPOINT ["echo", "hahaha!"]

#构建
docker build -t mytest
#运行
docker run mytest
#输出 hahaha!

docker run --entrypoint ls mytest /usr
输出
bin
lib
local
sbin
share

注意:在dockerfile中如果有多个CMD、ENTRYPOING指令,那么最后一个会覆盖前面的。

2)一句话区分CMD和ENTRYPOINT:

示例1:使用CMD:

#dockerfile
FROM busybox
CMD ["echo", "hello world"]

#构建
docker build -t mytest
#运行
docker run mytest
#输出 hello world

# 运行
docker run myimage echo hi
# 输出:hi

说明:CMD 定义的命令可以被你在 docker run 中输入的新命令替换掉。

示例2:ENTRYPOINT示例:

#dockerfile
FROM busybox
ENTRYPOINT ["echo", "hello"]

# 运行
docker run myimage
# 输出:hello

#运行
docker run myimage world
# 输出:hello world

说明:这里 ENTRYPOINT 是固定命令 echo,而 docker run myimage world 中的参数 world 会被当作 ENTRYPOINT 的参数。

示例3:你可以 组合 ENTRYPOINT + CMD 来实现“固定命令 + 默认参数”模式

#dockerfile
FROM busybox
ENTRYPOINT ["sleep"]
CMD ["60"]

#运行
docker run myimage
# 等价于:sleep 60

docker run myimage 10
# 等价于:sleep 10

说明:

3、docker run运行容器时的命令覆盖和参数传递

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

[COMMAND] 和 [ARG...] 到底是「命令覆盖」还是「参数传递」,取决于 Dockerfile 的 ENTRYPOINT/CMD 组合形式。

Dockerfile 内容

启动行为

docker run IMAGE ... 的效果

1️⃣ 只有 CMD

CMD 是默认命令

运行时的 [COMMAND] 会 覆盖 CMD

2️⃣ 只有 ENTRYPOINT

ENTRYPOINT 是固定命令

对于exec风格下,运行时的 [COMMAND] 会被 当作参数传递给 ENTRYPOINT

3️⃣ ENTRYPOINT + CMD

ENTRYPOINT 是固定命令,CMD 提供默认参数

对于exec风格下,运行时的 [COMMAND] 会 覆盖 CMD(参数部分),但 不会覆盖 ENTRYPOINT

1)示例1:CMD 的shell和exec风格:

#docker file
FROM alpine:3.20
CMD echo hello

#构建、运行
docker build -t my-test .
docker run my-test 
输出 hello

docker run my-test echo hi
输出 hi

docker run my-test hi
报错,“hi” executable file not found in $PATH: unknown.

说明:将dockerfile替换成CMD ["echo", "hello"] 运行结果一样!所以,对于CMD指令来说,只有ENTRYPOINT + CMD的组合的时候,才能通过docker run传递参数给CMD,只有CMD的时候,传递的“字符串”都是当命令来执行的,无法单独为其传递参数!

2)示例2:ENTRYPOINT的shell风格:

#docker file
FROM alpine:3.20
ENTRYPOINT echo hahaha

#构建、运行
docker build -t my-test .
docker run my-test 
输出 hahaha

docker run my-test hi
输出 hahaha

docker run my-test echo hi
输出 hahaha

说明:对于ENTRYPOINT的shell风格,无法传递参数!

3)示例3:ENTRYPOINT的exec风格:

#docker file
FROM alpine:3.20
ENTRYPOINT ["echo", "hahaha!"]

#构建、运行
docker build -t my-test .
docker run my-test 
输出 hahaha!

docker run my-test hi
输出 hahaha! hi

docker run my-test echo hi
输出 hahaha! echo hi 

说明:对于ENTRYPOINT的Exec风格,传递的字符串会当作参数传递,并追加进去!

4)示例4:组合模式exec风格

#docker file
FROM alpine:3.20
ENTRYPOINT ["echo"]
CMD ["hi"]

#构建、运行
docker build -t my-test .
docker run my-test 
输出 hi

docker run my-test abc
输出 abc

docker run my-test echo 123
输出 echo 123

说明:docker run后面的字符串,会当作参数覆盖CMD。

5)示例5:组合模式shell风格

#docker file
FROM alpine:3.20
ENTRYPOINT echo
CMD hi

#构建、运行
docker build -t my-test .
docker run my-test 
输出 空

docker run my-test abc
输出 空

docker run my-test echo 123
输出 空

说明:

docker官方文档描述:

When using the exec form of ENTRYPOINT, the CMD value is passed as arguments to ENTRYPOINT.When using the shell form of ENTRYPOINT, the CMD value is ignored.

也就是说, Shell 形式的 ENTRYPOINT 会忽略 CMD 的内容。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文