为何要写这一篇呢?其实在之前的文章docker 学习笔记中没有展开讲,我们都知道使用docker build
命令或使用Docker Hub的自动构建功能构建Docker镜像时,都需要一个Dockerfile文件。Dockerfile文件是一个由一系列构建指令组成的文本文件,docker build
命令会根据这些构建指令完成Docker镜像的构建。
目录
构建上下文
docker build
命令会根据Dockerfile文件及上下文构建新Docker镜像。
构建上下文是指Dockerfile所在的本地路径或一个URL
(Git
仓库地址)。构建上下文环境会被递归处理,所以,构建所指定的路径还包括了子目录,而URL
还包括了其中指定的子模块。
构建镜像的运行环境
构建会在Docker后台守护进程(daemon)中执行,而不是CLI
中。
构建前,构建进程会将全部内容(递归)发送到守护进程。大多情况下,应该将一个空目录作为构建上下文环境,并将Dockerfile文件放在该目录下。
在构建上下文中使用的Dockerfile文件,是一个构建指令文件。为了提高构建性能,可以通过.dockerignore
文件排除上下文目录下,不需要的文件和目录。
后台守护进程首先会对Dockerfile进行语法检查,有语法错误时会返回,通过后才会执行!
缓存
Docker 守护进程会一条一条的执行Dockerfile中的指令,而且会在每一步提交并生成一个新镜像,最后会输出最终镜像的ID。生成完成后,Docker 守护进程会自动清理你发送的上下文。
Dockerfile文件中的每条指令会被独立执行,并会创建一个新镜像,RUN cd /tmp
等命令不会对下条指令产生影响。Docker 会重用已生成的中间镜像,以加速docker build
的构建速度。
构建缓存仅会使用本地父生成链上的镜像。如果不想使用本地缓存的镜像,也可以通过--cache-from
指定缓存。指定后将再不使用本地生成的镜像链,而是从镜像仓库中下载。
以下是一个使用了缓存镜像的执行过程(Using cache):
$ 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
Dockerfile文件格式
- Dockerfile文件中指令不区分大小写,但为了更易区分,约定使用大写形式
- 由于指令会依次执行,文件中的第一条指令必须是
FROM
,FROM
指令用于指定一个基础镜像 - 以
#
开头的行,Docker会认为是注释。但#
出现在指令参数中时,则不是注释。
# 注释
指令 参数# Comment
RUN echo 'we are running some # of cool things'
下面将开始介绍本文的重点内容,Dockerfile中使用指令。
1. FROM
FROM
指令用于指定其后构建新镜像所使用的基础镜像。FROM
指令必是Dockerfile文件中的首条命令,启动构建流程后,Docker将会基于该镜像构建新镜像,FROM
后的命令也会基于这个基础镜像。
FROM <image>
FROM <image>:<tag>
FROM <image>:<digest>
tag
或digest
是可选的,如果不使用这两个值时,会使用latest
版本的基础镜像
2. RUN
RUN
用于在镜像容器中执行命令,RUN
指令创建的中间镜像会被缓存,并会在下次构建中使用。其有以下两种命令执行方式:
shell
执行,在这种方式会在shell
中执行命令,Linux下默认使用/bin/sh -c
,Windows下使用cmd /S /C
。格式:RUN <command>exec
执行,格式:RUN ["executable", "param1", "param2"]
也可以在同一行中,通过分号分隔命令:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
3. CMD
CMD
用于指定在容器启动时所要执行的命令。CMD
在Dockerfile文件中仅可指定一次,指定多次时,会覆盖前的指令。
另外,如果docker run
运行容器时,使用了Dockerfile中CMD
相同的命令,就会覆盖Dockerfile中的CMD
命令。比如:Dockerfile文件中使用CMD ["/bin/bash"],运行时指令$sudo docker run -i -t itbilu/test 就会发生覆盖。
如果不想使用CMD
中指定的命令,就可以在docker run
命令的结尾指定所要运行的命令:$sudo docker run -i -t itbilu/test /bin/ps,这时,docker run
结尾指定的/bin/ps
命令覆盖了Dockerfile的CMD
中指定的命令。
CMD
有以下三种格式:
CMD ["executable","param1","param2"]
CMD ["param1","param2"]
CMD command param1 param2
CMD和RUN
CMD
用于指定在容器启动时所要执行的命令,而RUN
用于指定镜像构建时所要执行的命令。但功能实现上也有相似之处:
docker run -t -i itbilu/static_web_server /bin/true
等价于:
cmd ["/bin/true"]
4. ENTRYPOINT
ENTRYPOINT
用于给容器配置一个可执行程序。Dockerfile中只允许有一个ENTRYPOINT
命令,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT
指令。也就是说,每次使用镜像创建容器时,通过ENTRYPOINT
指定的程序都会被设置为默认程序。ENTRYPOINT
有以下两种形式:
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2
ENTRYPOINT
与CMD
非常类似,不同的是通过docker run
执行的命令不会覆盖ENTRYPOINT
,而docker run
命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT
。docker run
运行容器时指定的参数都会被传递给ENTRYPOINT
,且会覆盖CMD
命令指定的参数。如,执行docker run <image> -d
时,-d
参数将被传递给入口点。也可以通过docker run --entrypoint
重写ENTRYPOINT
入口点。比如:Dockerfile中有ENTRYPOINT ["/usr/bin/nginx"],运行时$sudo docker run -i -t itbilu/test -g "daemon off;",最终在容器中执行的命令为/usr/sbin/nginx -g "daemon off;"。
5. LABEL
LABEL
用于为镜像添加元数据,元数以键值对的形式指定,一条LABEL
指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔:LABEL version="1.0" description="这是一个Web服务器" by="IT笔录"
推荐将所有的元数据通过一条LABEL
指令指定,以免生成过多的中间镜像。
注:Dockerfile中还有个MAINTAINER
命令,该命令用于指定镜像作者。但MAINTAINER
并不推荐使用,更推荐使用LABEL
来指定镜像作者。如:LABEL maintainer="itbilu.com"
6. EXPOSE
EXPOSE
用于指定容器在运行时监听的端口:EXPOSE <port> [<port>...] 。EXPOSE
并不会让容器的端口访问到主机。要使其可访问,需要在docker run
运行容器时通过-p
来发布这些端口,或通过-P
参数来发布EXPOSE
导出的所有端口。
7. ENV
ENV
用于设置环境变量,其有以下两种设置形式:
ENV <key> <value>
ENV <key>=<value> ...
这些环境变量不仅可以构建镜像过程(在ENV
命令之后)使用,使用该镜像创建的容器中也可以使用。
ENV ITBILU_PATH /home/itbilu/
WORKERDIR $ITBILU_PATH
$ docker run -i -t itbilu/test
root@196ca123c0c3:/# cd $ITBILU_PATH
root@196ca123c0c3:/home/itbilu#
8. ADD
ADD
用于复制构建环境中的文件或目录到镜像中。<src>是源文件位置可以是一个构建上下文中的文件或目录,也可以是一个URL
,但不能访问构建上下文之外的文件或目录,<dest>
来指定目标位置。其有以下两种使用方式:
ADD <src>... <dest>
ADD ["<src>",... "<dest>"]
ADD http://wordpress.org/latest.zip $ITBILU_PATH,$ITBILU_PATH
是我们使用ENV
指定的一个环境变量。
9. COPY
COPY
同样用于复制构建环境中的文件或目录到镜像中。COPY
指令非常类似于ADD
,不同点在于COPY
只会复制构建目录下的文件,不能使用URL
也不会进行解压操作。其有以下两种使用方式:
COPY <src>... <dest>
COPY ["<src>",... "<dest>"]
10. VOLUME
VOLUME
用于创建挂载点,即向基于所构建镜像创始的容器添加卷:VOLUME ["/data"]。一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
- 卷可以容器间共享和重用
- 容器并不一定要和其它容器共享卷
- 修改卷后会立即生效
- 对卷的修改不会对镜像产生影响
- 卷会一直存在,直到没有任何容器在使用它
VOLUME
让我们可以将源代码、数据或其它内容添加到镜像中,而又不并提交到镜像中,并使我们可以多个容器间共享这些内容。运行容器时,需-v
参将能本地目录绑定到容器的卷(挂载点)上,以使容器可以访问宿主机的数据。
ENV ITBILU_PATH /home/itbilu/
VOLUME [$ITBILU_PATH]
$ sudo docker run -i -t -v ~/code/itbilu:/home/itbilu/ itbilu/test
root@31b0fac536c4:/# cd /home/itbilu/
root@31b0fac536c4:/home/itbilu# ls
11. USER
USER
用于指定运行镜像所使用的用户,用户可以使用用户名、UID
或GID
,或是两者的组合。
使用USER
指定用户后,Dockerfile中其后的命令RUN
、CMD
、ENTRYPOINT
都将使用该用户。镜像构建完成后,通过docker run
运行容器时,可以通过-u
参数来覆盖所指定的用户。
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group
12. WORKDIR
WORKDIR
用于在容器内设置一个工作目录,通过WORKDIR
设置工作目录后,Dockerfile中其后的命令RUN
、CMD
、ENTRYPOINT
、ADD
、COPY
等命令都会在该目录下执行。在使用docker run
运行容器时,可以通过-w
参数覆盖构建时所设置的工作目录。
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
pwd
最终将会在/a/b/c
目录中执行
13. ARG
ARG
用于指定传递给构建运行时的变量,
ARG site
ARG build_user=IT笔录
$sudo docker build --build-arg site=itiblu.com -t itbilu/test .
以上我们指定了site
和build_user
两个变量,其中build_user
指定了默认值。在使用docker build
构建镜像时,可以通过--build-arg <varname>=<value>
参数来指定或重设置这些变量的值
14. ONBUILD
ONBUILD
用于设置镜像触发器,当所构建的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被钥触发。
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]
15. STOPSIGNAL
STOPSIGNAL
用于设置停止容器所要发送的系统调用信号:STOPSIGNAL signal。所使用的信号必须是内核系统调用表中的合法的值,如:9
、SIGKILL
。
16. SHELL
SHELL
用于设置执行命令(shell
式)所使用的的默认shell
类型:SHELL ["executable", "parameters"]。SHELL
在Windows环境下比较有用,Windows下通常会有cmd
和powershell
两种shell
,可能还会有sh
。这时就可以通过SHELL
来指定所使用的shell
类型:
FROM microsoft/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
总结
Dockerfile是由一系列命令
和参数
组成的一个文件。其中,每条件命令都要大写(如:FROM
),且其后都要跟一个参数(如:ubuntu:16.04
)。构建镜像时,Dockerfile中的命令会按顺序从上到下执行,在编写Dockerfile文件时应注意各条命令的顺序安排。Dockerfile
文件中的每条命令,都会创建一个新的镜像层并会提交镜像。