一个计算机技术爱好者与学习者

0%

好好学Docker:Dockerfile中的CMD和ENTRYPOINT

1. 前言

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

2. 指令执行方式

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

2.1. 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 ,变量会被替换。

2.2. exec方式

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

数组中每个元素都是一个整体。
如果写成 ["executable param1 param2"]executable param1 param2会被当成一个可执行程序,是找不到路径的。
如果写成 ["executable", "param1 param2"]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 ,变量会被替换。

3. CMD和ENTRYPOINT

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

3.2. 执行特定命令

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

1
docker run ubuntu:16.04 sleep 3600

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

1
2
3
FROM ubuntu:16.04

CMD ["sleep","3600"]

生成新的镜像,执行默认命令 sleep 3600

1
2
docker build -t ubuntu-sleeper .
docker run ubuntu-sleeper

3.3. 特定参数

如果我们想要修改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"]
1
2
3
4
5
6
7
8
# 生成新镜像
docker build -t ubuntu-sleeper .

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

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

ENTRYPOINT里的命令是否可以被替换的呢?也是可以的,以执行 sleep2.0 1200 为例

1
docker run --entrypoint sleep2.0 ubuntu-sleeper 1200

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

4. k8s中的args和command

在k8s中定义pod时,有args和command两个字段。这两个字段,分别覆盖CMD和ENTRYPOINT。
k8s中的args和command,只支持exec的执行方式,因此参数格式为:
["executable", "param1", "param2", ...]

例如:

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