wangxiaoaer
V2EX  ›  问与答

Docker 镜像的缓存

  •  
  •   wangxiaoaer · Aug 18, 2018 · 5206 views
    This topic created in 2867 days ago, the information mentioned may be changed or developed.

    docker 镜像每个 RUN 命令都会创建一层缓存:

    比如

    1 RUN apt-get xx

    2 mkdir xx && cd xx

    3 RUN make xx

    上面的第一行命令装了一大堆依赖,大概 500 多 M,每次 build 都比较耗时。

    这个命令会不会创建一个缓存?如果可以,并且如果这行命令一直不变的话,修改后面命令,重新构建的时候会不会利用这个缓存?还是重新 apt 安装?因为根据我自己的测试,好像并没有用到缓存,具体是什么情况?

    换句话说 docker 的缓存有么有直接跟 dockerfile 中的命令关联起来?能不能跨 dockefile 利用呢?

    Supplement 1  ·  Aug 18, 2018

    抱歉没说清楚,我的测试是这样子:

    第一次构建:

    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没有改动,应该是产生了一个缓存层,第二次构建应该使用缓存,但是结果并没有,这才是我疑惑的地方。

    30 replies    2018-08-19 18:06:08 +08:00
    qsnow6
        1
    qsnow6  
       Aug 18, 2018 via iPhone
    可以
    KeatingSmith
        2
    KeatingSmith  
       Aug 18, 2018 via iPhone
    每层镜像会有唯一的 Hash 码,每一层镜像,都会在本地存在缓存。

    假如我现在构建 C 容器,C 依赖 B。第一次在本地构建的时候,会先构建 B,再构建 C。假如还需要构建 D,D 也依赖于 B,因为之前已经构建 B 了,所以只系要直接构建 D 就行了。

    如果你换台机器,那么需要全部重新构建。
    KeatingSmith
        3
    KeatingSmith  
       Aug 18, 2018 via iPhone
    如果你在 Dockerfile 中使用每一层的命令,因为每一次 apt 不能保证对镜像都有相同的修改,导致 hash 不同。
    presoul
        4
    presoul  
       Aug 18, 2018 via Android
    cd xx 没用 不是 shell
    willxiang
        5
    willxiang  
       Aug 18, 2018
    会的,2-3L 解释的很清楚了
    只要你生成的镜像不立马删除,重新 run build 时会直接使用之前 run 过的缓存
    momocraft
        6
    momocraft  
       Aug 18, 2018
    "缓存"是个结果, 如果某一层可以复用以前的中间层, 才会跳过执行来直接使用上次的. 你不讲具体怎么测试鬼知道什么具体情况.
    shiny
        7
    shiny  
    PRO
       Aug 18, 2018
    我看到很多 Dockerfile 会尽量把构建合并到一条指令里,是不是因为这样生成出来的镜像尺寸比较小?
    whileFalse
        8
    whileFalse  
       Aug 18, 2018
    关键是你在哪一步 add 你的代码的。如果在 apt-get 之前,什么缓存也没用。
    yuanfnadi
        9
    yuanfnadi  
       Aug 18, 2018 via iPhone   ❤️ 1
    @shiny 对的 是因为一句命令就是一层。
    你上一句话加了一个文件 下一句把文件删了。不会缩小镜像的体积,因为不是一层。


    另外 docker 支持多步构建,可以在镜像 a 把代码 build 好,然后把二进制放到一个超级小的镜像。最终结果会非常小。
    xmgit
        10
    xmgit  
       Aug 18, 2018   ❤️ 1
    @yuanfnadi 写到一句,并不会使得最终镜像变小,而是层少了,层的数目会影响到容器执行后的 IO 性能。
    wangxiaoaer
        11
    wangxiaoaer  
    OP
       Aug 18, 2018
    @KeatingSmith #2 你说的这个依赖是通过 from 这种方式吗?里面的 A B C D 是指一个单独的镜像还是 dockerfile 里面的一条命令?
    wangxiaoaer
        12
    wangxiaoaer  
    OP
       Aug 18, 2018
    @momocraft #6 看 append
    DiamondYuan
        13
    DiamondYuan  
       Aug 18, 2018
    @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
    Busy
        14
    Busy  
       Aug 18, 2018
    @DiamondYuan 这是因为没有进行清理操作,或者清理到位

    @wangxiaoaer 因为每一次 apt 结果的 hash 不可能相同,如果你想省去每一次的 apt,那么第一次在 apt 后就打一个 tag,后面的构建都 from 这个 tag 来进行。
    wangxiaoaer
        15
    wangxiaoaer  
    OP
       Aug 18, 2018 via Android
    @Havee 我想过这样子做,把耗时操作打成一个镜像,但这样分发的时候就要把那个镜像也拷过去,除非搭建私服。
    Busy
        16
    Busy  
       Aug 18, 2018
    譬如如下的两次构建,会直接用缓存,你可以试一下

    FROM alpine
    RUN echo "1" > 1

    FROM alpine
    RUN echo "1" > 1
    RUN echo "2" > 2
    Busy
        17
    Busy  
       Aug 18, 2018
    第一次回复说错了,同样的安装环境,应该同样的 hash,譬如

    FROM alpine
    RUN apk add --no-cache curl

    FROM alpine
    RUN apk add --no-cache curl
    RUN apk add --no-cache git

    第二次构建会直接使用前一次 cache
    Busy
        18
    Busy  
       Aug 18, 2018
    或许,是因为 apt-get 后产生的日志文件中,时间不同,导致最后的 hash 不一致?
    derek80
        19
    derek80  
       Aug 18, 2018
    https://docs.docker.com/develop/develop-images/multistage-build/

    编译一个静态文件放 alpine 这种镜像里就好。
    Kilerd
        20
    Kilerd  
       Aug 18, 2018
    如果是编译成二进制的,那么用多端构建,放进 alpine 里面就好了
    如果是 Python 等类型的项目,可以试下 docker-slim 来缩小 image 的大小
    KeatingSmith
        21
    KeatingSmith  
       Aug 18, 2018 via iPhone
    @wangxiaoaer

    Dockerfile 的每一条命令就是一层镜像。

    为了精简容器的体积,建议在构建镜像的时候,不要把业务代码放进去,而是在运行容器的时候,利用 volumne 挂载。
    KeatingSmith
        22
    KeatingSmith  
       Aug 18, 2018 via iPhone
    apt 在安装会卸载软件的时候,会在 /var/log 下,至少是在这个目录下,生成日志文件,所以,导致每次的 Hash 都不一样。

    如果要实现你说的依赖缓存快速构建,可以先将耗时的一些命令打包成镜像 A,然后在 Dockerfile 中 FROM A
    Reficul
        23
    Reficul  
       Aug 18, 2018
    可以手动把老的镜像 pull 下来,然后 build 的时候指定 cache-from 参数
    wangxiaoaer
        24
    wangxiaoaer  
    OP
       Aug 19, 2018 via Android
    @KeatingSmith #21 但我看很多项目都是用 cp,用完删掉就行了吧。
    wangxiaoaer
        25
    wangxiaoaer  
    OP
       Aug 19, 2018 via Android
    @KeatingSmith 22
    看 15 楼。
    KeatingSmith
        26
    KeatingSmith  
       Aug 19, 2018 via iPhone
    @wangxiaoaer

    不好意思啊,手机回复的帖子,没看到已经有前人提供了方法。
    momocraft
        27
    momocraft  
       Aug 19, 2018
    选以前的中间结果大致是基于 (build context, 上一个命令得到的层, 下一个 dockerfile 命令)

    不会是什么基于文件的 hash (docker 不可能先验知道 "如果不用 cache 会得到同样 hash" 然后选择用)

    除了 dockerfile 有其他东西变化吗? 比如 build context?
    wangxiaoaer
        28
    wangxiaoaer  
    OP
       Aug 19, 2018 via Android
    上午搜了一大堆资料,经过几次测试感觉自己好像理解了:

    1 每一条命令都会对应一个缓存层。

    2 build 的时候按照 dockerfile 里面命令的顺序一条一条执行,如果命令不变,同时涉及到的文件无变动,就可能会使用缓存层(如果有的话)。

    3 如果某一层缓存失效,会导致下面的缓存同样失效。

    ……………………

    不清楚理解的对不对,请大佬指点。
    Busy
        29
    Busy  
       Aug 19, 2018
    从来没有用过 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 文件看看?
    wangxiaoaer
        30
    wangxiaoaer  
    OP
       Aug 19, 2018 via Android
    @Havee 看 28 楼的回复,我之前文件写的有问题。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   4924 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 88ms · UTC 09:37 · PVG 17:37 · LAX 02:37 · JFK 05:37
    ♥ Do have faith in what you're doing.