type
status
date
slug
summary
tags
category
icon
password
1、Dockerfile是什么
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明,通过
docker build
命令执行 Dockerfile 文件,可以构建一个Docker镜像。Dockerfile 的文件命名就为
Dockerfile
,文本格式是一行行指令,每个指令有一个命令和参数组成,类似于命令行可执行文件。例如:一般会把 Dockerfile 放在一个空目录里面,这个目录里面的文件就称为 build 的上下文。移动到 Dockerfile 所在的目录,然后执行
指定自定义镜像名称,生成新的镜像。
2、Dockerfile原理
2.1 Dockerfile上下文
在使用 docker build 命令通过 Dockerfile 创建镜像时,会产生一个上下文(build context),build context 为镜像构建提供所需要的文件或目录。Dockerfile 通过 ADD、COPY 等命令可以将 build context 中的所有文件发送给 Docker daemon 添加到镜像中。在 build 过程中可以引用上下文中的任何文件,但不能引用上下文以外的文件。
示例:
一般情况下 build 上下文应该为一个空文件夹。因为 docker 客户端会将上下文的全部内容挂载到 docker daemon。如果使用包含很多内容的目录例如根目录
/
作为 PATH,会将硬盘驱动器的全部内容全部暴露,增加系统风险。对于 COPY 和 ADD 命令来说,如果要把本地的文件拷贝到镜像中,那么本地的文件必须是在上下文目录中的文件。因为在执行 build 命令时,docker 客户端会把上下文中的所有文件发送给 docker daemon。考虑 docker 客户端和 docker daemon 不在同一台机器上的情况,build 命令只能从上下文中获取文件。如果我们在 Dockerfile 的 COPY 和 ADD 命令中引用了上下文中没有的文件,就会收到类似下面的错误:
2.2 Dockerfile缓存
Dockerfile 是由一行行指令组成的,每一行指令都会产生一行镜像缓存。如下 Dockerfile 文件
执行 docker build,输出内容如下:
生成镜像
test_busybox:v1.0
,Dockerfile 的三条指令分别对应 Step 1-3,生成了三层镜像缓存。如果我们再修改一下 Dockerfile,增加一条指令 ENV MY_VAR 123
,变成:再执行 docker build,输出内容如下:
生成镜像
test_busybox:v2.0
,Step 也变成 4 步,注意 Step 2/4 里面的Using cache
,表示使用了上一步构建产生的缓存层 c0dcd0ba7521
。所以每次 docker build 都会尽量去复用已经存在的镜像缓冲层,这样可以节省磁盘空间和提高构建速度。我们也可以指定 --no-cache
参数,表示不使用已有的镜像缓存,这样每步 Step 都会生成新的镜像缓存。如果我们对同一个 Dockerfile 执行多次 docker build,如果文件内容不变,只是修改生成镜像的名称,不会生成多个镜像,只会保留一个镜像,镜像名称是最后执行 docker build 指定的命名。
注意,这里的镜像缓冲层和 overlay2 里面的 layer 不是同一个概念。
- overlay2 的 layer 保存在
/var/lib/docker/overlay2
目录。
- 镜像缓冲层保存
/var/lib/docker/image/overlay2/imagedb/
目录。
2.3 Dockerfile和docker commit的区别和联系
Dockerfile 和 docker commit 都可以生成新的镜像,两者的区别是:
- 使用 docker commit 方式创造的镜像,使用 docker history 看不到构建镜像过程中执行的命令。这意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知,后期极难维护。
- 使用 Dockerfile 方式创造的镜像,可以使用 docker history 清楚看到镜像构建过程中执行了哪些命令;能够自由灵活的与宿主机联系(docker commit因为在容器里面,无法做复制拷贝宿主机文件等动作);后期可扩展性强,有一个 Dockerfile 文件就可以随时运行镜像了。强烈推荐使用 Dockerfile 的方式来创建镜像。
例如使用 Dockerfile 生成镜像 test_busybox:v1.0
使用 docker history 查看镜像,每一行都跟 Dockerfile 一一对应
即便是用 Dockerfile 构建镜像,底层也是 docker commit 一层一层构建新镜像的。通过Dockerfile构建镜像的过程:
- 从base镜像运行一个容器。
- 执行一条指令,对容器做修改。
- 执行类似 docker commit 的操作,生成一个新的镜像层。
- Docker再基于刚刚提交的镜像运行一个新容器。
- 重复2~4步,直到 Dockerfile 中的所有指令执行完毕。
2.4 .dockerignore
.dockerignore
文件的作用类似于 git 工程中的 .gitignore
。不同的是 .dockerignore
应用于 docker 镜像的构建,它存在于 docker 构建上下文的根目录,用来排除不需要上传到 docker 服务端的文件或目录。docker 在构建镜像时首先从构建上下文找有没有
.dockerignore
文件,如果有的话则在上传上下文到 docker 服务端时忽略掉 .dockerignore
里面的文件列表。这么做显然带来的好处是:- 构建镜像时能避免不需要的大文件上传到服务端,从而拖慢构建的速度、网络带宽的消耗;
- 可以避免构建镜像时将一些敏感文件及其他不需要的文件打包到镜像中,从而提高镜像的安全性;
.dockerignore
文件的写法和 .gitignore
类似,支持正则和通配符,具体规则如下:- 每行为一个条目;
- 以
#
开头的行为注释;
- 空行被忽略;
- 构建上下文路径为所有文件的根路径;
文件匹配规则具体语法如下:
规则 | 行为 |
*/temp* | 匹配根路径下一级目录下所有以 temp 开头的文件或目录 |
*/*/temp* | 匹配根路径下两级目录下所有以 temp 开头的文件或目录 |
temp? | 匹配根路径下以 temp 开头,任意一个字符结尾的文件或目录 |
**/*.go | 匹配所有路径下以 .go 结尾的文件或目录,即递归搜索所有路径 |
*.md !README.md | 匹配根路径下所有以 .md 结尾的文件或目录,但 README.md 除外 |
如果两个匹配语法规则有包含或者重叠关系,那么以后面的匹配规则为准。
3、Dockerfile指令
3.1 FROM
指定基础镜像,后续的操作都是基于基础镜像。一般 Dockerfile 文件都是以 FROM 开始,FROM 后面只跟一个基础镜像。
在17.05版本之前的 Docker,只允许 Dockerfile 中出现一个 FROM 指令;Docker 17.05 版本以后允许 Dockerfile支持多个 FROM 指令,但是仍以最后一条 FROM 为准。
多个 FROM 指令的意义是:每一条 FROM 指令都是一个构建阶段,多条 FROM 就是多阶段构建。虽然最后生成的镜像只能是最后一个阶段的结果,但是能够将前置阶段中的文件拷贝到后边的阶段中。
3.2 ARG
ARG 是唯一一个可以放在 FROM 语句之前的指令。ARG 指定可以定义变量,它的规则是:
- 在 FROM 之前声明的 ARG 在构建阶段之外,所以只可以在 FROM 语句中使用,一般设置默认值。
- 在 FROM 之后声明的 ARG 可以在声明后全局使用。
- 构建 Docker 镜像时,必须为所有 ARG 提供值。如果没有设置默认值,必须在执行 docker build 命令通过 –build-arg 参数传进来;如果设置了默认值,可以通过 –build-arg 参数传入新的值,也可以不传参数使用默认值。
第一个 VERSION 设置了默认值,但只能在 FROM 里面使用。第二个 VERSION 没有默认值,必须通过参数传进来。
3.3 MAINTAINER
MAINTAINER命令用于说明谁在维护这个Dockerfile文件。
MAINTAINER命令并不常用,因为这类信息在Git存储或其他地方有了。
3.4 USER
USER 可以用来指定 RUN、CMD 和 ENTRYPOINT 运行容器和程序时的用户及用户组。如果不指定用户组,默认为 root。
3.5 RUN
RUN 指令执行命令并创建新的镜像层,常用于安装软件包。RUN 指令有 Shell 和 Exec 两种格式:
executable是一个可执行的文件,如果是
/bin
目录下的文件就不需要指定路径,否则就需要指定完整路径或者先通过 WORKDIR 切换到可执行文件所在的目录。例如
3.6 CMD
CMD 指令用于设置容器启动后默认执行的命令及其参数,但 CMD 能够被 docker run 后面跟的命令行参数替换。
CMD 指令只能出现一次。如果 Dockerfile 中出现多个 CMD 指令,只有最后一个有效。
CMD 指令也可以使用 Shell 或 Exec 格式,有三种形式:
3.7 ENTRYPOINT
ENTRYPOINT 指令也可以配置在容器启动时要执行的命令。ENTRYPOINT 和 CMD 指令的区别是:
- ENTRYPOINT 不会被 docker run 的命令行参数指定的指令所覆盖,除非使用
—entrypoint
选项强行覆盖 ENTRYPOINT 指令。
- docker run 的命令行参数会被当作参数送给 ENTRYPOINT 指令。例如下面例子,在容器启动的时候执行的完整命令为:
/bin/ping localhost
。
ENTRYPOINT 指令也只能出现一次。Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。
ENTRYPOINT 也包含 Shell 或 Exec 两种格式:
在为 ENTRYPOINT 选择格式时必须小心,因为这两种格式的效果差别很大。
- Exec格式:用于设置要执行的命令及其参数,同时可通过 CMD 提供额外的参数。ENTRYPOINT中的参数始终会被使用,而 CMD 的额外参数可以在容器启动时动态替换掉。
- Shell格式:会忽略任何 CMD 或 docker run 提供的参数。
RUN、CMD 和 ENTRYPOINT 的区别
- RUN:在容器 build 阶段执行,创建新的镜像层,RUN 经常用于安装软件包。
- CMD:在容器 run 阶段启动的时候执行,设置容器启动后默认执行的命令及其参数,但 CMD 能够被 docker run 后面跟的命令行参数替换。
- ENTRYPOINT:和 CMD 一样,但是一定会执行,不会被 docker run 后面跟的命令行参数替换。
Shell 和 Exec 格式的区别我们可用两种方式指定 RUN、CMD和ENTRYPOINT 要运行的命令:Shell 格式和 Exec 格式。两者的区别是
- Shell 格式:使用格式
<instruction> <command>
。当指令执行时,底层会调用/bin/sh -c [command]
来执行命令,可以解析变量。
- Exec 格式:使用格式
<instruction> ["executable", "param1", "param2", ...]
。当指令执行时,会直接调用[command]
,不会被 shell 解析。默认不能解析变量。CMD 和 ENTRYPOINT 推荐使用 Exec 格式,因为指令可读性更强,更容易理解。RUN 则两种格式都可以。
3.8 COPY
从上下文目录中复制文件或者目录到容器里指定路径。
例如复制文件:把主机的
/myapp/target/myapp.jar
文件复制到Docker进行中的 /myapp/myapp.jar
文件。复制目录:把主机的
/myapp/config/prod
目录复制到Docker镜像中的 /myapp/config
目录。复制多个文件到 Docker 镜像中的一个目录中:将主机的
/myapp/config/prod/conf1.cfg
文件和 /myapp/conig/prod/conf2.cfg
文件复制到Docker镜像中的 /myapp/config/
目录中。注意,目标目录必须以/(斜杠)结束。3.9 ADD
类似于 COPY,区别在于:
- 可以添加远程服务器上的文件。例如下载远程地址的 myapp.jar 到 Docker 镜像的 /myapp/ 目录中。如果是添加一个远程的压缩 tar 包,Docker 并不会解压这个文件。
- 添加的文件如果为打包文件 tar 格式或者压缩文件 gzip、bzip2、xz 格式,会自动复制并解压到。这里会解压 myapp.tar 到 Docker 镜像的 /myapp/ 目录中。
3.10 ENV
设置环境变量,在后续的指令或者在容器内部,通过
$
符号就可以使用这个环境变量。有两种格式例如设置变量 BASE_URL
3.11 VOLUME
定义匿名数据卷。在启动容器时没有没有使用
-v
参数显式挂载数据卷,会自动挂载到匿名卷。挂载的意义是持久化保存在宿主机上,避免容器重启后数据丢失。3.12 WORKDIR
用于设置 Dockerfile 中的 RUN、CMD 和 ENTRYPOINT 指令执行命令的工作目录(默认为根目录)。WORKDIR 指定的工作目录,必须是提前创建好的。
WORKDIR 可以在一个 Dockerfile 中出现多次,如果使用相对路径,那它会相对于前面一个 WORKDIR 指令对应的目录。例如:
最后输出的路径为:
/a/b/c
。3.13 EXPOSE
暴露端口。
3.14 LABEL
LABEL 可以为镜像添加元数据,元数据以键值对的形式出现。推荐将所有的元数据放在同一条 label 指令。
可以通过
docker inspect
查看容器定义的 label。3.15 HEALTHCHECK
定期执行健康检查,监视 Docker 容器中运行的应用程序的运行状况。如果命令返回 0,Docker 将认为应用程序和容器正常;如果命令返回 1,Docker 会认为应用程序和容器不正常。
options说明:
- –interval:健康检查间隔时间,默认为30s
- –start-period:健康检查开始时间。有些应用程序可能需要一段时间启动,因此,只有经过某段时间后再进行健康检查才有意义。默认情况下,Docker会立即检查Docker容器的监控状况。
- –timeout:健康检查最大超时时间,如果健康检查超时,Docker也会认为容器不健康。
- –retries:监控检查最大重试次数。
例如使用了java应用程序的com.jenkov.myapp.HealthCheck作为健康检查的命令,每60s执行一次。
3.16 ONBUILD
ONBUILD 是一个特殊的指令,后面跟的是其它指令。ONBUILD 指定的命令,在本次构建镜像的过程中不会执行;只有当以当前镜像为基础镜像(通过 FROM 引用),去构建下一级镜像的时候才会被执行。
例如使用以下 Dockerfile 构建镜像 my-node,三行 ONBUILD 指令不会执行。
新的 Dockerfile 以 my-node 作为基础镜像引入时,之前基础镜像的那三行 ONBUILD 就会开始执行。
4、Dockerfile实战
通过基础镜像 centos:7,在该镜像中安装 jdk 和 tomcat 以后将其制作为一个新的镜像 mycentos:7。
- Author:mcbilla
- URL:http://mcbilla.com/article/7423e343-e51a-4623-88ab-1d4705b833fb
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts