使用Dockerfile定制镜像

Dockerfile是一个文本文件,其中包含额一条一条的指令,每一条指令构建一层,因此每一条指令的作用就是描述这一层应当如何的构建。

以构建nginx镜像为例,使用Dockerfile构建的步骤如下:

在一个空的目录下创建一个名为Dockerfile的文件:

sudo mkdir mynginx
cd mynginx
sudo touch Dockerfile

编辑Dockerfile文件,内容如下:

FROM nginx
RUN echo '<h1>hello,Docker!</h1>' > /usr/share/nginx/html/index.html

上述的Dockerfile文件很简单,只有两个步骤,涉及到两个指令:FROM和RUN

FROM指定基础镜像

所谓的定制镜像,就是以一个镜像为基础,在其上进行定制。而FROM指令的作用就是指定基础镜像,因此在一个Dockerfile文件中FROM指令是必须存在的,并且必须是第一条指令。

在 Docker Store 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如
nginx 、 redis 、 mongo 、 mysql 、 httpd 、 php 、 tomcat 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 node 、 openjdk 、 python 、 ruby 、 golang 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。

如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如
ubuntu 、 debian 、 centos 、 fedora 、 alpine 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。

除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch 。这个镜像
是虚拟的概念,并不实际存在,它表示一个空白的镜像。如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。

RUN执行命令

RUN 指令是用来执行命令行命令的。由于命令行的强大能力, RUN 指令在定制镜像时是最
常用的指令之一。其格式有两种:

  • shell 格式: RUN <命令> ,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式。
  • exec 格式: RUN ["可执行文件", "参数1", "参数2"] ,这更像是函数调用中的格式

既然RUN就像SHELL脚本一样可以执行命令,那么我们能否向SHELL脚本那样把每条命令对应一个RUN呢?比如一下这样:

FROM debian:jessie
RUN sudo apt-get update
RUN sudo apt-get install -y gcc libc6-dev make
RUN sudo wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
RUN sudo mkdir -p /usr/src/redis
RUN sudo  tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN sudo make -C /usr/src/redis
RUN sudo make -C /usr/src/redis install

之前说过Dockerfile的每条指令对应一层,RUN也一样,每一个RUN的行为建立一个新的层,在其上执行这些命令,执行结束后, commit 这一层的修改,构成新的镜像。而上面的写法创建了7层镜像,这是完全没有意义的,很多运行时候不需要的东西被封进了镜像,比如更新的软件包和编译环境。这样产生的镜像就很臃肿,容易出错。

Union FS是有最大层数限制的,不得超过127层,上面的Dockerfile的正确的写法如下:

FROM debian:jessie
RUN buildDeps='gcc libc6-dev make' \
		&& apt-get update \
		&& apt-get install -y $buildDeps \
		&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
		&& mkdir -p /usr/src/redis \
		&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
		&& make -C /usr/src/redis \
		&& make -C /usr/src/redis install \
		&& rm -rf /var/lib/apt/lists/* \
		&& rm redis.tar.gz \
		&& rm -r /usr/src/redis \
		&& apt-get purge -y --auto-remove $buildDeps

这里使用&&将多个指令链接起来,将原来的7层简化为1层。在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建。这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 apt 缓存文件。这是很重要的一步,我们之前说过,镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。

构建镜像

构建镜像可以使用docker build命令,命令格式如下:

docker build [选项] <上下文路径/URL/->

让我们来构建之前nginx的定制镜像,进入到Dockerfile所在的目录,执行一下命令:

sudo docker build -t nginx:v3 .

Sending build context to Docker daemon 2.048 kB
Step 1 : FROM nginx
---> e43d811ce2f4
Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
---> Running in 9cdc27646c7b
---> 44aa4490ce2c
Removing intermediate container 9cdc27646c7b
Successfully built 44aa4490ce2c

从命令的输出结果中,我们可以清晰的看到镜像的构建过程。在 Step 2 中,如同我们之前所说的那样, RUN 指令启动了一个容器 9cdc27646c7b ,执行了所要求的命令,并最后提交了这一层 44aa4490ce2c ,随后删除了所用到的这个容器 9cdc27646c7b。

未完待续

posted @ 2018-01-08 20:46  木易森林  阅读(236)  评论(0编辑  收藏  举报