Dockerfile参考
docker可以通过读取Dockerfile
中的指令来自动构建镜像。一个Dockerfile
就是一个文本文档,其中包含了编译一个镜像的所有命,然后调用docker build
来创建出相应的镜像。
本章我们会讲述一下Dockefile中可以使用的命令。
1. 用法
docker build
命令通过一个Dockerfile
和上下文环境
来创建docker镜像。构建的上下文环境
就是就是一个指定的PATH
或者URL
。PATH
是指本地文件系统中的一个目录;而URL
是指一个git仓库地址。
一个上下文环境
会被递归的处理。因此,一个PATH
中的任何子目录或者URL
下的所有子仓库或子模块都属于该上下文。如下的例子使用当前目录来作为构建上下文:
# docker build . Sending build context to Docker daemon 6.51 MB
这个构建过程是由docker daemon来完成的,而并不是由docker client来完成。构建过程的第一步就是将整个上下文(recursively)发送给docker daemon。在大部分情况下,最好使用一个空目录来作为上下文环境,在该目录下存放Dockerfile文件,然后只添加一些必要的文件来构建该Dockerfile。
要在构建上下文中使用一个文件,Dockerfile
必须通过相应的指令来引用到该文件,例如:COPY
指令。为了提高整个构建的效率,可以通过添加一个.dockerignore
文件来排除掉上下文目录中的一些文件或目录。
通常,构建时用到的Dockerfile
名称为Dockerfile,并且被放在构建上下文的根目录。你也可以在docker build
命令后通过使用-f
选项来指定Dockerfile的存放位置。例如:
# docker build -f /path/to/a/Dockerfile .
也可以通过使用一个-t
选项来为新构建的镜像指定仓库
和标签
:
# docker build -t shykes/myapp .
如果要为构建出来的镜像指定不同的仓库,则可以在docker build
命令后添加多个-t
选项:
# docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
在docker daemon运行Dockerfile中的指令之前,docker daemon会进行一个基本的检查,假如有语法错误的话则返回相应的报错:
# docker build -t test/myapp . Sending build context to Docker daemon 2.048 kB Error response from daemon: Unknown instruction: RUNCMD
Docker daemon
会一条一条的执行Dockerfile中的指令。如果有必要,则会将每一条指令的执行结果提交到新的镜像中,最后会输出新构建的docker镜像的sha256摘要信息。构建完成后,Docker Daemon
就会清除传送给它的上下文环境。值得注意的是,每一条指令都是独立运行的,并没有上下文关系,例如RUN cd /tmp
指令执行后并不会影响到下一条指令的执行(即并不是在/tmp目录)。
在任何时候,docker都会复用缓存的镜像来加速docker build
进程,这可以通过输出控制台中的Using cache
信息看出。例如:
注意: 构建时所采用的缓存只会来源于有相同parent chain的本地镜像。
2. Dockerfile格式
Dockerfile的基本格式如下:
# Comment INSTRUCTION arguments
其中指令部分是不区分大小写的,然而通常建议使用大写,并以此来区分arguments。
dockr按Dockerfile中指令的顺序执行。一个Dockerfile必须以'FROM'指令起始
。FROM
指令指定了你当前构建所采用的基础镜像。只有ARG
指令可以出现在FROM指令之前,用于声明FROM指令所使用的参数。
Docker将以#
开头的行作为注释行,除非该行是一个有效的parser指令
。在行中的#
标志都会被视为一个参数。例如:
# Comment RUN echo 'we are running some # of cool things'
注意: 在注释中并不支持line continuation字符
3. Parser指令
Parser指令
是可选的,如果存在的话会影响到后续Dockerfile的处理。Parser指令
在镜像构建时并不会增加镜像的层数,并且并不会在构建时显示为一个单独的构建步骤。Parser指令被写作一种特殊类型的注释# directive=value
, 并且一个单独的解析指令只能出现一次。
一旦一个注释行、空行或者构建指令被处理完成,Docker就不会再去寻找parser指令了,这时docker会将所有的解析指令当做注释。因此,所有的解析指令必须出现在Dockerfile的最前面。
Parser指令是不区分大小写的,然而通常将它们写成小写。在parser指令之后,通常会跟一个空行。parser指令不允许换行。针对上面的规则,如下都是无效的parser指令:
1) 无效解析指令: 换行
# direc \ tive=value
2) 无效指令: 多次重复出现
# directive=value1 # directive=value2 FROM ImageName
3) 无效指令: 出现在构建指令之后
下面由于出现在构建指令之后,因此会被当做是注释:
FROM ImageName # directive=value
4) 无效指令: 出现在注释行之后
下面由于出现在注释行之后,因此并不是一个有效的解析指令:
# About my dockerfile # directive=value FROM ImageName
5) 无效指令: 出现在不能识别的指令之后
下面因为unknowndirective
不能被识别,因此不会被认为是一个解析指令,会被当做注释;而后续的knowndirective
则由于出现在注释之后,因此也不是一个有效的解析指令。
# unknowndirective=value # knowndirective=value
6) 解析指令等价性
如下的解析指令是等价的(由于解析指令不区分大小写、不支持换行):
#directive=value # directive =value # directive= value # directive = value # dIrEcTiVe=value
7) escape解析指令
Dockerfile当前支持的解析指令有:escape指令。例如:
# escape=\ (backslash)
或者:
# escape=` (backtick)
escape指令
用于设置Dockerfile的转义符号。假如没有被指定,默认情况下是\
。
转义符号不但用于一行中的转义字符中,还可用于转义一个新行(即当做本行处理)。值得注意的是,不管Dockerfile中存不存在escape指令,RUN
命令中的字符都不会发生转义,除非是在行尾部。
4. Environment替换
Environment变量(通过ENV指令定义)在Dockerfile中可以被用于很多指令中。在Dockerfile中可以用$variable_name
或者${variable_name}
的方式来引用,这两种方式都是相同的。${variable_name}
形式在类似与${foo}_bar
这样的情况下很实用。
同时${variable_name}
语法形式也支持一些标准的bash
修正:
-
${variable:-word}
: 表示假如variable被定义,则结果为variable值;否则结果为word -
${variable:+word}
: 表示假如variable被定义,则结果为world;否则结果为空字符串
下面举一个使用Environment变量的例子( #后面作为解释):
Environment变量可以出现在Dockerfile的下列指令中:
ADD / COPY / ENV / EXPOSE / FROM / LABEL STOPSIGNAL / USER / VOLUME / WORKDIR
在整个指令中,环境变量替换都会用相同的值。例如:
ENV abc=hello ENV abc=bye def=$abc ENV ghi=$abc
上面def的值为hello
; 而ghi的值为bye
。
5. .dockerignore
文件
在Docker Client发送上下文给docker daemon之前,其会在上下文根目录寻找一个.dockerignore
文件。假如该文件存在,Docker Client就会修改上下文以排除所匹配到的文件和目录。这可以避免发送一些不必要的大文件或敏感文件到docker daemon,并通过ADD
或COPY
命令添加到镜像中。
可以通过.dockerignore
文件定义所需要忽略的文件列表。例如:
# comment */temp* */*/temp* temp?
6. FROM指令
FROM
指令格式如下:
FROM
指令初始化整个构建stage,并为后续的指令设置Base Image
。通常情况下,一个Dockerfile文件以FROM
指令起始,并且该基础镜像是可以为任何有效的镜像。
-
在Dockerfile中,
ARG
指令是唯一可以放在FROM
指令之前的指令 -
在一个单独的Dockerfile中,
FROM
指令可以出现多次,以此来构建出多个镜像 -
在
FROM
指令后可以为该构建stage命名(通过AS name
),并在后续的FROM
指令或者COPY --from=<name|index>
指令中来引用该stage构建出的镜像 -
可以添加一个
tag
或者digest
(可选),假如没有添加的话则默认为latest
7. ARG指令与FROM指令的交互
FROM
指令支持由ARG
指令所声明的变量。
ARG CODE_VERSION=latest FROM base:${CODE_VERSION} CMD /code/run-app FROM extras:${CODE_VERSION} CMD /code/run-extras
在FROM
指令之前的ARG
指令是不属于当前构建stage的,因此其不能在FROM
指令之后继续使用。而为了要继续使用FROM
指令之前的ARG
指令的默认值,我们可以再次声明:
8. RUN指令
RUN
指令有两种形式:
-
RUN <command>
: 此种形式为shell形式,即该指令在shell环境中运行。在Linux操作系统上默认是/bin/sh -c
来执行;在Windows上默认是cmd -S -C
来执行 -
RUN ["executable","param1","param2"]
: 此种形式为exec形式
RUN
指令会在当前镜像基础上的一个新layer执行command,并且将结果提交到该新layer。产生的提交后的镜像又会被用于Dockerfile中的下一步。exec
形式可以避免shell字符串整理,且可以指定在另外一种非默认shell环境下执行命令。例如:
RUN ["/bin/bash", "-c", "echo hello"]
说明: exec形式中,是以json形式来解析命令,因此必须使用双引号。
9. CMD指令
CMD指令
有三种形式:
-
CMD ["executable","param1","param2"]
: exec形式,这也是我们所推荐的形式 -
CMD ["param1","param2"]
: 作为ENTRYPOINT的默认参数 -
CMD command param1 param2
: shell形式
在一个Dockerfile中只能有一个CMD
指令。假如有多个CMD指令的话,则只有最后一个CMD有效。CMD指令
的主要目的提供一个默认操作。该默认操作可以包括一个可执行动作;或者为ENTRYPOINT提供相应的参数(形式2)
对于CMD
指令的shell形式,则<command>
会在/bin/sh -c
中执行:
FROM ubuntu CMD echo "This is a test." | wc
假若你并不想要在shell中执行<command>
,则必须指定可执行文件的全路径。这也是我们所推荐的形式:
FROM ubuntu CMD ["/usr/bin/wc","--help"]
假如你想要容器每次都运行同一个可执行文件,你应该考虑CMD
结合ENTRYPOINT
一起使用。
假如你为docker run
指定了相应的参数,则这些参数会覆盖默认的CMD指令。
10. LABEL指令
基本格式如下:
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."
一个镜像可以有多个label,你可以在一行中指定多个label,也可以分开多行来指定。label是可以被继承的,FROM
中镜像的label可以被子镜像继承。假如有两个labe的key相同,但是value不同,则后一个label会覆盖前一个label的值。
11. MAINTAINER指令
当前本指令已过时,请用LABEL
指令代替。