Dockerfile是一个文本格式的配置文件,用户可以使用Dockerfile来快速创建自定义的镜像。
8.1 基本结构
Dockerfile由一行行命令语句组成,并且支持以 # 开头的注释行。
一般而言,Dockerfile从上到下分为四部分:
- 基础镜像信息
- 维护者信息(Maintainer)
- 镜像操作指令(典型的是RUN指令。RUN指令将对镜像执行紧随其后的命令。没运行完一条RUN指令,镜像就添加新的一层,并提交)
- 容器启动时执行的指令(不一定有)
例:
FROM debian:jessieMAINTAINER NGINX Docker Maintainers "docker-maint@nginx.com"ENV NGINX_VERSION 1.10.1-1~jessieRUN apt-key adv .......RUN ln -sf .......EXPOSE 80 443CMD ["nginx","-g","daemon off;"]
8.2 指令说明
官方文档:https://docs.docker.com/engine/reference/builder/
| 指令 | 作用 |
|---|---|
| FROM | 指定所创建镜像的基础镜像 |
| MAINTAINER | 指定维护者信息 |
| RUN | 指定构建镜像时需执行的命令 |
| CMD | 指定启动容器时默认执行的命令 |
| LABEL | 指定生成镜像的元数据标签信息 |
| EXPOSE | 声明镜像内服务所监听的端口 |
| ENV | 指定环境变量 |
| ADD | 复制指定的 |
| COPY | 复制本地主机的 |
| ENTRYPOINT | 指定容器的默认入口 |
| VOLUME | 创建数据卷挂载点 |
| USER | 指定运行容器时的用户名或UID |
| WORKDIR | 配置工作目录 |
| ARG | 指定镜像内使用的参数(例如版本号信息等) |
| ONBUILD | 配置当所创建的镜像作为其他镜像的基础镜像时,所执行的创建操作指令。 |
| STOPSIGNAL | 指定容器退出的信号值 |
| HEALTHCHECK | 指定如何进行健康检查 |
| SHELL | 指定使用shell时的默认shell类型 |
1. FROM
指定所创建镜像的基础镜像,如果本地本存在,则默认回去Docker Hub下载指定镜像。一个合法的Dockerfile一定是以FROM指令开头。如果要在同一个Dockerfile中创建多个镜像,可以使用多个FROM指令,每个镜像一个。
格式:
FROM <image> [AS <name>]FROM <image>[:<tag>] [AS <name>]FROM <image>[@<digest>] [AS <name>]
2. MAINTAINER
指定维护者信息。该信息会写入生成镜像的Author属性域中。
格式:
MAINTAINER <name>
根据官方文档,该指令已过时。应该使用 LABEL 指令代替,因为它更灵活。
LABEL maintainer="SvenDowideit@home.org.au"
3. RUN
指定构建镜像时运行的指令。每条RUN指令都将在当前镜像基础上创建一个新层,执行指定命令,然后提交,从而产生一个新镜像。这个新镜像也将成为下一个RUN命令的的当前镜像。
有两种格式:
RUN <command>
# shell形式。将在默认的shell终端中运行命令。在linux下是/bin/sh -c。
# 命令太长时可用\换行
RUN ["executable", "param1", "param2"]
# exec形式。使用exec执行,不会启动shell。
# 注意这里数组将被解析为JSON数组,因此必须用双引号。
4. CMD
指定从生成的镜像启动容器时,默认执行的命令。
有三种形式:
CMD ["executable","param1","param2"]
# exec形式。使用exec执行,不会启动shell。
# 是官方推荐的形式。
CMD command param1 param2
# shell形式。将在默认的shell终端中运行命令。在linux下是/bin/sh -c。
CMD ["param1","param2"]
# 这种形式不是指定默认命令,而是为ENTRYPOINT指定的命令提供参数。
# 所有数组中的值豆浆作为ENTRYPOINT指定的命令的参数。
每个Dockerfile只能有一条CMD命令。如果指定了多条,则只有最后一条会被执行。
如果用户在启动容器时手动指定了运行的命令(作为run或create的参数),则会覆盖掉CMD。
5. LABLE
指定生成的镜像的元数据信息。
格式:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
可以指定多个LABLE。
父镜像的LABEL会被子镜像继承。如果子镜像指定了同名LABEL,则会覆盖。
可通过 docker inspect 指令来查看label。
6. EXPOSE
声明容器运行时将监听的端口。
格式:
EXPOSE <port> [<port>/<protocol>...]
# 例:
EXPOSE 80
EXPOSE 80/tcp
EXPOSE 80/udp
可指定监听TCP或UDP端口。如果不指定协议,则默认监听TCP端口。
使用EXPOSE指令并不会真的发布该端口(即监听宿主主机的相应端口)。为了真的发布该端口,需要在docke run时使用-p或-P参数指定发布端口。
7. ENV
指定环境变量。指定的环境变量在后续的构建过程中生效,也会存在于启动后的容器中。
格式:
ENV <key> <value>
ENV <key>=<value> ...
指定的环境变量可在启动容器时覆盖:
docker run --env <key>=<value> image
8. ADD
该指令将指定的文件、目录或URL指定的远程文件拷贝到镜像中的指定目录下。
格式:
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] # 当路径中有空格时,使用该形式
其中
- Dockerfile所在目录的一个相对路径(文件或目录)。注意只能访问该目录及其子目录。
- 一个URL
- 一个tar文件
路径支持正则表达式。
9. COPY
该指令将指定的文件、目录拷贝到镜像中的指定目录下。
格式:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
路径支持正则表达式。
COPY和ADD对比
COPY和ADD的功能基本一致。ADD可视为增强版的COPY。
COPY指令只能复制当前构建上下文中的文件(即当前Dockerfile所在目录下的文件)。
ADD指令除了可复制当前构建上下文中的文件外,还支持其他两种数据源:通过URL访问远程文件和指定一个需要自动解压的tar文件。
大部分情况下,我们都只是复制本机文件到镜像,因此应该使用COPY。而对于拷贝远程文件、解压文件等情形,虽然ADD可以完成,但不如直接使用RUN CURL等命令直接和清晰。因此绝大部分情况下,都应该使用COPY,不要使用ADD。
参考:
- https://www.cnblogs.com/sparkdev/p/9573248.html
- https://nickjanetakis.com/blog/docker-tip-2-the-difference-between-copy-and-add-in-a-dockerile
10. ENTRYPOINT
指定镜像的默认入口命令。
格式:
ENTRYPOINT ["executable", "param1", "param2"]
# exec形式。使用exec执行,不会启动shell。
# 是官方推荐的形式。
ENTRYPOINT command param1 param2
# shell形式。将在默认的shell终端中运行命令。在linux下是/bin/sh -c。
当指定了ENTRYPOINT时,CMD指令只能用于提供参数。
每个Dockerfile中只能有一个ENTRYPOINT,当指定多个时,只有最后一个生效。
在启动容器时,必须使用—entrypoint参数才能覆盖掉Dockerfile中的配置。镜像名后跟命令的形式不行。
CMD和ENTRYPOINT对比
两者的最终目的都是为了给基于该镜像启动的容器提供默认执行项。 如果直接启动一个不带CMD或ENTRYPOINT的镜像,将会报错:
$ docker run alpine
FATA[0000] Error response from daemon: No command specified
因此,通常情况下CMD和ENTRYPOINT至少应该指定一个。
另一方面,CMD相比于ENTRYPOINT来说,更加容器被覆盖(docker run最后的参数即会覆盖CMD,不需要专门指定参数)。如果你的镜像用途并不明确,没有预期用户会怎么使用,那么推荐使用CMD。这样你的镜像使用上更加灵活。
如果你的镜像明确是一个“可执行应用”,期望用户就是把它当成一个应用来用,那么就应该使用ENTRYPOINT。并且此时推荐ENTRYPOINT和CMD同时使用。ENTRYPOINT负责指定执行的命令,而CMD负责该命令的提供默认参数。当用户启动容器、不指定任何参数时,将使用CMD指定的参数。而当用户指定了参数时,则使用用户提供的参数。
参考资料:
- https://www.ctl.io/developers/blog/post/dockerfile-entrypoint-vs-cmd/
11. VOLUME
创建一个挂载点。
格式:
容器在运行时,由于采用的是联合文件系统,在其中进行写操作很慢,并且容器关闭后数据就丢失了。为了提高性能,需要绕过联合文件系统,直接访问宿主主机的文件系统。这便是VOLUME的作用。VOLUME既可以在容器启动时指定,也可以在Dockerfile中指定,从而镜像构建时便确定。对于数据库一类的应用,VOLUME很重要,需要将产生的数据放到一个VOLUME中。为了避免用户忘记声明VOLUME,于是需要在Dockerfile中指定。VOLUME ["<路径1>", "<路径2>"...] VOLUME <路径>12. USER
该指令指定一个用户名(或UID),也可指定一个用户组(或GID)。在镜像构建阶段,Dockerfile中位于USER指令之后的RUN、CMD、ENTRYPOINT指令的执行将使用USER指定的用户名和用户组。
格式:USER <user>[:<group>] or USER <UID>[:<GID>]13. WORKDIR
该指令为后续的RUN,CMD,ENTRYPOINT,COPY和ADD指令指定工作目录。
格式:
如果指定的工作目录不存在,将自动创建一个新目录。WORKDIR /path/to/workdir
一个Dockerfile中可以使用多个WORKDIR指令,根据需要不断切换工作目录。如果使用的路径是相对路径,则会相对于前一个工作目录进行计算。
WORKDIR指令可以解析它前面通过ENV设置的环境变量:ENV DIRPATH /path WORKDIR $DIRPATH/$DIRNAME RUN pwd # 这里pwd输出为/path/$DIRNAME14. ARG
该指令用于定义一些构建参数,用户可以在执行docker build命令时传入这些参数值。典型的,比如版本号。注意,ARG用于定于构建参数,准确点说应该叫BUILD_ARG。ARG只会存在于构建时,不会存在于容器启动时。另外,CMD和ENTRYPOINT是在启动时进行解析的。因此,ARG不能用于CMD和ENTRYPOINT命令中。因为那个时候ARG已经不存在了。一个解决方案是,把ARG赋值给环境变量ENV,ENV是会保留到运行时的。
格式:
ARG <name>[=<default value>]
用户指定参数值:
docker build --build-arg <varname>=<value>
如果指定的参数不存在,则会参数如下警告信息:
[Warning] One or more build-args [foo] were not consumed.
可以多次使用ARG指令,指定多个参数。
注意:不要通过ARG指令传递敏感信息,因为传递的构建参数值是可以通过 docker history 命令看到的。
15. ONBUILD
该指令指定一些其他指令,这些指令将在当前镜像作为别的镜像的基础镜像时,在其构建阶段执行。
格式:
ONBUILD [INSTRUCTION]
用ONBUILD指定的指令,在本镜像的构建过程中不会执行,而是存储在镜像中。当创建另一个Dockerfile,并且用FROM指令指定本镜像作为基础镜像时,在子镜像的构建阶段,读取到FROM指令时,Docker会寻找基础镜像的ONBUILD指令,并按顺序执行其指定的指令。注意ONBUILD指令只会影响直接继承的子镜像的构建过程,对于孙子镜像没有影响。
该指令常用于制作“执行环境”类型的镜像,且镜像名通常会添加“onbuild”后缀。
16. STOPSIGNAL
指定所创建的镜像启动的容器所接受的退出信号值。
格式:
STOPSIGNAL signal # signal可以为无符号整数,如9,或信号名,如SIGKILL
17. HEALTHCHECK
该指令告诉Docker如何检查启动的容器是否处于正常工作状态。
有两种格式:
HEALTHCHECK [OPTIONS] CMD command (通过在容器内执行指定的命令来检查容器是否健康)
HEALTHCHECK NONE (禁用从基础镜像继承的健康检查)
选项:
--interval=DURATION(default:30s)--timeout=DURATION(default:30s)--start-period=DURATION(default:0s)--retries=N(default:3)
一个Dockerfile中只能指定一个HEALTHCHECK指令。指定多个时只有最后一个生效。
18. SHELL
为后续的RUN、CMD、ENTRYPOINT的shell形式命令指定默认shell。
格式:
SHELL ["executable", "parameters"]
8.3 创建镜像
当创建了一个Dockerfile之后,即可通过docker build指令来创建镜像。
docker build [OPTIONS] PATH | URL | -
选项:
https://docs.docker.com/engine/reference/commandline/build/
常用选项:
- —file , -f,指定dockerfile所在路径。
- —tag , -t,指定生成镜像的标签信息。
docker build命令依靠上下文和dockerfile文件来构建镜像。上下文是PATH或URL参数指定的目录及其子目录。Dockerfile默认也在上下文中找,但是可以通过-f参数指定上下文以外的其他文件。该命令将上下文和dockerfile发送给Docker服务端,由服务端来创建镜像。
8.4 使用.dockerignore文件
在使用docker build命令时,在docker CLI将上下文发送给docker守护进程之前,它会在上下文的根目录中寻找.dockerignore文件。如果该文件存在,CLI将使用该文件中指定的模式对context目录下的文件进行匹配,并将匹配成功的文件从上下文中移除,然后再发送。这样可以避免发送大文件和敏感文件。
.dockerignore文件中每一行代表一个模式。
例:
*/temp*
*/*/temp*
temp?
# 注释
8.5 最佳实践
- 精简镜像用途。
- 选用合适的基础镜像。
- 提供足够清晰的命令注释和维护者信息。
- 正确使用版本号。
- 减少容器层数。需要尽量合并指令。
- 及时删除临时文件和缓存文件。
- 合理使用缓存,减少内容目录下文件,使用.dockerignore,以提高生产速度。
- 合理指令顺序。在开启缓存的情况下,内容不变的指令尽量放在前面。
- 减少外部源干扰。
