参考原文博客地址:《Docker之构建上下文详解》
简单回顾Docker镜像构建
-
cd
命令进入 Dockerfile 文件所在目录 -
docker build -t [imageName:imageTag](imagename:imageTag) .
命令进行构建
对于以上的常规操作,很容易让人陷入如下误区:
- docker build 后面的构建上下文路径只能为 Dockerfile 所在的目录
.
; - Dockerfile 文件名必须为 Dockerfile;
针对上面的两点错误理解要想准确理解其含义,首先我们需要先了解下 Docker 的架构和 docker build
的工作原理。
Docker架构简介
Docker 引擎使用C/S (客户端/服务器)体系的架构,Docker 客户端与 Docker 引擎的守护进程通信,Docker 引擎守护进程负责构建,运行和分发 Docker 容器。Docker 客户端和 Docker 引擎守护进程可以在同一个系统上运行,也可以将 Docker 客户端连接到远程 Docker 引擎守护进程。Docker 客户端和 Docker 引擎守护进程使用 REST API 通过UNIX套接字或网络接口进行通信。
总结:docker build 动作发生在 Docker 引擎中
docker build工作原理
- Client端执行
docker build -t [imageName:imageTag](imagename:imageTag) .
命令 ; - Docker 客户端会将构建命令后面指定的路径(
.
)下的所有文件打包发送给 Docker 服务端; - Docker服务端对客户端发送的包进行解压,然后根据 Dockerfile 里面的指令进行镜像的分层构建;
镜像构建上下文
Dockerfile 中使用 COPY 指令、ADD 等这些指令时。往往是期望对宿主机上的某些文件进行操作;而 docker build
命令构建镜像的过程其实并非在客户端本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得客户端的本地文件呢?
这就引入了上下文的概念。当构建的时候,用户需要指定构建镜像上下文的路径,docker build
命令会将路径下的所有内容打包并上传给 Docker 引擎。最后 Docker 引擎解压,这样在构建时就可以获取镜像所需的一切文件。
如果在 Dockerfile 中这么写:
# 这表示将构建上下文路径下的
# package.json文件复制到容器内的/app/下
COPY ./package.json /app/
上述COPY
指令仅仅表明复制的是构建上下文路径下的 package.json。它既不是要复制执行 docker build
命令所在目录下的 package.json,也不是复制 Dockerfile 所在目录下的 package.json。
因此,COPY
这类指令中的源文件的路径都是相对路径。这也是初学时经常迷惑为什么 COPY ../package.json /app
或者 COPY /opt/xxxx /app
无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。
示例验证
基于上面的理解;对其进行实验验证:
验证命题:
docker build
命令后面的构建上下文路径不是必须为 Dockerfile 所在的目录.
;还可以指定上下文路径。- 如果指定上下文路径,则必须使用
-f
选项指定Dockerfile路径;因为指定上下文路径不是.
,即使在Dockerfile所在目录下执行docker build
也不好使。
验证思路:
编写 Dockerfile 文件保存在宿主机的
/home/dockerfile/demo/
目录下;Dockerfile 的内容就以 nginx 为例,将宿主机下自定义/tmp/index.html
文件COPY
到 nginx 容器/usr/share/nginx/html/
进行替换
第一步:准备index.html
将编写好的index.html
文件上传到服务器的/tmp/
路径下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>docker构建上下文路径</title>
</head>
<body>
<h1>docker构建上下文路径可以自定义指定验证</h1>
</body>
</html>
第二步:编写Dockerfile文件
# 这是一个简单Dockerfile练习
FROM nginx
# 将‘构建上下文路径’下的index.html进行复制替换操作
COPY ./index.html /usr/share/nginx/html
编写完成以后将文件上传至服务器的/home/dockerfile/demo/
第三步:撇弃默认,自定义上下文路径进行构建
下图为Dockerfile
和index.html
在服务器上的位置
注意:在撇弃默认的构建上下文路径
.
;自定义上下文路径的情况下,一定要使用-f
指定Dockerfile文件路径(即使在Dockerfile所在目录下执行docker build
也需要);否则会出现如下错误:[root@VM-8-3-opencloudos demo]# docker build -t nginx-test:01 /tmp unable to prepare context: unable to evaluate symlinks in >Dockerfile path: lstat /tmp/Dockerfile: no such file or >directory
第四步:正确构建的命令选项
[root@VM-8-3-opencloudos ~]# docker build -f /home/dockerfile/demo/Dockerfile -t nginx-test:01 /tmp
Sending build context to Docker daemon 24.24MB
Step 1/2 : FROM nginx
---> 021283c8eb95
Step 2/2 : COPY ./index.html /usr/share/nginx/html
---> 6789af79468c
Successfully built 6789af79468c
Successfully tagged nginx-test:01
示例总结
基于自定义构建上下文路径的情况下,必须使用-f
指定Dockerfile路径(即使在Dockerfile所在目录下执行docker build
也需要)
构建上下文经验之谈
理解构建上下文对于镜像构建是很重要的,可以避免犯一些不应该的错误。比如有些初学时在发现 COPY /opt/xxxx /app
不工作后,于是干脆将 Dockerfile 放到了硬盘根目录去构建,结果发现 docker build
执行后,在发送一个几十 GB 的东西,极为缓慢而且很容易构建失败。那是因为这种做法是在让 docker build
打包整个硬盘,这显然是使用错误。
-
一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。并且构建上下文路径就使用
.
-
如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 .gitignore 一样的语法写一个.dockerignore,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。
-
实际上 Dockerfile 的文件名并不要求必须为 Dockerfile,而且并不要求必须位于上下文目录中,比如可以用
-f
指定某个文件作为 Dockerfile。 -
一般习惯性的会使用默认的文件名 Dockerfile,以及会将其置于镜像构建上下文目录中。