【问题标题】:Why && rather than a new RUN为什么 && 而不是一个新的 RUN
【发布时间】:2017-11-01 19:04:10
【问题描述】:

Dockerfiles 我看到大多数人都在使用这种语法

RUN apt-get -y update \
    && apt-get install -y libicu-dev

超过这个

RUN apt-get -y update
RUN apt-get install -y libicu-dev

对我来说,第一个只缓存一行(层),而第二个缓存两个(我错了吗?),一旦命令不成功就停止。

除此之外,我不觉得第一个更具可读性。

那么我们为什么要使用第一种语法呢?

【问题讨论】:

标签: shell docker dockerfile


【解决方案1】:

是对docker镜像层的优化。我也推荐阅读Best practices for writing Dockerfiles

还有来自 DockerCon EU 2017 的有趣 presentation

【讨论】:

  • 感谢您提供此链接,我会阅读该链接并尝试找出我们更喜欢组合命令的原因。
【解决方案2】:

层数越少,图像越好。

因此,使用 && 组合命令将创建一个层。

有两个 RUN 将创建两个层。

【讨论】:

  • 为什么层数越少,图像越好?如果我在一个大图层中修改某些内容,我将不得不重建整个图层。此处的磁盘空间不应真正受到影响,因为它无论如何都会产生差异。
  • 图像就像虚拟文件系统层。基本上,层或图像层是对图像或中间图像的更改。您在 Dockerfile 中指定的每个命令(FROM、RUN、COPY 等)都会导致先前的映像发生更改,从而创建一个新层。当您使用 git 时,您可以将其视为暂存更改:您添加一个文件的更改,然后是另一个,然后是另一个。因此,越少越好。
  • 层是一种“补丁”,仅包含与前一层的差异。因此,除非我们运行的命令完全恢复之前的层,否则在使用的磁盘空间方面不会有很大的差距。
  • 是的,同意。因此,所有逻辑上相似的命令都应该形成一个单一的层。因此,在这种特殊情况下,更新和安装应该形成一个单一的层。
  • 是的,这就是我现在理解的问题
【解决方案3】:

根据images and layers documentation

每一层只是与前一层的一组差异

因此,例如创建不同文件的 2 层不会使用更多磁盘空间。特别是从 Docker 17.05 allows multi-stage builds 开始。但是,如果第二个完全从第一个修改文件,它仍然可以使用更多空间。

Khapov Igor's comment之后,我在best practice doc中找到了原始问题的真正答案:

RUN 语句中单独使用apt-get update 会导致缓存问题,并且后续的apt-get install 指令会失败。

实际上更多的是与先前命令的层依赖关系,这些命令的结果会随着时间的推移而演变,例如apt-get update

这就是他们说的原因:

始终将RUN apt-get updateapt-get install 组合在同一个RUN 语句中

【讨论】:

    【解决方案4】:

    Dockerfile 中的每个命令创建另一个映像层

    组合命令是一种最终减少整体层数的方法。

    https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/#images-and-layers

    【讨论】:

      【解决方案5】:

      这一行:

      RUN apt-get -y update \
      && apt-get install -y libicu-dev
      

      将创建一个单一的 docker 层和these lines:

      RUN apt-get -y update
      RUN apt-get install -y libicu-dev
      

      将创建两个不同的层。

      这就是为什么当你需要在你的 docker 机器上安装一些东西时(例如:通过 APT)你倾向于将所有东西都放在一行中(又名层)

      【讨论】:

        【解决方案6】:

        正如其他答案已经说过的那样,每个命令都会生成一个图层,并且通常希望每个图像具有最少的图层数。

        每一层只是与其前一层的一组差异。这些层相互堆叠。当您创建一个新容器时,您会在基础层之上添加一个新的可写层。

        这意味着除非您要“压缩”您的图像(在构建过程中使用--squash 选项转换),否则您最终会得到一个图像占用空间。

        例子

        # Dockerfile
        FROM ubuntu
        
        RUN apt-get update
        RUN apt-get install -y --no-install-recommends dnsutils
        RUN echo $( dig somewhere.nowhere )
        RUN apt-get remove --purge dnsutils
        RUN rm -rf /var/lib/apt/lists/*
        
        COPY magicalScript.sh /
        CMD /magicalScript.sh
        

        在这种情况下,您将拥有仅包含开销的层:

        • 1 缓存来自apt-get update
        • 1 安装了dnsutils
        • 1 包含删除dnsutils
        • 1 包含删除缓存

        问题是所有这些层都保留在那里,并且无缘无故地占用空间。

        为什么squash 并不总是一个好的解决方案?因为这些层也代表一个缓存。当您需要执行大量构建并且需要它们尽可能快时,它非常有用。

        通常最好将与在操作系统上安装新软件包相关的操作组合在一起:

        # Dockerfile
        
        FROM ubuntu
        
        RUN useradd docker \
            && mkdir /home/docker \
            && chown docker:docker /home/docker \
            && addgroup docker staff
        
        RUN apt-get update \ 
            && apt-get install -y --no-install-recommends ed less locales vim-tiny wget ca-certificates fonts-texgyre \
            && rm -rf /var/lib/apt/lists/*
        
        RUN echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen \
            && locale-gen en_US.utf8 \
            && /usr/sbin/update-locale LANG=en_US.UTF-8
        
        CMD ["mySpecialCommand"]
        

        【讨论】:

        • 是的,层仍然存在,我非常感谢,因为我大量使用缓存系统。问题是多层不一定比一层占用更多的磁盘空间。
        • 在您决定删除内容之前,开销并不是很大。您需要记住的是,对于生成的每一层,您的构建都必须创建一个中间容器,该容器在流程结束时会被删除。 Docker 使用联合 fs。我建议检查一下这个问题:stackoverflow.com/questions/32775594/…
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-08
        • 2010-09-05
        • 1970-01-01
        相关资源
        最近更新 更多