Dockerfile参考
来自docker官方网址:https://docs.docker.com/engine/reference/builder/
docker能够从Dockerfile中读取指令并自动构建一个镜像。Dockerfile是一个文本文档,它包含用户可以在命令行上调用的所有命令来组装一个镜像。使用docker构建,用户可以创建一个连续执行多个命令行指令的自动化构建。
这个页面描述了你可以在Dockerfile中使用的命令。读完此页后,请参阅Dockerfile最佳实践(Dockerfile
Best Practices)以获得面向tip的指南。
前言
翻译这篇文章前我们先阐述一个重要的概念,那就是上下文(context):docker构建时会将上下文的所有内容(.dockerignore中指定的内容除外)发送到daemon中进行处理。那么如何指定这个上下文呢?我们看一个命令来更容易的理解:
docker build -t xxxxx .
注意这行命令后面有一个.(小点),这个小点的作用不是指定dockerfile的位置,指定dockerfile的位置是用-f参数来指定的,那你以为这个小点是干嘛的?他就是用来指定构建镜像的上下文的。
理解了以上的例子后,我们再看看COPY这个命令的真是含义:
COPY ./package.json /app/
COPY是将源目标复制到镜像中的一个命令,那么上面这个./package.json是指什么路径呢?这并不是要复制执行 docker build
命令所在的目录下的 package.json
,也不是复制 Dockerfile
所在目录下的 package.json
,而是复制 上下文(context) 目录下的 package.json
。
用法描述
docker构建命令(docker build
)从Dockerfile和上下文构建一个映像。构建的上下文是位于指定位置路径或URL的文件集。路径是本地文件系统上的一个目录。URL是一个Git存储库位置。
上下文会被递归的处理。因此,路径包含任何子目录,URL包含存储库及其子模块。这个例子显示了一个使用当前目录作为上下文的构建命令:
$ docker build . Sending build context to Docker daemon 6.51 MB ...
构建由Docker守护进程运行,而不是由CLI运行。构建过程要做的第一件事是将整个上下文(递归地)发送给守护进程。在大多数情况下,最好从一个空目录作为上下文开始,并将Dockerfile保存在该目录中。并且只添加构建Dockerfile所需的文件。
警告:不要使用根目录/作为路径,因为它会导致构建将硬盘驱动器的全部内容传输到Docker守护进程。
要在构建上下文中使用文件,Dockerfile引用一条指令中指定的文件,例如一条COPY指令。要提高构建的性能,可以通过向上下文目录添加.dockerignore文件来排除文件和目录。有关如何创建.dockerignore文件的信息,请参阅此页上的文档。 create a .dockerignore
file
通常,Dockerfile的文件名称默认就是Dockerfile(首字母D大写),位于上下文的根目录中。在docker build中使用-f标志指向文件系统中任何位置的Dockerfile。
docker build -f /path/to/a/Dockerfile .
您可以指定一个存储库(repository)和用于保存新映像的标记(tag),这会在镜像构建成功之后应用:
docker build -t shykes/myapp .
要在构建之后将映像标记到多个存储库中,请在运行构建命令时添加多个-t参数:
docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
在Docker守护进程运行Dockerfile中的指令之前,它对Dockerfile进行初步验证,如果语法不正确,则返回一个错误:
$ docker build -t test/myapp . Sending build context to Docker daemon 2.048 kB Error response from daemon: Unknown instruction: RUNCMD
Docker守护进程逐个运行Dockerfile中的指令,如果需要,将每个指令的结果提交到一个新镜像中,最后输出新镜像的ID。Docker守护进程将自动清理您发送的上下文。
注意,每条指令都是独立运行的,并且会创建一个新镜像——因此,运行cd /tmp不会对接下来的指令产生任何影响。
只要有可能,Docker将重用中间镜像(缓存),以显著加快Docker的构建过程。这由控制台输出中的Using cache消息表示。(有关更多信息,请参阅Dockerfile最佳实践指南中的Build cache部分 Build cache section):
$ docker build -t svendowideit/ambassador . Sending build context to Docker daemon 15.36 kB Step 1/4 : FROM alpine:3.2 ---> 31f630c65071 Step 2/4 : MAINTAINER [email protected] ---> Using cache ---> 2a1c91448f5f Step 3/4 : RUN apk update && apk add socat && rm -r /var/cache/ ---> Using cache ---> 21ed6e7fbb73 Step 4/4 : CMD env | grep _TCP= | (sed ‘s/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/‘ && echo wait) | sh ---> Using cache ---> 7ea8aef582cc Successfully built 7ea8aef582cc
构建缓存仅用于具有本地父链(parent chain)的映像。这意味着这些映像是由以前的构建创建的,或者整个映像链是用docker加载的。如果希望使用特定映像的构建缓存,可以使用--cache-from选项指定它。使用--cache-from指定的镜像不需要父链,可以从其他仓库(registries)获取。
完成构建之后,就可以考虑浏览Pushing a repository to its registry了。
构建工具BuildKit
从18.09版本开始,Docker支持一个新的moby/buildkit项目提供的后端来执行构建(build)。与旧的实现相比,BuildKit后端提供了许多优点。例如,BuildKit可以:
- 检测并跳过未使用的构建阶段(stage)
- 并行化构建独立的构建阶段
- 在构建之间只会增量地传输构建上下文中更改的文件
- 检测并跳过在构建上下文中传输未使用的文件
- 使用带有许多新特性的外部Dockerfile实现
- 避免API其余部分的副作用(中间镜像和容器)
- 为自动修剪(prune)设置构建缓存的优先级
要使用BuildKit后端,您需要在调用docker构建之前在CLI上设置一个环境变量DOCKER_BUILDKIT=1。
要了解基于BuildKit的构建可用的实验性Dockerfile语法,请参考BuildKit存储库中的文档: refer to the documentation in the BuildKit repository.
格式
下面是Dockerfile的格式:
# 注释 指令 参数
指令不区分大小写。但是,习惯上它们是大写的,以便更容易地将它们与参数区分开。
Docker按顺序在Dockerfile中运行指令。Dockerfile必须以“FROM”指令开头。它可能会在解析器指令、注释和全局作用域的参数之后。FROM指令指定要从中构建的父映像。FROM之前可能只有一个或多个ARG指令,它们声明在Dockerfile的FROM行中使用的参数。
Docker将以#开头的行作为注释,行中任何位置的#标记都被视为注释,除非该行是有效的解析器指令。这允许这样的语句:
# Comment RUN echo ‘we are running some # of cool things‘
注释中不支持行延续字符。
解析器指令
解析器指令是可选的,并且影响Dockerfile中后续行的处理方式。解析器指令不向构建中添加层,也不会显示为构建步骤。解析器指令被编写为形式# directive=value中的一种特殊类型的注释。单个指令只能使用一次。
一旦一个注释,空的行和构建指令被执行,Docker就不再寻找解析器指令。相反,它将任何格式化为解析器指令的内容视为注释,并且不尝试验证它是否可能是解析器指令。因此,所有解析器指令必须位于Dockerfile的最顶层。
解析器指令不区分大小写。但是,习惯上它们都是小写的。约定还包括任何解析器指令后面的空行。解析器指令不支持行延续字符。
由于这些规则,下面的例子都是无效的:
- 由于行延续无效:
# direc tive=value
- 无效,因为出现两次:
# directive=value1 # directive=value2 FROM ImageName
- 由于出现在构建指令之后而被视为注释:
FROM ImageName # directive=value
- 由于出现在非解析器指令的注释之后,所以被视为注释:
# About my dockerfile # directive=value FROM ImageName
- 未知指令将被视为注释,因为它不被识别。此外,由于出现在注释之后,已知的指令被视为注释,而注释不是解析器指令。
# unknowndirective=value # knowndirective=value
- 解析器指令中允许非断行空白。因此,下列各行均被同等对待:
#directive=value # directive =value # directive= value # directive = value # dIrEcTiVe=value
- 支持以下解析器指令:
- syntax
- escape
syntax
格式:
# syntax=[remote image reference]
# syntax=docker/dockerfile # syntax=docker/dockerfile:1.0 # syntax=docker.io/docker/dockerfile:1 # syntax=docker/dockerfile:1.0.0-experimental # syntax=example.com/user/repo:[email protected]:abcdef...
只有在使用BuildKit后端时该指令才有用。
syntax指令定义用于构建当前Dockerfile的构建器的位置。BuildKit后端允许无缝地使用构建器的外部实现,这些构建器以Docker镜像的形式分发,并在容器沙箱环境中执行。
自定义Dockerfile的实现允许你:
- 在不用更新后台守护程序的情况下自动的获取错误修正
- 确保所有用户使用相同的实现来构建Dockerfile
- 在不升级后台守护程序的情况下获取最新的功能
- 尝试新的实验性或第三方特性
官方的版本
escape
环境的替换
.dockerignore文件
.dockerignore文件的主旨是要“排除”一些发网daemon的文件,但是里面也有一些逻辑是不排除的,你要先明确这一点。
在docker CLI将上下文发送到docker守护进程之前,它在上下文的根目录中查找一个名为.dockerignore的文件。如果该文件存在,CLI将修改上下文以排除与其中模式匹配的文件和目录。这有助于避免不必要地向守护进程发送大型或敏感的文件和目录,并可能使用ADD或COPY将它们添加到映像中。
CLI将.dockerignore文件解释为一个新行分隔的模式列表,类似于Unix shell的文件globs。为了进行匹配,上下文的根被认为是工作目录和根目录。例如,模式/foo/bar和foo/bar都排除了路径的foo子目录或位于URL的git存储库根目录中名为bar的文件或目录。两者都不排斥其他任何东西。
如果.dockerignore文件中的一行以第1列中的#开始,那么这一行将被视为注释,并在CLI解释之前被忽略。
# comment */temp* */*/temp* temp?
该文件导致以下构建行为:
规则 | 行为 |
---|---|
# comment |
忽略掉. |
*/temp* |
排除在根目录的任何直接子目录中名称以temp开头的文件和目录。例如,排除了普通文件/somedir/temporary.txt,以及/somedir/temp目录。 |
*/*/temp* |
从根目录下两层的任何子目录中排除以temp开头的文件和目录。例如,/somedir/subdir/temporary.txt被排除。 |
temp? |
排除根目录中名称为temp的单字符扩展名的文件和目录。例如,排除/tempa和/tempb。 |
使用Go语言的filepath的Match方法来匹配规则。预处理步骤删除开头和结尾的空白并使用Go语言的filepath.Clean方法消除.和. .。预处理后为空的行将被忽略。
依赖于filepath.Match提供的规则,Docker使用一个特殊的通配符**来匹配任意数量的目录,比如:**/*.go这个会匹配所有目录中.go扩展名的文件,包括构建的根目录。
以!(感叹号)开始的行可用于排除的例外情况。下面是一个例子,.dockerignore文件使用这个机制:
*.md !README.md
除了README.md外所有的markdown文件都被排除在上下文外了。
放置!的地方影响如下行为:.dockerignore中与特定文件匹配的最后一行决定它是被包含还是被排除。考虑下面的例子:
*.md !README*.md README-secret.md
首先所有的markdown文件都被排除了。
在!README*.md下面还有一行 README-secret.md那么结果就是除了这个README-secret.md的所有README开头的markdown文件不会被排除。
在来看下面这个例子:
*.md README-secret.md !README*.md
首先所有的markdown文件都被排除了
然后第二行README-secret.md这个可以不写,因为第一行已经指定了规则,它也符合第一行的规则。
第三行又将所有README开头的markdown文件包含了进来,也就是说第二行根本不会起作用了,它指定的这个文件因为第三行的规则会被包括进去。
甚至可以使用.dockerignore文件来排除Dockerfile和.dockerignore文件。这些文件仍然被发送到守护进程,因为守护进程需要它们来完成自己的工作。但是ADD和COPY指令不会将它们复制到镜像。
最后,你可能希望指定要在上下文中包含哪些文件,而不是要排除哪些文件。要实现这一点,将*指定为第一个模式,然后是一个或多个模式!例外模式。
note:作为历史原因你应该了解,.模式被忽略了。
FROM
原文地址:https://www.cnblogs.com/pangjianxin/p/11793894.html