Docker镜像构建指南:Dockerfile语法与docker build命令全解析
文章目录
- 一、镜像制作简介
- 二、Dockerfile文件配置
- 2.1 常用命令清单
- 2.2 命令详解
- 三、docker build 指令
一、镜像制作简介
Docker镜像能将应用程序及其所有依赖(如库、配置、运行时环境等)打包成一个标准化的、可移植的文件,确保应用在任何支持Docker的环境中都能以相同方式运行,从而解决"在我这能跑,到你那却不行"的环境一致性问题,同时简化部署流程、提高扩展效率,并通过容器化实现资源隔离与轻量化管理,让应用的开发、测试和生产环境保持统一,大幅降低跨环境部署的复杂度和出错概率。
制作镜像主要有两种方式:
- 使用
docker commit
指令:基于运行中的容器手动创建镜像,这种方式操作简单但缺乏可重复性,镜像构建过程难以追溯和维护,不推荐用于生产环境。 - 使用Dockerfile文件:通过编写结构化的文本文件定义镜像构建步骤,支持版本控制、自动化构建和精确配置,能清晰记录镜像制作过程,便于团队协作与迭代优化,是推荐的标准做法。
相比docker commit,使用 Dockerfile 构建镜像是绝对的主流做法,核心优势如下:
1. 自动化 & 可重复
- 文本文件定义流程,
docker build
一键生成。 - 环境、配置、步骤完全一致,杜绝“在我这好使”的问题。
2. 透明 & 可追溯
- 所有构建步骤(装了啥、改了啥)一目了然。
- 文件可存入 Git,谁改了哪里,历史可查。
3. 高效 & 快速
- 利用分层缓存:只重建变更的步骤,大幅提升构建速度。
4. 易维护 & 最佳实践
- 轻松实施优化(如用小体积基础镜像、合并指令)。
- 结构清晰,易于理解和修改。
5. 天生适合 CI/CD
- 完美集成自动化流程,实现代码提交后自动构建、测试、部署。
二、Dockerfile文件配置
Dockerfile 就是镜像的蓝图,Dockerfile与镜像的关系如同图纸与房子的关系
Dockerfile格式:
# Comment
INSTRUCTION arguments
2.1 常用命令清单
官方地址:https://docs.docker.com/reference/dockerfile/
FROM
:构建镜像基于哪个镜像,也就是基础镜像LABEL
:为镜像添加元数据COPY
:拷贝文件或目录到镜像中,跟 ADD 类似,但不具备自动下载或解压的功能ADD
:拷贝文件或目录到镜像中,如果是 URL 或压缩包便会自动下载或自动解压WORKDIR
:指定工作目录RUN
:指定 docker build 过程中运行的程序VOLUME
:指定容器挂载点EXPOSE
:声明容器的服务端口(仅仅是声明)ENV
:设置环境变量CMD
:运行容器时执行的命令ENTRYPOINT
:运行容器时程序入口ARG
:指定构建时的参数USER
:指定当前用户HEALTHCHECK
:健康检测指令
2.2 命令详解
功能:
FROM
指令用于为镜像文件构建过程指定基础镜像,后续的指令运行于此基础镜像所提供的运行环境;
注意事项:
FROM
指令必须是 Dockerfile 中非注释行或者ARG
之后的第一个指令;- 实践中,基准镜像可以是任何可用镜像文件,默认情况下,
docker build
会在 docker 主机上查找指定的镜像文件,在其不存在时,则会自动从 Docker 的公共库 pull 镜像下来。如果找不到指定的镜像文件,docker build
会返回一个错误信息; FROM
可以在一个 Dockerfile 中出现多次,如果有需求在一个 Dockerfile 中创建多个镜像,或将一个构建阶段作为另一个的依赖;- 如果
FROM
语句没有指定镜像标签,则默认使用latest
标签。
语法:
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
参数:
<platform>
:构建的 cpu 架构,如linux/amd64
,linux/arm64
,windows/amd64
<image>
: 指定作为 base image 的名称;<tag>
: base image 的标签,省略时默认latest
;<digest>
: 是镜像的哈希码;AS <name>
: 指定构建步骤的名称,配合COPY --from=<name>
可以完成多级构建
示例:
- 注意:可在一个 Dockerfile 中多次使用FROM指令,每个FROM指令开启一个新的构建阶段。每个阶段都可以有自己的安装、配置和构建步骤。后面的构建阶段可以从前面的构建阶段中选择性地复制所需的文件或目录。即“多阶段构建”
LABEL
功能:
- 为镜像添加元数据
语法:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
示例:
FROM ubuntu:22.04 as buildstage1
LABEL version="1.0" desc="create by bit"
COPY
功能:
- 用于从 docker 主机复制新文件或者目录至创建的新镜像指定路径中。
语法:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
参数:
<src>
:要复制的源文件或目录,支持使用通配符;<dest>
:目标路径,即正在创建的 image 的文件系统路径;建议<dest>
使用绝对路径,否则,COPY 指定以 WORKDIR 为当前路径
在路径中有空白字符时,通常使用第 2 种格式;--chown
:修改用户和组--from <name>
可选项:可以从之前构建的步骤中拷贝内容,结合FROM ... AS <name>
往往用作多级构建,后续我们有实战课专门完成多级构建
注意:
<src>
必须是 build 上下文中的路径,不能是其父目录中的文件;- 如果
<src>
是目录,则其内部文件或子目录会被递归复制,但<src>
目录自身不会被复制; - 如果指定了多个
<src>
,或在<src>
中使用了通配符,则<dest>
必须是一个目录,且必须以/
结尾; - 如果
<dest>
事先不存在,它将会被自动创建,这包括父目录路径。
示例:
效果:
ENV
功能:
- 用于为镜像定义所需的环境变量,并可被 Dockerfile 文件中位于其后的其它指令(如 ENV、ADD、COPY 等)所调用
- 调用格式为
$variable_name
或${variable_name}
语法:
ENV <key>=<value> ...
示例:
效果:
与ENV配合使用:
ENV MYROOTDIR=/data/web/html/
COPY ./index.html ${MYROOTDIR}
WORKDIR
功能:
- 为 Dockerfile 中所有的
RUN
、CMD
、ENTRYPOINT
、COPY
和ADD
指定设定工作目录,相当于cd到一个目录然后执行指令
语法:
WORKDIR /path/to/workdir
注意:
- 默认的工作目录是
/
- 如果提供了相对路径,它将相对于前一条
WORKDIR
指令的路径。 WORKDIR
指令可以解析先前使用设置的环境变量ENV
示例:
效果:
- 注意:在 Dockerfile 中,如果存在多个 WORKDIR 指令,在后续的 WORKDIR 指令出现之前,所有需要指定工作目录的指令(如 RUN、CMD、ENTRYPOINT、COPY、ADD 等)都会以上一个 WORKDIR 所设置的路径作为工作目录。
ADD
功能:
- ADD 指令类似于 COPY 指令,ADD 支持使用 TAR 文件和 URL 路径,会自动完成解压和下载,而copy不支持这个功能
语法:
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
参数:
<src>
:要复制的源文件或目录,支持使用通配符;<dest>
:目标路径,即正在创建的 image 的文件系统路径;建议<dest>
使用绝对路径,否则,ADD 指定以 WORKDIR 为起始路径;在路径中有空白字符时,通常使用第 2 种格式;--chown
:修改用户和组
示例:
效果:
如果是本地下载tar,然后给容器会自动解压。如果是远端则不会。
RUN
功能:
- 用于指定
docker build
过程中运行的程序,其可以是任何命令
语法:
#shell form
RUN <command>
#exec form
RUN ["executable", "param1", "param2"]
参数:
- 第一种格式中,
<command>
通常是一个 shell 命令,且以/bin/sh -c
来运行它,Windows 默认为cmd /S /C
。如果一个脚本test.sh
不能自己执行,必须要/bin/sh -c test.sh
的方式来执行,那么,如果使用RUN
的 shell 形式,最后得到的命令相当于:
/bin/sh -c "/bin/sh -c 'test.sh'"
- 第二种语法格式中的参数是一个 JSON 格式的数组,其中
<executable>
为要运行的命令,后面的<paramN>
为传递给命令的选项或参数;然而,此种格式指定的命令不会以/bin/sh -c
来发起,因此常见的 shell 操作如变量替换以及通配符(?
,*
等)替换将不会进行;不过,如果要运行的命令依赖于此 shell 特性的话,可以将其替换为类似下面的格式。
RUN ["/bin/bash", "-c", "<executable>", "<param1>"]
示例:
注意:多使用&&把指令放在同一个RUN,减少层
CMD
功能:
- 类似于
RUN
指令,CMD
指令也可用于运行任何命令或应用程序,不过,二者的运行时间点不同 RUN
指令运行于映像文件构建过程中,而CMD
指令运行于基于Dockerfile
构建出的新映像文件启动一个容器时CMD
指令的首要目的在于为启动的容器指定默认要运行的程序,且其运行结束后,容器也将终止;不过,CMD
指定的命令其可以被docker run
的命令行选项所覆盖- 在
Dockerfile
中可以存在多个CMD
指令,但仅最后一个会生效
语法:
CMD ["executable","param1","param2"] (exec form, this is the preferred form)
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
CMD command param1 param2 (shell form)
注意:
- 第二种则用于为
ENTRYPOINT
指令提供默认参数 - json 数组中,要使用双引号,单引号会出错
示例:
CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
ENTRYPOINT
功能:
- 用于指定容器的启动入口
语法:
#exec form
ENTRYPOINT ["executable", "param1", "param2"]
# shell form
ENTRYPOINT command param1 param2
参数:
- json 数组中,要使用双引号,单引号会出错
注意:ENTRYPOINT与CMD功能类似,但ENTRYPOINT不会被docker run指令完全覆盖,docker run后面的参数会作为ENTRYPOINT的参数。
示例:
test:v0.7使用CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
test:v0.8使用ENTRYPOINT ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]
EXPOSE
功能:
- 用于为容器声明打开指定要监听的端口以实现与外部通信
- 该
EXPOSE
指令实际上并不发布端口。它充当构建图像的人和运行容器的人之间的一种文档,关于要发布哪些端口。要在运行容器时实际发布端口,使用-p
参数发布和映射一个或多个端口,或者使用-P
flag 发布所有暴露的端口并将它们映射宿主机端口。
语法:
EXPOSE <port> [<port>/<protocol>...]
参数:
<protocol>
:tcp/udp 协议<port>
:端口
示例:
EXPOSE 80/tcp
ARG
功能:
ARG
指令类似ENV
,定义了一个变量;区别于ENV
:用户可以在构建时docker build --build-arg <varname> = <value>
进行对变量的修改;ENV
不可以;- 如果用户指定了未在
Dockerfile
中定义的构建参数,那么构建输出警告。
语法:
ARG <name>[=<default value>]
注意:
Dockerfile
可以包含一个或多个ARG
指令ARG
支持指定默认值- 使用范围:定义之后才能使用,定义之前为空,如下面的案例,执行命令
docker build --build-arg username=what_user .
第二行计算结果为some_user
,不是我们指定的build-arg
中的参数值what_user
注意:ARG和ENV同时存在时,ENV会覆盖ARG
示例:
Dockerfile文件
ARG SYSVERSION=22.04
FROM ubuntu:${SYSVERSION}
构建时可以更改变量从而更改Ubuntu版本:
docker build -t test:v0.9 --build-arg SYSVERSION=22.10
VOLUME
功能:
- 用于在 image 中创建一个挂载点目录
- 通过
VOLUME
指令创建的挂载点,无法指定主机上对应的目录,是自动生成的。
语法:
VOLUME <mountpoint>
VOLUME ["<mountpoint>"]
参数:
mountpoint
: 挂载点目录
注意:
- 如果挂载点目录路径下此前有文件存在,
docker run
命令会在卷挂载完成后将此前的所有文件复制到新挂载的卷中 - 其实
VOLUME
指令只是起到了声明容器中的目录作为匿名卷,但是并没有将匿名卷绑定到宿主机指定目录的功能。 volume
只是指定了一个目录,用以在用户忘记启动时指定-v
参数也可以保证容器的正常运行。比如mysql
,你不能说用户启动时没有指定-v
,然后删了容器,就把mysql
的数据文件都删了,那样生产上是会出大事故的,所以mysql
的dockerfile
里面就需要配置volume
,这样即使用户没有指定-v
,容器被删后也不会导致数据文件都不在了。还是可以恢复的。volume
与-v
指令一样,容器被删除以后映射在主机上的文件不会被删除。- 如果
-v
和volume
指定了同一个位置,会以-v
设定的目录为准,其实volume
指令的设定的目的就是为了避免用户忘记指定-v
的时候导致的数据丢失,那么如果用户指定了-v
,自然而然就不需要volume
指定的位置了。
示例:
VOLUME ["/data1"]
USER
功能:
- 用于指定运行 image 时的或运行 Dockerfile 中任何
RUN
、CMD
或ENTRYPOINT
指令定的程序时的用户名或 UID - 默认情况下,container 的运行身份为 root 用户
语法:
USER <user>[:<group>]
USER <UID>[:<GID>]
参数:
- user: 用户
- group: 用户组
- uid: 用户 id
- gid: 组 id
注意:
<UID>
可以为任意数字,但实践中其必须为/etc/passwd
中某用户的有效UID
,否则将运行失败
示例:
USER root:root
RUN groupadd mysql
RUN useradd mysql -g mysql
USER mysql:mysql
RUN whoami > /tmp/user.txt
HEALTHCHECK
功能:
HEALTHCHECK
指令告诉 Docker 如何测试容器以检查它是否仍在工作。- 即使服务器进程仍在运行,这也可以检测出陷入无限循环且无法处理新连接的 Web 服务器等情况。
语法:
HEALTHCHECK [OPTIONS] CMD command (check container health by running a command inside the container)
HEALTHCHECK NONE (disable any healthcheck inherited from the base image)
OPTIONS
选项参数:
--interval=DURATION
(default: 30s):每隔多长时间探测一次,默认 30 秒--timeout= DURATION
(default: 30s):服务响应超时时长,默认 30 秒--start-period= DURATION
(default: 0s):服务启动多久后开始探测,默认 0 秒--retries=N
(default: 3):认为检测失败几次为宕机,默认 3 次
返回值:
- 0:容器成功是健康的,随时可以使用
- 1:不健康的容器无法正常工作
- 2:保留不使用此退出代码
示例:
FROM nginx:1.24.0
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl -fs http://localhost/ || exit 1
三、docker build 指令
功能:
docker build
命令用于使用 Dockerfile 创建镜像。
语法:
docker build [OPTIONS] PATH | URL | -
关键参数:
--build-arg=[]
:设置镜像创建时的变量;-f
:指定要使用的 Dockerfile 路径;--label=[]
:设置镜像使用的元数据;--no-cache
:创建镜像的过程不使用缓存;--pull
:尝试去更新镜像的新版本;--quiet, -q
:安静模式,成功后只输出镜像 ID;--tag, -t
:镜像的名字及标签,通常name:tag
或者name
格式;可以在一次构建中为一个镜像设置多个标签。--network
:默认default
。在构建期间设置RUN
指令的网络模式