Docker – 如何更新图片
我读过Dockerfile
和layer一起工作,所以当用Dockerfile
创build一个container
的Dockerfile
,你先从基础镜像开始,然后后续的命令运行给容器添加一个图层,所以如果你保存新容器的状态,你有一个新的图片。 有几件事我很想知道。
如果我从一个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 build
的Dockerfile
。 图像不是一个运行的东西。
图像由图层组成。 一个图像可能只有一个图层,或者它可能有很多图层。
容器 :一个运行的东西。 它使用图像作为其起始模板。
这类似于一个二进制程序和一个进程。 你在磁盘上有一个二进制程序(比如/bin/sh
),当你运行它时,它是你系统上的一个进程。 这与图像和容器之间的关系相似。
将图层添加到基本图像
你可以从一个基本的图像(例如你的例子中的ubuntu
)build立你自己的图像。 Dockerfile
一些命令将在最终图像中创build一个新图层。 其中一些是RUN
, COPY
和ADD
。
第一层没有父层。 但是其他每一层都会有一个父层。 通过这种方式,它们相互链接,像煎饼一样堆叠起来。
每个图层都有一个唯一的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无效。 例如,如果您使用ADD
或COPY
,则始终使caching无效。 这是因为Docker只跟踪构build命令的内容。 它不会试图找出“我正在复制与上次一样的文件的这个版本吗?”
因此,以FROM
开头,然后放置非常静态的东西,比如使用apt-get
等来安装软件包的RUN
命令是一种常见的做法。在Dockerfile初始写入之后,这些事情往往不会发生很大的变化。 在文件的后面是一个更方便的地方,把经常变化的东西。
在这方面很难简洁地给出好的build议,因为这实际上取决于所讨论的项目。 但是学习构buildcaching如何工作并尝试利用它是值得的。