我在RUN
中看到过使用Dockerfile
命令的两种不同方法,我将其命名为v1和v2。
每行一个命令
FROM ubuntu/latest
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
RUN apt-get -y install php5-dev
RUN libcurl4-openssl-dev
...
每行多个命令
FROM ubuntu/latest
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && \
apt-get -y install \
php5-dev \
libcurl4-openssl-dev
...
这两种方法都有其优点,使用缓存的不同方法最为明显。还有什么其他原因可以使用一种方法而不是另一种方法?
N.B。如果这个问题被认为过于模糊或对意见持开放态度,我向社会的意愿屈服;但是,我在这里发布它是因为我希望有很好的情况来分组命令,而不是很好的情况 - 我想知道它们是什么。
答案 0 :(得分:25)
要回答这个问题,首先必须了解“提交”的概念,以及Docker的缓存。最后,我提供了一个经验法则供你使用。
以下是一个例子:
# Dockerfile
FROM ubuntu/latest
RUN touch /commit1
RUN touch /commit2
当您运行docker build .
时,docker会执行以下操作:
ubuntu/latest
图片启动容器。touch /commit1
),并创建一个新图像。touch /commit2
),并创建一个新图像。您需要了解的是,如果您将命令分组到一个RUN
语句中,那么它们将在同一个容器中执行,并且将对应于单个提交。
相反,如果您在单个RUN
语句中断开命令,它们将不会在同一容器中运行,以后的命令将重用先前命令创建的映像。
当您运行docker build .
时,docker会重用之前创建的图像。换句话说,如果您编辑上述Dockerfile以在最后包含RUN touch /commit3
,并运行docker build .
,那么Docker将重用#4中创建的图像。
这很重要,因为当您在Dockerfile中包含RUN apt-get update
时,不能保证这将在RUN apt-get install php5
之前运行几秒钟。
大家知道,RUN apt-get update
的提交可能是在一个月前创建的。 APT缓存不再是最新的,但Docker仍在重用该提交。
通常更容易在一个RUN
命令中对所有内容进行分组,并在您希望开始利用缓存时开始分解(例如,加快构建过程)。
执行此操作时,请确保不要将必须在彼此的特定时间间隔内运行的命令(例如,更新和升级)分开。
一个好的做法是避免命令的副作用(即在安装所需的软件包后清理APT缓存)。
在您的示例中,v2
是正确的,而v1
是错误的(因为它会对缓存apt-get update
产生反作用)。