来自docker官方网址:https://docs.docker.com/engine/reference/builder/linux
docker可以从Dockerfile中读取指令并自动构建一个镜像。Dockerfile是一个文本文档,它包含用户能够在命令行上调用的全部命令来组装一个镜像。使用docker构建,用户能够建立一个连续执行多个命令行指令的自动化构建。nginx
这个页面描述了你能够在Dockerfile中使用的命令。读完此页后,请参阅Dockerfile最佳实践(Dockerfile
Best Practices)以得到面向tip的指南。git
翻译这篇文章前咱们先阐述一个重要的概念,那就是上下文(context):docker构建时会将上下文的全部内容(.dockerignore中指定的内容除外)发送到daemon中进行处理。那么如何指定这个上下文呢?咱们看一个命令来更容易的理解:github
docker build -t xxxxx .
注意这行命令后面有一个.(小点),这个小点的做用不是指定dockerfile的位置,指定dockerfile的位置是用-f参数来指定的,那你觉得这个小点是干吗的?他就是用来指定构建镜像的上下文的。golang
理解了以上的例子后,咱们再看看COPY这个命令的真是含义:docker
COPY ./package.json /app/
COPY是将源目标复制到镜像中的一个命令,那么上面这个./package.json是指什么路径呢?这并非要复制执行 docker build
命令所在的目录下的 package.json
,也不是复制 Dockerfile
所在目录下的 package.json
,而是复制 上下文(context) 目录下的 package.json
。shell
docker构建命令(docker build
)从Dockerfile和上下文构建一个映像。构建的上下文是位于指定位置路径或URL的文件集。路径是本地文件系统上的一个目录。URL是一个Git存储库位置。apache
上下文会被递归的处理。所以,路径包含任何子目录,URL包含存储库及其子模块。这个例子显示了一个使用当前目录做为上下文的构建命令:json
$ docker build . Sending build context to Docker daemon 6.51 MB ...
构建由Docker守护进程运行,而不是由CLI运行。构建过程要作的第一件事是将整个上下文(递归地)发送给守护进程。在大多数状况下,最好从一个空目录做为上下文开始,并将Dockerfile保存在该目录中。而且只添加构建Dockerfile所需的文件。ubuntu
警告:不要使用根目录/做为路径,由于它会致使构建将硬盘驱动器的所有内容传输到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 SvenDowideit@home.org.au ---> 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了。
从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=[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:tag@sha256:abcdef...
只有在使用BuildKit后端时该指令才有用。
syntax指令定义用于构建当前Dockerfile的构建器的位置。BuildKit后端容许无缝地使用构建器的外部实现,这些构建器以Docker镜像的形式分发,并在容器沙箱环境中执行。
自定义Dockerfile的实现容许你:
.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 <image> [AS <name>]
FROM <image>[:<tag>] [AS <name>]
FROM <image>[@<digest>] [AS <name>]
FROM指令初始化一个新的构建阶段,并为后续指令设置基本镜像。所以,一个有效的Dockerfile必须以FROM指令开始。镜像能够是任何有效的镜像—从公共存储库(docker hub)中提取镜像尤为容易。
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在构建阶段以外,所以在FROM以后的任何指令中都不能使用它。若要使用在第一个以前声明的ARG的默认值,请使用在构建阶段内没有值的ARG指令:
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version
RUN有两种格式:
#shellform,命令在shell中运行,默认是Linux上的/bin/sh -c或Windows上的cmd /S /C RUN <command> #execform RUN ["executable", "param1", "param2"]
RUN指令将在当前镜像之上的新层中执行任何命令并提交结果。生成的提交镜像将用于Dockerfile中的下一步。
分层RUN指令和生成提交符合Docker的核心概念,其中提交很廉价,能够从镜像历史中的任何点建立容器,很像源代码控制。
execform格式的RUN指令在避免shell字符串转换和在一个不包含指定的可执行shell的基础镜像上运行RUN指令成为可能。
shellform格式下的默认shell能够经过SHELL命令来修改。
在shellform的格式下,可使用\(反斜杠)将单个运行指令延续到下一行。例如,考虑这两行:
RUN /bin/bash -c 'source $HOME/.bashrc; \ echo $HOME'
与下面这一行是等同的:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
note:使用exec form格式时,能够将目标shell传入,这可使你使用一个不一样的shell,而不是‘/bin/sh’。以下例:
RUN ["/bin/bash", "-c", "echo hello"]
note:exec from格式命令会被解析成json数组,这意味着你必须使用双引号而不是单引号来括住数组中的每个元素。
与shellform不一样,execform不调用命令shell。这意味着不会发生正常的shell处理。例如,RUN ["echo", "$HOME"]不会对$HOME执行变量替换。若是想要shell处理,那么要么使用shellform,要么直接执行shell,例如:RUN ["sh", "-c", "echo $HOME"]。当使用execform并直接执行shell时(如shellform的状况),执行环境变量扩展的是shell,而不是docker。
在JSON格式中(execform会被看成json处理,还记得吗),必须转义反斜杠。这在以反斜杠为路径分隔符的windows系统特别相关。下面这行代码因为不是有效的JSON,将被视为shellform进行处理,并以一种意外的方式失败:运行["c:\windows\system32\tasklist。本例的正确语法是:RUN ["c:\\windows\\system32\\tasklist.exe"]
RUN指令的缓存不会在下一次构建期间自动失效。像RUN apt-get distt -upgrade -y这样的指令的缓存将在下一个构建过程当中重用。RUN指令的缓存能够经过使用--no-cache标志来失效,例如docker build --no-cache。
从 Dockerfile
Best Practices guide 查看更多信息。
运行指令的缓存能够经过添加指令失效。详见下文below。
关于RUN指令已提交的ISSUE
Issue 783 is about file permissions problems that can occur when using the AUFS file system. You might notice it during an attempt to rm
a file, for example.
For systems that have recent aufs version (i.e., dirperm1
mount option can be set), docker will attempt to fix the issue automatically by mounting the layers with dirperm1
option. More details on dirperm1
option can be found at aufs
man page
If your system doesn’t have support for dirperm1
, the issue describes a workaround.
CMD有三种格式:
#exec form, 推荐格式 CMD ["executable","param1","param2"] #做为ENTRYPOINT的默认参数 CMD ["param1","param2"] #shell form CMD command param1 param2 (shell form)
Dockerfile中只能存在一个CMD,若是你有多个CMD,那么最后那个CMD命令会生效。
CMD的主要用途是为执行容器提供缺省值。这些缺省值能够包括可执行文件,也能够省略可执行文件,在这种状况下,您还必须指定一个ENTRYPOINT指令。
NOTE:若是CMD用于为ENTRYPOINT指令提供默认参数,那么应该使用JSON数组格式指定CMD和ENTRYPOINT指令。
NOTE:exec form格式的会被看成JSON数组来对待,这意味着你必须在数组里面使用双引号而不是单引号。
NOTE:exec form不会像shell form那样会调用shell命令,这意味着不会发生普通的shell处理,好比CMD [ "echo", "$HOME" ]这条命令不会在$HOME上发生变量替换。若是想要shell处理,那么要么使用shell form,要么直接执行shell,好比:CMD [ "sh", "-c", "echo $HOME" ]。当使用exec form并直接执行shell时(如shell form的状况),执行环境变量扩展的是shell,而不是docker。
在shell或exec格式中使用时,CMD指令设置在运行镜像时要执行的命令。
若是你使用的是shell form的格式,CMD会在/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运行的参数,那么他们将覆盖CMD中指定的默认值。
不要将RUN与CMD混淆。RUN实际运行命令并提交结果;CMD在构建时不执行任何操做,可是为镜像指定想要的命令。
LABEL <key>=<value> <key>=<value> <key>=<value> ...
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."
一个镜像能够有多个标签。能够在一行中指定多个标签。在Docker 1.10以前,这下降了最终镜像的大小,但如今再也不是这样了。你可能仍然选择以如下两种方式在单个LABEL指令中指定多个标签:
LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \ multi.label2="value2" \ other="value3"
FROM行中指定的基础镜像或父镜像中的LABEL会被你的镜像继承,若是一个LABEL已经存在可是有不一样的值,那么最近使用的LABEL的值会覆盖以前声明的那个值。
使用docker inspect命令来查看LABEL:
"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" },
LABEL指令是这个指令的替代。
不描述这个指令了,不推荐用,若是你有新项目,那么使用LABEL来代替它,没有必要在使用这个指令。
EXPOSE <port> [<port>/<protocol>...]
EXPOSE指令通知Docker容器在运行时监听指定的网络端口。能够指定端口监听TCP仍是UDP,若是没有指定协议,则默认为TCP。
EXPOSE指令实际上并不发布端口。它的功能相似于构建镜像的人员和运行容器的人员之间的一种文档类型,关于要发布哪些端口。也就是说这个命令就是一个提示做用。要在运行容器时实际发布端口,可使用docker run上的-p标志来发布和映射一个或多个端口,或者使用-p标志来发布全部公开的端口并将它们映射到高阶端口。
默认状况下EXPOST假设使用的TCP协议,你能够指定其余协议:
EXPOSE 80/udp
要在相同端口上暴露两个端口,能够指定两行:
EXPOSE 80/tcp EXPOSE 80/udp
在这种状况下,若是在docker run中使用-P,那么TCP和UDP端口将分别公开一次。请记住,-P在主机上使用临时的高阶主机端口,所以TCP和UDP的端口将不相同。
不管EXPOSE作了怎么样的设置,你均可以在运行镜像时经过-p标志来覆盖他们:
docker run -p 80:80/tcp -p 80:80/udp ...
要在主机系统上设置端口重定向,请参阅 using the -P flag.。docker network命令支持在容器之间建立通讯网络,而不须要公开或发布特定的端口,由于链接到docker network的容器能够经过任何端口相互通讯。有关详细信息,请参阅 overview of this feature。
ENV <key> <value>
ENV <key>=<value> ...
ENV指令将环境变量<key>设置为值<value>。此值将位于构建阶段中全部后续指令的环境中,也能够内联地替换( 详见上文环境替换)许多指令。
ENV指令有两种形式。第一种形式是ENV <key> <value>,它将把单个变量设置为一个值。第一个空格以后的整个字符串将被视为<value>—包括空白字符。该值将被解释为其余环境变量,所以若是没有转义,引号字符将被删除。
第二种形式,ENV <key>=<value>…,容许同时设置多个变量。注意,第二种形式在语法中使用了等号(=),而第一种形式没有。与命令行解析同样,引号和反斜杠可用于在值中包含空格。
例子:
ENV myName="John Doe" myDog=Rex\ The\ Dog \ myCat=fluffy
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy
上面这两个例子会在最终的镜像中产生同样的结果。
当从结果镜像运行容器时,使用ENV设置的环境变量将保持不变。可使用docker inspect查看这些值,并使用docker run --env <key>=<value>更改它们。
NOTE:环境持久性可能会致使意想不到的反作用。例如,设置ENV DEBIAN_FRONTEND noninteractive可能会使基于debian的图像上的apt-get使用者感到困惑。要设置单个命令的值,使用RUN <key>=<value> <command>。
ADD有两种格式:
ADD [--chown=<user>:<group>] <src>... <dest>
#包含空格的路径须要此格式 ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
NOTE--chown特性只支持用于构建Linux容器的Dockerfiles,不能用于Windows容器。因为用户和组全部权概念不能在Linux和Windows之间转换,所以使用/etc/passwd和/etc/group将用户名和组名转换为IDs,这限制了该特性只能用于基于Linux os的容器。
ADD指令从<src>复制新的文件、目录或远程文件url,并将它们添加到路径<dest>的镜像文件系统中。
能够指定多个<src>资源,可是若是它们是文件或目录,它们的路径将被解释为相对于构建上下文的源。
每个<src>能够指定通配符,这个通配符会被GO语言的 filepath.Match 提供的逻辑进行处理。好比:
ADD hom* /mydir/ # 添加全部以"hom"开头的文件 ADD hom?.txt /mydir/ # ? 会被一个单独的字符代替, 好比, "home.txt"
<dest>
表示一个绝对路径,获取是相对于WORKDIR的路径,它用来表示资源会被拷贝到目标容器中的位置。
ADD test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/ ADD test /absoluteDir/ # adds "test" to /absoluteDir/
在添加包含特殊字符(如[和])的文件或目录时,须要转义那些遵循Golang规则的路径,以防止它们被视为匹配模式。例如,添加一个名为arr[0].txt的文件,使用如下:
ADD arr[[]0].txt /mydir/ # copy a file named "arr[0].txt" to /mydir/
全部新建立的文件和目录的UID和GID都是0,除非可选的--chown标志指定了一个给定的用户名、groupname或UID/GID组合来请求添加内容的特定全部权--chown标志的格式容许在任何组合中使用用户名和groupname字符串或直接整数UID和GID。提供没有groupname的用户名或没有GID的UID将使用与GID相同的数字UID。若是提供了用户名或groupname,则将使用容器的根文件系统/etc/passwd和/etc/group文件分别执行从名称到整数UID或GID的转换。下面的例子展现了--chown标志的有效定义:
ADD --chown=55:mygroup files* /somedir/ ADD --chown=bin files* /somedir/ ADD --chown=1 files* /somedir/ ADD --chown=10:11 files* /somedir/
若是容器根文件系统不包含/etc/passwd或/etc/group文件,而且在--chown标志中使用了用户名或组名,那么在添加操做时构建将失败。使用数字id不须要查找,也不依赖于容器根文件系统的内容。
在<src>是远程文件URL的状况下,目的地的权限为600。若是正在检索的远程文件具备HTTP Last-Modified标头,则来自该标头的时间戳将用于设置目标文件的时间。可是,与添加过程当中处理的任何其余文件同样,在肯定文件是否更改以及是否应该更新缓存时,并不包括mtime。
NOTE:若是经过STDIN (docker build - < somefile)传递Dockerfile进行构建,则没有构建上下文,所以Dockerfile只能包含基于URL的ADD指令。您还能够经过STDIN (docker build - < archive.tar.gz)传递压缩的归档文件,归档文件根目录下的Dockerfile和归档文件的其他部分将用做构建的上下文。注:STDIN是标准输入的意思。
NOTE:若是你的URL文件使用身份验证进行保护,那么您将须要在容器中使用RUN wget、RUN curl或其余工具,由于ADD指令不支持身份验证。
NOTE:若是<src>的内容发生了变化,那么第一个遇到的ADD指令将使Dockerfile中的全部后续指令的缓存无效。这包括使RUN指令的缓存无效。有关更多信息,请参阅Dockerfile最佳实践指南:Dockerfile
Best Practices guide 。
ADD指令遵循下列规则:
NOTE:目录自己不是复制的,只是它的内容。
不管目标路径上存在什么
COPY有两种格式:
COPY [--chown=<user>:<group>] <src>... <dest>
#该格式是用于路径中有空格的状况 COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
COPY指令基本与ADD指令差很少,差异就在ADD指令会在识别到压缩文件后对文件进行解压缩,COPY不会。COPY的内容就不翻译了,之后有变化再说。
ENTRYPOINT有两种格式:
#(exec form, 推荐的格式) ENTRYPOINT ["executable", "param1", "param2"] #(shell form) ENTRYPOINT command param1 param2
ENTRYPOINT容许你配置一个可看成可执行程序运行的容器。
例如,下面的命令启动一个nginx容器,使用默认的内容,而且监听80端口:
docker run -i -t --rm -p 80:80 nginx
docker run <image>的命令行参数将追加到以exec form格式执行的ENTRYPOINT指令中的全部元素以后,并将覆盖使用CMD指定的全部元素。这容许参数被传递到入口点,也就是说,docker run <image> -d将把-d参数传递给入口点。你可使用docker run - ENTRYPOINT标志覆盖ENTRYPOINT指令。
shell form格式的能够防止使用任何CMD或run命令行参数,可是有一个缺点,即你的ENTRYPOINT
将做为/bin/sh -c的子命令启动,该命令不传递信号。这意味着可执行文件不是容器的PID 1,也不会收到Unix信号,因此你的可执行文件不会收到来自docker stop <container>的终止信号。
只有最后的ENTRYPOINT
会生效。
您可使用ENTRYPOINT的exec form格式来设置至关稳定的默认命令和参数,而后使用CMD的任何一种形式来设置更可能更改的其余默认值。
FROM ubuntu ENTRYPOINT ["top", "-b"]#相对稳定的命令和参数 CMD ["-c"]#可能会更改的参数
当你是用上面的Dockfile运行容器,你能够看到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
你可使用docker stop测试优雅地请求top关闭。
下面的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信号:
#!/usr/bin/env 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 <container>'" 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来运行这个镜像,你能够用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
NOTE:您可使用--ENTRYPOINT覆盖ENTRYPOINT设置,但这只能将二进制文件设置为exec(不使用sh -c)。
NOTE:exec form的格式的命令会被解析为json数组,意味着你必须将数组的元素用双引号括住而不是单引号。
NOTE:与shell form不一样,exec form不调用命令shell。这意味着不会发生正常的shell处理。例如,ENTRYPOINT ["echo", "$HOME"]不会对$HOME执行变量替换。若是您想要shell处理,那么要么使用shell form,要么直接执行shell,例如:ENTRYPOINT ["sh", "-c", "echo $HOME"]。当使用exec form并直接执行shell时(如shell form的状况),执行环境变量扩展的是shell,而不是docker。
之后再说
CMD和ENTRYPOINT指令都定义了在运行容器时执行什么命令。这里有一些规则描述他们的协做关系。
下表显示了对不一样的ENTRYPOINT/ CMD组合执行的命令:
无ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”] | |
---|---|---|---|
无CMD | 错误,不被容许这样作 | /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_entry p1_entry exec_cmd p1_cmd |
CMD [“p1_cmd”, “p2_cmd”] | p1_cmd p2_cmd | /bin/sh -c exec_entry p1_entry | 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 | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
NOTE:若是CMD是从基础镜像定义的,那么设置ENTRYPOINT将把CMD重置为空值。在这种状况下,必须在当前镜像中定义CMD才能得到一个值。
VOLUME ["/data"]
VOLUME指令使用指定的名称建立一个挂载点,并将其标记为持有来自本机主机或其余容器的外部挂载卷。该值能够是一个JSON数组,VOLUME ["/var/log/"],也能够是一个有多个参数的普通字符串,好比VOLUME /var/log或VOLUME /var/log/ var/db。有关经过Docker客户端的更多信息/示例和安装说明,请参阅经过卷文档共享目录: Share Directories via Volumes.。
docker run命令使用存在于基本镜像中指定位置的任何数据初始化新建立的卷。例如,考虑如下Dockerfile片断:
FROM ubuntu RUN mkdir /myvol RUN echo "hello world" > /myvol/greeting VOLUME /myvol
这个Dockerfile会产生一个镜像,这个镜像会致使docker运行时在/myvol处建立一个新的挂载点,并将greeting文件复制到新建立的卷中。
关于Dockerfile中的卷,请记住如下几点。
后续翻译
WORKDIR /path/to/workdir
WORKDIR指令为Dockerfile中的任何RUN、CMD、ENTRYPOINT、COPY和ADD指令设置工做目录。若是WORKDIR不存在,即便在后续的Dockerfile指令中不使用它,也会建立它。
WORKDIR指令能够在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
这个Dockerfile中的最后一个pwd命令的输出是/path/$DIRNAME