docker 镜像每个 RUN 命令都会创建一层缓存:
比如
1 RUN apt-get xx
2 mkdir xx && cd xx
3 RUN make xx
上面的第一行命令装了一大堆依赖,大概 500 多 M,每次 build 都比较耗时。
这个命令会不会创建一个缓存?如果可以,并且如果这行命令一直不变的话,修改后面命令,重新构建的时候会不会利用这个缓存?还是重新 apt 安装?因为根据我自己的测试,好像并没有用到缓存,具体是什么情况?
换句话说 docker 的缓存有么有直接跟 dockerfile 中的命令关联起来?能不能跨 dockefile 利用呢?
抱歉没说清楚,我的测试是这样子:
第一次构建:
dockerfile:
1 RUN apt-get xx
2 mkdir xx && cd xx
构建:docker build -t m:v1 .
耗费很久但是构建成功,中间通过控制台日志输出可以看到apt-get的日志信息。
然后修改dockerfile文件,添加第三行:
3 RUN make xx
第二次构建
docker build -t m:v1 .
和第一次构建一样,仍然有apt-get的信息。按我自己的理解,第一行的RUN没有改动,应该是产生了一个缓存层,第二次构建应该使用缓存,但是结果并没有,这才是我疑惑的地方。
1
qsnow6 2018-08-18 18:37:21 +08:00 via iPhone
可以
|
2
KeatingSmith 2018-08-18 19:08:59 +08:00 via iPhone
每层镜像会有唯一的 Hash 码,每一层镜像,都会在本地存在缓存。
假如我现在构建 C 容器,C 依赖 B。第一次在本地构建的时候,会先构建 B,再构建 C。假如还需要构建 D,D 也依赖于 B,因为之前已经构建 B 了,所以只系要直接构建 D 就行了。 如果你换台机器,那么需要全部重新构建。 |
3
KeatingSmith 2018-08-18 19:13:40 +08:00 via iPhone
如果你在 Dockerfile 中使用每一层的命令,因为每一次 apt 不能保证对镜像都有相同的修改,导致 hash 不同。
|
4
presoul 2018-08-18 19:17:31 +08:00 via Android
cd xx 没用 不是 shell
|
5
willxiang 2018-08-18 19:19:27 +08:00
会的,2-3L 解释的很清楚了
只要你生成的镜像不立马删除,重新 run build 时会直接使用之前 run 过的缓存 |
6
momocraft 2018-08-18 19:21:23 +08:00
"缓存"是个结果, 如果某一层可以复用以前的中间层, 才会跳过执行来直接使用上次的. 你不讲具体怎么测试鬼知道什么具体情况.
|
7
shiny 2018-08-18 19:21:56 +08:00
我看到很多 Dockerfile 会尽量把构建合并到一条指令里,是不是因为这样生成出来的镜像尺寸比较小?
|
8
whileFalse 2018-08-18 19:24:11 +08:00
关键是你在哪一步 add 你的代码的。如果在 apt-get 之前,什么缓存也没用。
|
9
yuanfnadi 2018-08-18 19:33:31 +08:00 via iPhone 1
@shiny 对的 是因为一句命令就是一层。
你上一句话加了一个文件 下一句把文件删了。不会缩小镜像的体积,因为不是一层。 另外 docker 支持多步构建,可以在镜像 a 把代码 build 好,然后把二进制放到一个超级小的镜像。最终结果会非常小。 |
11
wangxiaoaer OP @KeatingSmith #2 你说的这个依赖是通过 from 这种方式吗?里面的 A B C D 是指一个单独的镜像还是 dockerfile 里面的一条命令?
|
12
wangxiaoaer OP @momocraft #6 看 append
|
13
DiamondYuan 2018-08-18 21:16:00 +08:00
@Bardon
会变小的。例如我的前端项目。最终结果就是一个 nginx 的镜像加一层 html 脚本。 FROM node:alpine as build COPY . /project/ WORKDIR /project RUN echo "Installing dependencies..." && \ npm install RUN echo "Starting dist build..." && \ npm run-script build FROM nginx:stable-alpine COPY --from=build /project/dist /usr/share/nginx/html/ EXPOSE 80 |
14
Havee 2018-08-18 21:25:22 +08:00
@DiamondYuan 这是因为没有进行清理操作,或者清理到位
@wangxiaoaer 因为每一次 apt 结果的 hash 不可能相同,如果你想省去每一次的 apt,那么第一次在 apt 后就打一个 tag,后面的构建都 from 这个 tag 来进行。 |
15
wangxiaoaer OP @Havee 我想过这样子做,把耗时操作打成一个镜像,但这样分发的时候就要把那个镜像也拷过去,除非搭建私服。
|
16
Havee 2018-08-18 21:46:31 +08:00
譬如如下的两次构建,会直接用缓存,你可以试一下
FROM alpine RUN echo "1" > 1 FROM alpine RUN echo "1" > 1 RUN echo "2" > 2 |
17
Havee 2018-08-18 21:53:34 +08:00
第一次回复说错了,同样的安装环境,应该同样的 hash,譬如
FROM alpine RUN apk add --no-cache curl FROM alpine RUN apk add --no-cache curl RUN apk add --no-cache git 第二次构建会直接使用前一次 cache |
18
Havee 2018-08-18 21:55:47 +08:00
或许,是因为 apt-get 后产生的日志文件中,时间不同,导致最后的 hash 不一致?
|
19
derek80 2018-08-18 22:21:19 +08:00
|
20
Kilerd 2018-08-18 22:49:51 +08:00
如果是编译成二进制的,那么用多端构建,放进 alpine 里面就好了
如果是 Python 等类型的项目,可以试下 docker-slim 来缩小 image 的大小 |
21
KeatingSmith 2018-08-18 23:32:26 +08:00 via iPhone
|
22
KeatingSmith 2018-08-18 23:36:00 +08:00 via iPhone
apt 在安装会卸载软件的时候,会在 /var/log 下,至少是在这个目录下,生成日志文件,所以,导致每次的 Hash 都不一样。
如果要实现你说的依赖缓存快速构建,可以先将耗时的一些命令打包成镜像 A,然后在 Dockerfile 中 FROM A |
23
Reficul 2018-08-18 23:36:16 +08:00
可以手动把老的镜像 pull 下来,然后 build 的时候指定 cache-from 参数
|
24
wangxiaoaer OP @KeatingSmith #21 但我看很多项目都是用 cp,用完删掉就行了吧。
|
25
wangxiaoaer OP @KeatingSmith 22
看 15 楼。 |
26
KeatingSmith 2018-08-19 08:25:39 +08:00 via iPhone
|
27
momocraft 2018-08-19 14:59:18 +08:00
选以前的中间结果大致是基于 (build context, 上一个命令得到的层, 下一个 dockerfile 命令)
不会是什么基于文件的 hash (docker 不可能先验知道 "如果不用 cache 会得到同样 hash" 然后选择用) 除了 dockerfile 有其他东西变化吗? 比如 build context? |
28
wangxiaoaer OP 上午搜了一大堆资料,经过几次测试感觉自己好像理解了:
1 每一条命令都会对应一个缓存层。 2 build 的时候按照 dockerfile 里面命令的顺序一条一条执行,如果命令不变,同时涉及到的文件无变动,就可能会使用缓存层(如果有的话)。 3 如果某一层缓存失效,会导致下面的缓存同样失效。 …………………… 不清楚理解的对不对,请大佬指点。 |
29
Havee 2018-08-19 18:00:00 +08:00
从来没有用过 ubuntu 镜像,刚才写了两个简单的 dockerfile,也是直接用缓存的呀
FROM ubuntu RUN apt-get -y update && apt-get -y install curl FROM ubuntu RUN apt-get -y update && apt-get -y install curl RUN apt-get -y install git 在第二次构建的时候 Sending build context to Docker daemon 2.048kB Step 1/3 : FROM ubuntu ---> 735f80812f90 Step 2/3 : RUN apt-get -y update && apt-get -y install curl ---> Using cache ---> b201fe246f08 Step 3/3 : RUN apt-get -y install git ---> Running in 9ffc294479eb 。。。。。。 楼主要不要抛出完整的 Dockerfile 文件看看? |
30
wangxiaoaer OP @Havee 看 28 楼的回复,我之前文件写的有问题。
|