您的位置:首页 > 运维架构 > Docker

docker从零开始 存储(五)存储驱动介绍

2018-08-22 16:14 513 查看

关于存储驱动程序

要有效地使用存储驱动程序,了解Docker如何构建和存储镜像以及容器如何使用这些镜像非常重要。您可以使用此信息做出明智的选择,以确定从应用程序中保留数据的最佳方法,并避免在此过程中出现性能问题。

存储驱动程序允许您在容器的可写层中创建数据。容器停止后,文件将不会保留,并且读取和写入速度都很低。

了解如何使用volumes来保存数据并提高性能。

 

FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py

此Dockerfile包含四个命令,每个命令都创建一个层。该 

FROM
声明首先从
ubuntu:15.04
图像创建一个图层。该
COPY
命令从Docker客户端的当前目录中添加一些文件。该
RUN
命令使用该命令构建应用程序
make
。最后,最后一层指定在容器中运行的命令。

每个图层只是与之前图层的一组差异。这些层堆叠在彼此之上。创建新容器时,可以在基础图层的顶部添加新的可写层。该层通常称为“容器层”。对正在运行的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写入此可写容器层。下图显示了基于Ubuntu 15.04映像的容器。

图像和图层

Docker镜像是从一系列图层构建的。每个图层代表镜像的Dockerfile中的指令。除最后一层之外的每一层都是只读的。考虑以下Dockerfile:

 

一个存储驱动程序处理这些层相互交互的方式的细节。可以使用不同的存储驱动程序,它们在不同情况下具有优点和缺点。

容器和图层

容器和图像之间的主要区别在于顶部可写层。对添加新数据或修改现有数据的容器的所有写入都存储在此可写层中。删除容器时,也会删除可写层。基础图像保持不变。

由于每个容器都有自己的可写容器层,并且所有更改都存储在此容器层中,因此多个容器可以共享对同一基础映像的访问权限,但仍具有自己的数据状态。下图显示了共享相同Ubuntu 15.04映像的多个容器。

注意:如果需要多个镜像才能共享访问完全相同的数据,请将此数据存储在Docker卷中并将其装入容器中。

Docker使用存储驱动程序来管理图像层和可写容器层的内容。每个存储驱动程序以不同方式处理实现,但所有驱动程序都使用可堆叠图像层和写时复制(copy on write)策略。

 

磁盘上的容器大小

要查看正在运行的容器的大致大小,可以使用该

docker ps -s
 命令。两个不同的列与大小有关。

  • size
    :用于每个容器的可写层的数据量(在磁盘上)

  • virtual size
    :用于容器使用的只读镜像数据的数据量加上容器的可写层
    size
    。多个容器可以共享一些或所有只读镜像数据。从同一图像开始的两个容器共享100%的只读数据,而具有不同图像的两个具有共同层的容器共享这些公共层。因此,您不能只计算虚拟大小。这会过度估计总磁盘使用量可能是非常重要的数量

 

磁盘上所有正在运行的容器使用的总磁盘空间是每个容器

size
virtual size
值的某种组合。如果多个容器从相同的精确映像启动,则磁盘上这些容器的总大小将为(
size
容器)加上一个镜像大小(
virtual size
size
)之和。

这也不计算容器占用磁盘空间的以下其他方式:

  • 如果使用
    json-file
    日志记录驱动程序,则用于日志文件的磁盘空间。如果容器生成大量日志记录数据并且未配置日志轮换,则这可能非常重要。
  • 容器使用的卷和绑定装入。
  • 用于容器配置文件的磁盘空间,通常很小。
  • 写入磁盘的内存(如果启用了交换)。
  • 检查点,如果您正在使用实验检查点/恢复功能。

 

写时复制(copy on write)策略

写时复制是一种共享和复制文件的策略,可实现最高效率。如果文件或目录存在于图像中的较低层,而另一层(包括可写层)需要对其进行读访问,则它只使用现有文件。第一次另一个图层需要修改文件时(构建图像或运行容器时),文件将被复制到该图层并进行修改。这最小化了I / O和每个后续层的大小。下面将更深入地解释这些优点。

现在想象你有两个不同的Dockerfiles。您使用第一个创建一个名为的图像

acme/my-base-image:1.0

FROM ubuntu:16.10
COPY . /app
docker build -t acme/my-base-image:1.0 .

 

第二个是基于

acme/my-base-image:1.0
,但有一些额外的层:

FROM acme/my-base-image:1.0
CMD /app/hello.sh
docker build -t acme/my-base-image:2.0 .

 

第二个图像包含第一个图像中的所有图层,以及带有

CMD
指令的新图层和读写容器图层。Docker已经拥有了第一张镜像中的所有图层,因此不需要再次拉取它们。这两个镜像共享它们共有的任何层。

如果从两个Dockerfiles构建映像,您可以使用

docker image ls
和 
docker history
命令来验证共享层的加密ID相同。

 

请注意,除第二个图像的顶层外,所有图层都相同。所有其他图层在两个图像之间共享,并且仅存储一次

/var/lib/docker/
。新层实际上根本不占用任何空间,因为它不会更改任何文件,而只会运行命令。

注意:输出中的

<missing>
docker history
表示这些层是在另一个系统上构建的,并且在本地不可用。这可以忽略。

 

复制使容器变得高效

启动容器时,会在其他图层的顶部添加一个薄的可写容器图层。容器对文件系统所做的任何更改都存储在此处。容器未更改的任何文件都不会复制到此可写层。这意味着可写层尽可能小。

当修改容器中的现有文件时,存储驱动程序将执行写时复制操作。涉及的具体步骤取决于特定的存储驱动程序。对于默认的

aufs
驱动程序和
overlay
overlay2
 驱动程序时,写入时复制操作遵循以下顺序粗糙:

  • 在图像图层中搜索要更新的文件。该过程从最新层开始,一次一层地向下移动到基础层。找到结果后,会将它们添加到缓存中以加快将来的操作。

  • copy_up
    对找到的文件的第一个副本执行操作,以将文件复制到容器的可写层。

  • 对此文件副本进行任何修改,并且容器无法查看较低层中存在的文件的只读副本。

Btrfs,ZFS和其他驱动程序以不同方式处理写时复制。您可以在稍后的详细说明中阅读有关这些驱动程序的方法的更多信息。

写入大量数据的容器比不使用容器的容器消耗更多空间。这是因为大多数写入操作会占用容器的可写顶层中的新空间。

注意:对于大量写入的应用程序,不应将数据存储在容器中。而是使用Docker卷,它独立于正在运行的容器,并且设计为对I / O有效。此外,卷可以在容器之间共享,也不会增加容器可写层的大小。

一个

copy_up
操作可能导致性能显着的开销。根据正在使用的存储驱动程序,此开销会有所不同。大文件,大量图层和深层目录树可以使影响更加明显。每个
copy_up
操作仅在第一次修改给定文件时发生,这可以减轻这种情况。

为了验证写时复制的工作方式,以下过程根据

acme/my-final-image:1.0
我们之前构建的映像旋转了5个容器,并检查它们占用了多少空间。

注意:此过程不适用于Docker for Mac或Docker for Windows。

1.从Docker主机上的终端,运行以下

docker run
命令。末尾的字符串是每个容器的ID。 

[root@benjamincloud test2]# docker run -dit --name my_container_1 acme/my-base-image:2.0 bash && docker run -dit --name my_container_2 acme/my-base-image:2.0 bash && docker run -dit --name my_container_3 acme/my-base-image:2.0 bash && docker run -dit --name my_container_4 acme/my-base-image:2.0 bash && docker run -dit --name my_container_5 acme/my-base-image:2.0 bash
62efe3548a4a7c54d335ae477365e53e6cdb53b8d89ba5dc17b7036d2d199bb8
6059b0a7bb6483ea26659dfb75a9c6f227a333614044638a58e52d1ec84c6b1a
b9b0d3fa2d883ebd799cdd445700e1081de7f2b6ca0962d778112a5d6a739630
21ae9e1fc0995845fdfced133896be55607bb8b61cad9077e90c252b7b5af4d8
49c873927e5f91eced820d696c9ca9aa3b2d0b2422194a9be257f510c4dfaf49

2.运行该

docker ps
命令以验证5个容器正在运行。

[root@benjamincloud test2]# docker ps
CONTAINER ID        IMAGE                    COMMAND             CREATED             STATUS              PORTS               NAMES
49c873927e5f        acme/my-base-image:2.0   "bash"              42 seconds ago      Up 41 seconds                           my_container_5
21ae9e1fc099        acme/my-base-image:2.0   "bash"              42 seconds ago      Up 42 seconds                           my_container_4
b9b0d3fa2d88        acme/my-base-image:2.0   "bash"              43 seconds ago      Up 43 seconds                           my_container_3
6059b0a7bb64        acme/my-base-image:2.0   "bash"              43 seconds ago      Up 43 seconds                           my_container_2
62efe3548a4a        acme/my-base-image:2.0   "bash"              44 seconds ago      Up 43 seconds                           my_container_1

3.列出本地存储区域的内容。

[root@benjamincloud test2]# ll /var/lib/docker/containers/
total 20
drwx------ 4 root root 4096 Aug 23 15:03 21ae9e1fc0995845fdfced133896be55607bb8b61cad9077e90c252b7b5af4d8
drwx------ 4 root root 4096 Aug 23 15:03 49c873927e5f91eced820d696c9ca9aa3b2d0b2422194a9be257f510c4dfaf49
drwx------ 4 root root 4096 Aug 23 15:03 6059b0a7bb6483ea26659dfb75a9c6f227a333614044638a58e52d1ec84c6b1a
drwx------ 4 root root 4096 Aug 23 15:03 62efe3548a4a7c54d335ae477365e53e6cdb53b8d89ba5dc17b7036d2d199bb8
drwx------ 4 root root 4096 Aug 23 15:03 b9b0d3fa2d883ebd799cdd445700e1081de7f2b6ca0962d778112a5d6a739630

4.现在看看它们的大小:

[root@benjamincloud test2]# du -sh /var/lib/docker/containers/*
36K     /var/lib/docker/containers/21ae9e1fc0995845fdfced133896be55607bb8b61cad9077e90c252b7b5af4d8
36K     /var/lib/docker/containers/49c873927e5f91eced820d696c9ca9aa3b2d0b2422194a9be257f510c4dfaf49
36K     /var/lib/docker/containers/6059b0a7bb6483ea26659dfb75a9c6f227a333614044638a58e52d1ec84c6b1a
36K     /var/lib/docker/containers/62efe3548a4a7c54d335ae477365e53e6cdb53b8d89ba5dc17b7036d2d199bb8
36K     /var/lib/docker/containers/b9b0d3fa2d883ebd799cdd445700e1081de7f2b6ca0962d778112a5d6a739630

每个容器只占用文件系统上36k的空间。

写时复制不仅节省了空间,而且还减少了启动时间。当您启动容器(或来自同一图像的多个容器)时,Docker只需要创建可写的容器图层。

如果Docker每次启动新容器时都必须制作底层映像堆栈的完整副本,则容器启动时间和使用的磁盘空间将显着增加。这类似于虚拟机的工作方式,每个虚拟机有一个或多个虚拟磁盘。

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: