本文介绍: Dockerfile简介从之前的学习我们可以了解到:镜像定制实际上就是定制每一层所添加配置文件。那么如果我们可以把每一层修改安装构建操作命令写入一个脚本,用这个脚本构建定制镜像,那么之前提及的无法重复问题镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是Dockerfile。Dockerfile描述了组装镜像步骤,其中每一条命令都是单独执行的,除了FROM。

Dockerfile简介

从之前的学习中我们可以了解到:镜像定制实际上就是定制每一层所添加配置文件。那么如果我们可以把每一层修改、安装、构建操作命令写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是Dockerfile

Dockerfile描述了组装镜像的步骤,其中每一条命令都是单独执行的,除了FROM指令外,其他每一条指令都在上一条指定生成的镜像基础上执行执行完会生成一个新的镜像层,新的镜像层覆盖在原来的镜像层之上,从而形成了新的镜像。Dockerfile生成的最终镜像就是在基础叠加镜像上一层层的镜像层组成的。

Dockerfile中,指令不区分大小写,但是为了与参数区分,推荐大写。Docker顺序执行Dockerfile中的指令,第一条必须是FROM指令,它用于指定构建镜像的基础镜像。在Dockerfile中,以#开头的行是注释

下面我们开始介绍Dockerfile基本的两条指令:FROM指令和RUN指令。

FROM指令和RUN指令

FROM指定基础镜像; 格式FROM &lt;image&gt;FROM <image&gt;:<tag&gt;

FROM指令的功能是为后面的指令提供基础镜像,因此一个有效的Dockerfile必须以FROM指令作为第一条非注解指令。若FROM指令中tag参数为空,则tag默认latest;若参数imagetag指定镜像不存在,则返回错误

RUN执行命令; 格式RUN <command&gt;shell格式)或RUN [“executable”, “param1“, “param2”]exec格式,非常推荐)。

RUN 指令是用来执行命令行命令的。RUN指令会在前一条命令创建出的镜像的基础上创建一个容器,并在容器中运行命令。在命令结束运行提交容器为新镜像,新镜像被Dockerfile的下一条指令使用

之前说过,Dockerfile中每一个指令都会建立一个镜像层,RUN也不例外。每一个RUN行为,就和之前学习的docker commit定制镜像的过程一样:在之前镜像的基础上创建一个容器,在其上执行这些命令,执行结束后,最后 commit 这一层的修改,构成新的镜像。

使用Dockerfile构建一个镜像

下面介绍使用Dockerfile构建一个镜像,步骤如下:

下面举一个实例使用Dockerfile构建一个名为testimage的镜像,该镜像具备ubuntu:latest运行环境,而且在镜像的/目录下创建了一个dir1文件夹

#先创建一个新的空文件夹
mkdir newdir
#进入这个新文件夹cd newdir
#创建一个Dockerfile文件
touch Dockerfile
#补全Dockerfile的内容(为了方便展示这里用的是echo向Dockerfile中输入内容echo "FROM ubuntu:latest" &gt; Dockerfile
echo "RUN mkdir /dir1" &gt;&gt; Dockerfile
#使用该Dockerfile构建一个名为testimage的镜像
docker build -t testimage .
Dockerfile构建镜像的过程详解

上面的实例创建了一个Dockerfile文件Dockerfile的内容如下:

FROM ubuntu:latest
RUN mkdir /dir1

执行docker build命令,指定使用Dockerfile构建一个镜像。执行结果如下所示

[root@localhost newdir]# docker build -t testimage .
Sending build context to Docker daemon 2.048 kB
Step 1/2 : FROM ubuntu
---> 14f60031763d
Step 2/2 : RUN mkdir dir1
---> Running in c5117d908931
---> cb0193727724
Removing intermediate container c5117d908931
Successfully built cb0193727724

Docker指令是从上到下一层一层执行的,所以在使用这个Dockerfile构建镜像时,首先执行FROM ubuntu:latest这条指令。

FROM ubuntu:latest指定ubuntu:latest作为基础镜像,也就是将ubuntu:latest镜像的所有镜像层放置在testimage镜像的最下面。

然后执行RUN mkdir dir1指令,前面我们说过,执行RUN指令时,会在之前指令创建出的镜像的基础上创建一个临时容器,在这里的容器Idc5117d908931,并在容器中运行命令。在命令结束运行提交新容器为新镜像,并删除临时创建的容器c5117d908931

Dockerfile的所有指令执行完后,新镜像就构建完成了!

注意事项,谨慎使用RUN
修改前的Dokcerfile文件

既然RUN就像 Shell 脚本一样可以执行命令,那么是否可以Shell 脚本一样把每个命令对应一个RUN呢?比如这样:

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

上面这个Dockerfile是为了编译、安装 redis可执行文件。虽然它能够完成了所需的功能,但是正如之前说过,Dockerfile中每一个指令都会建立一层,RUN 也不例外。每一个RUN行为,都会创建一个新的镜像层。

而上面的这种写法,创建了8层镜像(1层基础镜像+7层由RUN执行创建的镜像)。这是完全没有意义的,而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境更新软件包等等。结果就是产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署时间,也很容易出错。

修改后的Dockerfile文件

因为之前所有的命令只有一个目的,就是编译、安装 redis 可执行文件。因此没有必要建立很多层,这只是一层的事情。因此,修改之后的Dockerfile文件没有使用很多个RUN指令,而仅仅使用一个RUN 指令,并使用 &amp;&amp;各个命令串联起来。除此以外,把redis编译环境更新软件包也通通清除掉了,减少镜像占用存储空间。如下所示,修改之后的Dockerfile构建完成后是就只会有2层镜像了(1层基础镜像+1层由RUN执行创建的镜像)。

FROM debian:jessie
RUN buildDeps='gcc libc6-dev make' 
&amp;&amp; apt-get update 
&amp;&amp; apt-get install -y $buildDeps 
&amp;&amp; wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" 
&amp;& mkdir -p /usr/src/redis 
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-component
s=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

Dockerfile编写过程中一定要牢记一点:镜像的每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。删除前一层文件操作,实际不是真的删除前一层的文件,而是仅在当前标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。

#创建一个空文件夹,并进入其中
mkdir newdir1
cd newdir1
#创建一个Dockerfile文件
touch Dockerfile
#假设我的Dockerfile文件为
#FROM ubuntu
#RUN mkdir dir1
#可以这么写:
# echo 'FROM ubuntu' > Dockerfile
# echo 'RUN mkdir dir1'>> Dockerfile
#输入Dockerfile文件内容
#********** Begin *********#
#以busybox为基础镜像

#在基础镜像的基础上,创建一个hello.txt文件
echo 'FROM busybox' > Dockerfile
echo 'RUN touch hello.txt' >>  Dockerfile
#********** End **********#
#使用Dockerfile创建一个新镜像,镜像名为busybox:v1
docker build -t busybox:v1 .

docker build、COPY和ADD

docker build命令详解

Dockerfile创建完成后,可以使用docker build命令根据Dockerfile构建一个镜像。在上一关中,我们在Dockerfile所在的文件夹下执行docker build -t myimage .这条命令,然后镜像就被构建了。现在我们来详细地将这条命令。该docker build的命令格式如下:

  1. docker build [OPTIONS] 上下文路径|URL

其中:

  • docker build: 用Dockerfile构建镜像的命令关键词
  • [OPTIONS]: 命令选项,常用的指令包括-t指定镜像的名字,-f显示指定Dockerfile,如果不使用-f,则默认将上下文路径下的名为Dockerfile的文件认为是构建镜像的“Dockerfile”;
  • 上下文路径|URL: 指定构建镜像的上下文的路径,构建镜像的过程中,可以且只可以引用上下文中的任何文件。

现在让我们在看看docker build -t myimage .这条命令,在这条命令中,使用-t指定了镜像名为myimage,由于没有使用-f指令,所以默认使用上下文路径下名为Dockerfile的文件认为是构建镜像的“Dockerfile”。最后指定上下文路径,在这条命令中,上下文路径是.

如果你学过Linux,你应该非常清楚上述命令中的小数点.代表的意思。在Linux中,小数点.代表着当前目录。所以docker build -t myimage .中小数点.其实就是将当前目录设置为上下文路径。

执行docker build后,会首先将上下文目录的所有文件都打包然后传给Docker daemon,这样Docker daemon收到这个上下文包后,展开就会获得构建镜像所需的一切文件。

下图所示,在执行完docker build后,会首先sending build context to Deckor daemon,也就是将上下文目录下所有文件打包发给Docker daemon所以在使用Dockerfile文件时构建镜像时,一般将它放在一个空文件夹下,就是为了防止将其他多余的文件传出去然后依次执行Dockerfile的指令,如果指令正确执行,则继续执行下一条,直到所有指令执行正确完毕,镜像构建完成;如果指令执行出错,终止镜像构建。

[root@localhost newdir]# docker build -t myimage .
Sending build context to Docker daemon 2.048 kB
Step 1/2 : FROM ubuntu
---> 14f60031763d
Step 2/2 : RUN mkdir dir1
---> Running in c5117d908931
---> cb0193727724
Removing intermediate container c5117d908931
Successfully built cb0193727724

除了从本地构建以外,docker build支持URL构建,比如可以直接从Git repo中构建,这里也不展开介绍了,如果你对这个感兴趣,可以查看

docker build | Docker Docs

COPY指令和ADD指令

COPY复制文件; 格式COPY <源路径> <目标路径>;

COPY 指令将从构建上下文目录中 <源路径> 的文件或目录复制到新的一层的镜像内的 <目标路径> 位置。<源路径>所指定的源必须在上下文中,即必须是上下文根目录的相对路径!<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR指令来指定,后面介绍)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建目录。

ADD更高级的文件复制格式ADD <源路径> <目标路径>;

ADDCOPY指令在功能上十分相似,但是在COPY的基础上增加了一些功能比如,源路径可以是一个指向一个网络文件的URL,这种情况下,Docker引擎会试图下载这个URL指向的文件到<目标路径>去。

此外,当<源路径>为一个tar压缩文件时,该压缩文件在被复制到容器中时会被解压提取。但是使用COPY指令只会将tar压缩文件拷贝到<目标路径>中。如下图所示

[root@localhost tempdir]# docker build -t myimage .
Sending build context to Docker daemon 12.8 kB
Step 1/2 : FROM ubuntu
---> 14f60031763d
Step 2/2 : COPY ./hello.txt.tar /dir1/
---> 070559867e22
Removing intermediate container 1e55f9f19333
Successfully built 070559867e22
[root@localhost tempdir]# docker run myimage ls /dir1/
hello.txt.tar

ADD指令如果 <源路径> 为一个tar压缩文件的话,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。如下图所示:

[root@localhost tempdir]# docker build -t myimage .
Sending build context to Docker daemon 12.8 kB
Step 1/2 : FROM ubuntu
---> 14f60031763d
Step 2/2 : ADD ./hello.txt.tar /dir1/
---> ead6431f75ba
Removing intermediate container f5fdcd97e196
Successfully built ead6431f75ba
[root@localhost tempdir]# docker run myimage ls /dir1/
hello.txt

这样,如果你只需要tar包中的文件内容而不需要tar包,不要先COPY ./hello.txt.tar.gz然后RUN tar –xvf hello.txt.tar.gz && rm hello.txt.tar.gz。请直接使用ADD指令,ADD ./hello.txt.tar.gz

因为镜像的每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。

使用Dockerfile构建一个名为busybox:v3的镜像,Dockerfile的内容为:以busybox为基础镜像,并将上下文目录下的dir1.tar解压提取后”,拷贝busybox:v3/中。

#创建一个空文件夹,并进入其中
mkdir newdir2
cd newdir2
#创建一个文件夹dir1,将其压缩,然后删除dir1
mkdir dir1 && tar -cvf dir1.tar dir1 && rmdir dir1
#创建一个Dockerfile文件
touch Dockerfile
#假设我的Dockerfile文件为
#FROM ubuntu
#RUN mkdir dir1
#可以这么写:
# echo 'FROM ubuntu' > Dockerfile
# echo 'RUN mkdir dir1'>> Dockerfile
#输入Dockerfile文件内容
#********** Begin *********#
#以busybox为基础镜像
echo 'FROM busybox' > Dockerfile
#并将上下文目录下的dir1.tar“解压提取后”,拷贝到busybox:v3的/
echo 'ADD dir1.tar /' >>  Dockerfile

#********** End **********#

#文件内容完毕,在当前文件夹中执行
#********** Begin *********#
#以该Dockerfile构建一个名为busybox:v3的镜像
docker build -t busybox:v3 .
#********** End **********#

原文地址:https://blog.csdn.net/qq_58647543/article/details/134689652

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_11319.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注