Docker – 如何更新图片

我读过Dockerfile和layer一起工作,所以当用Dockerfile创build一个containerDockerfile ,你先从基础镜像开始,然后后续的命令运行给容器添加一个图层,所以如果你保存新容器的状态,你有一个新的图片。 有几件事我很想知道。

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

2.)如何更新通过修改Dockerfile构build的图像? 我用这个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 

并用它来开始创build图像。 所以每次我创build一个盒子,它会加载所有这些environment variables ,如果我完全重build盒子,重新安装包和所有额外的东西。 我需要添加一个新的环境variables,所以我把它和一个testingvariables一起添加到了Dockerfile的底部:

 ENV COMPOSE_CONVERT_WINDOWS_PATHS 1 ENV TEST_ENV_VAR TEST 

当我删除容器和图像,并build立一个新的容器,似乎都相应地,它告诉我,它创build了新的步骤4:ENV

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

所以它的一些东西在这些中间的容器转换中会丢失。 这是caching系统如何工作,每一个新的层是一个intermediate container ? 所以考虑到这一点,如何添加一个新的图层,你是否总是需要在Dockerfile的底部添加新的数据? 或者,一旦图像被构build,将Dockerfile保留在一个单独的状态会更好,只需修改container并构build一个新的图像?

编辑我只是试图安装一个图像,一个名为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 

所以它绝对安装了一个包,并不是一个一个去。 这些作品,不同的图像?

图像与容器

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

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

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

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

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

将图层添加到基本图像

你可以从一个基本的图像(例如你的例子中的ubuntu )build立你自己的图像。 Dockerfile一些命令将在最终图像中创build一个新图层。 其中一些是RUNCOPYADD

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

每个图层都有一个唯一的ID(您已经看到很长的hex哈希值)。 他们也可以有人性化的名字,被称为标签 (例如ubuntu:16.04 )。

什么是图层与图像?

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

但是按照惯例,术语“图像”一般意味着具有标签关联的图层。 当你运行docker images ,它会显示所有的顶层图像,并隐藏下面的图层(但你可以用-a显示它们)。

什么是中间容器?

docker build运行时,它将在容器内完成所有工作(自然是!)所以,如果遇到RUN步骤,它将从当前顶层创build一个容器,在那里运行指定的命令,然后将结果保存为一个新的层。 然后它会从这个新层创build一个容器,运行下一个东西…等等

中间容器只用于构build过程,并在构build后丢弃。

层文件系统是如何工作的

你问是否有人下载你的基于ubuntu的镜像只是部分下载,如果他们已经在本地的ubuntu镜像。

是! 这是完全正确的。

每层使用下面的图层作为基础。 新层基本上是该层与新状态之间的差异。 虽然这不是git commit可能会起作用的差异。 它在文件级别工作,而不是在行级别。

假设你从ubuntu开始,你运行了这个Dockerfile。

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

这将导致一个两层图像。 第一层将是ubuntu图像。 第二个可能只有less数的变化。

  • /etc/passwd与用户“dan”的较新副本
  • /etc/group更新副本“dan”
  • 一个新的目录/home/dan
  • 几个像/home/dan/.bashrc这样的默认文件

就是这样。 如果你从这个映像启动一个容器,那么这些文件将会在最上层,而其他所有的东西都将来自ubuntu映像中的文件系统。

容器中最顶层的读写层

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

图像是不变的,所以一旦存在,就不能改变。 你可以build立一个新的版本,但这是一个新的形象。 它会有一个不同的ID,不会是相同的图像。

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

在哪里添加更改

你需要添加新的东西到Dockerfile的底部吗? 不,你可以在任何地方添加任何东西(或改变任何东西)。

但是,由于构buildcaching的工作方式,你如何做事情会影响构build时间。

Docker会在构build过程中尝试caching结果。 如果它发现通过Dockerfile读取FROM是相同的,第一个RUN是相同的,第二个RUN是相同的……它会认为它已经完成了这些步骤,并将使用caching的结果。 如果遇到与上次构build不同的内容,则会使caching无效。 一切从这一点上将重新运行。

有些东西总是使caching无效。 例如,如果您使用ADDCOPY ,则始终使caching无效。 这是因为Docker只跟踪构build命令的内容。 它不会试图找出“我正在复制与上次一样的文件的这个版本吗?”

因此,以FROM开头,然后放置非常静态的东西,比如使用apt-get等来安装软件包的RUN命令是一种常见的做法。在Dockerfile初始写入之后,这些事情往往不会发生很大的变化。 在文件的后面是一个更方便的地方,把经常变化的东西。

在这方面很难简洁地给出好的build议,因为这实际上取决于所讨论的项目。 但是学习构buildcaching如何工作并尝试利用它是值得的。