通过dockerfile定义文件和docker build命令构建,是推荐使用的镜像构建方式。通常在执行docker build的命令时,最后会加上".", 这个被当做是当前目录的"."是指dockerfile所在的路径吗?
环境简介
假如我们有如下的结构:
- 当前位于/root/docker/demo,该目录下有个Dockerfile,是我们即将用于构建镜像的定义文件
- dockerfile的内容如下:从ubuntu基础镜像开始,然后试图导入待执行的jar包文件到指定目录下
- 待导入的jar包文件目录当前dockerfile的上级目录
下面的环境帮助你理解上述
# cat Dockerfile
FROM ubuntu:latest
#
ENV RUN_PATH /home/work/bin/demo
RUN mkdir -p $RUN_PATH
#加入待运行的包
ADD ../spring-demo-0.0.1-SNAPSHOT.jar $RUN_PATH/spring-demo-0.0.1-SNAPSHOT.jar
# pwd
/root/docker/demo
# ll
total 4
-rw-r--r--. 1 root root 302 Mar 28 10:59 Dockerfile
# tree ../
../
├── demo
│ └── Dockerfile
├── demoApp
│ ├── Dockerfile
│ └── spring-demo-0.0.1-SNAPSHOT.jar
└── spring-demo-0.0.1-SNAPSHOT.jar
执行构建
按照上述的环境及dockerfile, 执行如下构建,会发生什么了?
# docker build -t "docker/demo:v1" .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM ubuntu:latest
---> 72300a873c2c
Step 2/4 : ENV RUN_PATH /home/work/bin/demo
---> Using cache
---> 7ef7956d22f5
Step 3/4 : RUN mkdir -p $RUN_PATH
---> Using cache
---> ac1218623a72
Step 4/4 : ADD ../spring-demo-0.0.1-SNAPSHOT.jar $RUN_PATH/spring-demo-0.0.1-SNAPSHOT.jar
ADD failed: Forbidden path outside the build context: ../spring-demo-0.0.1-SNAPSHOT.jar ()
ADD命令执行失败,意思是jar包的位置超出了当前构建的上下文。在此注意如果认为docker build命令最后的"."是指构建时dockerfile的位置,这样是不准确的;实际上它是指当前的构建上下文。Dockerfile 的文件名并不要求必须为 Dockerfile,而且并不要求必须位于上下文目录中,比如可以用 -f …/Dockerfile 参数指定某个文件作为Dockerfile。
理解docker build 的工作原理
Docker实质上是一个C/S架构的程序。Docker的引擎提供了一组REST API,被称为Docker Remote API,而如docker命令这样的cli指令,则是通过API与Docker引擎交互。
当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如上文的ADD指令。而docker build 命令构建镜像,其实并非在本地构建,而是在server端,即Docker引擎中构建的。那么在这种C/S的架构中,如何才能让服务端获得本地文件呢?这就是build context(构建上下文)的意义。当构建的时候,用户会指定构建镜像上下文的路径,docker build命令得知这个路径后,会将路径下的所有内容打包,然后上传给Docker引擎。Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
因此,ADD这类指令中的源文件的路径都是基于上下文的相对路径。而当指定构建的时的上下文是".“时,自然本地的”…/"路径就超出了上下文的范围。同样
ADD /root/docker/demo/spring-demo-0.0.1-SNAPSHOT.jar $RUN_PATH/spring-demo-0.0.1-SNAPSHOT.jar 也是无法正常工作的。
那么如何修正上述错误了?
将错就错(不推荐)
那么在现有目录结构下该如何构建了?修改Dockfile如下
FROM ubuntu:latest
#
ENV RUN_PATH /home/work/bin/demo
RUN mkdir -p $RUN_PATH
#加入待运行的包
ADD ./spring-demo-0.0.1-SNAPSHOT.jar $RUN_PATH/spring-demo-0.0.1-SNAPSHOT.jar
构建:
docker build -t "docker/demo:v1" -f ../demo/Dockerfile ../
你看,如果将上一级目录("…/")作为构建上下文, 那么你需要指定dockerfile的位置,而此时ADD命令执行的是将构建环境目录下的jar包,添加到docker的指定目录下。
为什么不推荐:
如果你将一级目录("…/")作为构建上下文,那么上一级目录下的所有文件都会被打包上传至docker引擎,而demoApp目录下文件,对本次构建来说,完全是多余的,这不仅增加了构建成本,也使得构建结果镜像臃肿。
最佳实践
建一个空目录,创建Dockerfile,并将你要用的文件和所有包置于该目录下。
# tree .
.
├── Dockerfile
└── spring-demo-0.0.1-SNAPSHOT.jar