Dockerfile 介绍
容器docker
可以通过读取dockerfile
中的指令来自动构建镜像。Dockerfile是一个文本文档,它包含用户可以在命令行上调用的所有命令,并组装一个映像。
使用docker build
,用户可以创建一个连续执行多个命令行指令的自动化构建的镜像。
Docker镜像是分层结构,由只读层组成,每个只读层表示Dockerfile指令。这些层被堆叠起来,每一层都是前一层变化的增量。例如:
FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py
上面没条指令的含义:
- FROM :创建一个基础镜像层(基于ubuntu:18.04),之后的指令都是基于该层的。因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令。
- COPY :将docker客户端当前目录中的文件全部 copy 到容器中的/app目录中。
- RUN :使用make构建应用程序,RUN 指令是用来执行命令行命令的。
- CMD :指定在容器中运行什么命令。
当您运行一个镜像像并生成一个容器时,将在底层层上添加一个新的可写层(“容器层”)。对正在运行的容器所做的所有更改,如写入新文件、修改现有文件和删除文件,都被写入这个可写的容器层。
理解docker构建上下文
我们在构建镜像是,其格式为:
docker build [选项] <上下文路径/URL/->
从上面指令可以看到 docker build 命令最后有一个 .。. 表示当前目录,而 Dockerfile 就在当前目录,因此不少初学者以为这个路径是在指定 Dockerfile 所在路径,这么理解其实是不准确的。如果对应上面的命令格式,你可能会发现,这是在指定 上下文路径。那么什么是上下文呢?
首先我们要理解 docker build 的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?
这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
发出docker构建命令时,当前工作目录便是构建上下文。默认情况下,Dockerfile位于此处,但是您可以使用file标志(-f)指定一个不同的位置。无论Dockerfile实际位于何处,当前目录中的文件和目录的所有递归内容都将作为构建上下文发送到Docker守护进程。
例如,如果在 Dockerfile 中这么写:
COPY ./app.yml /app
这并不是要复制执行 docker build 命令所在的目录下的 app.yml,也不是复制 Dockerfile 所在目录下的 app.yml,而是复制 上下文(context) 目录下的 app.yml。
docker build /opt/myapp/ -f ~/Dockerfile
上文问目录为“/opt/myapp/”, 而Dockerfile文件为“~/Dockerfile”。
镜像的构建方式
基于Dockerfile构建
我们创建一个myproject的目录,并cd进入到该目录。创建一个hello文件,内容为hello。然后我们创建一个Dockerfile,用来查看hello文件的内容。
mkdir myproject && cd myproject
echo "hello" > hello
echo -e "FROM busybox:latest\nCOPY hello /\nRUN cat /hello" > Dockerfile
docker build -t helloapp:v1 .
然后我们将Dockerfile移动到其他的目录,通过-f
参数来制定Dockerfile文件的位置,并构建镜像:
mkdir -p dockerfiles context
mv Docekerfile dockerfiles && mv hello context
docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context
注意:
我们在docker build
的时候,应该尽可能地避免引入不必要的文件或目录,这样会导致镜像体积增加;一个庞大体积的镜像在pull,push
的过程中会大大增加时间,以及容器运行的体积。要想查看你容器有多大,在build的过程中会看到这样的提示:
Sending build context to Docker daemon 2.607kB
基于管道和标准输入构建
Docker能够通过使用本地或远程构建上下文,通过stdin管道传输Dockerfile来构建映像。
通过stdin管道传输Dockerfile对于执行一次性构建
非常有用,不需要将Dockerfile写入磁盘,或者在生成Dockerfile的情况下,不应该在以后持久化Dockerfile。
例如:
echo -e 'FROM busybox\nRUN echo "hello, world~"' | docker build -
或者
docker build - <<EOF
FROM busybox
RUN echo "hello, world~"
EOF
说明:
上面的例子中,我们在构建的使用,使用了连接符-
占据了路径的位置,指示Docker从stdin而不是从目录中读取构建上下文(只包含Dockerfile),这种情况不会将任何文件作为构建上下文发送给守护进程。
使用git repo进行构建
docker build
支持直接从url进行构建:
$ docker build https://github.com/username/project.git#version
这行命令指定了构建所需的 Git repo,并且指定默认的 master 分支,构建目录为 /version/(例如版本是1.1,那么构建目录便是/1.1/),然后 Docker 就会自己去 git clone 这个项目、切换到指定分支、并进入到指定目录后开始构建。
通过给定tar压缩包构建
$ docker build http://server/context.tar.gz
如果所给出的 URL 不是个 Git repo,而是个 tar 压缩包,那么 Docker 引擎会下载这个包,并自动解压缩,以其作为上下文,开始构建。