Dockerfile
Dockerfile 是一个文本文件,其内包含了一条条指令,每一条指令构建镜像的一层,因此每一条指令的内容,就是描述该层应当如何构建。 定制镜像,可以将镜像制作的每一层的修改、安装、构建、操作的命令,都写入 Dockerfile 中,使用 Dockerfile 来构建、定制镜像。Dockerfile 相比于快照方法制作镜像,具有方便自动化构建、维护修改,二次开发方便、更加标准化的优点。
快照方法(docker commit)制作的镜像,需要手动一个命令一个命令执行,其制作过程不可考,因此生成的镜像又称为黑箱镜像,制作步骤比较繁琐,维护起来较麻烦,也难以进行二次开发。
在制作 Dockerfile 时,要尽量将不变的因素放在前面,易变化的因素放在后面。如为了减镜像的实际大小,我们往往会在镜像里安装 gcc,当 gcc 编译完代码文件后就删除 gcc,来释放空间。代码文件是易变化的文件,如果将 COPY
代码的指令放在 ADD
gcc 指令的前面,由于 Dockerfile 是逐条构建,每次更改代码文件都需要重新安装和删除 gcc,造成镜像构建时间变长。但如果我们将 COPY
代码的指令放在 ADD
gcc 指令的前面,由于镜像构建时会查找可用的缓存 ,即不需要重新安装 gcc 就可以重新编译代码,使得镜像构建时间大大缩短。
1. docker build
docker build
命令用于使用 Dockerfile 创建镜像。有三种语法:上下文构建(path),使用某个 Docerfile 构建;网络构建(URL),连接某个主机使用其 Dockerfile 构建;通过标准输入构建(-),可以指定 Docerfile 或 tar 文件。
语法:
docker build [options] [path]
docker build [options] [URL]
docker build [options] [-]
选项:
- --build-arg=[]: 设置镜像创建时的变量。
- -f: 设定要使用的 Dockerfile 路径。
- --label=[]: 设置镜像使用的元数据。
- --no-cache: 创建镜像的过程中不使用缓存(即不使用引用的方法,重新构建每一层)。
- --pull: 尝试更新镜像的新版本。
- -q,--quiet: 安静模式,成功后只输出镜像 ID。
- -t,--tag: 镜像的名字及标签。
- --network: 默认使用,在构建期间设置
RUN
指令的网络模式。
示例:
shell
docker build -t myimage:1.0.0 .
docker build -t myimage:1.0.0 https://127.0.0.1/Dockerfile
docker build -t myimage:1.0.0 - < Dockerfile
2. .dockerignore文件
.dockerignore
文件可以标记在执行 docker build
时忽略的路径和文件,避免发送不必要的数据内容,从而加快整个镜像创建的过程。使用方法简单,只需要在其中添加需要忽略的文件的路径和名字即可,支持通配符。
*.txt
表示忽略所有后缀名为 .txt
的文件。
3. Dockerfile指令
Dockerfile 的指令不区分大小写,但是为了将其于一般的命令和参数区分开来,约定俗成使用大写 ,Dockerfile 的注释符号为 #
且必须在行首使用才会被识别为注释,行中其他地方出现 #
都会被视为参数。
FROM
FROM
用于指定镜像文件构建过程中的基础镜像,后续的指令运行于此基础镜像所提供的运行环境 。因此,FROM
必须是 Dockerfile 中非注释行或者 ARG
之后的第一个指令 。 FROM
可以在一个 Dokerfile 里多次出现,即可以使用一个 Dockerfile 创建多个镜像,或者将一个构建阶段作为另一个的依赖。如果 FROM
语句没有指定镜像标签,则默认使用 latest 标签。
默认情况下,
docker build
会在 docker 主机上查找指定的镜像文件,如果本地不存在该镜像,则会自动从 Docker 的公共库 pull 镜像下来,如果还是找不到指定的镜像文件,docker build
会返回一个错误信息。
语法:
dockerfile
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
。 - <image>: 指定作为 base image 的名称。
- <tag>: base image 的标签,省略时默认 latest。
- <digest>: 镜像的哈希码。
- AS <name>: 指定构建步骤的名称,配合
COPY --from=<name>
可以完成多级构建。
示例:
dockerfile
FROM nginx:latest
LABEL
用于为镜像添加元数据,元数据是键值对的形式。使用 docker inspect
可以查看信息。
语法:
dockerfile
LABEL <key>=<value> <key>=<value>...
示例:
dockerfile
LABEL author="laimaxgg"
COPY
用于从 docker 主机复制新文件或者目录到创建的新镜像指定路径中。
语法:
dockerfile
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",..."<dest>"]
参数:
- --chown: 修改用户和组。
- <src>: 要复制的源文件或目录,支持使用通配符
*
。 - <dest>: 目标路径,即正在创建的 iamge 的文件系统路径。建议使用绝对路径 ,否则
COPY
将指定以WORKDIR
为当前路径。 - --from <name>: 可选项,可以从之前构建的步骤中拷贝内容,往往用于多级构建中。
注意:
<src> 必须是 build 上下文中的路径,不能是其父目录中的文件。
如果 <src> 是目录 ,则其内部文件或子目录会被递归复制 ,但 <src> 目录自身不会被复制。
如果指定了多个 <src> ,或在 <src> 使用了通配符,则 <dest> 必须是一个目录,且必须以
/
结尾。如果 <dest> 事先不存在,它将会被自动创建,包括它的父目录路径。
示例:
dockerfile
COPY index,html /data/web/html
ENV
用于为镜像定义所需的环境变量,并可被 Dockerfile 文件中位于其后的其他指令(如 ENV
、 ADD
、 COPY
)调用。调用时使用 $env_name
或 ${env_name}
调用。
语法:
dockerfile
ENV <key>=<value> ...
示例:
dockerfile
ENV env_val1="1"
WORKDIR
用于为 Dockerfile 中所有 RUN
、 CMD
、ENTRYPOINT
、COPY
、 ADD
指定设定工作目录。
语法:
dockerfile
WORKDIR /a/b/c
注意:
WORKDIR
的默认路径是/
,且支持多次定义,定义时,如果没有重新从/
开始指定,则会将前一条WORKDIR
指令的路径视为父目录,如:
dockerfileWORKDIR /a WORKDIR b WORKDIR c
最终的
WORKDIR
为/a/b/c
。
ADD
类似于 COPY
指令,但是 ADD
支持使用 tar
文件和 URL 路径,会自动完成解压和下载。
语法:
dockerfile
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",..."<dest>"]
路径中包含空格字符时,一般使用第二种格式。
RUN
用于 docker build
过程中运行的程序,其可以是任何命令。RUN
指令有两种格式。
语法:
dockerfile
#shell格式
RUN [command]
#exec格式
RUN ["executable","param1","param2"]
-
shell 格式中, [command] 通常是一个 shell 命令,且以
/bin/sh -c
来运行,如果一个脚本 test.sh 不能自己运行,就必须要使用/bin/sh -c test.sh
的方式来执行,如果使用 shell 格式,最后的指令相当于:bash/bin/sh -c "/bin/sh -c 'test.sh'"
-
exec 格式中的参数是一个 JSON 格式的数组,其中
executable
为要运行的命令,后面的paramN
为要传递给命令的选项或参数。但是这种格式指定的命令不会以/bin/sh -c
来发起,因此常见的 shell 操作如变量替换以及通配符(?,*
等)都将不会进行。不过,如果要运行的命令依赖于此 shell 特性的话,可以使用以下格式:dockerfileRUN ["/bin/bash", "-c", "<executable>", "<param1>"]
示例:
dockerfile
ENV WEB_SERVER_PACKAGE nginx-1.21.1.tar.gz
RUN cd ./src && tar -xf ${WEB_SERVER_PACKAGE}
CMD
类似于 RUN
,CMD
指令用于运行任何命令或应用程序,但两者运行的时间点不同。RUN
指令运行于镜像文件构建过程中,而 CMD
指令运行于基于 Dockerfile 构建出的新镜像文件启动一个容器时。 因此,CMD
指令的首要目的在于为启动的容器指定默认要运行的程序,且其运行结束后,容器也将终止。
CMD
指定的命令可以被 docker run
命令的选项所覆盖,在 Dockerfile 中也可以存在多个 CMD
指令,但最终只会生效最后一个。
语法:
dockerfile
#exec格式
CMD ["executable","param1","param2"]
#ENTRYPOINT指令默认参数
CMD ["param1","param2"]
#shell格式
CMD command param1 param2
注意:
-
第二种格式用于为 ENTRYPOINT 指令提供默认参数。
-
json 数组中,要使用
""
双引号,单引号会出错。
示例:
dockerfile
CMD ["/user/bin/wc","--help"]
EXPOSE
用于为容器声明 打开指定要监听的端口以实现于外部通信。但 EXPOSE
并不会发布端口,它只是充当构建图像的人和运行容器的人之间的一种文档,即如果要让容器暴露端口,还是需要使用 -p
参数。
语法:
dockerfile
EXPOSE <port>
EXPOSE <port>/<protocol>
参数:
<port>: 端口。
<protocol>: tcp/udp 协议。
示例:
dockerfile
EXPOSE 80/tcp
ENTRYPOINT
用于指定容器的启动入口。如果是 CMD 是可以被覆盖的,如果是 ENTRYPOINT 则不能被覆盖。定义了 ENTRYPOINT 会将命令作为参数传给程序。
语法:
dockerfile
#exec格式
ENTRYPOINT ["executable","param1","param2"]
#shell格式
ENTRYPOINT command param1 param2
注意,json 数组中要使用 ""
双引号,单引号会出错。
示例:
dockerfile
ENTRYPOINT ["nginx","-g","daeom off;"]
ARG
类似于 ENV
,ARG
用于在 Dockerfile 里定义一个变量,但不同是 ENV
的是,用户可以在构建时使用 docker build --build-arg <varname>=<val>
对变量进行修改,而 ENV
不能。
语法:
dockerfile
ARG <name>[=<default value>]
注意:
- Dockerfile 可以包含一个或多个
ARG
指令。 ARG
支持指定默认值。ARG
在docker build
后就会失效,一般用于控制镜像的版本,如通过--build-arg
更改 Dockerfile 默认的ARG
控制的Ubuntu
版本号,实现在外部切换镜像制作出来的版本。
示例:
dockerfile
ARG CONT_IMG_VER=v1.0.0
在外部更改值:
shell
docker build --build-arg CONT_IMG_VER=v2.0.0
VOLUME
用于在镜像中创建一个挂载点目录。通过 VOLUME
指令创建的挂载点,无法指定主机上对应的目录,是自动生成的名字,属于匿名卷。
语法:
dockerfile
VOLUME <moutpoint>
VOLUME ["<moutpoint>"]
注意:
- 如果挂载点目录路径下此前有文件存在,docker run 命令会在卷挂载完成后将此前的所有文件复制到新挂载的卷中。
VOLUME
指令只是声明了容器中的目录作为匿名卷,但是并没有将匿名卷绑定到宿主机指定目录的功能。VOLUME
只是指定了一个目录,用于在用户启动时忘记指定-v
选项也可以保证容器正常运行,即使用户没有指定-v
,容器被删除后也不会导致数据文件被删除。VOLUME
和-v
一样,容器被删除以后映射在主机上的文件不会被删除。- 如果
-v
和VOLUME
指定了同一个位置,会以-v
设定的目录为准。
示例:
dockerfile
VOLUME ["/data1","/data2"]
SHELL
SHELL
指令允许覆盖用于 shell 命令形式的默认 shell。Linux 上的默认 shell 是 ["/bin/sh","-c"]
,在 Windows 上是 ["cmd","/S","/C"]
。SHELL
指令必须以 JSON 格式写入 Dockerfile 中。
语法:
dockerfile
SHEL ["executable","parameters"]
注意:
- 该
SHELL
指令在 Linux 上使用较少,但在 Windows 上特别有用,因为 Windows 有两种不同的 shell(cmd 和 powershell)。
USER
用于指定运行 image 时的或运行 Dockerfile 中任何 RUN
、CMD
或 ENTRYPOINT
指令定的程序时的用户名或 UID。默认情况下,container 的运行身份为 root。
语法:
dockerfile
USER <user>:<group>
USER <UID>:<GID>
参数:
- <user>: 用户
- <group>: 用户组
- <UID>: 组 id
- <GID>: 组 id
示例:
dockerfile
USER docker:docker
HEALTHCHECK
用于告诉 Docker 如何测试容器以检查它是否仍在工作。即使服务器进程仍在运行,也可以检测出陷入无限循环且无法处理新连接的 Web 服务器等情况。
语法:
dockerfile
HEALTHCHECK [options] CMD command
HEALTHCHECK NONE
参数:
[options]
有:- --interval=DURATION(default:30s): 每隔多长时间探测一次,默认 30 秒。
- --timeout=DURATION(default:30s): 服务响应超时时长,默认 30 秒。
- --start-period=DURATION(default:0s): 服务启动多久后开始探测,默认 0 秒。
- --retres=N(default:3): 认为检测失败几次为宕机,默认 3 次。
- 返回值:
0
:容器是健康的,随时可以使用。1
:容器不健康,无法正常工作。2
:保留不使用此退出码。
示例:
dockerfile
HEALTHCHECK --interval=5m --timeout=3s CMD curl -f https://localhost/ || exit 1
ONBUILD
用于在 Dockerfile 中定义一个触发器。以该 Dockerfile 中的作为基础镜像由 FROM 指令在 build 过程中被执行时,将会触发创建其 base iamge 的 Dockerfile 文件中的 ONBULD 指令定义的触发器。
语法:
dockerfile
ONBUILD <instruction>
参数:
- <instruction>: Dockefile 的一条指令。
示例:
dockerfile
ONBUILD ADD ./app/src
STOPSIGHAL
用于设置将发送到容器的系统调用信号,即设置 docker stop
时触发的信号。此信号可以是与内核的系统调用表中的位置匹配的有效无符号数。
语法:
dockerfile
STOPSIGNAL signal
示例:
dockerfile
STOPSIGNAL 9
4. CMD和ENTRYPOINT的组合使用
CMD
和 ENTRYPOINT
在 Dockerfile 里是两个功能十分相近的指令,但 Docker 官方实际更希望我们组合使用这两个指令。
4.1 CMD和ENTRYPOINT的异同
相同点:
-
Dockerfile 里含有多条
CMD
或ENTRYPOINT
命令时,后面的CMD
或ENTRYPOINT
会覆盖前一条指令,最终只生效一条。dockerfileCMD echo "123" CMD echo "456"
最终
echo "456"
有效,ENTRYPOINT
同理。 -
CMD
和ENTRYPOINT
都支持 shell 和 exec 格式。当使用 shell 格式时,命令行程序作为 sh 程序的子程序运行 ,docker 用/bin/sh -c
的语法调用,如果用docker ps
命令查看运行的 docker,就可以看出我们启动的程序的 PID 不是 1,由于/bin/sh
命令不会转发消息给实际运行的命令,所以从外部发送任何 POSIX 信号到 docker 容器中都不能安全的关闭 docker 容器;exec 格式直接运行提供的命令,命令进程的 PID 是 1 。所以实践中,都强烈推荐使用 exec 格式。
不同点:
- 使用
docker run
传递参数时会覆盖CMD
的内容,但ENTRYPOINT
无法被覆盖,除非使用--entryopint
选项。 - 如果
ENTRYOPINT
使用的是 exec 格式,那么docker run
传递的参数会作为ENTRYOPINT
的参数(实际是触发了CMD
和ENTRYPOINT
的组合模式)
特点:
- 当
ENTRYPOINT
使用 exec 格式时,CMD
和ENTRYPOINT
可触发组合模式。
4.2 组合模式
当 ENTRYPOINT
使用 exec 格式时,CMD
和 ENTRYPOINT
可触发组合模式。在组合模式下,当使用 docker run
运行容器时,docker 会将和 ENTRYPOINT
和 CMD
拼接在一起,如:
dockerfile
ENTRYPOINT ["echo"]
CMD ["Hello, Docker!"]
将会拼接成 echo "Hello, Docker!"
。再加上,CMD
可以被 docker run
的参数覆盖,所以实际组合模式是 Docker 官方更推荐的写法。