Docker - 如何更新图像

时间:2017-01-12 01:47:18

标签: docker docker-compose

我读到docker使用图层,因此在创建container时使用Dockerfile时,从基本图像开始,然后后续命令运行向容器添加图层,因此如果保存新容器的状态,你有一个新的图像。我想知道有几件事情。

如果我从Ubuntu图像开始,这是一个非常庞大且笨重的图像,因为它是一个完整的操作系统,然后我添加了一些工具并将其保存为我上传到集线器的新图像。如果有人下载了我的图片,并且他们已经在images folder中保存了Ubuntu图片,这是否意味着他们可以跳过下载Ubuntu,因为他们已经拥有该图片了?如果是这样,当我修改原始图像的某些部分时,这是如何工作的,Docker是否使用其缓存数据在加载后有选择地将这些更改应用于Ubuntu image

2.。)如何通过修改Dockerfile来更新我构建的图像?我用这个Dockerfile设置了一个简单的django项目:

FROM python:3.5

ENV PYTHONBUFFERED 1
ENV APPLICATION_ROOT /app
ENV APP_ENVIRONMENT L

RUN mkdir -p $APPLICATION_ROOT
WORKDIR $APPLICATION_ROOT
ADD requirements.txt $APPLICATION_ROOT
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
ADD . $APPLICATION_ROOT

并用它来开始创建图像。所以每当我创建一个盒子时,它会加载所有这些environment variables,如果我完全重建盒子,它会重新安装包和所有额外的东西。我需要添加一个新的环境变量,所以我将它添加到Dockerfile的底部,以及一个测试变量:

ENV COMPOSE_CONVERT_WINDOWS_PATHS 1
ENV TEST_ENV_VAR TEST

当我删除容器和图像,并构建一个新容器时,一切似乎都相应,它告诉我它创建了新的第4步:ENV

COMPOSE_CONVERT_WINDOWS_PATHS 1
 ---> Running in 75551ea311b2
 ---> b25b60e29f18
Removing intermediate container 75551ea311b2

所以它就像在某些中间容器转换中丢失了一些东西。这是缓存系统的工作原理,每个新层都是intermediate container吗?因此,考虑到这一点,如何添加新图层,您是否总是必须在Dockerfile的底部添加新数据?或者在构建映像后将Dockerfile单独保留是否更好,只需修改container并构建新映像?

编辑我刚刚尝试安装了一个名为bwawrik/bioinformatics的图片,这是一个基于CentOS的容器,它安装了各种各样的工具。

它冻结了一半,所以我退出了它,然后再次运行它,看看是否已经安装了所有东西:

$ docker pull bwawrik/bioinformatics
Using default tag: latest
latest: Pulling from bwawrik/bioinformatics

a3ed95caeb02: Already exists
a3ed95caeb02: Already exists
7e78dbe53fdd: Already exists
ebcc98113eaa: Already exists
598d3c8fd678: Already exists
12520d1e1960: Already exists
9b4912d2bc7b: Already exists
c64f941884ae: Already exists
24371a4298bf: Already exists
993de48846f3: Already exists
2231b3c00b9e: Already exists
2d67c793630d: Already exists
d43673e70e8e: Already exists
fe4f50dda611: Already exists
33300f752b24: Already exists
b4eec31201d8: Already exists
f34092f697e8: Already exists
e49521d8fb4f: Already exists
8349c93680fe: Already exists
929d44a7a5a1: Already exists
09a30957f0fb: Already exists
4611e742e0b5: Already exists
25aacf0148db: Already exists
74da82504b6c: Already exists
3e0aac083b86: Already exists
f52c7e0ac000: Already exists
35eee92aaf2f: Already exists
5f6d8eb70885: Already exists
536920bfe266: Already exists
98638e678c51: Already exists
9123956b991d: Already exists
1c4c8a29cd65: Already exists
1804bf352a97: Already exists
aa6fe9359956: Already exists
e7e38d1250a9: Already exists
05e935c831dc: Already exists
b7dfc22c26f3: Already exists
1514d4797ffd: Already exists
Digest: sha256:0391808e21b7b5cc0eb44fc2dad0d7f5415115bdaafb4534c0b6a12efd47a88b
Status: Image is up to date for bwawrik/bioinformatics:latest

所以它确实将包装成碎片,而不是一次性安装。这些是不同的图像吗?

1 个答案:

答案 0 :(得分:52)

图像与容器

首先,让我澄清一些术语。

image :一个静态的,不可变的对象。这是您使用docker build运行Dockerfile时构建的内容。图像不是运行的东西。

图像由图层组成。图像可能只有一个图层,或者可能有多个图层。

容器:正在运行的东西。它使用图像作为起始模板。

这类似于二进制程序和进程。您在磁盘上有一个二进制程序(例如/bin/sh),当您运行它时,它是您系统上的一个进程。这类似于图像和容器之间的关系。

将图层添加到基本图像

您可以从基本图像(例如示例中的ubuntu)构建自己的图像。 Dockerfile中的某些命令会在最终图像中创建一个新图层。其中一些是RUNCOPYADD

第一层没有父图层。但是每个其他层都有一个父层。通过这种方式,它们相互连接,像煎饼一样堆叠起来。

每个图层都有一个唯一的ID(您已经看过的长十六进制哈希值)。它们还可以包含人性化的名称,称为标记(例如ubuntu:16.04)。

什么是图层与图像?

从技术上讲,每个图层也是一个图像。如果您构建一个新图像并且它有5个图层,则可以使用该图像,它将包含所有5个图层。如果使用堆栈中的第三层作为图像ID运行容器,也可以这样做 - 但它只包含3层。你指定的那个和它的祖先是两个。

但作为惯例,术语"图像"通常表示具有关联标记的图层。当您运行docker images时,它会显示所有顶级图片,并隐藏下面的图层(但您可以使用-a显示所有图片。)

什么是中间容器?

docker build运行时,它会在容器内完成所有工作(当然!)因此,如果它遇到RUN步骤,它将从当前顶层创建一个容器,运行指定的其中的命令,然后将结果保存为新图层。然后它将从这个新层创建一个容器,运行下一个......等等。

中间容器仅用于构建过程,并在构建后被丢弃。

图层文件系统如何工作

您询问是否有人下载基于ubuntu的图片只是部分下载,如果他们已在本地拥有ubuntu图片。

是的!这完全正确。

每个图层都使用它下面的图层作为基础。新图层基本上是该图层与新状态之间的差异。但是,与git提交可能有效的方式不同,它不是差异。它适用于文件级别,而不是行级别。

假设您从ubuntu开始,并且运行了此Dockerfile。

FROM: ubuntu:16.04
RUN groupadd dan && useradd -g dan dan

这将产生两层图像。第一层是ubuntu图像。第二个可能只有一些变化。

  • 用户" dan"
  • /etc/passwd的较新副本
  • /etc/group的新版本,其中包含" dan"
  • 新目录/home/dan
  • 一些默认文件,例如/home/dan/.bashrc

就是这样。如果您从此图像启动容器,那么这几个文件将位于最顶层,其他所有文件都来自ubuntu图像中的文件系统。

容器中最顶层的读写层

另一点。运行容器时,可以在文件系统中写入文件。但是,如果您停止容器并从同一图像运行另一个容器,则会重置所有内容。那么文件在哪里写的?

图像是不可变的,所以一旦它们存在,它们就无法改变。您可以构建新版本,但这是一个新图像。它将具有不同的ID,并且不会是相同的图像。

容器具有顶层读写层,该层位于图像层之上。任何写入都发生在该层中。它就像其他层一样工作。如果您需要修改文件(或添加一个或删除一个文件),则在顶层完成,并且不会影响较低层。如果文件已存在,则将其复制到读写层,然后进行修改。这称为写时复制(CoW)。

添加更改的位置

您是否必须在Dockerfile的底部添加新内容?不,你可以在任何地方添加任何东西(或改变任何东西)。

但是,由于构建缓存的工作原理,您的工作方式确实会影响构建时间。

Docker将尝试在构建期间缓存结果。如果它在通过Dockerfile读取时发现FROM是相同的,则第一个RUN是相同的,第二个RUN是相同的...它将假设它已经完成这些步骤,将使用缓存的结果。如果遇到与上一次构建不同的内容,则会使缓存无效。从那时起,一切都将重新焕发新生。

有些事情总是会使缓存失效。例如,如果您使用ADDCOPY,那些始终会使缓存无效。这是因为Docker只跟踪构建命令是什么。它并没有试图找出"这个版本的文件我是否正在复制与上次相同的文件?"

因此,通常的做法是从FROM开始,然后放置非常静态的内容,例如安装包的RUN命令。 apt-get等等。在最初编写Dockerfile之后,这些事情往往不会发生很大变化。稍后在文件中放置更频繁更改的内容会更方便。

很难简明扼要地提出这方面的建议,因为这实际上取决于相关项目。但要了解构建缓存是如何工作并尝试利用它的,这是值得的。