本文发自 http://www.binss.me/blog/learn-docker-with-me-about-dockerfile/,转载请注明出处。
本文将介绍如何利用Dockerfile来构建镜像。
在《跟我一起学Docker——搭建编译环境篇》,我们为了搭建一个c++编译环境,新建了3个容器,commit了3次,生成了3个镜像,实在是麻烦至极。有了dockerfile,我们原来的工作将会变得非常简单。
首先介绍下Dockerfile:Dockerfile是一份文本文档,描述了组装镜像的步骤,也就是说,只要把Dockerfile写好了,然后build一下,我们的镜像就做好了。Dockerfile由指令构成,一条指令一行,build时,指令由上到下依次执行,一次执行一条,执行完后会生成一个镜像层,然后下一条指令会在该基础上执行,从而产生新的镜像层。于是一层一层叠起来,我们的目标镜像就这样诞生了。
Dockerfile的基本格式如下:
# Comment
INSTRUCTION arguments
其中#号为注释符,以其开头的行不会被执行。有效行由两部分组成,一是指令名称(INSTRUCTION),二是参数。
那么Dockerfile有哪些指令呢?下面会依次介绍(参考自:https://docs.docker.com/reference/builder/)
FROM
Dockfile的第一条指令(必须!),它指定了构建镜像的基础镜像。
格式:FROM
示例:FROM ubuntu:14.04
MAINTAINER
指定镜像的作者
格式:MAINTAINER
示例:MAINTAINER binss
RUN
使用前一条指令创建的镜像生产容器,并在容器中执行命令,执行结束后会自动提交成为新的镜像
格式:RUN
注意,由于exec格式参数被当作json解析,所以不能用单引号,不能用环境变量
示例:RUN echo "hello"
CMD
为容器提供运行时的默认值,作为容器启动的默认第一条指令。在Dockerfile只能有一条,有多条的话以最后一条为准。
格式:CMD command param1 param2 or CMD ["param1","param2"] or CMD ["executable","param1","param2"]
示例:CMD echo "Hello World." | wc
LABEL
为生成的镜像添加描述信息。可以使用inspect查看。
格式:LABEL
示例:LABEL "com.example.vendor"="ACME Incorporated"
EXPOSE
声明容器在运行时将会监听的特定端口,即对外暴露的端口。但是真正要暴露还是要在run时候用-p或-P指定。不要使用EXPOSE作端口映射。
格式:EXPOSE
示例:EXPOSE 8888
ENV
设置环境变量,作用于Dockerfile后续的所有指令。而且还会作用到生成镜像所创建出来的容器中。
格式:ENV
示例:ENV myName Binss
ADD
从
格式:ADD
示例:ADD http://www.binss.me/hello.jpg hello/hello.jpg
COPY
从
格式:COPY
示例:COPY hello.jpg hello/hello.jpg
ENTRYPOINT
为容器提供运行时的默认值,不同于cmd的是ENTRYPOINT只能传入指令,而CMD还可以传入参数
格式:ENTRYPOINT ["executable", "param1", "param2"] or ENTRYPOINT command param1 param2
示例:ENTRYPOINT ["top", "-b"]
VOLUME
建立挂载点
格式:VOLUME
示例:VOLUME ["/data"]
USER
设置用户名称或uid
格式:USER
示例:USER daemon
WORKDIR
指定工作目录,该目录可用于RUN, CMD, ENTRYPOINT, COPY 和 ADD 指令。可以是绝对目录,也可以是相对目录。
格式:WORKDIR
示例:WORKDIR /path/to/workdir
ONBUILD
为镜像添加触发器,可以使用inspect查看。当该镜像被作为其他Dockerfile中FROM指令的参数时,ONBUILD注册的指令会按顺序执行,构建完成后,触发器指令被删除。注意ONBUILD不能嵌套。
格式:ONBUILD [INSTRUCTION]
示例:
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
下面我们使用Dockerfile来构建之前我们曾经构建过的C++编译环境,Dockerfile如下:
ROM ubuntu
MAINTAINER binss [email protected]
# update apt-get
RUN echo "deb http://mirrors.163.com/ubuntu/ trusty main restricted universe multiverse\n\
deb http://mirrors.163.com/ubuntu/ trusty-security main restricted universe multiverse\n\
deb http://mirrors.163.com/ubuntu/ trusty-updates main restricted universe multiverse\n\
deb http://mirrors.163.com/ubuntu/ trusty-proposed main restricted universe multiverse\n\
deb http://mirrors.163.com/ubuntu/ trusty-backports main restricted universe multiverse\n\
deb-src http://mirrors.163.com/ubuntu/ trusty main restricted universe multiverse\n\
deb-src http://mirrors.163.com/ubuntu/ trusty-security main restricted universe multiverse\n\
deb-src http://mirrors.163.com/ubuntu/ trusty-updates main restricted universe multiverse\n\
deb-src http://mirrors.163.com/ubuntu/ trusty-proposed main restricted universe multiverse\n\
deb-src http://mirrors.163.com/ubuntu/ trusty-backports main restricted universe multiverse\n\
" > /etc/apt/sources.list
RUN apt-get update
# install g++
RUN apt-get -y install g++
# install make
RUN apt-get -y install make
# install boost
RUN apt-get -y install libboost-all-dev
从Dockerfile可以看出,我们要构建的镜像基于ubuntu,作者是binss,首先修改apt-get的源(从国外下实在太慢),更新apt-get,然后顺序安装g++、make和boost。
将以上内容保存为Dockerfile,然后执行以下命令开始构建:
$ docker build -t binss/ubuntu:build .
构建流程摘要如下:
Sending build context to Docker daemon 3.072 kB
Step 0 : FROM ubuntu
---> 91e54dfb1179
Step 1 : MAINTAINER binss [email protected]
---> Running in e9d0e2082f37
---> 6359ff0631e0
Removing intermediate container e9d0e2082f37
Step 2 : RUN echo "deb http://mirrors.163.com/ubuntu/ ......" > /etc/apt/sources.list
---> Running in d8bd16fc86df
---> 17b054b0dbdb
Removing intermediate container d8bd16fc86df
Step 3 : RUN apt-get update
---> Running in fabc0b68bdcb
......
Fetched 22.4 MB in 6s (3557 kB/s)
Reading package lists...
---> b0b99128a94b
Removing intermediate container fabc0b68bdcb
Step 4 : RUN apt-get -y install g++
---> Running in 680fb3c96274
......
---> d9ae06f72620
Removing intermediate container 680fb3c96274
Step 5 : RUN apt-get -y install make
---> Running in ef11f8832dfa
......
---> 0bf6b8449c6b
Removing intermediate container ef11f8832dfa
Step 6 : RUN apt-get -y install libboost-all-dev
---> Running in eacd93b99ebd
.....
---> 665b8cad1ad4
Removing intermediate container eacd93b99ebd
Successfully built 665b8cad1ad4
于是,我们的镜像binss/ubuntu:build就构建完成了。T T真是太方便了。此外,比起镜像,Dockerfile不但构建过程透明,还易于修改,因此个人感觉更适合分享。
一些注意事项:
-
为了提高组装镜像的步骤,Docker会缓存构建过程中的中间镜像,如果存在相同指令生成的子镜像,则命中缓存,直接使用。
-
指令较长时,可以使用
\
换行
1F swordman 6 years, 5 months ago 回复
为什么要重复构建镜像啊,不是构建一次后生成容器重复利用就好了吗?虽然我知道肯定有用,但是我是小白TT
2F binss MOD 6 years, 5 months ago 回复
回复 [1F] swordman:首先明确一个概念:我们每次构建的是一个镜像层(layer),它是相对于原镜像的修改。
docker build 构建镜像的原理是这样的:以 FROM 指定的镜像为基础。然后针对 Dockerfile 中每一条有实际操作的指令,执行以下流程:将当前已构建的层合并起来作为镜像创建容器,执行操作,提交产生新的 layer 。当 build 完成时,我们就能得到很多layer,将它们堆叠(mount)起来,才是我们最终所说的镜像。
相信你在 docker build 中会发现一个现象:当你修改了 Dockerfile 中的某行后再次 build,速度会比第一次 build 时快很多。这是因为被修改语句之前的 layer 其实已经构建过了,直接使用即可,体现在 log 中就是 "Using cache"。这也能说明构建流程是增量地、基于已有 layer 的。