Dockerfile 的详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Allocator/article/details/69486608

Dockerfile 的详解

使用Docker技术构建自己的镜像是最常见的应用场景,目前为止Docker官方也提供两种构建镜像的方法。一种是使用docker commit 指令将当前的容器状态保存为一个镜像,另外一种则是编写Dockerfile然后使用docker build指令根据Dockerfile的内容构建相关的镜像。在实际的工程应用中非常推荐使用第二种方式即Dockerfile来构建镜像。因为采用这种方法可以非常灵活的构建镜像;其次这种构建方式中的Dockerfile本身就可以作为重要的文件保存或者是传递,这样可以让开发者更好的对容器镜像的内容变更进行寻迹,并且在docker registry本身访问不方便的情况下可以在其他机器上仅仅使用此Dockerfile文件进行镜像的构建,总之这是一种最为推荐的构建docker 镜像的方式.

Dockerfile基本知识

首先是使用Dockerfile构建一个最简单的web服务容器

# version:1.0
FROM ubuntu:14.04
MAINTAINER Alex
RUN apt-get update
RUN apt-get install -y nginx
RUN echo 'Hello world' > /usr/share/nginx/html/index.html
EXPOSE 80

Dockerfile 中由一系列的指令参数构成,并且指令都要求用大写FROM ubuntu:14.04表示构建指令使用的基本镜像。MAINTAINER XXX表示创建这个file的人的信息。RUN表构建镜像需要运行的指令。EXPOSE X表示使用该镜像启动的容器对外暴露的端口。然后使用docker build -t=repository/imagename:tag 构建镜像。之后以该镜像启动容器(需要将镜像开放的80端口映射到宿主机端口,不然外部无法访问容器的服务),容器中执行/usr/sbin/nginx 启动nginx(后面会说明如何让容器启动时后台执行nginx进程,这样就不需要在容器里面人为执行这个过程)。外部通过访问宿主机IP与相关的映射端口即可获取到Hello world的web页面。这就是一个最简单的利用Dockerfile构建镜像启动容器运行进程的实例。
下面简单总结一下Dockerfile编译构建镜像的原理。当执行Dockerfile中的每一句指令的时候,其实都创建了运行一个当前镜像层对应的容器,执行完其中指令自动使用commit将更改的容器提交为新的镜像层,然后执行下一条语句的时候又创建容器,执行指令并提交新的镜像层,直到将整个Dockerfile中的指令执行完毕。可以看做每一次run一个容器,然后执行对应的指令,退出容器然后执行commit提交镜像。
Dockerfile的编译过程同样提供了一种机制,当某一条指令执行失败时,依然可以编译出可运行的镜像,只不过镜像的状态在失败指令的上一条指令位置,因此构建者可以启动一个容器定位编译失败具体的指令位置进行文件修改重新编译。

Dockerfile 构建镜像缓存问题

使用Dockerfile构建镜像的时候会有缓存的问题。使用Dockerfile构建镜像的时候会使用缓存。每一次执行build指令会将已经生成的镜像与当前Dockerfile的变动进行比较,如过按顺序执行的指令并未改变,那么会使用当前的镜像在其上进行构建。这个时候使用的就是缓存。但是实际的构建过程中也存在不用使用缓存的应用场景。比如Dockerfile中存在update之类的语句,那么就需要使用--no-cache的参数保证构建镜像的时候不适用缓存,这样每一次构建镜像可以保证获取到最新的系统安装包。推荐在使用docker build的时候使用--no-cache参数保证启动的容器中的软件为最新的。需要详细的了解整个镜像的构建过程需要使用docker history images查看到整个镜像层的详细的信息。

Dockerfile中的基本指令详解

FROM

FROM 指令为Dockerfile中的第一条指令,后面接上一个镜像的版本FROM ubuntu:14.04表示整个镜像的基础镜像是ubuntu:14.04当然也可以在自己构建好的镜像上面再构建

MAINTAINER

提供构建镜像的人的详细信息,如名字和邮箱,以便使用者联系。

RUN

构建镜像时非常重要的指令,即是在每一次提交镜像对应的容器中执行linux指令。使用RUN指令可以有两种格式第一种是直接使用linux指令的格式,第二种是使用[]传递指令参数的格式。第二种指令的形式是为了解决在非linux系统下要使用Dockerfile构建的镜像。本身默认的构建镜像使用的指令语法是使用shell里面的命令包装器/bin/sh -c执行。而在非Linux的系统下使用EXEC格式的RUN指令。选择centos构建镜像选择第一种run的方式。

CMD指令

表示由镜像启动容器的时候需要首先运行的程序。功能与docker run 最后面接的linux命令类似。多用于镜像以后台方式启动的场景,需要使用CMD。这样可以不用将这个要启动运行的linux指令写在docker run里面。如果docker run后面加了要运行的指令,那么这个指令会覆盖CMD中的指令。

CMD ["ins","param"]

docker 规范中推荐使用这种数组的方式来设置执行的命令。并且一个dockerfile中的CMD指令只有最后的一条有用,其他条的指令会被最后一条覆盖掉。

ENTRYPOINT

功能与CMD类似,可以说ENTRYPOINT 的功能更加的强大,CMD本身容易被容器启动时执行的run后指定的运行命令给覆盖掉,并且一个容器启动时也只能够运行一个CMD指令。ENTRYPOINT 弥补了这个不足。这个指令启动容器不会被docker run 启动容器给覆盖掉。docker run 最后传递的值以及CMD数组中的内容对于ENTRYPOINT来说都是参数,ENTRYPOINT指令永远不变,CMD后者docker run只是添加运行参数过来。如下的一个demo
以nginx web服务器为例子,

ENTRYPOINT ["/usr/sbin/nginx"]

设置容器启动时都是运行nginx服务
然后在docker run -it XXX -g “daemon off;” 此时-g daemon off 会被当参数传递到ENTRYPOINT数组运行整个容器。这样以前台方式启动Nginx作为web服务器运行。等价于ENTRYPOINT [‘/usr/sbin/nginx’,’-g’,’daemon off’]
所以ENTRYPOINT 与CMD以及docker run可以灵活的使用,以不同的参数启动服务。如说启动一个容器显示nginx的帮助信息

ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-h"]

其中-h会被作为启动时的参数传递给ENTRYPOINT。其实上述等价于

ENTRYPOINT ['/usr/sbin/nginx','-h']

使用docker启动docker run --name XXX -it XXX此时可以在命令行下打印nginx的帮助信息。

WORKDIR

镜像构建容器运行时,会在容器内部设定一个当前工作目录,以便后续的RUN 指令内容在这个WORKDIR设置的目录下正常运行。
可以理解为类似Linux下的cd操作,切换当前的工作目录

ENV

镜像的构建过程中设置环境变量。

ENV SSS /usr/bin

设置名称为SSS的环境变量。值为/usr/bin 这个环境变量可以在后续的任何RUN指令中使用。同时环境变量可以在WORKDIR中使用比如

WORKDIR $AA

可以使用变量名称为AA的环境变量。这些环境变量将被保存在镜像中,并且以此启动的容器都会保留此镜像,所以用处很大。当用镜像启动容器时,在容器中使用ENV可以查看到Dockerfile中设置的环境变量。同时使用Docker run -e "name=value"也可以设置环境变量。只不过不会适用到Dockerfile中,只是对当前容器有效。所以当需要设置环境变量时最佳方式是在dockerfile中

USER

指定运行该容器的用户类型,在不指定的默认情况下都是root用户。

VOLUME

该指令用于向基于镜像创建的容器添加卷。容器在运行过程中难免需要存储与操作数据,但是一般不建议在容器中存储数据,因为提交镜像时会将内容一并存储在镜像里面导致镜像过大。所以数据会以数据卷的形式存储在宿主机器上面。使用这个卷可以完成宿主机与容器中的文件系统进行关联。其实就是挂载宿主机文件系统到启动的容器当中,而不会再镜像中进行保存。

VOLUME ["/src","/dir"]

实现以此镜像构建的容器共享数据卷。该镜像构建的所有的容器都是使用此数据卷。在镜像构建的容器中都是这个路径下作为挂载卷。类似于EXPOSE指令,当需要将容器中的内容与宿主机的文件系统挂钩时还需要在容器启动的时候指定要挂载的宿主机文件位置。
对于挂载卷最佳方式还是docker run -v src:dir运行时进行卷的挂载。因为即便是同一个镜像的不同容器为了保证运行时数据不被覆盖也会选择不同的文件夹进行数据的存储。而且有些情况下如果卷写在dockerfile里面构建镜像运行的时候会有一些莫名其妙的权限问题,综合考虑卷挂载还是在启动容器的时候指定。

ADD 指令

将构建宿主机器中的文件添加到镜像中去。ADD source directory 操作源可以是目录或者是文件。如果是以/结尾那么就是目录,如果是非/结尾那么会认为是文件。值得注意的是ADD指令使用时如果目标文件不存在指令会在镜像中自动创建这个目录,并且目录的访问权限是绝对开放的。

COPY 指令

COPY指令与ADD类似,但是ADD指令会做文件的压缩与解压,而COPY不会,它只是纯粹的将构建上下文的文件夹或者文件复制到指定的目录当中。COPY要求源文件在Dockerfile所在的目录中,其他目录中的文件是无法复制过去的,目的位置要求是容器中的绝对位置。所以综合考虑ADD指令比COPY更好用

ONBUILD

镜像中添加触发器,可以理解为回调,在构建镜像的过程中会触发这个触发器。当一个镜像作为其他镜像的基本镜像构建时,此时触发器会被触发运行。该触发器编写与基础镜像之中。触发器在构建的过程中会插入新的指令,这些指令可以认为在FROM之后运行。ONBUILD的触发只是在子镜像构建过程中触发,所以一般讲一些通用的配置或者执行指令写入到ONBUILD中让构建的镜像作为模板,以此模板构建的子镜像的过程中,都会触发ONBUILD指令对应的构建内容,而在孙子镜像的构建过程中不会触发这个ONBUILD构建过程。

构建镜像中的注意事项

构建镜像使用docker build -t=image's name ./path path指定的是有Dockerfile的文件夹路径。可以是当前的文件夹下的Dockerfile文件也可以是远程网络上的文件。比如github的项目的根目录下的文件可以用于构建镜像。

猜你喜欢

转载自blog.csdn.net/Allocator/article/details/69486608
今日推荐