我读到docker使用图层,因此在创建a container
时Dockerfile
,从基本图像开始,然后后续命令运行向容器添加图层,因此如果保存新容器的状态,则会有新图像.我想知道有几件事情.
如果我从Ubuntu
图像开始,这是一个非常庞大和笨重的图像,因为它是一个完整的操作系统,然后我添加了一些工具并将其保存为我上传到集线器的新图像.如果有人下载我的图像,并且他们已经保存了Ubuntu图像images folder
,这是否意味着他们可以跳过下载,Ubuntu
因为他们已经有了图像?如果是这样,当我修改原始图像的某些部分时,这是如何工作的,Docker是否使用其缓存数据Ubuntu image
在加载后有选择地将这些更改应用于它?
2.)如何通过修改Dockerfile来更新我构建的图像?我用这个设置了一个简单的django项目Dockerfile
:
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
所以它绝对安装了包装,而不是一次性安装.这些碎片,不同的图像?
首先,让我澄清一些术语.
image:一个静态的,不可变的对象.这是你docker build
使用a 运行时构建的东西Dockerfile
.图像不是运行的东西.
图像由图层组成.图像可能只有一个图层,或者可能有多个图层.
容器:一个运行的东西.它使用图像作为起始模板.
这类似于二进制程序和过程.您在磁盘上有一个二进制程序(例如/bin/sh
),当您运行它时,它是您系统上的一个进程.这类似于图像和容器之间的关系.
您可以从基本图像构建自己的图像(例如ubuntu
在您的示例中).您的一些命令Dockerfile
将在最终图像中创建一个新图层.他们有些是RUN
,COPY
和ADD
.
第一层没有父层.但是每个其他层都有一个父层.通过这种方式,它们相互连接,像煎饼一样堆叠起来.
每个图层都有一个唯一的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
图像.第二个可能只有少数变化.
/etc/passwd
用户"dan"的较新副本
/etc/group
包含"dan"组的较新副本
一个新目录 /home/dan
一些默认文件,如 /home/dan/.bashrc
就是这样.如果从此映像启动容器,那么这些文件将位于最顶层,其他所有文件都将来自ubuntu
映像中的文件系统.
另一点.运行容器时,可以在文件系统中写入文件.但是,如果您停止容器并从同一图像运行另一个容器,则会重置所有内容.那么文件在哪里写的?
图像是不可变的,因此一旦存在,就无法更改.您可以构建新版本,但这是一个新图像.它将具有不同的ID,并且不会是相同的图像.
容器具有顶层读写层,该层位于图像层之上.任何写入都发生在该层中.它就像其他层一样工作.如果您需要修改文件(或添加一个或删除一个文件),则在顶层完成,不会影响较低层.如果文件已存在,则将其复制到读写层,然后进行修改.这称为写时复制(CoW).
你必须在Dockerfile的底部添加新东西吗?不,你可以在任何地方添加任何东西(或改变任何东西).
但是,由于构建缓存的工作原理,您的工作方式确实会影响构建时间.
Docker将尝试在构建期间缓存结果.如果它通过Dockerfile读取它FROM
是相同的,第一个RUN
是相同的,第二个RUN
是相同的......它将假设它已经完成了这些步骤,并将使用缓存的结果.如果遇到与上一次构建不同的内容,则会使缓存无效.从那时起,一切都将重新焕发新生.
有些事情总是会使缓存失效.例如,如果您使用ADD
或COPY
,那些总是使缓存无效.那是因为Docker只跟踪构建命令是什么.它没有试图弄清楚"我正在复制与上次相同的文件的这个版本吗?"
所以这是一个常见的做法FROM
,然后放置非常静态的东西,比如RUN
用例如apt-get
等安装软件包的命令.在最初编写Dockerfile之后,这些东西往往不会发生很大的变化.稍后在文件中是一个更方便的地方,可以更频繁地更改内容.
很难简明扼要地提出这方面的建议,因为这实际上取决于所讨论的项目.但是了解构建缓存如何工作并试图利用它是值得的.