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 或更高 ...
随机推荐
- 揭开网络编程常见API的面纱【上】
Linux网络编程API函数初步剖析 今天我们来分析一下前几篇博文中提到的网络编程中几个核心的API,探究一下当我们调用每个API时,内核中具体做了哪些准备和初始化工作. 1.socket(famil ...
- golang and intellij
有一个项目,混合了java和go,需要在intellij中安装go的插件. OK,网上的信息简直混乱不堪,两个流派,一个流派就是装插件,一个流派就是编译插件,各种折腾,还是安装不了,谁知柳暗花明又一村 ...
- TCP的挥手协议和握手协议2
三次握手协议:三次握手协议的主要过程是交互彼此之间的初始序列号,如果没有确认的ACK帧可以么?肯定是可以的 client A -------> server B client A 发送了自己的初 ...
- 【Python】Python—判断变量的基本类型
type() >>> type(123)==type(456) True >>> type(123)==int True >>> type('ab ...
- Ubuntu下Eclipse中运行Hadoop程序的参数问题
需要统一的参数: 当配置好eclipse中hadoop的程序后,几个参数需要统一一下: hadoop安装目录下/etc/core_site.xml中 fs.default.name的端口号一定要与ha ...
- [CF543D]Road Improvement
题目大意:给定一个无根树,给每条边黑白染色,求出每个点为根时,其他点到根的路径上至多有一条黑边的染色方案数,模$1e9+7$. 题解:树形$DP$不难想到,记$f_u$为以$1$为根时,以$u$为根的 ...
- 【bzoj2064】分裂【压状dp】
Description 背景: 和久必分,分久必和... 题目描述: 中国历史上上分分和和次数非常多..通读中国历史的WJMZBMR表示毫无压力. 同时经常搞OI的他把这个变成了一个数学模型. 假设中 ...
- 洛谷P4589 [TJOI2018]智力竞赛 【floyd + 二分 + KM】
题目链接 洛谷P4589 题意可能不清,就是给出一个带权有向图,选出\(n + 1\)条链,问能否全部点覆盖,如果不能,问不能覆盖的点权最小值最大是多少 题解 如果要问全部覆盖,就是经典的可重点的DA ...
- 【POJ 1201 Intervals】
Time Limit: 2000MSMeamory Limit: 65536K Total Submissions: 27949Accepted: 10764 Description You are ...
- [学习笔记]扩展LUCAS定理
可以先做这个题[SDOI2010]古代猪文 此算法和LUCAS定理没有半毛钱关系. [模板]扩展卢卡斯 不保证P是质数. $C_n^m=\frac{n!}{m!(n-m)!}$ 麻烦的是分母. 如果互 ...