0%

Dockerfile中的CMD和ENTRYPOINT

前言

Dockerfile中的CMD和ENTRYPOINT有什么区别?
docker run时默认执行什么命令,怎样覆盖默认命令?
pod定义中的args和command字段有什么作用?
本文,我们来回答一下这些问题。主要参考Docker RUN vs CMD vs ENTRYPOINTMumshad Mannambeth的课程

指令执行方式

RUN和ENTRYPOINT指令都可以有两种执行方式:shell方式和exec方式。

shell方式

shell方式格式:
<instruction> <command>

例子:

1
2
3
RUN apt-get install python3
CMD echo "Hello world"
ENTRYPOINT echo "Hello world"

当指令以shell方式执行时,它会在后台调用 /bin/sh -c <command>,并且会进行常规的shell处理。例如,Dockerfile中的以下定义:

1
2
ENV name voidking
ENTRYPOINT echo "Hello, $name"

docker run 会输出 Hello, voidking ,变量会被替换。

exec方式

exec方式格式:
<instruction> ["executable", "param1", "param2", ...]
例子:

1
2
3
RUN ["apt-get", "install", "python3"]
CMD ["/bin/echo", "Hello world"]
ENTRYPOINT ["/bin/echo", "Hello world"]

当指令以exec方式执行时,它将直接调用可执行文件,并且不会进行shell处理。例如,Dockerfile中的以下定义:

1
2
ENV name voidking
ENTRYPOINT ["/bin/echo", "Hello, $name"]

docker run 会输出 Hello, $name ,变量不会被替换。

如果需要运行bash而不是sh,需要使用exec方式。在这种情况下,将进行常规的shell处理。例如,Dockerfile中的以下定义:

1
2
ENV name voidking
ENTRYPOINT ["/bin/bash", "-c", "echo Hello, $name"]

docker run 会输出 Hello, voidking ,变量会被替换。

CMD和ENTRYPOINT

CMD定义

访问dockerhub ubuntu,Supported tags and respective Dockerfile links,随便选择一个系统版本,这里选择 16.04 。点击链接,可以看到Dockerfile的定义。

1
2
3
4
5
6
FROM scratch
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /

# overlook all the definition

CMD ["/bin/bash"]

可以看到,Dockerfile中定义了CMD为 /bin/bash ,也就是定义了默认命令为 /bin/bash

docker run ubuntu:16.04 会执行默认命令 /bin/bash

执行特定命令

我们想要执行命令,那么需要在docker run时指定命令,覆盖默认命令。
docker run ubuntu:16.04 sleep 3600,会执行 sleep 3600

如果想要使这个特定命令永久生效,那么需要使用Dockerfile定义一个新的镜像。

1
2
3
FROM ubuntu:16.04

CMD ["sleep","3600"]

docker build -t ubuntu-sleeper .,生成新的镜像。

docker run ubuntu-sleeper,执行默认命令 sleep 3600

特定参数

如果我们想要修改sleep的时间,该怎么做?

1
2
docker run ubuntu:16.04 sleep 3600
docker run ubuntu:16.04 sleep 1200

sleep命令没有变,变化的只有参数,sleep是否可以省略?可以的,定义一个新的镜像。

1
2
3
4
FROM ubuntu:16.04

ENTRYPOINT ["sleep"]
CMD ["3600"]

docker build -t ubuntu-sleeper .,生成新的镜像。

docker run ubuntu-sleeper,执行默认命令 sleep 3600

docker run ubuntu-sleeper 1200,执行命令 sleep 1200

那么,ENTRYPOINT里的命令是否可以被替换的呢?也是可以的。
docker run --entrypoint sleep2.0 ubuntu-sleeper 1200,执行命令 sleep2.0 1200

综上,docker run会默认执行 ENTRYPOINT + CMD
通常情况下,我们会在Dockerfile中定义ENTRYPOINT作为固定命令,定义CMD作为默认参数。

args和command

在k8s中定义pod时,有args和command两个字段。这两个字段,分别覆盖CMD和ENTRYPOINT。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: ubuntu
name: ubuntu
spec:
containers:
- image: ubuntu:16.04
name: ubuntu
resources: {}
command: ["sleep"]
args: ["1200"]
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}

该pod启动后的执行命令为 sleep 1200