Dockerfile
文件,Dockerfile
是一个包含创建镜像所有命令的文本文件,Docker 通过 Dockerfile
的内容来自动构建镜像。docker build
命令构建镜像需要一个 Dockerfile
和一个构建环境(context)。Dockerfile
和镜像构建所需要的文件。/
作为构建环境,否则会发送当前整个文件系统给 Docker daemon。.dockerignore
文件来排除一些不必要的文件和目录(类似 .gitignore
)。-f
选项来指定 Dockerfile
,如果不指定则 docker build
默认读取当前名为 Dockerfile 的文件。-t
可以指定镜像仓库和标签:-t
选项可以指定多次:Dockerfile
中的指令,在必要时将每条指令的结果提交成为一个新的镜像,并输出新的镜像 ID。Docker daemon 会自动清除发送过去的环境(context)。Docker 中每个指令都是独立的,一条指令创建一个镜像。因为镜像的分层机制,Docker 构建过程中会利用中间镜像(缓存),用来提升构建效率。--cache-from
选项指定,--cache-from
不需要拥有一个父链并且可以从其它镜像仓库获取。--cache-from
实际过程中应该使用的很少,笔者基本没有这样的应用场景。Dockerfile
的格式是:Dockerfile
指令并不区分大小写,但是为了区分,建议指令统一采用 大写
Dockerfile
指令是顺序执行的,一个 Dockerfile
文件必须以 FROM
指令开始。FROM
指令指定了构建镜像的基础镜像。ENV
可以在 Dockerfile 中声明一个变量,有些指令可以直接通过 $variable_name
或者 ${variable_name}
获取变量(这种方式同 bash 中引用一样)。当然,${variable_name}
还支持标准的 bash
修饰符:${variable:-word}
表示如果 variable
有值则使用该值,否则为值 word
${variable:+word}
表示如果 variable
有值则使用 word
,否则为空值\
转义环境变量:Dockerfile
指令支持环境变量,当前支持的有如下指令:ADD
COPY
ENV
EXPOSE
FROM
LABEL
STOPSIGNAL
USER
VOLUME
RUN
WORKDIR
.dockerignore
, 它的功能类似 .gitignore
。它需要存放在构建环境根目录下才会起作用,通过 .dockerignore
定义匹配规则来排除文件和目录。通过 .dockerignore
可以避免不必要的大型或敏感文件和目录发送给 Docker daemon,从而避免 ADD
或者 COPY
. 拷贝这些文件和目录。.dockerignore
文件如下:# comment
*/temp*
temp
开头的文件和目录。如 /somedir/temp
、/somedir/temporary.txt
都将会被排除*/*/temp*
temp
开头的文件和目录,如 /somedir/subdir/temporary.txt
会被排除temp?
/tempa
、/tempb
文件目录都会被排除.dockerignore
的匹配规则遵循 Go 的 filepath.Match 规则。除了该规则外,Docker 还支持了一些特殊的通配符,**
匹配任意层级的目录。例如,**/*.go
将排除构建环境根目录下所有以 .go
为后缀的文件。!
表示忽略排除,如下:README.md
外所有以 .md
为后缀的文件。!README.md
在 *.md
之前,则以 *.md
为规则,README.md
依然会被排除。.dockerignore
来排除 Dockerfile
和 .dockerignore
文件。但是这些文件依然会发送到 Docker daemon。不过,ADD
和 COPY
指令将不会拷贝它们。FROM
用来指定构建镜像的基础镜像,如果本地没有指定的镜像,在构建过程中会自动从相应镜像仓库 pull。如果 FROM
语句没有指定镜像标签,则默认使用 latest
标签。RUN
有两种格式:RUN <command>
(shell 格式,命令会在 shell 中执行,默认是 /bin/sh -c
)RUN ["executable", "param1", "param2"]
(exec 格式)RUN
指令会在当前镜像的新层上执行命令并提交执结果,后续 Dockerfile
的指令操作则基于此最新提交的镜像。分层 RUN
指令提交方式是 Docker 的核心理念,首先提交的成本比较低,并且容器可以基于任何历史镜像点创建,好比源码版本控制(git checkout
)。exec
格式会被解析成一个 JSON 数组,所以必须使用 双引号 ,而非单引号。exec
格式执行命令不会调用 command shell,所以也不会继承环境变量。HOME
变量,正确在 exec
这种格式下集成环境变量可以使用如下方式:RUN
指令操作缓存在下次构建时不会自动失效,如果不想利用缓存,则可以添加 --no-cache
选项禁用缓存,即 docker build --no-cache
。RUN <command>
shell 格式类型:RUN
指令主要是在镜像构建过程中,执行一系列的 Linux 命令以达到定制镜像的目的。CMD
有三种格式:CMD ["executable","param1","param2"]
(exec 格式, 推荐使用这种格式)CMD ["param1","param2"]
(作为 ENTRYPOINT
指令参数)CMD command param1 param2
(shell 格式,默认 /bin/sh -c
)Dockerfile
只能有一个 CMD
指令,如果有多个,则只有最后一个 CMD
会生效。CMD
的主要作用是用于容器启动的默认执行命令或者作为 ENTRYPOINT
指令的参数。RUN
指令的 exec
格式,CMD
指令的 exec
格式也会被解析成一个 JSON 数组,所以必须使用 双引号 ,而非单引号。同样 exec
格式执行命令不会调用 command shell,所以也不会继承环境变量。RUN
只会在构建就像时执行,CMD
是在容器启动时才会执行里面的命令,并且在 Dockerfile
中只能有一个 CMD
。LABEL
指令主要用于添加镜像的元数据,是一个 key-value 键值对,使用示例如下:docker inspect
可以查看镜像相关的标签信息。EXPOSE
指令通知 Docker 在容器运行时对外暴露的监听端口。可以指定 TCP
或者 UDP
,默认是 TCP。EXPOSE
指令并不会实际对外暴露指定端口,如果需要暴露,则还需要在 docker run
时添加 -p
或者 -P
选项,其中 -p
可以指定某个或某几个端口映射,而 -P
选择则把 EXPOSE
的所有端口映射到宿主。ENV
指令通过键值对定义环境变量。Dockerfile
中定义的环境变量,可以在执行 docker run
的时候通过 -e
选项替换值。RUN <key>=<value> 设置
。ADD
有两种格式:ADD <src>... <dest>
ADD ["<src>",... "<dest>"]
(这种格式一般在路径有空格的情况下使用)ADD
指令复制本地主机文件、目录或者远程文件 URLS 从 <src>
添加到镜像中的路径 <dest>
(其中如果远程 URL 需要认证,则只能通过 RUN wegt
或者 RUN curl
代理,不过一般也不用 ADD
添加远程文件)。<src>
支持正则匹配,基于 Go 的 filepath.Match 规则。例如:<src>
根目录不是以系统 /
开始的,而是当前构建环境的根目录,如构建环境目录为 ~/docker/app/
,则 ADD
拷贝本地文件目录只能局限于 ~/docker/app/
下的子文件或者子目录。<dest>
是一个绝对路径,或者基于 WORKDIR
的绝对路径:ADD
添加的文件和目录在镜像文件系统中 UID 和 GID 都是 0。如果添加的是一个目录,则只会把目录下的内容(包括文件系统元数据)传输到镜像 <dest>
下,目录本身不拷贝。如果 <dest>
中目录不存在,则会自动层级创建相应目录。<src>
是一个本地 tar 包(tar.gz、tar.xz、tar.bz 都行),添加到镜像中会自动解压成一个文件(解压同 tar -x
),远程文件不支持。<src>
有多个资源指定,那么 <dest>
必须以斜线 /
结尾。COPY
有两种格式:COPY <src>... <dest>
COPY ["<src>",... "<dest>"]
(这种格式一般在路径有空格的情况下使用)COPY
作用同 ADD
,都是拷贝资源到镜像,不过 COPY
功能相对单一,不支持远程 URLs,也不支持自动解压 tar 文件。正常如果不是添加 tar 包的话,统一用 COPY
即可。ENTRYPOINT
有两种格式:ENTRYPOINT ["executable", "param1", "param2"]
(exec 格式,推荐优先使用这种格式)ENTRYPOINT command param1 param2
(shell 格式)ENTRYPOINT
和 CMD
指令有相同的作用,都可以用于容器启动执行命令。两者也可以结合使用,如:CMD
可以在 docker run
的时候轻易被覆盖,而如果要覆盖 ENTRYPOINT
,则必须添加 --entrypoint
选项。同 CMD
,一个 Dockerfile
中只能有一个 ENTRYPOINT
,如果有多个则最后一个生效。/bin/sh -c
的一个子命令启动,并且不会传递任何信号。意思就是说,执行命令在容器中并不会以 PID 1
运行,并且不会接收 UNIX 信号,那么容器在 docker stop
时就不能接收到 SIGTERM
完成正常的退出。exec
和 gosu
命令收到 Unix 信号,以完成程序优雅的退出:ENTRYPOINT
脚本能收到 Unix 信号,并且正常传递,那么你可以通过如下方式实现:ENTRYPOINT
可以通过 --entrypoint
覆盖,不过只能是以 exec 格式。exec 格式会被解析成一个 JSON 数组,所以必须是 双引号
。Dockerfile
中至少要指定 CMD
或者 ENTRYPOINT
中的一个。关于 CMD
和 ENTRYPOINT
的更多,建议参考官方文档 Understand how CMD and ENTRYPOINT interactVOLUME
指令创建一个指定名称的挂载点,并讲其标记为从本地主机或者其它容器外挂卷。该值可以为 JSON 数组,也可以是包含多个参数的普通字符串,如 VOLUME /var/log
或者 VOLUME /var/log /var/db
。USER [:]
USER
指令用来表示容器执行程序的用户(UID)和组(GID)。WORKDIR
用于设置工作目录,RUN
、CMD
、ENTRYPOINT
、COPY
和 ADD
指令将会遵从这一规则。WORKDIR
不存在,则会自动创建Dockerfile
还有一些高级技巧和黑魔法,比如可以通过 STOPSIGNAL signal
设置 system call 信号用以传送给容器退出。这里不做过多的介绍,更多参见 Dockerfile reference.dockerignore
文件.dockerignore
文件可以避免不必要的文件发送到 Docker daemon,以提升镜像构建效率,因此强烈建议使用 .dockerignore
文件。concern
一个容器一个进程
的理念,这是一个好的经验法则,但并不是一条硬性规定,实际过程中保持容器尽可能干净和模块化即可。RUN
、COPY
和 ADD
指令会创建镜像层,其它指令创建临时中间镜像,不再直接增加构建的大小\
换行:Dockerfile
中的顺序执行指令,Docker 会检测缓存中是否有可以复用的镜像,而不是直接创建新的镜像。如果不想使用缓存,可以通过 --no-cache=true
取消缓存读取。Dockerfile
中的指令与子镜像进行比较就够了。针对 COPY
和 ADD
指令则有些不同,除了比较指令是否相同,还需要校验比较镜像中的文件内容(忽略修改时间和访问时间)。如果文件中有任何内容改变,则缓存失效。RUN apt-get -y update
这类命令,则不会匹配缓存。Dockerfile
可读性、可理解、可维护性,通过 \
分隔比较长或者复杂的 RUN
指令:apt-get update
要和 apt-get install
指令要同时使用,否则单独使用 apt-get update
会导致缓存问题(直接使用缓存而不执行该条命令)并且导致 apt-get install 安装命令失败
。RUN
运行命令的时候,可能一些命令依赖 shell 管道的的功能,如:/bin/sh -c
,最后执行的命令退出码决定整个命令是否执行成功。也就是说管道前的命令 wget
即使执行失败,只要 wc -l
能成功执行,就不会停止镜像构建。为了规避这个问题,可以加入 set -o pipefail &&
来保证镜像正常构建:-o pipefail
选项的(比如 dash)。Debian 基础类的镜像默认 shell 是 dash,可以通过如下方式来支持 pipefail
:1RUN ["/bin/bash", "-c", "set -o pipefail && wget -O - https://some.site | wc -l > /number"]Copied!
CMD
应该以 CMD ["executable", "param1", "param2"…]
这种格式运行。不建议结合 ENTRYPOINT
使用,这样反而会带来一定的复杂性。EXPOSE
指令用来表面容器将监听连接的端口,建议使用标准的端口,如 Nginx Web 服务则是 EXPOSE 80
,而 MongoDB 服务则是 EXPOSE 27017
。至于外部映射的端口,用户则可以根据实际自己定义。Dockerfile
更方便维护,如:ADD
和 COPY
功能上很类似,一般建议优先使用 COPY
。COPY
相对 ADD
更透明,就是提供本地文件的拷贝。ADD
最好的应用场景就是,拷贝 tar 包,自动解压。其它场景建议一律使用 COPY
,针对远程文件的拷贝,则使用 RUN
结合 wget
或者 curl
命令代替:ENTRYPOINT
最好的就是用其设置镜像的主运行命令,方便镜像运行的时候直接指定命令参数(或者结合 CMD
设置默认参数)。VOLUME
指令用来定义数据存储路径,强烈建议有存储相关的路径通过 VOLUME
设置卷。gosu
。WORKDIR
使用绝对路径。另外,建议通过 WORKDIR
来替换类似 RUN cd … && do-something
指定,以带来更好的可读性、故障定位等。