Dockerfile
用 Dockerfile 创建上节的 ubuntu-with-vi,其内容则为:
FROM ubuntu
RUN apt-get update && apt-get install vim -y
下面我们运行 docker build 命令构建镜像并详细分析每个细节。
root@ubuntu:~# pwd ①
/root
root@ubuntu:~# ls ②
Dockerfile
root@ubuntu:~# docker build -t ubuntu-with-vi-dockerfile . ③
Sending build context to Docker daemon 32.26 kB ④
Step 1 : FROM ubuntu ⑤
---> f753707788c5
Step 2 : RUN apt-get update && apt-get install -y vim ⑥
---> Running in 9f4d4166f7e3 ⑦
......
Setting up vim (2:7.4.1689-3ubuntu1.1) ...
---> 35ca89798937 ⑧
Removing intermediate container 9f4d4166f7e3 ⑨
Successfully built 35ca89798937 ⑩
root@ubuntu:~#
① 当前目录为 /root。
② Dockerfile 准备就绪。
③ 运行 docker build 命令,-t 将新镜像命名为 ubuntu-with-vi-dockerfile,命令末尾的 . 指明 build context 为当前目录。Docker 默认会从 build context 中查找 Dockerfile 文件,我们也可以通过 -f 参数指定 Dockerfile 的位置。
④ 从这步开始就是镜像真正的构建过程。 首先 Docker 将 build context 中的所有文件发送给 Docker daemon。build context 为镜像构建提供所需要的文件或目录。
Dockerfile 中的 ADD、COPY 等命令可以将 build context 中的文件添加到镜像。此例中,build context 为当前目录 /root,该目录下的所有文件和子目录都会被发送给 Docker daemon。
所以,使用 build context 就得小心了,不要将多余文件放到 build context,特别不要把 /、/usr 作为 build context,否则构建过程会相当缓慢甚至失败。
⑤ Step 1:执行 FROM,将 ubuntu 作为 base 镜像。
ubuntu 镜像 ID 为 f753707788c5。
⑥ Step 2:执行 RUN,安装 vim,具体步骤为 ⑦、⑧、⑨。
⑦ 启动 ID 为 9f4d4166f7e3 的临时容器,在容器中通过 apt-get 安装 vim。
⑧ 安装成功后,将容器保存为镜像,其 ID 为 35ca89798937。
这一步底层使用的是类似 docker commit 的命令。
⑨ 删除临时容器 9f4d4166f7e3。
⑩ 镜像构建成功。
通过 docker images 查看镜像信息。
镜像 ID 为 35ca89798937,与构建时的输出一致。
在上面的构建过程中,我们要特别注意指令 RUN 的执行过程 ⑦、⑧、⑨。Docker 会在启动的临时容器中执行操作,并通过 commit 保存为新的镜像。
查看镜像分层结构
ubuntu-with-vi-dockerfile 是通过在 base 镜像的顶部添加一个新的镜像层而得到的。
这个新镜像层的内容由 RUN apt-get update && apt-get install -y vim 生成。这一点我们可以通过 docker history 命令验证。
docker history 会显示镜像的构建历史,也就是 Dockerfile 的执行过程。
ubuntu-with-vi-dockerfile 与 ubuntu 镜像相比,确实只是多了顶部的一层 35ca89798937,由 apt-get 命令创建,大小为 97.07MB。docker history 也向我们展示了镜像的分层结构,每一层由上至下排列。
用Dockerfile构建nginx
#zbw dockerfile
FROM centos
MAINTAINER zbw@163.com
WORKDIR /opt/mytest
COPY testfile .
ADD etc-docker.tar.gz .
RUN set -x && yum install epel-release -y && yum install nginx -y
RUN ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/nginx/error.log
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
镜像的缓存特性
Docker 会缓存已有镜像的镜像层,构建新镜像时,如果某镜像层已经存在,就直接使用,无需重新创建。
举例说明。
在前面的 Dockerfile 中添加一点新内容,往镜像中复制一个文件:
root@ubuntu:~# ls ①
Dockerfile testfile
root@ubuntu:~#
root@ubuntu:~# docker build -t ubuntu-with-vi-dockerfile-2 .
Sending build context to Docker daemon 32.77 kB
Step 1 : FROM ubuntu
---> f753707788c5
Step 2 : RUN apt-get update && apt-get install -y vim
---> Using cache ②
---> 35ca89798937
Step 3 : COPY testfile / ③
---> 8d02784a78f4
Removing intermediate container bf2b4040f4e9
Successfully built 8d02784a78f4
① 确保 testfile 已存在。
② 重点在这里:之前已经运行过相同的 RUN 指令,这次直接使用缓存中的镜像层 35ca89798937。
③ 执行 COPY 指令。
其过程是启动临时容器,复制 testfile,提交新的镜像层 8d02784a78f4,删除临时容器。
在 ubuntu-with-vi-dockerfile 镜像上直接添加一层就得到了新的镜像 ubuntu-with-vi-dockerfile-2。
如果我们希望在构建镜像时不使用缓存,可以在 docker build 命令中加上 --no-cache 参数。
Dockerfile 中每一个指令都会创建一个镜像层,上层是依赖于下层的。无论什么时候,只要某一层发生变化,其上面所有层的缓存都会失效。
也就是说,如果我们改变 Dockerfile 指令的执行顺序,或者修改或添加指令,都会使缓存失效。
也就是说,如果改变Dockerfile指令的执行顺序,或者修改或添加指令,都会使缓存失效。比如前面交换RUN和COPY的顺序
FROM ubuntu
COPY testfile /
RUN apt-get update && apt-get install -y vim
Docker需要重建受影响的镜像层。即这两层都需要重建。
调试Dockerfile
包括 Dockerfile 在内的任何脚本和程序都会出错。有错并不可怕,但必须有办法排查,所以本节讨论如何 debug Dockerfile。
先回顾一下通过 Dockerfile 构建镜像的过程:
从 base 镜像运行一个容器。
执行一条指令,对容器做修改。
执行类似 docker commit 的操作,生成一个新的镜像层。
Docker 再基于刚刚提交的镜像运行一个新容器。
重复 2-4 步,直到 Dockerfile 中的所有指令执行完毕。
从这个过程可以看出,如果 Dockerfile 由于某种原因执行到某个指令失败了,我们也将能够得到前一个指令成功执行构建出的镜像,这对调试 Dockerfile 非常有帮助。我们可以运行最新的这个镜像定位指令失败的原因。
我们来看一个调试的例子。Dockerfile 内容如下:
执行 docker build:
Dockerfile 在执行第四步 RUN 指令时失败。我们可以利用第三步创建的镜像 b00564464a41 进行调试,方式是通过 docker run -it 启动镜像的一个容器。
手工执行 RUN 指令很容易定位失败的原因是 ubuntu 镜像中没有 yum命令。虽然这是个极其简单的例子,但它很好地展示了调试 Dockerfile 的方法。