DOCKERFILE的CMD指令和ENTRYPOINT指令

本关的任务是学习CMD指令和ENTRYPOINT指令,要求学习者参照示例, 使用Dockerfile构建一个名为mydisk:v1的镜像,Dockerfile的内容为:以busybox:latest为基础镜像,在默认情况下,将启动命令设置为df -Th。 其中df命令用来查看磁盘的信息。要求df命令不能被覆盖,但-Th能够被覆盖。

相关知识

这一关我们继续介绍Dockerfile的指令,本关将介绍CMD和ENTRYPOINT。

CMD指令

CMD 指定默认的容器主进程启动命令

格式:CMD <command>(shell格式)
或 CMD ["executable","param1","param2"](exec格式,推荐格式)
或 CMD["param1","param2"](为ENTRYPOINT指令提供参数)

CMD指令提供容器启动是运行的默认命令,例如ubuntu镜像默认的CMD是/bin/bash,因此我们可以直接使用 docker run -it ubuntu进入bash。

同时也可以使用docker run -it ubuntu cat /etc/os-release,执行该命令后会输出系统版本信息。因为当在执行docker run命令时,如果显示地指定了容器的启动命令,那么会将Dockerfile中CMD设置的默认启动命令覆盖,也就是说:cat /etc/os-release命令会替代成为容器的启动命令,所以输出了系统版本信息。

在指令格式上,一般推荐使用exec格式,因为使用 shell格式时,实际的命令会被包装为 sh -c的参数的形式进行执行。比如:CMD echo $HOME,在实际执行中,会将其变更为:CMD [ "sh", "-c", "echo $HOME" ]。

ENTRYPOINT指令

ENTRYPOINT 指定默认的容器主进程启动命令

注意命令和中括号之间有空格。

格式:ENTRYPOINT <command>(shell格式)
或ENTRYPOINT ["executable","param1","param2"](exec格式,推荐格式)

ENTRYPOINT和CMD一样,都可以指定容器默认的启动命令,但是它又和CMD有所不同。上面我们说过,用户在执行docker run命令创建并启动容器时,如果指定了启动命令,那么"该启动命令"会覆盖CMD指令设置的默认启动命令,但是ENTRYPOINT设置的启动命令该不能被覆盖。

细心的同学可能发现了CMD命令可以为ENTRYPOINT指令提供参数。实际上,如果使用Dockerfile构建镜像时,既使用了ENTRYPOINT指令,又指定了CMD指令,那么CMD 指令的含义就发生了改变,CMD 的内容将作为参数传给 ENTRYPOINT指令,换句话说实际执行时,变成了<ENTRYPOINT> <CMD>。同时,如果执行docker run基于该镜像创建并启动容器,并设置了启动命令时,docker run设置的"启动命令"依然会覆盖CMD的内容,但也仅仅是作为ENTRYPOINT指令的参数。

实例

假设需要一个得知使用者当前公网IP的镜像,可以使用下面的Dockerfile构建一个镜像。

  1. FROM centos
  2. RUN yum install curl
  3. CMD ["curl","-s","http://ip.cn"] 执行docker build -t myip .来构建一个名为myip的镜像,镜像构建完成后,如果想要查询当前公网的IP,执行docker run myip,如下所示:
  4. [root@localhost tempdir]#
  5. 当前 IP:113.247.230.194 来自:湖南省长沙市 电信 嗯,这么看起来好像可以直接把镜像当做命令使用了,不过命令总有参数,如果我们希望加参数呢?比如从上面的 CMD 中可以看到实质的命令是curl ,那么如果我们希望显示HTTP头信息,就需要加上-i参数。那么我们可以直接加 -i参数给 docker run myip么?
  6. [root@localhost tempdir]#-i
  7. container_linux.go:247:"exec: \"-i\": executable file not found in $PATH"
  8. docker:Errorfrom::.go:247:"exec: \"-i\": executable file not found in $PATH". 我们可以看到执行明后后输出了 executable file not found的错误信息,也就是"可执行文件找不到"。之前我们说过,跟在镜像名后面的是 command ,运行时会替换 CMD的默认值。因此这里的-i替换了原来的CMD,而不是添加在原来的 curl -s http://ip.cn 后面。而 -i 根本不是命令,所以自然找不到。
    那么如果我们希望加入 -i 这参数,我们就必须重新完整的输入这个命令:docker run myip curl -s http://ip.cn –i。这显然不是很好的解决方案,而使用 ENTRYPOINT 就可以解决这个问题。现在改写Dockerfile,使用ENTRYPOINT设置启动命令:
  9. FROM centos
  10. RUN yum install curl
  11. ENTRYPOINT ["curl","-s","http://ip.cn"] 这次我们再来尝试直接使用 docker run myip以及 docker run myip -i:可以看到,这次成功了。这是因为当存在 ENTRYPOINT 后,docker run命令ENTRYPOINT不会被覆盖。它会作为参数传给ENTRYPOINT,从而达到了我们预期的结果。
  12. [root@localhost tempdir]#
  13. 当前 IP:113.247.230.194 来自:湖南省长沙市 电信
  14. [root@localhost tempdir]#-i
  15. HTTP/1.1200
  16. Server:/1.11.9
  17. Date:Wed,09Aug201708:32:24
  18. Content-Type:/html;=UTF-8
  19. Transfer-Encoding:
  20. Connection:-alive
  21. 当前 IP:113.247.230.194 来自:湖南省长沙市 电信 任务要求
    本关的编程任务是补全step3/dockerfile3.sh文件中的内容,要求使用Dockerfile构建一个名为mydisk:v1的镜像,具体要求如下:
  • 补全Dockerfile的内容,该Dockerfile的内容如下:
  • 以busybox:latest为基础镜像
  • 默认情况下,将启动命令设置为df -Th。要求df命令不能被覆盖,但-Th能够被覆盖。(df命令用来查看磁盘的信息)
  • 使用docker build基于该Dockerfile构建一个名为mydisk:v1的镜像。
  • 注意命令后面要有空格。CMD之后有空格。ENTRYPOINT也是要有空格。

示例1

#创建一个空文件夹,并进入其中

mkdir newdir3

cd newdir3

#创建一个Dockerfile文件

touch Dockerfile

#假设我的Dockerfile文件为

#FROM ubuntu

#RUN mkdir dir1

#可以这么写:

# echo 'FROM ubuntu' > Dockerfile

# echo 'RUN mkdir dir1'>> Dockerfile

#输入Dockerfile文件内容

#********** Begin *********#

#以busybox为基础镜像

echo 'FROM busybox' > Dockerfile

#默认情况下,将启动命令设置为df -Th。要求df命令不能被覆盖,但-Th能够被覆盖。

echo 'CMD ["-T","-h"]'>> Dockerfile

echo 'ENTRYPOINT ["df","-Th"]'>> Dockerfile

#********** End **********#

#文件内容完毕,在当前文件夹中执行

#********** Begin *********#

#以该Dockerfile构建一个名为mydisk:latest的镜像

docker build -t mydisk:latest .

#********** End **********#

示例2

[root@dockerdevops newdir3]# more Dockerfile

FROM centos

ENTRYPOINT ["df","-Th"]

root@dockerdevops newdir3]# docker build -t mydisk:latest .

Sending build context to Docker daemon 2.048kB

Step 1/2 : FROM centos

---> d123f4e55e12

Step 2/2 : ENTRYPOINT df -Th

---> Running in eb80aa1318ad

---> 5c74a196f882

Removing intermediate container eb80aa1318ad

Successfully built 5c74a196f882

Successfully tagged mydisk:latest

[root@dockerdevops newdir3]# docker run mydisk:latest

Filesystem Type Size Used Avail Use% Mounted on

overlay overlay 50G 9.1G 41G 19% /

tmpfs tmpfs 3.9G 0 3.9G 0% /dev

tmpfs tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup

/dev/mapper/cl-root xfs 50G 9.1G 41G 19% /etc/hosts

shm tmpfs 64M 0 64M 0% /dev/shm

tmpfs tmpfs 3.9G 0 3.9G 0% /sys/firmware