本文发自 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 or FROM : or FROM @

示例:FROM ubuntu:14.04

MAINTAINER

指定镜像的作者

格式:MAINTAINER

示例:MAINTAINER binss

RUN

使用前一条指令创建的镜像生产容器,并在容器中执行命令,执行结束后会自动提交成为新的镜像

格式:RUN (shell格式,使用/bin/sh -c 执行) or RUN ["executable", "param1", "param2"] (exec格式)

注意,由于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 or ENV =

示例:ENV myName Binss

ADD

复制文件到镜像中的可以是文件、目录,甚至是url。可以是绝对路径,也可以是相对路径(相对于workdir)。

格式:ADD ... or ADD ["",... ""]

示例:ADD http://www.binss.me/hello.jpg hello/hello.jpg

COPY

复制文件到镜像中的。比起ADD,只能复制本地文件。

格式:COPY ... or 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 ... or COPY ["",... ]

示例: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不但构建过程透明,还易于修改,因此个人感觉更适合分享。

一些注意事项:

  1. 为了提高组装镜像的步骤,Docker会缓存构建过程中的中间镜像,如果存在相同指令生成的子镜像,则命中缓存,直接使用。

  2. 指令较长时,可以使用\换行