Dockerfile是一个用于构建Docker镜像的文本文件,其中包含了创建Docker镜像的全部指令。基于这些指令,可以使用”docker build”命令来创建镜像。
一:用法
”docker build”命令通过Dockerfile,以及一个上下文环境来构建镜像。所谓构建上下文,是指本地目录中的所有文件。比如使用当前目录作为上下文来构建镜像:
$ docker build .
构建流程中,第一件事就是将上下文中的所有文件上传给Docker deamon。因此,一般情况下,上下文的目录中应该只包含Dockerfile,以及其他必要的文件。
一般情况下,Dockerfile的文件名就是”Dockerfile”,并位于上下文的根目录下。不过可以通过”-f”选项指定其他其他文件作为Dockerfile,比如:
$ docker build -f /path/to/a/Dockerfile .
可以通过”-t”选项,指明要创建的镜像名以及tag,比如:
$ docker build -t shykes/myapp:1.0.2 .
Docker daemon逐条执行Dockerfile中的指令,并将指令执行结果提交为中间镜像。注意,每条指令都是独立运行,因此向”RUN cd /tmp”这样的指令,对于后续的指令没有任何效果。
为了加快”docker build”的执行速度,Docker会尽可能的重用中间镜像,也就是所谓的缓存。
Docker在执行每条指令时,都会检查缓存中是否有可重用的镜像,若有则直接使用该镜像,而无需创建新的重复镜像。检查镜像是否可重用的基本规则如下:
1:若父镜像已经处于缓存中,则会将下一条指令与该父镜像的所有子镜像进行对比,查看这些子镜像是否是使用相同指令构建的,若是,则可以使用该子镜像。
2:一般情况下,仅将Dockerfile中的指令与子镜像进行比对就足够了。不过还有其他一些指令需要额外的检查。
比如像ADD和COPY指令,除了对比指令之外,还会检查镜像中的文件内容。对于这两条指令,会检查相应文件的内容,计算其校验码,并与镜像中相应文件的校验码进行匹配。因此,如果文件内容发生了变化,则该子镜像也不再可用。
3:一旦缓存镜像不可用,则Dockerfile中的后续指令将被执行,并产生新的中间镜像。
下面就是一个利用缓存构建镜像的输出:
$ docker build -t svendowideit/ambassador .
Sending build context to Docker daemon 15.36 kB
Step 0 : FROM alpine:3.2
---> 31f630c65071
Step 1 : MAINTAINER SvenDowideit@home.org.au
---> Using cache
---> 2a1c91448f5f
Step 2 : RUN apk update && apk add socat && rm -r /var/cache/
---> Using cache
---> 21ed6e7fbb73
Step 3 : 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的格式如下:
# Comment
INSTRUCTION arguments
指令不区分大小写。但是,一般约定为全部大写,以便与其参数区分开。
以”#”作为开头的行,将被视为注释行。如果”#”出现在行中间,则将会被视为参数。
三:常用指令:
1:FROM
Dockerfile中,第一条指令必须是”FROM”,以便指定基础镜像。所谓基础镜像,就是没有父镜像的镜像。 FROM指定镜像基于哪个基础镜像创建。比如
FROM ubuntu
这表示新的镜像将基于Ubuntu的镜像来构建。
2:MAINTAINER
一般情况下,FROM指令的下一条指令就是” MAINTAINER”指令。用于指定新镜像的作者:
MAINTAINER <name>
3:RUN
在shell或者exec的环境下执行的命令。有两种格式:
RUN <command> (shell form, the command is run in a shell - /bin/sh -c)
RUN ["executable", "param1", "param2"] (exec form)
RUN指令会在新创建的镜像上添加新的层面,接下来提交的结果用在Dockerfile的下一条指令中。
4: ADD
复制文件指令。它有两个参数<source>和<destination>。<destination>是容器内的路径,而<source>可以是一个URL,或者是上下文中的一个文件。ADD指令将<source>所表示的文件或者目录添加到容器的<destination>中。
注意:
a:如果<src>是本地文件,则该文件必须位于上下文中。
b:如果<src>是目录,在目录中的所有内容都会复制到容器中。
c:如果<src>是本地的一个tar压缩文件,则复制时会将其解压。
d:如果<dest>不存在,则会在容器中创建,包括其中包含的目录。
5:CMD
提供了容器默认的执行命令。有三种格式,推荐exec格式:
CMD ["executable","param1","param2"] (exec form, this is the preferred form)
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
CMD command param1 param2 (shell form)
Dockerfile只允许使用一次CMD指令。如果有多个CMD指令,则只有最后一个指令生效。
CMD指令主要用于提供运行容器时,该容器默认需要执行的命令。
如果使用的是CMD的shell形式,则CMD后的命令将以”/bin/sh -c”执行。若不希望在shell中执行,则CMD需要使用exec的形式,这也是更被提倡的做法。
如果用户在执行”docker run”时提供了容器要运行的命令,则该命令会覆盖CMD的内容。
6:ENTRYPOINT
提供了容器默认的执行命令。有两种格式,推荐exec格式:
ENTRYPOINT ["executable", "param1", "param2"] (exec form, preferred)
ENTRYPOINT command param1 param2 (shell form)
在使用”dockerrun”命令运行容器时,指定的命令参数都会追加到exec形式的ENTRYPOINT中,并且会覆盖掉CMD中的内容。比如"docker run <image>-d",就会将”-d”追加到exec形式的ENTRYPOINT中。
shell形式的ENTRYPOINT会覆盖掉CMD或者”dockerrun”中的命令。但是这种形式的命令是在”/bin/sh -c”下执行的。
只有最后一个ENTRYPOINT会起作用。
可以在ENTRYPOINT中指定容器要执行的命令和参数,并在CMD中指定该命令的其他参数,比如:
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
虽然CMD和ENTRYPOINT都用来指定容器运行时的指令。但是它们又有不同的意义:
a:Dockerfile中应该至少指定一个CMD或者ENTRYPOINT;
b:CMD中可用于指定ENTRYPOINT中命令的参数;如果使用”docker run”指定了容器运行指令,则它会覆盖掉CMD中的内容。
下表描述了ENTRYPOINT和CMD相互作用的结果:
7:EXPOSE
指定容器在运行时监听的端口。语法如下:
EXPOSE <port> [<port>...]
注意,EXPOSE指令并不指定容器与主机间的端口映射。要想指定端口映射,必须在docker run时使用-p或-P参数。
8:ENV
设置环境变量。有两种格式:
ENV <key> <value>
ENV <key>=<value> ...
该指令将环境变量<key>设置为<value>。该环境变量在Dockerfile后续的指令中保持有效。该环境变量在容器运行时也保持有效。
四:注意
1:Dockerfile中,通过ENTRYPOINT和CMD指定的容器默认运行程序,比如apache、redis等,如果以后台的方式运行,则该容器启动后会立即结束,因为容器运行时,必须要有一个前台程序。
2:容器中应该只运行一个进程;
五:例子
下面是一个制作redis镜像的Dockerfile:
FROM oraclelinux:6.6
MAINTAINER hehe
RUN yum update –y && yum -y install redis
ADD redis.conf /etc/redis.conf
EXPOSE 6379
CMD ["redis-server", "/etc/redis.conf"]
该Dockerfile很简单:
FROM指明该镜像以oraclelinux:6.6为基础镜像;
MAINTAINER指明镜像作者是hehe;
然后就是运行”yum –y install redis”命令安装redis;
接下来,使用ADD指令,将上下文中的redis.conf文件复制到容器中的/etc/目录下;
然后使用EXPOSE指明该镜像监听6379端口;
最后,使用CMD指明容器运行时的指令是”redis-server /etc/redis.conf”
写好Dockerfile之后,使用下面的命令构建镜像:
docker build -t redis .