docker从零开始 存储(五)存储驱动介绍
关于存储驱动程序
要有效地使用存储驱动程序,了解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" seconds ago Up seconds my_container_5
21ae9e1fc099 acme/my-base-image:2.0 "bash" seconds ago Up seconds my_container_4
b9b0d3fa2d88 acme/my-base-image:2.0 "bash" seconds ago Up seconds my_container_3
6059b0a7bb64 acme/my-base-image:2.0 "bash" seconds ago Up seconds my_container_2
62efe3548a4a acme/my-base-image:2.0 "bash" seconds ago Up seconds my_container_1
3.列出本地存储区域的内容。
[root@benjamincloud test2]# ll /var/lib/docker/containers/
total
drwx------ root root Aug : 21ae9e1fc0995845fdfced133896be55607bb8b61cad9077e90c252b7b5af4d8
drwx------ root root Aug : 49c873927e5f91eced820d696c9ca9aa3b2d0b2422194a9be257f510c4dfaf49
drwx------ root root Aug : 6059b0a7bb6483ea26659dfb75a9c6f227a333614044638a58e52d1ec84c6b1a
drwx------ root root Aug : 62efe3548a4a7c54d335ae477365e53e6cdb53b8d89ba5dc17b7036d2d199bb8
drwx------ root root Aug : 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每次启动新容器时都必须制作底层映像堆栈的完整副本,则容器启动时间和使用的磁盘空间将显着增加。这类似于虚拟机的工作方式,每个虚拟机有一个或多个虚拟磁盘。
docker从零开始 存储(五)存储驱动介绍的更多相关文章
- docker从零开始(五)堆栈初体验,stacks
先决条件 安装Docker 1.13或更高版本. 获取Docker Compose,请参考第三节 按照第四节中的描述获取Docker Machine. 在第二节中了解如何创建容器. 确保您的图像作为已 ...
- docker从零开始 存储(六)存储驱动如何选择
Docker存储驱动程序 理想情况下,将非常少的数据写入容器的可写层,并使用Docker卷来写入数据.但是,某些工作负载要求您能够写入容器的可写层.这是存储驱动程序的用武之地. Docker使用可插拔 ...
- Docker镜像的目录存储讲解
我们成功安装完docker后,执行命令行sudo docker run hello-world, 如果是第一次执行,则会从远程拉取hello-world的镜像到本地,然后运行,显示hello worl ...
- centos7下安装docker(13.1docker存储--data volume)
我们现在知道docker 有两种存储方式:storage driver和data volume stroage driver这种存储方式主要是存储那些无状态的数据,是镜像层和容器层组成的,而data ...
- [MySQL数据库之表的详细操作:存储引擎、表介绍、表字段之数据类型]
[MySQL数据库之表的详细操作:存储引擎.表介绍.表字段之数据类型] 表的详细操作 存储引擎 mysql中建立的库======>文件夹 库中建立的表======>文件 用来存储数据的文件 ...
- docker容器的持久化存储:Volume
独立于docker容器的持久化存储: 法(1):自动将服务器文件夹挂载到容器内部文件夹/usr/share/nginx/html,这样只修改服务器文件夹下的内容即可对应修改容器内部文件夹的内容 将服务 ...
- 【转】HTML5 本地存储五种方案
1.LocalStorage LocalStorage就是Key-Value的简单键值对存储结构,Web Storage除了localStorage的持久性存储外,还 有针对本次回话的sessionS ...
- 1.Ceph 基础篇 - 存储基础及架构介绍
文章转载自:https://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247485232&idx=1&sn=ff0e93b9 ...
- Docker 入门 第五部分:Stacks
目录 Docker 入门 第五部分:Stacks 先决条件 介绍 添加一个新的服务并重新部署 保存数据 回顾 Docker 入门 第五部分:Stacks 先决条件 安装 Docker 1.13 或更高 ...
随机推荐
- NO2——最短路径
[Dijkstra算法] 复杂度O(n2) 权值必须非负 /* 求出点beg到所有点的最短路径 */ // 邻接矩阵形式 // n:图的顶点数 // cost[][]:邻接矩阵 // pre[i]记录 ...
- linux进程同步之信号量
首先了解一下,信号量机概念是由荷兰科学家Dijkstr引入,值得一提的是,它提出的Dijksrtr算法解决了最短路径问题. 信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是共享 ...
- 【python】Python3中出现'gbk' codec can't encode characte的成功解决方法?
亲身测试,所遇问题完全解决!2018/07/08 21:37 环境:windows,Pycharm,python3.6.2 使用Python写文件的时候,或者将网络数据流写入到本地文件的时候,大部分情 ...
- javaScript的流程控制语句学习笔记
JavaScript提供了5种流程控制语句,if条件判断语句,switch语句,for循环语句,while循环语句,do-while循环语句. 1.条件判读语句 对变量或表达式进行判定,并根据判定结果 ...
- Codeforces Round #531 (Div. 3) ABCDEF题解
Codeforces Round #531 (Div. 3) 题目总链接:https://codeforces.com/contest/1102 A. Integer Sequence Dividin ...
- 开发中常遇到的Python陷阱和注意点
最近使用Python的过程中遇到了一些坑,例如用datetime.datetime.now()这个可变对象作为函数的默认参数,模块循环依赖等等. 在此记录一下,方便以后查询和补充. 避免可变对象作为默 ...
- 浏览器 连不上网 (3):DNS 服务器问题
解决:设置一下DNS服务器的地址 步骤: 打开网络和共享中心(网络和 Internet设置)-> 更改适配器 -> 双击我们连接的 无线网(WiFi式) 或 以太网(网线式): 从出现的窗 ...
- npm获取配置值的两种方式
命令行标记 在命令行上放置--foo bar设置foo配置参数为bar. 一个 -- 参数(argument)告诉cli解析器停止读取flags.一个 在命令行结尾的--flag参数(paramete ...
- 代码管理工具 (含git、npm、gulp)
1 Git 分布式代码管理工具(基于Linux,可在本地进行提交)代码同时储存在本地和服务器中 ① Git基本操作命令 (1)初始化,创建初始化仓库 git init ------- 文件初始化,初始 ...
- 使用bcrypt进行加密的简单实现
Bcrypt百度百科: bcrypt,是一个跨平台的文件加密工具.由它加密的文件可在所有支持的操作系统和处理器上进行转移.它的口令必须是8至56个字符,并将在内部被转化为448位的密钥. 除了对您的数 ...