Dockerfile使用介绍

Dockerfile是一个文本格式的配置文件,用户可以使用Dockerfile来快速创建自定义的镜像。

1.Dockerfile概念

我们使用Dockerfile定义镜像,依赖镜像来运行容器,因此Dockerfile是镜像和容器的关键。首先通过一张图来了解Docker镜像、容器和Dockerfile三者之间的关系。

img

通过上图可以看出使用Dockerfile定义镜像,运行镜像然后启动容器。

Dockerfile镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

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

Dockerfile是一个文本文件,其内包含了一条条指令,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了Dockerfile,当我们需要定制自己额外的需求时,只需要在Dockerfile上添加或者修改指令,重新生成image即可,省去了敲命令的麻烦。

2.Dockerfile的基本结构

Dockerfile由一行行命令语句组成,并支持以#开头的注释行。Dockerfile文件格式如下:

##  Dockerfile文件格式
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# 1、第一行必须指定 基础镜像信息
FROM ubuntu
# 2、维护者信息
MAINTAINER docker_user [email protected]
# 3、镜像操作指令
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
 
# 4、容器启动执行指令
CMD /usr/sbin/nginx

Dockerfile分为四部分:1.基础镜像信息、2.维护者信息、3.镜像操作指令、4.容器启动执行指令。一开始必须要指明所基于镜像名称,接下来一般会说明维护者信息;后面则是镜像操作指令,例如RUN指令。每执行一条RUN指令,镜像添加新的一层,并提交;最后是CMD指令,来指明运行容器时的操作指令。

3.构建镜像

docker build命令会根据Dockerfile文件以及上下文构建新Docker镜像。构建上下文是指Dockerfile所在的本地路径或一个URL(Git仓库地址)。构建上下文环境会被递归处理,所以构建所指定的路径还包含了子目录,而URL还包括了其中指定的子模块。

将当前目录做为构建上下文时,可以像下面这样使用docker build命令构建镜像:

docker build .
Sending build context to Docker daemon  6.51 MB
...

说明:构建会在Docker后台守护进程(daemon)中执行,而不是CLI中。构建前,构建进程会将全部内容(递归)发送到守护进程。大多情况下,应该将一个空目录作为构建上下文环境,并将Dockerfile文件放在该目录下。

在构建上下文中使用的Dockerfile文件,是一个构建指令文件。为了提高构建性能,可以通过.dockerignore文件排除上下文目录下不需要的文件和目录。

在 Docker 构建镜像的第一步,docker CLI会先在上下文目录中寻找.dockerignore文件,根据.dockerignore 文件排除上下文目录中的部分文件和目录,然后把剩下的文件和目录传递给 Docker 服务。

Dockerfile 一般位于构建上下文的根目录下,也可以通过-f指定该文件的位置:

docker build -f /path/to/a/Dockerfile .

构建时,还可以通过-t参数指定构建成镜像的仓库、标签。

3.缓存

Docker 守护进程会一条一条的执行Dockerfile中的指令,而且会在每一步提交并生成一个新镜像,最后会输出最终镜像的ID。生成完成后,Docker 守护进程会自动清理你发送的上下文。Dockerfile文件中的每条指令会被独立执行,并会创建一个新镜像,RUN cd /tmp等命令不会对下条指令产生影响。 Docker 会重用已生成的中间镜像,以加速docker build的构建速度。以下是一个使用了缓存镜像的执行过程:

$ docker build -t svendowideit/ambassador .
Sending build context to Docker daemon 15.36 kB
Step 1/4 : FROM alpine:3.2
 ---> 31f630c65071
Step 2/4 : MAINTAINER [email protected]
 ---> Using cache
 ---> 2a1c91448f5f
Step 3/4 : RUN apk update &&      apk add socat &&        rm -r /var/cache/
 ---> Using cache
 ---> 21ed6e7fbb73
Step 4/4 : CMD env | grep _TCP= | (sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/' && echo wait) | sh
 ---> Using cache
 ---> 7ea8aef582cc
Successfully built 7ea8aef582cc

构建缓存仅会使用本地父生成链上的镜像,如果不想使用本地缓存的镜像,也可以通过--cache-from指定缓存。指定后将不再使用本地生成的镜像链,而是从镜像仓库中下载。

3.1寻找缓存的逻辑

Docker 寻找缓存的逻辑其实就是树型结构根据Dockerfile指令遍历子节点的过程。下图可以说明这个逻辑。

     FROM base_image:version           Dockerfile:
           +----------+                FROM base_image:version
           |base image|                RUN cmd1  --> use cache because we found base image
           +-----X----+                RUN cmd11 --> use cache because we found cmd1
                / \
               /   \
       RUN cmd1     RUN cmd2           Dockerfile:
       +------+     +------+           FROM base_image:version
       |image1|     |image2|           RUN cmd2  --> use cache because we found base image
       +---X--+     +------+           RUN cmd21 --> not use cache because there's no child node
          / \                                        running cmd21, so we build a new image here
         /   \
RUN cmd11     RUN cmd12
+-------+     +-------+
|image11|     |image12|
+-------+     +-------+

大部分指令可以根据上述逻辑去寻找缓存,除了ADD和COPY。这两个指令会复制文件内容到镜像内,除了指令相同以外,Docker 还会检查每个文件内容校验(不包括最后修改时间和最后访问时间),如果校验不一致,则不会使用缓存。

除了这两个命令,Docker 并不会去检查容器内的文件内容,比如 RUN apt-get -y update,每次执行时文件可能都不一样,但是 Docker 认为命令一致,会继续使用缓存。这样一来,以后构建时都不会再重新运行apt-get -y update

如果 Docker 没有找到当前指令的缓存,则会构建一个新的镜像,并且之后的所有指令都不会再去寻找缓存。

4.利用Dockerfile构建镜像的简单示例

4.1编辑Dockerfile文件

接下来用一个简单示例来感受一下Dockerfile是如何用来构建镜像启动容器。我们以定制Nginx镜像为例,在一个空白目录中,建立一个文本文件,并命名为Dockerfile

mkdir mynginx
cd mynginx
vim Dockerfile

构建一个Dockerfile文件内容为:

FROM nginx
# 前提是使用yum安装nginx,或者指定nginx中的index.html的位置
RUN echo '

Hello,Docker!

' > /usr/share/nginx/html/index.html

这个Dockerfile很简单,一共就两行涉及到了两条指令:FROMRUNFROM表示获取指定基础镜像,RUN执行命令,在执行的过程中重写了nginx的默认页面信息,并将信息替换为:Hello,Docker!

4.2构建镜像

Dockerfile文件所在的目录执行(或者通过-f选项来指定其路径):

# 命令最后有一个.表示当前目录
docker build -t nginx:v1 .
image.png

4.3查看镜像

构建完成之后,使用docker image命令查看所有镜像,如果存在REPOSITORYnginxTAGv1的信息,就表示构建成功。

image.png

4.4运行镜像

接下来使用docker run命令来启动容器:

docker run  --name docker_nginx_v1   -d -p 80:80 nginx:v1
image.png

这条命令会用nginx镜像启动一个容器,命名docker_nginx_v1,并且映射了 80 端口,这样我们可以用浏览器去访问这个 nginx 服务器:xx.xx.xx.xx,页面返回信息:

image.png

这样一个简单使用Dockerfile构建镜像,运行容器的实例就完成了。

4.5修改容器内容

容器启动后,需要对容器内的文件进行进一步的完善,可以使用docker exec -it xx bash命令再次进行修改,以上面的示例为基础,修改nginx启动页面内容:

docker exec -it docker_nginx_v1   bash
root@3729b97e8226:/# echo '

Hello, Docker Michaeljian!

' > /usr/share/nginx/html/index.html root@3729b97e8226:/# exit exit
image.png

以交互式终端方式进入docker_nginx_v1容器,并执行了bash命令,也就是获得一个可操作的 Shell。然后,我们用

Hello, Docker Michaeljian!

覆盖了 /usr/share/nginx/html/index.html 的内容。再次刷新浏览器,会发现内容被改变。

image.png

修改了容器的文件,也就是改动了容器的存储层,可以通过docker diff命令看到具体的改动。

image.png

参考资料

纯洁的微笑

Docker官网文档

你可能感兴趣的:(Dockerfile使用介绍)