0%

docker CMD 与 ENTRYPOINT 的区别与选择

在面试中被问到这个问题,平时写 Dockerfile 都是从各个文件中copy的,能用就行,没有关心里面的细节,这次来弄懂一下 CMD 与 ENTRYPOINT。

一、CMD与ENTRYPOINT 的区别

资料:

1.1 CMD

CMD 执行三种方式:

  • CMD ["executable","params1","params2"] exec 形式,常见的形式

  • CMD ["param1","param2"] 作为 ENTRYPOINT的默认参数

  • CMD command param1 param2 (shell form),是以 /bin/sh -c来执行的
    CMD echo 'test'

只能有一个CMD指令,如果有多个,只有最后一个会生效

CMD的主要目的是为执行的容器提供默认执行的命令。

默认值可以包含一个可执行文件。

如果不包含可执行文件,则必须制定一条ENTRYPOINT指令。

在EXEC这模式或者默认参数时,都是作为json来解析的,所以必须使用双引号

命令如果不以 shell 形式的话,必须使用 可执行文件的完成路径来执行

CMD ["/bin/ls","-la","/root"]

1
2
3
4
5
FROM busybox:1.35.0
ENV mode=prod
CMD ["sh","-c","echo ${mode}" ]


1.2 ENTRYPOINT

ENTRYPOINT 执行形式:

  • ENTRYPOINT ["executable", "param1", "param2"]exec 形式

  • ENTRYPOINT command param1 param2shell 形式

这个跟CMD 类似的,不同在于以 docker run命令行参数将追加在 exec 形式的ENTRYPOINT,并会覆盖CMD中的所有的元素

可以使用—-entrypoint覆盖默认的 ENTRYPOINT

docker run -i -t --rm -p 8000:80 --entrypoint ls nginx

ENTRYPOINTshell模式阻止CMD或者run命令行参数,但是 ENTRYPOINT指令是用 /bin/sh -c执行的。

这样导致PID 1的进程是 /bin/sh而不是ENTRYPOINT可执行程序,这样导致收不到Unix signals(只有PID 1的进程可以收到)

也就收不到来自 docker stop <container>SIGTERM,可以使用exec 模式解决这个问题

1
2
3
4
5
6
7
8
9
#!/bin/sh
trap "echo 123" TERM 15
echo $$
while true; do
echo abc
sleep 1
done


官方文档提示的捕获TERM信号,来 gracefully shutdown

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/sh
# Note: I've written this using sh so it works in the busybox container too

# USE the trap if you need to also do manual cleanup after the service is stopped,
# or need to start multiple services in the one container
trap "echo TRAPed signal" HUP INT QUIT TERM

# start service in background here
/usr/sbin/apachectl start

echo "[hit enter key to exit] or run 'docker stop <container>'"
read

# stop service and clean up here
echo "stopping apache"
/usr/sbin/apachectl stop

echo "exited $0"

二、疑问

2.1 CMD 与 ENTRYPOINT 什么时候使用

通过示例展示如何使用

1
2
3
4
#!/bin/sh
# test.sh
echo $*

1
2
3
4
FROM ubuntu:latest
COPY test.sh /data/
ENTRYPOINT ["/data/test.sh"]
CMD ["-ok"]
1
2
3
4
5
docker build . -t cmd:test
docker run cmd:test # 打印 -ok
docker run cmd:test -b # 打印 -b
# 使用 ENTRYPOINT 结合 CMD,提供默认执行入口和默认参数,并提供可以覆盖的参数

官方有个表格,展示了CMDENTRYPOINT执行时的情况

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 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

简单总结就是:&#x20;

  1. 使用 ENTRYPOINT shell形式的,就不会管 CMD,一般不用 shell模式,进程PID不为1,被/bin/sh占用了

  2. 使用 ENTRYPOINT exec 形式的,会结合CMD

总是使用 ENTRYPOINT总没错的

2.2 CMD中如何获取ENV

  1. 使用 CMD echo ${mode} shell 形式

  2. 使用CMD ["/bin/sh","-c","echo ${mode}"]
    使用 CMD ["echo","${HOME}" ]是获取不到HOME的,因为使用exec模式,不是使用 sh来执行命令的