2019独角兽企业重金招聘Python工程师标准>>>
dockerfike快速创建自定义的Docker镜像
基本语法格式:INSTRUCTION arguments (指令+参数)不分大小写
注释格式:# 注释
第一个指令必须是FROM,标示使用什么镜像
一、DockerFile结构
DockerFile分为四部分组成:基础镜像信、维护者信息、镜像操作指令和容器启动时执行指令。例如:
#第一行必须指令基于的基础镜像
From ubutu
#维护者信息
MAINTAINER docker_user [email protected]
#镜像的操作指令
apt/sourcelist.list
RUN apt-get update && apt-get install -y ngnix
RUN echo "\ndaemon off;">>/etc/ngnix/nignix.conf
#容器启动时执行指令
CMD /usr/sbin/ngnix
二、DockerFile指令详解
1、From指令 :
FROM
OR
FROM
OR
FROM
DockerFile第一条必须为From指令。如果同一个DockerFile创建多个镜像时,可使用多个From指令(每个镜像一次),镜像可以是任何有效的镜像 - 通过从公共存储库拉取镜像
2、MAINTAINER
格式为maintainer ,指定维护者的信息
3、RUN
格式为Run 或者Run [“executable” ,”Param1”, “param2”] ,RUN ["指令", "参数1", "参数2"...](json格式,必须双引号)
前者在shell终端上运行,即/bin/sh -C,后者使用exec运行。例如:RUN [“/bin/bash”, “-c”,”echo hello”]
RUN指令将在当前镜像之上的新层中执行任何命令,并提交结果。 生成的已提交映像将用于Dockerfile中的下一步。分层RUN指令和生成提交符合Docker的核心概念,其中提交很方便,可以从镜像历史中的任何点创建容器,就像源代码控制一样。exec形式使得可以避免shell字符串变化,以及使用不包含指定的shell可执行文件的基本映像来运行RUN命令。当命令比较长时,可以使用\(反斜杠)将单个RUN指令继续到下一行。
用于RUN指令的高速缓存在下一次构建期间不会自动失效。 用于诸如RUN apt-get dist-upgrade之类的指令的高速缓存将在下一次构建期间被重用。 可以通过使用--no-cache标志(例如docker build --no-cache)使用于RUN指令的高速缓存无效
4、CMD指令
支持三种格式:
CMD [“executable” ,”Param1”, “param2”]使用exec执行,推荐
CMD command param1 param2,在/bin/sh上执行
CMD [“Param1”, “param2”] 提供给ENTRYPOINT做默认参数。
CMD的主要目的是为执行容器提供默认值。 这些默认值可以包括可执行文件,或者它们可以省略可执行文件,在这种情况下,您还必须指定ENTRYPOINT指令。每个容器只能执行一条CMD命令,多个CMD命令时,只最后一条被执行。
5、EXPOSE
格式为 EXPOSE […] 。
告诉Docker服务端容器暴露的端口号,供互联系统使用。在启动Docker时,可以通过-P,主机会自动分配一个端口号转发到指定的端口。使用-P,则可以具体指定哪个本地端口映射过来
例如:
EXPOSE 22 80 8443
6、ENV
格式为 ENV 。 指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持。
格式:ENV
ENV指令将环境变量
ENV指令有两种形式。
第一种形式,ENV
第二种形式,ENV
例如
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
7、ADD
格式:ADD
ADD指令从
ADD遵守以下规则:
路径必须在构建的上下文中; 你不能ADD ../something /东西,因为docker build的第一步是发送上下文目录(和子目录)到docker守护进程。 - 如果
是URL并且 不以尾部斜杠结尾,则从URL下载文件并将其复制到 。 - 如果
是URL并且 以尾部斜杠结尾,则从URL中推断文件名,并将文件下载到 / 。 例如,ADD http://example.com/foobar /会创建文件/ foobar。 该网址必须有一个非平凡的路径,以便在这种情况下可以发现一个适当的文件名(http://example.com不会工作)。 - 如果
是目录,则复制目录的全部内容,包括文件系统元数据。
将文件
-
如果要ADD本地文件,则本地文件必须在 docker build
,指定的 目录下 -
如果要ADD远程文件,则远程文件必须在 docker build
,指定的 目录下。比如: docker build github.com/creack/docker-firefox
-
docker-firefox目录下必须有Dockerfile和要ADD的文件
注意
:使用docker build - < somefile方式进行build,是不能直接将本地文件ADD到container中。只能ADD url file.
ADD只有在build镜像的时候运行一次,后面运行container的时候不会再重新加载了。
8、COPY
格式:COPY
COPY指令从
COPY遵守以下规则:
路径必须在构建的目录中; 你不能COPY ../something / something,因为docker build的第一步是将当前目录(和子目录)发送到docker守护进程。 - 如果
是目录,则复制目录的全部内容,包括文件系统元数据。
当使用本地目录为源目录时,推荐使用 COPY 。
9、ENTRYPOINT
两种格式:
- ENTRYPOINT [“executable”, “param1”, “param2”]
- ENTRYPOINT command param1 param2 (shell中执行)。
ENTRYPOINT允许您配置将作为可执行文件运行的容器。配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。 每个Dockerfile中只能有一个 ENTRYPOINT ,当指定多个时,只有最后一个起效。
如:下面将启动 nginx,监听80端口
$ docker run -i -t --rm -p 80:80 nginx
docker run
- Dockerfile 应该至少指定CMD或ENTRYPOINT其中的1个;
- 容器用作可执行的程序应用时应该用ENTRYPOINT;
- CMD应该用作ENTRYPOINT的默认参数来使用,或是在容器中执行临时的命令;
- 启动容器时如果传递参数将覆盖dockerfile中指定的CMD参数;
下表列出了在CMD和ENTRYPOINT结合使用的情况下,哪个命令最终生效:
注意:如果从基础镜像中定义CMD,则设置ENTRYPOINT会将CMD重置为空值。 在这种情况下,必须在当前镜像中定义CMD才能获得值。
10、VOLUME
格式为 VOLUME [“/data”] 。
创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。
11、USER
格式为 USER daemon 。
指定运行容器时的用户名或UID,后续的 RUN 也会使用指定用户。
当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户,例如: RUN groupadd -r postgres && useradd -r -g postgres postgres 。要临时获取管理员权限可以使用 gosu ,而不推荐 sudo 。
12、WORKDIR
格式为 WORKDIR /path/to/workdir 。
为后续的 RUN 、 CMD 、 ENTRYPOINT 指令配置工作目录。
可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c 。
13、ONBUILD
格式为 ONBUILD [INSTRUCTION] 。
配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。
例如,Dockerfile使用如下的内容创建了镜像 image-A 。
[…]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build –dir /app/src
[…]
如果基于A创建新的镜像时,新的Dockerfile中使用 FROM image-A 指定基础镜像时,会自动执行 ONBUILD 指令内容,等价于在后面添加了两条指令。
FROM image-A
#Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src
使用 ONBUILD 指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild 。
三、创建镜像
通过Docker Build 创建镜像。
命令读取指定路径下(包括子目录)所有的Dockefile,并且把目录下所有内容发送到服务端,由服务端创建镜像。另外可以通过创建.dockerignore文件(每一行添加一个匹配模式)让docker忽略指定目录或者文件
格式为Docker Build [选项] 路径
需要制定标签信息,可以使用-t选项
例如:Dockerfile路径为 /tmp/docker_build/,生成镜像的标签为build_repo/my_images
$dudo docker build -t build_repo/my_images /tmp/docker_build/
Dockerfile文件使用
docker build命令会根据Dockerfile文件及上下文构建新Docker镜像。构建上下文是指Dockerfile所在的本地路径或一个URL(Git仓库地址)。构建上下文环境会被递归处理,所以,构建所指定的路径还包括了子目录,而URL还包括了其中指定的子模块。
构建镜像
将当前目录做为构建上下文时,可以像下面这样使用docker build命令构建镜像:
$ docker build .
Sending build context to Docker daemon 6.51 MB
...
说明:构建会在Docker后台守护进程(daemon)中执行,而不是CLI中。构建前,构建进程会将全部内容(递归)发送到守护进程。大多情况下,应该将一个空目录作为构建上下文环境,并将Dockerfile文件放在该目录下。
在构建上下文中使用的Dockerfile文件,是一个构建指令文件。为了提高构建性能,可以通过.dockerignore文件排除上下文目录下,不需要的文件和目录。
Dockerfile一般位于构建上下文的根目录下,也可以通过-f指定该文件的位置:
$ docker build -f /path/to/a/Dockerfile
构建时,还可以通过-t参数指定构建成后,镜像的仓库、标签等:
镜像标签
$ 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 守护进程会自动清理你发送的上下文。
Dockerfile文件中的每条指令会被独立执行,并会创建一个新镜像,RUN cd /tmp等命令不会对下条指令产生影响。
Docker 会重用已生成的中间镜像,以加速docker build的构建速度。以下是一个使用了缓存镜像的执行过程:
$ 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
构建缓存仅会使用本地父生成链上的镜像。如果不想使用本地缓存的镜像,也可以通过--cache-from指定缓存。指定后将再不使用本地生成的镜像链,而是从镜像仓库中下载。
以上内容参考官方文档:
https://docs.docker.com/engine/reference/builder/#usage
附:Dockerfile 参考文档
预览目录
- Dockerfile
- Usage
- Format
- Parser directives
- escape
- Environment replacement
- .dockerignore file
- FROM
- MAINTAINER
- RUN
- CMD
- LABEL
- EXPOSE
- ENV
- ADD
- COPY
- ENTRYPOINT
- VOLUME
- USER
- WORKDIR
- ARG
- ONBUILD
- STOPSIGNAL
- HEALTHCHECK
- SHELL
- Dockerfile examples
Dockerfile
Dockerfile
是由一系列命令和参数构成的脚本,一个Dockerfile
里面包含了构建整个image
的完整命令。Docker通过docker build
执行Dockerfile
中的一系列命令自动构建image
。
Dockerfile
其语法非常简单,此页面描述了您可以在Dockerfile中使用的命令。 阅读此页面后,你可以参阅Dockerfile最佳实践。
Usage
docker build
命令从Dockerfile
和context
构建image。context
是PATH
或URL
处的文件。PATH
本地文件目录。 URL
是Git repository的位置。
context
以递归方式处理。因此,PATH
包括任何子目录,URL
包括repository及submodules。一个使用当前目录作为context
的简单构建命令:
$ docker build .
Sending build context to Docker daemon 6.51 MB
...
构建由Docker守护程序运行,而不是由CLI运行。构建过程所做的第一件事是将整个context(递归地)发送给守护进程。大多数情况下,最好是将Dockerfile
和所需文件复制到一个空的目录,再到这个目录进行构建。
警告
:不要使用根目录/
作为PATH,因为它会导致构建将硬盘驱动器的所有内容传输到Docker守护程序。
build时添加文件,通过Dockerfile
引用指令中指定的文件,例如COPY
指令。要增加构建的性能,请通过将.dockerignore
文件添加到context
目录中来排除文件和目录。有关如何创建.dockerignore文件的信息,请参阅此页上的文档。
一般的,Dockerfile
位于context
的根中。但使用-f
标志可指定Dockerfile的位置。
$ docker build -f /path/to/a/Dockerfile .
如果build成功,您可以指定要保存新image的repository和tag:
$ docker build -t shykes/myapp .
要在构建后将image标记为多个repositories,请在运行构建命令时添加多个-t
参数:
$ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
Docker守护程序一个接一个地运行Dockerfile
中的指令,如果需要,将每个指令的结果提交到一个新image,最后输出新映像的ID。Docker守护进程将自动清理您发送的context。
请注意,每个指令独立运行,并导致创建一个新image - 因此RUN cd / tmp
对下一个指令不会有任何影响。
只要有可能,Docker将重新使用中间images(缓存),以显着加速docker build
过程。这由控制台输出中的使用缓存消息指示。(有关详细信息,请参阅Dockerfile
最佳实践指南构建缓存部分):
$ docker build -t svendowideit/ambassador .
Sending build context to Docker daemon 15.36 kB
Step 1 : FROM alpine:3.2
---> 31f630c65071
Step 2 : MAINTAINER [email protected]
---> Using cache
---> 2a1c91448f5f
Step 3 : RUN apk update && apk add socat && rm -r /var/cache/
---> Using cache
---> 21ed6e7fbb73
Step 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
构建成功后,就可以准备Pushing a repository to its registry。
Format
Dockerfile
的格式如下:
# Comment
INSTRUCTION arguments
INSTRUCTION
是不区分大小写的,不过建议大写。
Dockerfile中的指令第一个指令必需是
FROM`,指定构建镜像的Base Image。
Dockerfile中以#
开头的行都将视为注释,除非是[Parser directives]()解析器指令。不支持连续注释符。
# Comment
RUN echo 'we are running some # of cool things'
Parser directives
解析器指令是可选的,并且影响处理Dockerfile
中后续行的方式。解析器指令不会向构建中添加图层,并且不会显示在构建步骤。解析器指令是以# directive = value
形式写成一种特殊类型的注释。单个指令只能使用一次。
一旦注释,空行或构建器指令已经被处理,Docker不再寻找解析器指令。相反,它将任何格式化为解析器指令作为注释,并且不尝试验证它是否可能是解析器指令。因此,所有解析器指令必须位于Dockerfile
的最顶端。
解析器指令不区分大小写。然而,约定是他们是小写的。公约还要包括一个空白行,遵循任何解析器指令。解析器指令不支持行连续字符。
由于这些规则,以下示例都无效:
因行延续,无效:
# direc \
tive=value
因出现两次,无效:
# directive=value1
# directive=value2
FROM ImageName
因写在构建指令后,无效:
FROM ImageName
# directive=value
因写在不是解析器指令之后,无效:
# About my dockerfile
FROM ImageName
# directive=value
未知指令视为注释,之后的解析器指令也随之,无效:
# unknowndirective=value
# knowndirective=value
解析器指令中允许使用非换行符空格,下面几行被视为相同:
#directive=value
# directive =value
# directive= value
# directive = value
# dIrEcTiVe=value
支持以下解析器指令: * escape
escape
# escape=\ (backslash)
或者
# escape=` (backtick)
escape
指令设置用于在Dockerfile
中转义字符的字符。如果未指定,则缺省转义字符为\
。
转义字符既用于转义行中的字符,也用于转义换行符。这允许Dockerfile
指令跨越多行。注意,不管escape
解析器指令是否包括在Dockerfile
中,在RUN
命令中不执行转义,除非在行的末尾。
将转义字符设置为 在
Windows上特别有用,其中``是目录路径分隔符。
与Windows PowerShell一致。
请考虑以下示例,这将在Windows
上以非显而易见的方式失败。第二行末尾的第二个\
将被解释为换行符,而不是从第一个\
转义的目标。类似地,假设第三行结尾处的\
实际上作为一条指令处理,它将被视为行继续。这个dockerfile
的结果是第二行和第三行被认为是单个指令:
FROM windowsservercore
COPY testfile.txt c:\\
RUN dir c:\
结果是:
PS C:\John> docker build -t cmd .
Sending build context to Docker daemon 3.072 kB
Step 1 : FROM windowsservercore
---> dbfee88ee9fd
Step 2 : COPY testfile.txt c:RUN dir c:
GetFileAttributesEx c:RUN: The system cannot find the file specified.
PS C:\John>
上述的一个解决方案是使用/
作为COPY
指令和dir
的目标。然而,这种语法,最好的情况是混乱,因为它在Windows
上是不平常的路径,最坏的情况下,错误倾向,因为并不是Windows
上的所有命令支持/
作为路径分隔符。
通过添加转义解析器指令,下面的Dockerfile在Windows上使用文件路径的自然平台语义成功:
# escape=`
FROM windowsservercore
COPY testfile.txt c:\
RUN dir c:\
结果是:
PS C:\John> docker build -t succeeds --no-cache=true .
Sending build context to Docker daemon 3.072 kB
Step 1 : FROM windowsservercore
---> dbfee88ee9fd
Step 2 : COPY testfile.txt c:\
---> 99ceb62e90df
Removing intermediate container 62afbe726221
Step 3 : RUN dir c:\
---> Running in a5ff53ad6323
Volume in drive C has no label.
Volume Serial Number is 1440-27FA
Directory of c:\
03/25/2016 05:28 AM inetpub
03/25/2016 04:22 AM PerfLogs
04/22/2016 10:59 PM Program Files
03/25/2016 04:22 AM Program Files (x86)
04/18/2016 09:26 AM 4 testfile.txt
04/22/2016 10:59 PM Users
04/22/2016 10:59 PM Windows
1 File(s) 4 bytes
6 Dir(s) 21,252,689,920 bytes free
---> 2569aa19abef
Removing intermediate container a5ff53ad6323
Successfully built 2569aa19abef
PS C:\John>
Environment replacement
环境变量(使用ENV语句声明)也可以在某些指令中用作要由Dockerfile
解释的变量。还可以处理转义,以将类似变量的语法包含在语句中。
环境变量在Dockerfile
中用$variable_name
或${variable_name}
表示。它们被等同对待,并且括号语法通常用于解决不带空格的变量名的问题,例如${foo}_bar
。
${variable_name}
语法还支持以下指定的一些标准bash
修饰符:
${variable:-word}
表示如果设置了variable
,则结果将是该值。如果variable
未设置,那么word
将是结果。${variable:+word}
表示如果设置了variable
,那么word
将是结果,否则结果是空字符串。
在所有情况下,word
可以是任何字符串,包括额外的环境变量。
可以通过在变量之前添加\
来转义:\$foo
或\${foo}
,分别转换为$foo
和${foo}
。
示例(解析的表示显示在#
后面):
FROM busybox
ENV foo /bar
WORKDIR ${foo} # WORKDIR /bar
ADD . $foo # ADD . /bar
COPY \$foo /quux # COPY $foo /quux
Dockerfile
中的以下指令列表支持环境变量:
- ADD
- COPY
- ENV
- EXPOSE
- LABEL
- USER
- WORKDIR
- VOLUME
- STOPSIGNAL
以及:
- ONBUILD(当与上面支持的指令之一组合时)
注意
:在1.4之前,ONBUILD
指令不支持环境变量,即使与上面列出的任何指令相结合。
环境变量替换将在整个命令中对每个变量使用相同的值。换句话说,在这个例子中:
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc
将导致def
值为hello
,不再bye
。然而,ghi
的值为bye
,因为它不是设置abc
为bye
的相同命令的一部分。
.dockerignore file
在docker CLI将上下文发送到docker守护程序之前,它会在上下文的根目录中查找名为.dockerignore
的文件。如果此文件存在,CLI将修改上下文以排除匹配其中模式的文件和目录。这有助于避免不必要地向守护程序发送大型或敏感文件和目录,并可能使用ADD
或COPY
将其添加到映像。
CLI将.dockerignore
文件解释为换行符分隔的模式列表,类似于Unix shell
的file globs。为了匹配的目的,上下文的根被认为是工作目录和根目录。例如,模式/foo/bar
和foo/bar
都排除了PATH
的foo
子目录中的名为bar
的文件或目录,或者位于URL
处的git repository的根目录中。也不排除任何其他。
如果.dockerignore
文件中的一行以第1列中以#
开头,则此行被视为注释,并在CLI解释之前被忽略。
这里是一个例子.dockerignore
文件:
# 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。预处理后为空的行将被忽略。
除了Go的filepath.Match规则,Docker还支持一个特殊的通配符字符串**
,它匹配任何数量的目录(包括零)。 例如,**/*.go
将排除所有目录中找到的以.go
结尾的所有文件,包括构建上下文的根。
行开头!
(感叹号)可用于排除例外。 以下是使用此机制的.dockerignore
文件示例:
*.md
!README.md
除了README.md
之外的所有markdown文件都从上下文中排除。
放置!
异常规则影响行为:匹配特定文件的.dockerignore
的最后一行确定它是包括还是排除。思考下面的例子:
*.md
!README*.md
README-secret.md
除了README-secret.md
之外的README
文件,上下文中排除所有markdown文件。
现在思考这个例子:
*.md
README-secret.md
!README*.md
包括所有README文件。 中间行没有效果,因为最后的!README*.md
与README-secret.md
匹配。
您甚至可以使用.dockerignore
文件来排除Dockerfile
和.dockerignore
文件。这些文件仍然发送到守护程序,因为它需要它们来完成它的工作。但是ADD
和COPY
命令不会将它们复制到映像。
最后,您可能需要指定要包括在上下文中的文件,而不是要排除的文件。 要实现这一点,指定*
作为第一个模式,后面跟一个或多个!
异常模式。
注意
:由于历史原因.
模式。被忽略。
FROM
FROM
# 或则
FROM :
# 或则
FROM @
FROM
指令为后续指令设置Base Image。因此,有效的Dockerfile
必须具有FROM
作为其第一条指令。image可以是任何有效的image - 可以从Public Repositories pulling an image。
FROM
必须是Dockerfile
中的第一个非注释指令。FROM
可以在单个Dockerfile
中多次出现,以创建多个图像。只需记下在每个新的FROM
命令之前由提交输出的最后一个image ID。tag
或digest
是可选的。如果省略其中任何一个,构建器将默认使用latest
。如果构建器与tag
值不匹配,则构建器将返回错误。
MAINTAINER
MAINTAINER
MAINTAINER
指令允许您设置生成的images的作者字段。
RUN
RUN有2种形式:
RUN
(*shell*形式,命令在shell中运行,Linux上为/bin/sh -c
,Windows上为cmd /S/C
)RUN ["executable","param1","param2"]
(exec 形式)
RUN
指令将在当前image之上的新层中执行任何命令,并提交结果。生成的已提交image将用于Dockerfile
中的下一步。
分层RUN
指令和生成提交符合Docker的核心概念,其中提交很轻量,可以从image历史中的任何点创建容器,就像源代码控制一样。
exec
形式使得可以避免shell字符串变化,以及使用不包含指定的shell可执行文件的基本image来运行RUN
命令。
可以使用SHELL
命令更改shell表单的默认shell。
在shell形式中,可以使用\
(反斜杠)将单个RUN
指令继续到下一行。例如,考虑这两行:RUN /bin/bash -c 'source $HOME/.bashrc ; \ echo $HOME'
它们等同于这一行:RUN /bin/bash -c 'source $HOME/.bashrc ; echo $HOME'
注意
:要使用不同的shell,而不是’/bin/sh’,请使用在所需shell中传递的exec形式。例如,RUN [“/bin/bash”,“-c”,“echo hello”]
注意
:exec形式作为JSON数组解析,这意味着您必须在单词之外使用双引号(”)而不是单引号(’)。
注意
:与shell表单不同,exec表单不调用命令shell。这意味着正常的shell处理不会发生。例如,RUN ["echo","$HOME"]
不会在$HOME
上进行可变替换。如果你想要shell处理,那么使用shell形式或直接执行一个shell,例如:RUN ["sh","-c","echo $HOME"]
。当使用exec形式并直接执行shell时,正如shell形式的情况,它是做环境变量扩展的shell,而不是docker。
注意
:在JSON形式中,有必要转义反斜杠。这在Windows上特别相关,其中反斜杠是路径分隔符。因为不是有效的JSON,并且以意外的方式失败,以下行将被视为shell形式:RUN ["c:\windows\system32\tasklist.exe"]
此示例的正确语法为:RUN ["c:\\windows\\system32\\tasklist.exe"]
用于RUN
指令的高速缓存在下一次构建期间不会自动失效。用于诸如RUN apt-get dist-upgrade
之类的指令的高速缓存将在下一次构建期间被重用。可以通过使用--no-cache
标志来使用于RUN
指令的高速缓存无效,例如docker build --no-cache
。
有关详细信息,请参阅Dockerfile最佳实践指南。
用于RUN
指令的高速缓存可以通过ADD
指令无效。有关详细信息,请参见下文。
Known issues(RUN)
- Issue 783是关于在使用AUFS文件系统时可能发生的文件权限问题。例如,您可能会在尝试
rm
文件时注意到它。对于具有最近aufs版本的系统(即,可以设置dirperm1
安装选项),docker将尝试通过使用dirperm1
选项安装image来自动解决问题。有关dirperm1
选项的更多详细信息,请参见aufs手册页 如果您的系统不支持dirperm1
,则该问题描述了一种解决方法。
CMD
CMD指令三种形式:
CMD ["executable","param1","param2"]
(exec form, 首选形式)CMD ["param1","param2"]
(as default parameters to ENTRYPOINT)CMD command param1 param2
(shell form)
在Dockerfile
中只能有一个CMD
指令。如果您列出多个CMD
,则只有最后一个CMD
将生效。
CMD
的主要目的是为执行容器提供默认值。这些默认值可以包括可执行文件,或者它们可以省略可执行文件,在这种情况下,您还必须指定ENTRYPOINT
指令。
注意
:如果使用CMD
为ENTRYPOINT
指令提供默认参数,CMD
和ENTRYPOINT
指令都应以JSON数组格式指定。
注意
:exec形式作为JSON数组解析,这意味着您必须在单词之外使用双引号(”)而不是单引号(’)。
注意
:与shell表单不同,exec表单不调用命令shell。这意味着正常的shell处理不会发生。例如,CMD ["echo","$HOME"]
不会在$HOME
上进行可变替换。如果你想要shell处理,那么使用shell形式或直接执行一个shell,例如:CMD ["sh","-c","echo $HOME"]
。当使用exec形式并直接执行shell时,正如shell形式的情况,它是做环境变量扩展的shell,而不是docker。
当以shell或exec格式使用时,CMD
指令设置运行image时要执行的命令。
如果使用CMD
的shell形式,那么
将在/bin/sh -c
中执行:
FROM ubuntu
CMD echo "This is a test." | wc -
如果你想运行你的
没有shell,那么你必须将该命令表示为一个JSON数组,并给出可执行文件的完整路径。此数组形式是CMD
的首选格式。任何其他参数必须单独表示为数组中的字符串:
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
如果你希望你的容器每次运行相同的可执行文件,那么你应该考虑使用ENTRYPOINT结合CMD。 请参阅ENTRYPOINT。
如果用户指定docker run
参数,那么它们将覆盖CMD
中指定的默认值。
注意
:不要将RUN
和CMD
混淆。RUN
实际上运行一个命令并提交结果;CMD
在构建时不执行任何操作,但指定了image的预期命令。
LABEL
LABEL = = = ...
LABEL
指令向image添加元数据。LABEL
是键值对。要在LABEL
值中包含空格,请使用引号和反斜杠,就像在命令行解析中一样。几个使用示例:
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."
image可以有多个label。要指定多个label,Docker建议在可能的情况下将标签合并到单个LABEL
指令中。每个LABEL
指令产生一个新层,如果使用许多标签,可能会导致效率低下的图像。该示例产生单个图像层。
LABEL multi.label1="value1" multi.label2="value2" other="value3"
上面的也可写为:
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
标签是添加的,包括LABEL
在FROM
images中。如果Docker遇到已经存在的label/key,则新值将覆盖具有相同键的任何先前标签。
要查看image的labels,请使用docker inspect
命令。
"Labels": {
"com.example.vendor": "ACME Incorporated"
"com.example.label-with-value": "foo",
"version": "1.0",
"description": "This text illustrates that label-values can span multiple lines.",
"multi.label1": "value1",
"multi.label2": "value2",
"other": "value3"
},
EXPOSE
EXPOSE [...]
EXPOSE
指令通知Docker容器在运行时侦听指定的网络端口。EXPOSE
不使主机的容器的端口可访问。为此,必须使用-p
标志发布一系列端口,或者使用-P
标志发布所有暴露的端口。您可以公开一个端口号,并用另一个端口号在外部发布。
要在主机系统上设置端口重定向,请参阅使用-P标志。Docker网络功能支持创建网络,无需在网络中公开端口,有关详细信息,请参阅此功能的概述)。
ENV
ENV
ENV = ...
ENV
指令将环境变量
设置为值
。该值将在所有”descendant” Dockerfile
命令的环境中,并且可以在许多中被替换为inline。
ENV
指令有两种形式。第一种形式,ENV
,将单个变量设置为一个值。第一个空格后面的整个字符串将被视为
- 包括空格和引号等字符。
第二种形式,ENV
,允许一次设置多个变量。注意,第二种形式在语法中使用等号(=),而第一种形式不使用。与命令行解析类似,引号和反斜杠可用于在值内包含空格。
例如:
ENV myName="John Doe" myDog=Rex\ The\ Dog \
myCat=fluffy
# 和
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy
将在最终容器中产生相同的净结果,但第一种形式是优选的,因为它产生单个高速缓存层。
使用ENV
设置的环境变量将在从生成的image运行容器时保留。您可以使用docker inspect
查看值,并使用docker run --env
更改它们。
注意
:环境持久性可能会导致意外的副作用。例如,将ENV DEBIAN_FRONTEND
设置为非交互式可能会使apt-get用户混淆基于Debian的映像。要为单个命令设置值,请使用RUN
。=
ADD
两种形式:
ADD
... ADD ["
(对于包含空格的路径,此形式是必需的)",... " "]
ADD
指令从
复制新文件,目录或远程文件URL
,并将它们添加到容器的文件系统,路径
。
可以指定多个
资源,但如果它们是文件或目录,那么它们必须是相对于正在构建的源目录(构建的context)。
每个
可能包含通配符,匹配将使用Go的filepath.Match规则完成。 例如:
ADD hom* /mydir/ # adds all files starting with "hom"
ADD hom?.txt /mydir/ # ? is replaced with any single character, e.g., "home.txt"
是绝对路径或相对于WORKDIR
的路径,源将在目标容器中复制到其中。
ADD test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/
ADD test /absoluteDir/ # adds "test" to /absoluteDir/
所有新文件和目录都使用UID和GID为0创建。
在
是远程文件URL
的情况下,目标将具有600的权限。如果正在检索的远程文件具有HTTP Last-Modified
标头,则来自该标头的时间戳将用于设置目的地上的mtime
文件。然而,像在ADD
期间处理的任何其它文件一样,mtime
将不包括在确定文件是否已经改变并且高速缓存应该被更新。
注意
:如果通过传递一个Dockerfile
通过STDIN(docker build -
)构建,没有构建上下文,所以 Dockerfile
只能包含一个基于URL的ADD
指令。您还可以通过STDIN传递压缩归档文件:(docker build -
),归档根目录下的 Dockerfile
和归档的其余部分将在构建的上下文中使用。
注意
:如果您的URL文件使用身份验证保护,则您需要使用RUN wget
,RUN curl
或从容器内使用其他工具,因为ADD指令不支持身份验证。
注意
:如果的内容已更改,第一个遇到的
ADD
指令将使来自Dockerfile
的所有后续指令的高速缓存无效。这包括使用于RUN指令的高速缓存无效。有关详细信息,请参阅Dockerfile最佳实践指南。
ADD
遵守以下规则:
路径必须在构建的上下文中;你不能ADD ../something /something
,因为docker构建的第一步是发送上下文目录(和子目录)到docker守护进程。如果
是URL并且
不以尾部斜杠结尾,则从URL下载文件并将其复制到
。- 如果
是URL并且
以尾部斜杠结尾,则从URL中推断文件名,并将文件下载到
。例如,/ ADD http://example.com/foobar /
会创建文件/ foobar
。网址必须有一个非平凡的路径,以便在这种情况下可以发现一个适当的文件名(http://example.com
不会工作)。 - 如果
是目录,则复制目录的整个内容,包括文件系统元数据。
注意
:目录本身不被复制,只是其内容。
- 如果
是识别的压缩格式(identity,gzip,bzip2或xz)的本地tar存档,则将其解包为目录。来自远程URL的资源不会解压缩。当目录被复制或解压缩时,它具有与tar -x
相同的行为:结果是以下的联合:- 无论在目的地路径和
- 源树的内容,冲突以逐个文件为基础解析为“2.”。
注意
:文件是否被识别为识别的压缩格式,仅基于文件的内容,而不是文件的名称。例如,如果一个空文件以.tar.gz结尾,则不会被识别为压缩文件,并且不会生成任何解压缩错误消息,而是将该文件简单地复制到目的地。
- 如果
是任何其他类型的文件,它会与其元数据一起单独复制。在这种情况下,如果
以尾部斜杠/
结尾,它将被认为是一个目录,并且
的内容将被写在
。/base( ) - 如果直接或由于使用通配符指定了多个
资源,则
必须是目录,并且必须以斜杠/
结尾。 - 如果
不以尾部斜杠结尾,它将被视为常规文件,
的内容将写在
。 - 如果
不存在,则会与其路径中的所有缺少的目录一起创建。
COPY
两种形式:
COPY
... COPY ["
(this form is required for paths containing whitespace)",... " "]
基本和ADD类似,不过COPY
的
不能为URL。
ENTRYPOINT
两种形式:
- ENTRYPOINT [“executable”, “param1”, “param2”] (exec 形式, 首选)
- ENTRYPOINT command param1 param2 (shell 形式)
ENTRYPOINT
允许您配置容器,运行执行的可执行文件。
例如,以下将使用其默认内容启动nginx,侦听端口80:
docker run -i -t --rm -p 80:80 nginx
docker run
的命令行参数将附跟在 exec 形式的ENTRYPOINT
中的所有元素之后,并将覆盖使用CMD
指定的所有元素。这允许将参数传递到入口点,即docker run
将把-d
参数传递给入口点。您可以使用docker run --entrypoint
标志覆盖ENTRYPOINT
指令。
shell 形式防止使用任何CMD
或运行命令行参数,但是缺点是您的ENTRYPOINT
将作/bin/sh -c
的子命令启动,它不传递信号。这意味着可执行文件将不是容器的PID 1
,并且不会接收Unix信号,因此您的可执行文件将不会从docker stop
接收到SIGTERM
。
只有Dockerfile
中最后一个ENTRYPOINT
指令会有效果。
Exec form ENTRYPOINT example
您可以使用ENTRYPOINT
的*exec*形式设置相当稳定的默认命令和参数,然后使用任一形式的CMD
设置更可能更改的其他默认值。
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
运行容器时,您可以看到top是唯一的进程:
$ docker run -it --rm --name test top -H
top - 08:25:00 up 7:27, 0 users, load average: 0.00, 0.01, 0.05
Threads: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.1 us, 0.1 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem: 2056668 total, 1616832 used, 439836 free, 99352 buffers
KiB Swap: 1441840 total, 0 used, 1441840 free. 1324440 cached Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 19744 2336 2080 R 0.0 0.1 0:00.04 top
要进一步检查结果,可以使用docker exec
:
$ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 2.6 0.1 19752 2352 ? Ss+ 08:24 0:00 top -b -H
root 7 0.0 0.1 15572 2164 ? R+ 08:25 0:00 ps aux
并且你可以优雅地请求top
使用docker stop test
关闭。
以下Dockerfile
显示使用ENTRYPOINT
在前台运行Apache
(即,作为PID 1
):
FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
如果需要为单个可执行文件编写启动脚本,可以使用exec
和gosu
命令确保最终可执行文件接收到Unix信号:
#!/bin/bash
set -e
if [ "$1" = 'postgres' ]; then
chown -R postgres "$PGDATA"
if [ -z "$(ls -A "$PGDATA")" ]; then
gosu postgres initdb
fi
exec gosu postgres "$@"
fi
exec "$@"
最后,如果需要在关闭时进行一些额外的清理(或与其他容器通信),或者协调多个可执行文件,您可能需要确保ENTRYPOINT
脚本接收到Unix信号,传递它们,然后做一些更多的工作:
#!/bin/sh
# Note: I've written this using sh so it works in the busybox container too
# USE the trap if you need to also do manual cleanup after the service is stopped,
# or need to start multiple services in the one container
trap "echo TRAPed signal" HUP INT QUIT TERM
# start service in background here
/usr/sbin/apachectl start
echo "[hit enter key to exit] or run 'docker stop '"
read
# stop service and clean up here
echo "stopping apache"
/usr/sbin/apachectl stop
echo "exited $0"
如果你用docker run -it --rm -p 80:80 --name test apache
运行image,则可以使用·docker exec·或·docker top·检查容器的进程,然后请求脚本停止Apache:
$ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.0 4448 692 ? Ss+ 00:42 0:00 /bin/sh /run.sh 123 cmd cmd2
root 19 0.0 0.2 71304 4440 ? Ss 00:42 0:00 /usr/sbin/apache2 -k start
www-data 20 0.2 0.2 360468 6004 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
www-data 21 0.2 0.2 360468 6000 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
root 81 0.0 0.1 15572 2140 ? R+ 00:44 0:00 ps aux
$ docker top test
PID USER COMMAND
10035 root {run.sh} /bin/sh /run.sh 123 cmd cmd2
10054 root /usr/sbin/apache2 -k start
10055 33 /usr/sbin/apache2 -k start
10056 33 /usr/sbin/apache2 -k start
$ /usr/bin/time docker stop test
test
real 0m 0.27s
user 0m 0.03s
sys 0m 0.03s
注意
:您可以使用--entrypoint
覆盖ENTRYPOINT
设置,但这只能将二进制设置为*exec*(不使用sh -c
)。
注意
:*exec*形式作为JSON数组解析,这意味着您必须在单词之外使用双引号(”)而不是单引号(’)。
注意
:与shell
形式不同,*exec*形式不调用命令shell。这意味着正常的shell处理不会发生。例如,ENTRYPOINT ["echo","$ HOME"]
不会在$HOME
上进行可变替换。如果你想要shell处理,那么使用shell形式或直接执行一个shell,例如:ENTRYPOINT ["sh","-c","echo $HOME"]
。当使用exec形式并直接执行shell时,正如shell形式的情况,它是做环境变量扩展的shell,而不是docker。
Shell form ENTRYPOINT example
您可以为ENTRYPOINT
指定一个纯字符串,它将在/bin/sh -c
中执行。这中形式将使用shell处理来替换shell环境变量,并且将忽略任何CMD
或docker run
命令行参数。要确保docker stop
将正确地发出任何长时间运行的ENTRYPOINT
可执行文件,您需要记住用exec
启动它:
FROM ubuntu
ENTRYPOINT exec top -b
运行此image时,您将看到单个PID 1
进程:
$ docker run -it --rm --name test top
Mem: 1704520K used, 352148K free, 0K shrd, 0K buff, 140368121167873K cached
CPU: 5% usr 0% sys 0% nic 94% idle 0% io 0% irq 0% sirq
Load average: 0.08 0.03 0.05 2/98 6
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
1 0 root R 3164 0% 0% top -b
它奖在docker stop
干净的退出:
$ /usr/bin/time docker stop test
test
real 0m 0.20s
user 0m 0.02s
sys 0m 0.04s
如果忘记将exec
添加到您的ENTRYPOINT
的开头:
FROM ubuntu
ENTRYPOINT top -b
CMD --ignored-param1
然后,您可以运行它(给它一个名称为下一步):
$ docker run -it --name test top --ignored-param2
Mem: 1704184K used, 352484K free, 0K shrd, 0K buff, 140621524238337K cached
CPU: 9% usr 2% sys 0% nic 88% idle 0% io 0% irq 0% sirq
Load average: 0.01 0.02 0.05 2/101 7
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
1 0 root S 3168 0% 0% /bin/sh -c top -b cmd cmd2
7 1 root R 3164 0% 0% top -b
您可以从top的输出中看到指定的ENTRYPOINT不是PID 1。
如果你然后运行docker停止测试,容器将不会完全退出 - 停止命令将强制发送SIGKILL超时后:
$ docker exec -it test ps aux
PID USER COMMAND
1 root /bin/sh -c top -b cmd cmd2
7 root top -b
8 root ps aux
$ /usr/bin/time docker stop test
test
real 0m 10.19s
user 0m 0.04s
sys 0m 0.03s
Understand how CMD and ENTRYPOINT interact
CMD
和ENTRYPOINT
指令定义在运行容器时执行什么命令。这里有较少的规则描述他们的合作。
Dockerfile
应该至少指定一个CMD
或ENTRYPOINT
命令。- 当使用容器作为可执行文件时,应该定义
ENTRYPOINT
。 CMD
应该用作定义ENTRYPOINT
命令的默认参数或在容器中执行ad-hoc命令的一种方法。- 当运行带有替代参数的容器时,
CMD
将被覆盖。
下表显示了对不同ENTRYPOINT
/CMD
组合执行的命令:
no ENTRYPOINT | ENTRYPOINT exec_enty p1_entry | ENTRYPOINT [“exec_entry”,“p1_entry”] | |
---|---|---|---|
No CMD | error, not allowed | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
CMD [“exec_cmd”,“p1_cmd”] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry exec_cmd p1_cmd | exec_entry p1_entry exec_cmd p1_cmd |
CMD [“p1_cmd”, “p2_cmd”] | p1_cmd p2_cmd | /bin/sh -c exec_entry p1_entry p1_cmd p2_cmd | exec_entry p1_entry p1_cmd p2_cmd |
CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
VOLUME
VOLUME ["/data"]
VOLUME
指令创建具有指定名称的挂载点,并将其标记为从本机主机或其他容器保留外部挂载的卷。该值可以是JSON数组VOLUME ["/var/log/"]
或具有多个参数的纯字符串,例如VOLUME /var/log
或VOLUME /var/log /var/db
。有关通过Docker客户端的更多信息/示例和安装说明,请参阅通过卷文档共享目录。
docker run
命令用存在于基本image中指定位置的任何数据初始化新创建的卷。例如,思考以下Dockerfile
片段:
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol
此Dockerfile docker run
的映像,在/myvol
上创建一个新的挂载点,并将greeting
文件复制到新创建的卷中。
注意
:如果任何构建步骤在声明后更改卷中的数据,那么这些更改将被丢弃。
注意
:该列表解析为JSON数组,这意味着您必须在单词之外使用双引号(”)而不是单引号(’)。
USER
USER daemon
USER
指令设置运行image时使用的用户名或UID,以及Dockerfile
中的任何RUN,CMD
和ENTRYPOINT
指令。
WORKDIR
WORKDIR /path/to/workdir
WORKDIR
指令为Dockerfile
中的任何RUN
,CMD
,ENTRYPOINT
,COPY
和ADD
指令设置工作目录。如果WORKDIR
不存在,它将被创建,即使它没有在任何后续的Dockerfile
指令中使用。
它可以在一个Dockerfile
中多次使用。如果提供了相对路径,它将相对于先前WORKDIR指令的路径。 例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
在这个Dockerfile
中的最终pwd
命令的输出是/a/b/c
。
WORKDIR
指令可以解析先前使用ENV
设置的环境变量。您只能使用在Dockerfile
中显式设置的环境变量。 例如:
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
pwd
命令在该Dockerfile
中输出的最后结果是/path/$DIRNAME
。
ARG
ARG [=]
ARG
指令定义一个变量,用户可以使用docker build
命令使用--build-arg
标志,在构建时将其传递给构建器。如果用户指定了一个未在Dockerfile
中定义的构建参数,构建将输出错误。
One or more build-args were not consumed, failing build.
Dockerfile作者可以通过指定ARG
一个或多个变量,通过多次指定ARG
来定义单个变量。例如,一个有效的Dockerfile
:
FROM busybox
ARG user1
ARG buildno
...
Dockerfile作者可以可选地指定ARG
指令的默认值:
FROM busybox
ARG user1=someuser
ARG buildno=1
...
如果ARG
值具有缺省值,并且如果在构建时没有传递值,则构建器使用缺省值。
ARG
变量定义从在Dockerfile
中定义的行开始生效,而不是从命令行或其他地方的参数使用。例如,考虑这个Dockerfile:
1 FROM busybox
2 USER ${user:-some_user}
3 ARG user
4 USER $user
...
用户构建此文件如下:
$ docker build --build-arg user=what_user Dockerfile
第2行的USER
将评估为some_user
,因为用户变量在后续行3上定义。第4行的USER
在定义用户时估计为what_user
,在命令行中传递what_user
值。在通过ARG
指令定义之前,变量的任何使用都将导致空字符串。
警告
:不建议使用build-time变量来传递诸如github密钥,用户凭证等密码。构建时变量值使用docker history
命令对图像的任何用户可见。
可以使用ARG
或ENV
指令来指定RUN
指令可用的变量。使用ENV
指令定义的环境变量总是覆盖同名的ARG
指令。思考这个Dockerfile
带有ENV
和ARG
指令。
1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER v1.0.0
4 RUN echo $CONT_IMG_VER
然后,假设此image是使用此命令构建的:
$ docker build --build-arg CONT_IMG_VER=v2.0.1 Dockerfile
在这种情况下,RUN
指令使用v1.0.0
而不是用户传递的ARG
设置:v2.0.1
此行为类似于shell脚本,其中本地作用域变量覆盖作为参数传递或从环境继承的变量,从其定义点。
使用上述示例,但使用不同的ENV
规范,您可以在ARG
和ENV
指令之间创建更有用的交互:
1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}
4 RUN echo $CONT_IMG_VER
与ARG
指令不同,ENV
值始终保留在image中。考虑一个没有-build-arg
标志的docker构建:
$ docker build Dockerfile
使用这个Dockerfile示例,CONT_IMG_VER
仍然保留在映像中,但它的值将是v1.0.0
,因为它是ENV
指令在第3行中的默认设置。
此示例中的变量扩展技术允许您从命令行传递参数,并通过利用ENV
指令将它们持久保存在最终image中。仅对一组有限的Dockerfile指令支持变量扩展。
Docker有一组预定义的ARG
变量,您可以在Dockerfile中使用相应的ARG指令。
- HTTP_PROXY
- http_proxy
- HTTPS_PROXY
- https_proxy
- FTP_PROXY
- ftp_proxy
- NO_PROXY
- no_proxy
要使用这些,只需在命令行使用标志传递它们:
--build-arg =
Impact on build caching
ARG
变量不会持久化到构建的image中,因为ENV
变量是。但是,ARG
变量会以类似的方式影响构建缓存。如果一个Dockerfile
定义一个ARG
变量,它的值不同于以前的版本,那么在它的第一次使用时会出现一个“cache miss”,而不是它的定义。特别地,在ARG
指令之后的所有RUN
指令都隐式地使用ARG变量(作为环境变量),因此可能导致高速缓存未命中。
例如,考虑这两个Dockerfile:
1 FROM ubuntu
2 ARG CONT_IMG_VER
3 RUN echo $CONT_IMG_VER
1 FROM ubuntu
2 ARG CONT_IMG_VER
3 RUN echo hello
如果在命令行上指定--build-arg CONT_IMG_VER =
,则在这两种情况下,第2行的规范不会导致高速缓存未命中;行3确实导致高速缓存未命中。ARG CONT_IMG_VER
导致RUN
行被标识为与运行CONT_IMG_VER =
相同,因此如果
更改,我们将得到高速缓存未命中。
考虑在同一命令行下的另一个示例:
1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER $CONT_IMG_VER
4 RUN echo $CONT_IMG_VER
在此示例中,高速缓存未命中发生在第3行。由于变量在ENV
中的值引用ARG
变量并且该变量通过命令行更改,因此发生了未命中。在此示例中,ENV
命令使image包括该值。
如果ENV
指令覆盖同名的ARG
指令,就像这个Dockerfile:
1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER hello
4 RUN echo $CONT_IMG_VER
第3行不会导致高速缓存未命中,因为CONT_IMG_VER
的值是一个常量(hello)。因此,RUN
(第4行)上使用的环境变量和值在构建之间不会更改。
ONBUILD
ONBUILD [INSTRUCTION]
ONBUILD
指令在image被用作另一个构建的基础时,向image添加要在以后执行的*trigger*指令。trigger将在下游构建的上下文中执行,就好像它已经在下游Dockerfile中的1FROM1指令之后立即插入。
任何构建指令都可以注册为trigger。
如果您正在构建将用作构建其他image的基础的图像,例如应用程序构建环境或可以使用用户特定配置自定义的后台驻留程序,这将非常有用。
例如,如果您的image是可重用的Python应用程序构建器,则需要将应用程序源代码添加到特定目录中,并且可能需要在此之后调用构建脚本。你不能只是调用ADD
和RUN
现在,因为你还没有访问应用程序源代码,它将是不同的每个应用程序构建。您可以简单地为应用程序开发人员提供一个样板Dockerfile以将其复制粘贴到其应用程序中,但这是低效,容易出错,并且很难更新,因为它与应用程序特定的代码混合。
解决方案是使用ONBUILD
来注册提前指令,以便稍后在下一个构建阶段运行。
以下是它的工作原理:
- 当遇到
ONBUILD
指令时,构建器会向正在构建的image的元数据添加trigger。该指令不会另外影响当前构建。 - 在构建结束时,所有trigger的列表存储在image清单中的OnBuild键下。可以使用
docker inspect
命令检查它们。 - 稍后,可以使用
FROM
指令将image用作新构建的基础。作为处理FROM
指令的一部分,下游构建器会查找ONBUILD
triggers,并按照它们注册的顺序执行它们。如果任何触发器失败,则FROM
指令中止,这又导致构建失败。如果所有触发器都成功,则FROM
指令完成并且构建如常继续。触发器在执行后从最终image中清除。换句话说,它们不会被“grand-children”构建继承。 例如,您可以添加如下:[...] ONBUILD ADD . /app/src ONBUILD RUN /usr/local/bin/python-build --dir /app/src [...]
>警告
:不允许使用ONBUILD ONBUILD
链接ONBUILD
指令。 >警告
:ONBUILD
指令可能不会触发FROM
或MAINTAINER
指令。
STOPSIGNAL
STOPSIGNAL signal
STOPSIGNAL
指令设置将发送到容器以退出的系统调用信号。该信号可以是与内核系统调用表中的位置匹配的有效无符号数,例如9,或者是SIGNAME格式的信号名称,例如SIGKILL。
HEALTHCHECK
两种形式:
- HEALTHCHECK [OPTIONS] CMD command (通过在容器中运行命令来检查容器运行状况)
- HEALTHCHECK NONE (禁用从基本映像继承的任何运行状况检查)
HEALTHCHECK
指令告诉Docker如何测试容器以检查它是否仍在工作。这可以检测到诸如Web服务器被卡在无限循环中并且无法处理新连接的情况,即使服务器进程仍在运行。
当容器指定了healthcheck时,除了其正常状态外,它还具有健康状态。此状态最初开始。 每当健康检查通过,它变得健康(无论之前的状态)。在一定数量的连续故障后,它变得不健康。
在CMD之前可以出现的选项有:
--interval=DURATION
(default: 30s)--timeout=DURATION
(default: 30s)--retries=N
(default: 3)
运行状况检查将首先在容器启动后运行interval秒,然后在每次上次检查完成后再次运行interval秒。
如果检查的单次运行所花费的时间超过timeout秒数,则该检查被认为已失败。
它需要retries连续的健康检查的故障,容器被认为是不健康的。
在Dockerfile中只能有一个HEALTHCHECK
指令。如果您列出多个,则只有最后一个HEALTHCHECK
将生效。
CMD
关键字之后的命令可以是shell命令(例如HEALTHCHECK CMD /bin/check-running
)或exec数组(如同其他Dockerfile命令一样;详情参见ENTRYPOINT
)。
命令的退出状态表示容器的运行状况。 可能的值为:
- 0: success - the container is healthy and ready for use
- 1: unhealthy - the container is not working correctly
- 2: reserved - do not use this exit code
例如,要每五分钟检查一次Web服务器能够在三秒钟内为网站的主页提供服务:
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1
为了帮助调试失败的探测器,命令在stdout或stderr上写入的任何输出文本(UTF-8编码)将存储在运行状况状态,并可以使用docker inspect
查询。这样的输出应该保持短路(只存储当前的4096个字节)。
当容器的运行状况发生更改时,将生成具有新状态的health_status
事件。
HEALTHCHECK
功能在Docker 1.12中添加。
SHELL
SHELL ["executable", "parameters"]
SHELL
指令允许用于命令的shell形式的默认shell被覆盖。 Linux上的默认shell是["/bin/sh","-c"]
,在Windows上是["cmd","/S","/C"]
。SHELL
指令必须以JSON格式写在Dockerfile中。
SHELL
指令在Windows上特别有用,其中有两个常用的和完全不同的本机shell:cmd
和powershell
,以及包括sh
的备用Shell。
SHELL
指令可以多次出现。每个SHELL
指令覆盖所有先前的SHELL
指令,并影响所有后续指令。 例如:
FROM windowsservercore
# Executed as cmd /S /C echo default
RUN echo default
# Executed as cmd /S /C powershell -command Write-Host default
RUN powershell -command Write-Host default
# Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello
# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S"", "/C"]
RUN echo hello
以下指令可能受SHELL
指令的影响,当它们的shell形式用于Dockerfile:RUN
,CMD
和ENTRYPOINT
。
以下示例是Windows上的常见模式,可以使用SHELL指令进行简化:
...
RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
...
docker调用的命令将是:
cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
这是低效的,有两个原因。首先,有一个不必要的cmd.exe命令处理器(也称为shell)被调用。其次,shell中的每个RUN
指令都需要一个额外的powershell -command
。
为了更有效率,可以采用两种机制之一。 一种是使用JSON形式的RUN命令,如:
...
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]
...
虽然JSON形式是明确的,并且不使用不必要的cmd.exe,但它需要通过双引号和转义更详细。 备用机制是使用SHELL
指令和shell形式,为Windows用户提供更自然的语法,特别是与escape
解析指令结合使用时:
# escape=`
FROM windowsservercore
SHELL ["powershell","-command"]
RUN New-Item -ItemType Directory C:\Example
ADD Execute-MyCmdlet.ps1 c:\example\
RUN c:\example\Execute-MyCmdlet -sample 'hello world'
结果是:
PS E:\docker\build\shell> docker build -t shell .
Sending build context to Docker daemon 3.584 kB
Step 1 : FROM windowsservercore
---> 5bc36a335344
Step 2 : SHELL powershell -command
---> Running in 87d7a64c9751
---> 4327358436c1
Removing intermediate container 87d7a64c9751
Step 3 : RUN New-Item -ItemType Directory C:\Example
---> Running in 3e6ba16b8df9
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 6/2/2016 2:59 PM Example
---> 1f1dfdcec085
Removing intermediate container 3e6ba16b8df9
Step 4 : ADD Execute-MyCmdlet.ps1 c:\example\
---> 6770b4c17f29
Removing intermediate container b139e34291dc
Step 5 : RUN c:\example\Execute-MyCmdlet -sample 'hello world'
---> Running in abdcf50dfd1f
Hello from Execute-MyCmdlet.ps1 - passed hello world
---> ba0e25255fda
Removing intermediate container abdcf50dfd1f
Successfully built ba0e25255fda
PS E:\docker\build\shell>
SHELL
指令还可以用于修改外壳操作的方式。例如,在Windows上使用SHELL cmd /S /C /V:ON|OFF
,可以修改延迟的环境变量扩展语义。
SHELL
指令也可以在Linux上使用,如果需要一个替代shell,如zsh
,csh
,tcsh
和其他。
SHELL
功能在Docker 1.12中添加。
Dockerfile examples
下面你可以看到一些Dockerfile语法的例子。 如果你对更现实的东西感兴趣,可以看看Dockerization的例子。
# Nginx
#
# VERSION 0.0.1
FROM ubuntu
MAINTAINER Victor Vieux
LABEL Description="This image is used to start the foobar executable" Vendor="ACME Products" Version="1.0"
RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server
# Firefox over VNC
#
# VERSION 0.3
FROM ubuntu
# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN apt-get update && apt-get install -y x11vnc xvfb firefox
RUN mkdir ~/.vnc
# Setup a password
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way, but it does the trick)
RUN bash -c 'echo "firefox" >> /.bashrc'
EXPOSE 5900
CMD ["x11vnc", "-forever", "-usepw", "-create"]
# Multiple images example
#
# VERSION 0.1
FROM ubuntu
RUN echo foo > bar
# Will output something like ===> 907ad6c2736f
FROM ubuntu
RUN echo moo > oink
# Will output something like ===> 695d7793cbe4
# You᾿ll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with
# /oink.