首发于 Docker入门便炼狱-不是天堂就是地域
Docker-Dockerfile指令最全案例详解 RUN & CMD【上】(6)

Docker-Dockerfile指令最全案例详解 RUN & CMD【上】(6)

Dockerfile 指令是构建镜像的基础单元,所以具备构建一个简明、健壮、实用的镜像,熟练掌握这些指令的使用是必需的。本篇幅会从每个指令的含义用法,通过理解,再结合案例来掌握每个指令的使用;相信通过本篇幅后强加练习编写一个镜像不再是难题。

归纳整理

通过表格,把指令分为操作指令和配置指令两类;

操作指令

指令 解释
RUN 跑、Go很容易理解;运行命令
CMD 启动容器时指定默认执行的命令
ADD add 增加;添加内容到镜像
COPY copy 复制;复制内容到镜像

1、RUN 运行指定命令

格式:

  • RUN <命令>。运行方式:如同在终端直接运行 shell 命令
  • RUN ["<可执行文件>", "<参数1>", "<参数2>"]。这里的指令会被自动解析为 JSON 数组,所以在使用改格式时必须使用双引号。运行方式:使用 exec 执行,不启动 shell 环境

案例一:

在之前的篇章 Dockerfile上下文目录

$ cd /dockerfile_test/dockerinfo
$ vim Dockerfile

编写以下内容

FROM ubuntu
MAINTAINER logic
RUN apt-get update
RUN apt-get install -y nginx
RUN echo 'Use Dockerfile Build Image' > /var/www/html/index.nginx-debian.html
EXPOSE 80

构建一个名REPOSITORY为 nginx,标签TAG 为v100的镜像执行以下命令

$ docker build -t nginx:v100 .

构建完成后会提示两个 Successfully 的提示,执行命令查看当前的镜像文件

$ docker images

可以看到镜像 nginx:v100 构建 OK 了

镜像有了,基于这个镜像开始构建一个名为 web_nginx 容器 执行以下命令

$ docker run -dit -p 80 --name web_nginx nginx:v100 nginx -g "daemon off;"

紧接着查看当前容器执行命令

$ docker ps -a

可以看到一个 端口 49157 映射到了宿主机 80端口,也可以执行命令 docker port 容器id 查看端口信息

[root@instance-4sv44b3l dockerinfo]# docker port 3d59bc877aa3
80/tcp -> 0.0.0.0:49157
80/tcp -> :::49157

到目前为止,容器也有了,最后一步就是验证奇迹

在浏览器上输入 ip+容器端口号

当然也可以在 终端执行 curl ip + 端口号

到此,镜像、容器都构建成功了,这里面有很多命令可能对初学者有点陌生,没关系,先不要深入研究,把命令按照篇章内容敲起来领会一下即可,跟着篇章走后面就会讲到。

优化 Dockerfile

之前的篇章中有说到过,镜像是分层存储的。构建镜像的 Dockerfile 中的每个指令都会建立一层,每一层都会生成一个镜像ID, 回到当前的 Dokcerfile 一共用到了三个 RUN 指令,这样构建出来的镜像就是三层镜像。来看下刚刚构建镜像过程中的几个片段。

在复杂的镜像中这样的构建方式体现地会更加明显;这样构建方式也是不可取,非常不推荐。为什么不推荐这样做,主要的原因是运行时很多不需要的东西都会装到镜像中,比如更新的软件包、编译环境等等;而构建出来的镜像也是非常臃肿且易出错。在我们构建镜像的理念中保持一不三要原则:1:不需要的东西从不构建到镜像中,2:要保持镜像的干净实用、要保持镜像的健壮、要保持良好的编写习惯。对此对当前的镜像进行优化,在行尾添加 \ 的命令换行方 式,并使用 && 将各个所需命令串联起来;以及行首 # 进行注释的格式

案例二:

FROM ubuntu
MAINTAINER logic
RUN apt-get update \
&& apt-get install -y nginx \
&& echo 'Use Dockerfile Build Image' > /var/www/html/index.nginx-debian.html
EXPOSE 80

重新构建一次生成 nginx:v101 web服务镜像

可以看到这里比之前少了两步。

案例三:

这里改成第二种 JSON 数组格式构建 nginx:v102 web服务镜像

apt-get install -y nginx 改写 ["apt-get", "install", "-y", "nginx"]

需要注意的是这里改写成 JSON 数组的形式,前面的 RUN 不可省去简写,否则在构建时会报错,没有指令的错误结构,系统无法识别。

FROM ubuntu
MAINTAINER logic
RUN apt-get update
RUN ["apt-get", "install", "-y", "nginx"] \
&& echo 'Use Dockerfile Build Image' > /var/www/html/index.nginx-debian.html
EXPOSE 80

以上三个案例镜像构建完成后,执行命令 docker history <镜像名称:标签> 或者 <镜像ID>查看构建镜像的整个过程

也可以使用 命令 docker inspect image:tag 命令查看镜像的详细信息

由此总结得出,nginx:v101 镜像构建的层数是最少的,同时也是认为最优的方案。

2、CMD 启动容器时指定默认执行的命令

格式:

  • exec 格式 JSON 数组:CMD ["<可执行文件>", "<参数1>", "<参数2>", "<参数3>"...]
  • shell 命令格式:CMD <命令>
  • 提供参数的格式:CMD ["<参数1>", "<参数2>", "<参数3>"...];该格式是给指令 ENTRYPOINT 提供参数。

\color{red}{\text{注意:}}\ Dockerfile 文件有且只能有一条 CMD 命令,就算在 Dokcerfile 文件写了几百个这样的 CMD 命令,但是只有最后一条 CMD 命令会被执行。如若在启动容器时手动指定了运行命令则会覆盖掉 CMD 指定的命令。

CMD 到底是干嘛的,有什么作用呢?来看下几个案例

案例一:

docker run 执行镜像时输出当前的时间

FROM ubuntu
MAINTAINER logic
RUN apt-get update
RUN ["apt-get", "install", "-y", "nginx"] \
&& echo 'Use Dockerfile Build Image' > /var/www/html/index.nginx-debian.html
CMD echo $(date +%F%n%T)
EXPOSE 80

执行 docker build 构建镜像,在执行 docker run 该镜像时会输出 当前执行的时间。

案例二:

之前在构建容器时的命令中,执行的命令是:

$ docker run -dit -p 80 --name web_nginx nginx:v100 nginx -g "daemon off;"

这里的 nginx -g "daemon off;" 执行 nginx 以"前台"形式运行可执行的文件。 这里我们简化这条命令

FROM ubuntu
MAINTAINER logic
RUN apt-get update