Docker 镜像结构与相关核心知识总结
Docker 镜像结构与相关核心知识总结
Docker 镜像展开,从镜像与容器的关系、镜像分层结构、镜像构建方法及 Dockerfile 核心语法四个维度,系统阐述了 Docker 镜像的核心知识,旨在帮助使用者理解镜像本质、掌握镜像构建与定制技巧
一、镜像与容器的关系
1. 核心关联
- 镜像为容器提供基础:Docker 镜像是容器运行的前提,无镜像则无法创建容器,这是 Docker 的核心设计原则。镜像属于静态内容,包含容器运行所需的文件系统(如代码、二进制文件、运行时、依赖等),但不包含 Linux 内核;容器是动态进程,运行时占用内存、CPU、虚拟网络设备,并通过镜像的文件系统层获取资源
- 转化机制:镜像通过自身的
json
文件实现静态到动态的转化。Docker 守护进程(Docker Daemon)解析json
文件,获取容器需运行的进程、环境变量配置,为容器分配资源并启动进程,最终创建容器。容器启动后,json
文件失效,镜像仅作为文件系统供容器进程访问
2. 本质区别
维度 | Docker 镜像 | Docker 容器 |
---|---|---|
状态 | 静态(文件集合) | 动态(运行进程) |
核心作用 | 提供文件系统和运行配置 | 执行应用进程,占用系统资源 |
生命周期 | 构建后固定,可重复使用 | 启动后存在,停止后可删除 |
二、镜像的分层结构
1. 基础概念:Base 镜像
Base 镜像是分层结构的根基,具备两层核心含义:
- 不依赖其他镜像,从空白镜像
scratch
构建 - 可作为基础,供其他镜像扩展(如 CentOS、Ubuntu 等 Linux 发行版镜像)
不同 Linux 发行版 Base 镜像的区别主要在用户空间(rootfs),如 CentOS 7 用systemd
管理服务、yum
管理软件,Ubuntu 14.04 用upstart
和apt
,而内核空间(Kernel)依赖 Docker 主机(Docker Host)的内核,容器无法修改内核
2. 分层机制
- 镜像层:绝大多数镜像基于其他镜像构建,每一步操作(如安装软件、创建目录)生成一个新的镜像层,所有镜像层联合组成统一文件系统。若不同层有相同路径文件(如
/galaxy
),上层文件会覆盖下层,用户仅能访问上层文件 - 容器层:容器启动时,镜像顶部加载一个可写的容器层,容器层之下均为只读的镜像层。容器运行中的文件修改(如创建、删除文件)仅发生在容器层,不会影响镜像层,确保镜像可重复使用
三、Docker 镜像构建方法
Docker 提供三种镜像构建方式,各有适用场景与优缺点,具体对比如下:
构建方法 | 核心操作 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
docker commit 命令 | 1. 基于基础镜像启动容器;2. 在容器内安装软件 / 配置环境;3. 用docker commit <容器名> <新镜像名> 打包容器为镜像 | 操作直观,适合临时测试 | 手工操作易出错、效率低;镜像构建过程不透明,存在安全隐患(无法审计是否含恶意程序) | 快速生成临时镜像,不推荐生产环境 |
基于本地模板导入 | 1. 下载操作系统模板(如 OpenVZ 模板,地址:http://openvz.org/Download/templates/precreated);2. 用docker import <模板文件> <镜像名:标签> 导入 | 直接复用现成模板,操作简单 | 模板来源有限,定制化程度低 | 快速获取特定版本操作系统镜像 |
Dockerfile 构建(推荐) | 1. 编写 Dockerfile(含基础镜像、操作指令等);2. 用docker build -t <镜像名:标签> [路径] 执行构建 | 构建过程透明(可通过docker history <镜像名> 查看);可重复执行,支持版本控制;定制化灵活 | 需学习 Dockerfile 语法 | 生产环境、自定义应用镜像构建 |
四、Dockerfile 核心知识
1. 基本概念与文件结构
Dockerfile 是一个文本文件,包含一系列指令,每一条指令对应一层镜像,通过指令描述镜像的构建步骤。其文件结构分为四部分:
- 基础镜像信息:必须放在首行(非注释行),用
FROM
指定 - 维护者信息:用
MAINTAINER
(旧版)或LABEL
(新版)指定 - 镜像操作指令:如安装软件(
RUN
)、复制文件(COPY/ADD
)、暴露端口(EXPOSE
)等 - 容器启动执行指令:如
CMD
(指定默认启动进程)、ENTRYPOINT
(指定固定启动进程)
2. 常用指令及语法
Dockerfile 指令决定镜像的构建逻辑与容器启动行为,核心指令如下:
指令 | 作用 | 语法格式 | 关键说明 |
---|---|---|---|
FROM | 指定基础镜像 | FROM <仓库名>:<标签> 或 FROM <仓库名>@<哈希值> | 哈希值(Digest)用于防止镜像被冒名顶替,确保安全性 |
MAINTAINER/LABEL | 声明镜像作者 / 元数据 | MAINTAINER <邮箱> ;LABEL <键>=<值> <键>=<值> | MAINTAINER 已被LABEL 替代,LABEL 支持多键值对元数据 |
COPY/ADD | 复制文件到镜像 | COPY <源路径> <目标路径> ;ADD <源路径> <目标路径> | ADD 支持自动解压本地 tar 文件、下载 URL 文件(URL tar 不自动解压),COPY 仅复制本地文件,推荐优先用COPY |
RUN | 构建镜像时执行命令 | RUN <shell命令> 或 RUN ["可执行文件", "参数1", "参数2"] | 命令需在基础镜像中存在,运行于docker build 阶段 |
CMD | 指定容器默认启动进程 | 1. CMD ["可执行文件", "参数1", "参数2"] ;2. CMD <shell命令> ;3. CMD ["参数1", "参数2"] (配合ENTRYPOINT ) | 仅最后一个CMD 生效;docker run 命令行参数可覆盖CMD ;进程结束后容器终止 |
ENTRYPOINT | 指定容器固定启动进程 | 1. ENTRYPOINT ["可执行文件", "参数1", "参数2"] ;2. ENTRYPOINT <shell命令> | docker run 命令行参数会作为参数传递给ENTRYPOINT ,仅--entrypoint 选项可覆盖 |
EXPOSE | 声明容器待暴露端口 | EXPOSE <端口>/<协议> (如EXPOSE 80/tcp 443/udp ) | 仅为声明,需docker run -P (随机映射)或-p <主机端口>:<容器端口> 实现真正端口映射 |
ENV | 定义环境变量 | ENV <键> <值> 或 ENV <键>=<值> <键>=<值> | 变量可被后续指令调用($变量名 或${变量名} ),docker run -e 可覆盖变量值 |
WORKDIR | 指定工作目录 | WORKDIR <绝对/相对路径> | 影响后续指令的执行目录,可调用ENV 定义的变量 |
VOLUME | 创建镜像挂载点 | VOLUME <挂载点> 或 VOLUME ["<挂载点>"] | 仅支持 Docker 管理的卷(指定容器内路径),不支持宿主机路径,用于数据持久化 |
USER | 指定构建 / 运行用户 | USER <用户名>:<组名> 或 USER <UID>:<GID> | 默认用户为 root,用于限制容器进程权限 |
3. 镜像缓存机制
- 缓存原理:
docker build
时,Docker 会缓存每一层镜像。若构建新镜像时,指令与之前一致(如基础镜像未变、RUN
命令未改),则直接复用缓存层,加速构建 - 禁用缓存:若需强制重新构建所有层,可在
docker build
后加--no-cache
参数(如docker build --no-cache -t <镜像名> .
)
五、核心总结
- 镜像本质:静态文件系统集合,无内核,为容器提供运行基础;容器是动态进程,依赖镜像文件系统并占用系统资源
- 分层优势:Base 镜像为根基,镜像层只读可复用,容器层可写隔离修改,降低存储占用与构建时间。
- 构建首选:Dockerfile 构建是生产环境首选,因其透明、可重复、易维护,
docker commit
与模板导入仅适合临时场景 - 指令关键区别:
RUN
(构建时执行)与CMD/ENTRYPOINT
(容器启动时执行);CMD
可被命令行覆盖,ENTRYPOINT
仅能被--entrypoint
覆盖
分层结构
Docker里的镜像绝大部分都是在别的镜像的基础上去进行创建的,也就是使用镜像的分层结
构。实际上,Docker Hub中99%的镜像都是通过在base镜像中安装和配置需要的软件构建出来
的。比如我们现在构建一个新的镜像,具体操作如下:
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 07ccdb783875 14 hours ago 160MB
luoqi v1 e80543456e0c 8 days ago 204MB
nginx <none> 203ad09fc156 7 weeks ago 192MB
httpd latest 2416cb32cb59 2 months ago 117MB
httpd sysy 2416cb32cb59 2 months ago 117MB
centos 7 eeb6ee3f44bd 4 years ago 204MB
[root@docker ~]# vim Dockerfile
FROM centos:7
RUN mkdir /luoqi
RUN touch /luoqi/cy
CMD ["/bin/bash"]
~
[root@docker ~]# docker build -t luoqi:v1 .
[+] Building 0.8s (7/7) FINISHED docker:default=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 106B 0.0s=> [internal] load metadata for docker.io/library/centos:7 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [1/3] FROM docker.io/library/centos:7 0.0s=> [2/3] RUN mkdir /luoqi 0.4s=> [3/3] RUN touch /luoqi/cy 0.3s=> exporting to image 0.0s=> => exporting layers 0.0s=> => writing image sha256:f72f60e820dc6bbeca2e398bb6342d8aa98c07289fcfcf2cc338b97305127e 0.0s=> => naming to docker.io/library/luoqi:v1
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
luoqi v1 f72f60e820dc 46 seconds ago 204MB
[root@docker ~]# docker history luoqi:v1
IMAGE CREATED CREATED BY SIZE COMMENT
f72f60e820dc About a minute ago CMD ["/bin/bash"] 0B buildkit.dockerfile.v0
<missing> About a minute ago RUN /bin/sh -c touch /luoqi/cy # buildkit 0B buildkit.dockerfile.v0
<missing> About a minute ago RUN /bin/sh -c mkdir /luoqi # buildkit 0B buildkit.dockerfile.v0
<missing> 4 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 4 years ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 4 years ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4… 204MB
docker镜像构建三种方法
Docker提供了三种构建镜像的方法:
1、docker commit命令
2、基于本地模板导入
3、Dockerfile构建文件
docker commit
docker commit命令可以基于容器创建镜像,创建过程大致分为三步,先创建容器,在容器中安装我
们所需要的内容,再使用docker commit将容器打包为镜像即可
下面展示一个示例:在centos的base镜像中安装vim-common并保存为新镜像
1、先基于centos7运行容器,容器名为cy,并使用-it生成终端进入容器
[root@docker ~]# docker run --name sy -it centos:7 /bin/bash
2、在容器中安装vim-common
[root@306bed18de27 /]# yum -y install vim-common
Loaded plugins: fastestmirror, ovl
Determining fastest mirrors
Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=container error was
14: curl#6 - "Could not resolve host: mirrorlist.centos.org; Unknown error"One of the configured repositories failed (Unknown),and yum doesn't have enough cached data to continue. At this point the onlysafe thing yum can do is fail. There are a few ways to work "fix" this:1. Contact the upstream for the repository and get them to fix the problem.2. Reconfigure the baseurl/etc. for the repository, to point to a workingupstream. This is most often useful if you are using a newerdistribution release than is supported by the repository (and thepackages for the previous distribution release still work).3. Run the command with the repository temporarily disabledyum --disablerepo=<repoid> ...4. Disable the repository permanently, so yum won't use it by default. Yumwill then just ignore the repository until you permanently enable itagain or use --enablerepo for temporary usage:yum-config-manager --disable <repoid>orsubscription-manager repos --disable=<repoid>5. Configure the failing repository to be skipped, if it is unavailable.Note that yum will try to contact the repo. when it runs most commands,so will have to try and fail each time (and thus. yum will be be muchslower). If it is a very temporary problem though, this is often a nicecompromise:yum-config-manager --save --setopt=<repoid>.skip_if_unavailable=trueCannot find a valid baseurl for repo: base/7/x86_64
3、退出容器后,使用docker commit将v1容器打包为镜像,新镜像名为centos:v1
[root@docker ~]# docker commit sy centossy:7
sha256:4d740ad40a41bccac21a453d819248a1c2c11811acf4c1fb0f2624912f6c0aba
[root@docker ~]# docker images | grep centos
centossy 7 4d740ad40a41 13 seconds ago 228MB
centos 7 eeb6ee3f44bd 4 years ago 204MB
[root@docker ~]# docker run --name sy2 -it centossy:7
[root@f43620dfbe58 /]# [root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centossy 7 4d740ad40a41 About a minute ago 228MB
docker commit
这样一个新的镜像就构建完成了,centoscy:7镜像是在centos:7镜像基础之上创建的,通过查看镜像
属性,发现centoscy:7要比centos:7镜像大一些
然而,Docker并不建议用户通过这种方式构建镜像。这是一种手工创建镜像的方式,容易出错,效
率低且可重复性弱。更重要的,使用者并不知道镜像是如何创建出来的,里面是否有恶意程序。也就是说无法对镜像进行审计,存在安全隐患
基于本地模板导入
用户可以直接从一个操作系统模板文件导入一个镜像,主要使用 docker [container] import 命令
命令 格式为 docker [image] import [OPTIONS] file|URL|-[REPOSITORY[:TAG]] ,要直接导入一个镜
像,可以使用 OpenVZ 提供的模板来创建,或者用其他已导入的镜像模板来创建。OpenVZ 模板的下载
地址为 http://openvz.org/Download/templates/precreated
如:下载了 ubuntu:12.04 的模板压缩包,之后使用以下命令导入即可:
[root@docker ~]# rz -E
rz waiting to receive.
[root@docker ~]# ls
anaconda-ks.cfg Desktop Downloads nginx Templates
centos.tar.gz Dockerfile initial-setup-ks.cfg Pictures ubuntu-12.04-x86-minimal.tar.gz
data Documents Music Public Videos
[root@docker ~]# docker import ubuntu-12.04-x86-minimal.tar.gz syubuntu:v1
sha256:c24052f699a9d66f3875765db4a0960a17d8c3b185140083a5d6c12ae5a63fdf
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
syubuntu v1 c24052f699a9 5 seconds ago 146MB
dockerfile的基本概念
Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件
外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构
建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问
题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是Dockerfile
Dockerfile是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因
此每一条指令的内容,就是描述该层应当如何构建。有了Dockerfile,当我们需要定制自己额外的
需求时,只需在 Dockerfile上添加或者修改指令,重新生成镜像即可,省去了敲命令的麻烦
dockerfile文件格式
Dockerfile分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动执行指令。一开始必须要指明所基于的镜像名称,接下来一般会说明维护者信息;后面则是镜像操作指令,例如ADD指令。每执行一条ADD指令,镜像添加新的一层,并提交;最后是CMD指令,来指明运行容器时的操作命令。
示例如下,我们构建一个httpd镜像
[root@docker ~]# ls
Dockerfile
[root@docker ~]# vim Dockerfile
FROM centos:7 # 1、第一行必须指定,基础镜像信息
MAINTAINER shenyi@example.com # 2、维护者信息
RUN yum install -y httpd 3、镜像操作指令
EXPOSE 80
CMD ["/bin/bash"] # 4、容器启动执行指令
~
把构建容器所需要的指令都存放在Dockerfile文件中,这个文件的名字是固定的,不能够更改,再使用docker build命令构建容器,使用-t定义新的镜像名,如果构建镜像的Dockerfile文件不在当前目录下可以使用-f指定
Dockerfile文件路径,示例如下:
[root@docker ~]# docker build -t httpd:sysy /root/
[+] Building 1.3s (5/5) FINISHED docker:default=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 134B 0.0s=> [internal] load metadata for docker.io/library/centos:7 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> CACHED [1/2] FROM docker.io/library/centos:7 0.0s=> ERROR [2/2] RUN yum install -y httpd 1.3s
------ > [2/2] RUN yum install -y httpd:
0.506 Loaded plugins: fastestmirror, ovl
0.704 Determining fastest mirrors
1.216 Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=container error was
1.216 14: curl#6 - "Could not resolve host: mirrorlist.centos.org; Unknown error"
1.221
1.221
1.221 One of the configured repositories failed (Unknown),
1.221 and yum doesn't have enough cached data to continue. At this point the only
1.221 safe thing yum can do is fail. There are a few ways to work "fix" this:
1.221
1.221 1. Contact the upstream for the repository and get them to fix the problem.
1.221
1.221 2. Reconfigure the baseurl/etc. for the repository, to point to a working
1.221 upstream. This is most often useful if you are using a newer
1.221 distribution release than is supported by the repository (and the
1.221 packages for the previous distribution release still work).
1.221
1.221 3. Run the command with the repository temporarily disabled
1.221 yum --disablerepo=<repoid> ...
1.221
1.221 4. Disable the repository permanently, so yum won't use it by default. Yum
1.221 will then just ignore the repository until you permanently enable it
1.221 again or use --enablerepo for temporary usage:
1.221
1.221 yum-config-manager --disable <repoid>
1.221 or
1.221 subscription-manager repos --disable=<repoid>
1.221
1.221 5. Configure the failing repository to be skipped, if it is unavailable.
1.221 Note that yum will try to contact the repo. when it runs most commands,
1.221 so will have to try and fail each time (and thus. yum will be be much
1.221 slower). If it is a very temporary problem though, this is often a nice
1.221 compromise:
1.221
1.221 yum-config-manager --save --setopt=<repoid>.skip_if_unavailable=true
1.221
1.221 Cannot find a valid baseurl for repo: base/7/x86_64
------
Dockerfile:3
--------------------1 | FROM centos:72 | MAINTAINER shenyi@example.com3 | >>> RUN yum install -y httpd4 | EXPOSE 805 | CMD ["/bin/bash"]
--------------------
ERROR: failed to solve: process "/bin/sh -c yum install -y httpd" did not complete successfully: exit code: 1
通过以上镜像的构建过程可以看出,Dockerfile文件内的指令会逐一运行,构建过程如下:
1、下载centos7镜像
2、添加镜像构建者信息
dockerfile文件格式
3、基于centos7镜像启动容器,安装httpd软件,安装完毕后将容器打包为镜像
4、基于上一步生成的镜像启动容器,将80端口打开,打开后将容器打包为镜像
5、基于上一步生成的镜像启动容器,添加容器启动后需要执行的指令,再打包为镜像
也就说一条指令就是一层镜像,还可以通过docker history查看镜像的构建过程,这样我们构建的镜像就呈现出透明化,整个构建的过程都可以看到
[root@docker ~]# docker history httpd:sysy
IMAGE CREATED CREATED BY SIZE COMMENT
2416cb32cb59 2 months ago CMD ["httpd-foreground"] 0B buildkit.dockerfile.v0
<missing> 2 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
<missing> 2 months ago COPY httpd-foreground /usr/local/bin/ # buil… 138B buildkit.dockerfile.v0
<missing> 2 months ago STOPSIGNAL SIGWINCH 0B buildkit.dockerfile.v0
<missing> 2 months ago RUN /bin/sh -c set -eux; savedAptMark="$(a… 32.5MB buildkit.dockerfile.v0
<missing> 2 months ago ENV HTTPD_PATCHES= 0B buildkit.dockerfile.v0
<missing> 2 months ago ENV HTTPD_SHA256=58b8be97d9940ec17f7656c0c6b… 0B buildkit.dockerfile.v0
<missing> 2 months ago ENV HTTPD_VERSION=2.4.65 0B buildkit.dockerfile.v0
<missing> 2 months ago RUN /bin/sh -c set -eux; apt-get install --… 5.65MB buildkit.dockerfile.v0
<missing> 2 months ago WORKDIR /usr/local/apache2 0B buildkit.dockerfile.v0
<missing> 2 months ago RUN /bin/sh -c mkdir -p "$HTTPD_PREFIX" && … 0B buildkit.dockerfile.v0
<missing> 2 months ago ENV PATH=/usr/local/apache2/bin:/usr/local/s… 0B buildkit.dockerfile.v0
<missing> 2 months ago ENV HTTPD_PREFIX=/usr/local/apache2 0B buildkit.dockerfile.v0
<missing> 2 months ago # debian.sh --arch 'amd64' out/ 'trixie' '@1… 78.6MB debuerreotype 0.16
镜像缓存
使用DockerFile文件构建完镜像以后,Docker会把构建过程中的每一层临时镜像进行缓存。在构建新镜像时,可以直接使用之前缓存的镜像层,这样能加速镜像的构建。镜像缓存示例如下:
[root@docker ~]# vim Dockerfile
FROM centos:7 # 1、第一行必须指定,基础镜像信息
MAINTAINER shenyi@example.com # 2、维护者信息
RUN mkdir /galaxy # 3、镜像操作指令
~
修改之前的Dockerfile文件,然后我们再构建新的镜像,构建过程如下,通过构建过程可以得知,DockerFile文件里面共三条指令,前两条指令都是用之前构建镜像的缓存,只有第三个指令才重新构建了缓存层。如果希望在构建镜像时不使用缓存,可以在docker build命令中加上–no-cache参数
[root@docker ~]# docker build -t sycentos:7 .
[+] Building 0.4s (6/6) FINISHED docker:default=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 99B 0.0s=> [internal] load metadata for docker.io/library/centos:7 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> CACHED [1/2] FROM docker.io/library/centos:7 0.0s=> [2/2] RUN mkdir /galaxy 0.3s=> exporting to image 0.0s=> => exporting layers 0.0s=> => writing image sha256:74d5027f7c62dee6b2e318a3169c1bedd596e2a1ad2cdad3facfa36ccfbf2f 0.0s=> => naming to docker.io/library/sycentos:7
总结
1、镜像的来源多种多样,可以通过Docker Hub社区获取,或者通过国内的公有镜像仓库中获取
2、Docker镜像是Docker容器运行的基础,没有Docker镜像,就不可能有Docker容器,这也是Docker的设计原则之一
3、Docker可以同时支持多种Linux镜像,模拟出多种操作系统环境。容器只能使用Docker host的kernel,并且不能修改
4、base镜像有两层含义:不依赖其他镜像,从scratch构建;以此为基础镜像,进行扩展
5、构建镜像的方式有三种:使用docker commit构建、基于本地模板导入和使用Dockerfile构建构建
6、docker commit命令可以基于容器创建镜像,创建过程大致分为三步,先创建容器,在容器中安装我们所需要的内容,再使用docker commit将容器打包为镜像即可
7、用户可以使用docker import命令直接从一个操作系统模板文件导入一个镜像
- 构建镜像的方式有三种:使用docker commit构建、基于本地模板导入和使用Dockerfile构建构建
9、Dockerfile是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建
10、在Dockerfile语法中,RUN、CMD都是运行命令的指令,RUN是在构建镜像时运行的命令,CMD是容器启动时运行的命令