Device Mapper是一个基于kernel的框架,它增强了很多Linux上的高级卷管理技术。Docker的devicemapper驱动在镜像和容器管理上,利用了该框架的超配和快照功能。为了区别,本文使用Device Mapper指驱动中的框架,而devicemapper指Docker的存储驱动。

  注意:商业支持的Docker Engine(CS-Engine)建议在RHEL和CentOS上使用devicemapper存储驱动。

AUFS之外的另一种选择

  Docker最初运行在Ubuntu和Devian上,并且使用AUFS作为存储后端。当Docker变得流行后,很多想使用它的公司正在使用RHEL。不幸的是,因为Linux主线kernel并不包含AUFS,所以RHEL并没有支持AUFS。

  为了改变这种情况,Red Hat开发者研究将AUFS包含进kernel主线。最后,他们认为开发一种新的存储后端是更好的主意。此外,他们打算使用已经存在的Device Mapper技术作为新存储后端的基础。

  Red Hat与Docker公司合作贡献这个新驱动。因为这次合作,Docker Engine被重新设计为存储驱动插件化。因此devicemapper成为Docker支持的第二个存储驱动。

  Device Mapper在2.6.9之后就被何如Linux kernel主线,也是RHEL家族发布包的核心部分。这意味着devicemapper存储驱动基于稳定的代码,有着大量的工作产品和极强的社区支持。

镜像分层和共享

  devicemapper驱动把每个镜像和容器都存储在它自己的虚拟设备中,这些设备是超配的copy-on-write快照设备。Device Mapper技术工作在块级别而不是文件级别,也就是说devicemapper存储驱动的超配和copy-on-write操作直接操作块,而不是整个文件。

  注意:快照也是使用的thin设备或者虚拟设备。

  使用devicemapper时,Docker如下创建镜像:

    ▶devicemapper存储驱动创建一个thin池。

    这个池是在块设备或者loop mounted sparse file上创建的。

    ▶然后创建一个基础设备。

    该基础设备是一个带文件系统的thin设备。可以通过docker info命令中的Backing filesystem值,来查看后端使用的是哪种文件系统。

    ▶每个新的镜像(和镜像层)都是该基础设备的快照。

    这些都是超配的copy-on-write快照。也就是说它们初始化的时候是空的,只有在有数据写向它们时,才会从池中消耗空间。

  使用devicemapper,容器层是基于镜像的快照。和镜像一样,容器快照也是超配的copy-on-write快照。容器快照保存了容器的所有更新,当向容器写数据时,devicemapper按需从池中分配空间给容器层。

  下图展示了一个thin池、基础设备和两个镜像。

  

  如果你仔细观察图片,你会发现每个镜像层都是它下层的快照,镜像的最底层是池中基础设备的快照。这个基础设备是Device Mapper产生的,并不是一个Docker镜像层。

  而容器又是镜像的一个快照,下图展示了两个容器在整个存储驱动的层次。

  

devicemapper的读操作

  下图显示了在一个容器中读取一个块(地址是0x44f)的过程。

  

    ▶应用在容器中请求访问块0x44f。

    因为容器是镜像的一个thin快照,它没有实际的数据。但它有指针,指向镜像栈中数据所在的镜像快照。

    ▶存储驱动根据指针找到对应镜像层a005的快照块0xf33。

    ▶devicemapper拷贝镜像快照中块0xf33的数据到容器内存中。

    ▶容器驱动将数据返回给请求应用。

写操作

   使用devicemapper驱动,写数据到容器是通过一个按需分配操作来完成的。更新已有的数据使用了copy-on-write操作。不过Device Mapper是基于块存储的技术,所以这些操作都发生在块的级别。

   例如,要对容器中的一个大文件作小的修改,devicemapper驱动不拷贝整个文件,它只拷贝要修改的内容对应的块,每个块大小为64KB。

写数据

  向容器写55KB的数据:

    ▶应用向容器发起写56KB数据的请求;

    ▶按需分配操作给容器快照分配了一个新的64KB的块。

    如果写的数据大于64KB,就需要分配多个块给容器快照。

    ▶数据写向新分配的块。

覆写已有数据

  第一次修改已有的数据:

    ▶应用向容器发起修改数据的请求;

    ▶copy-on-write操作定位到需要更新的块;

    ▶分配新的块给容器快照,并拷贝数据到这些块中;

    ▶修改的数据写向新分配的这些块。

  容器中的应用并不会感知到这些按需分配和copy-on-write操作。然而,这些操作还是可能会对应用的读写带来一些延迟。

Docker中配置devicemapper

  devicemapper是部分Linux发行版的默认Docker存储驱动,这其中就包括RHEL和它的分支。当前,以前发行版支持该驱动:

   ●RHEL/CentOS/Fedora

   ●Ubuntu

   ●Debian

   ●Arch Linux

  Docker host使用loop-lvm的配置模式来运行devicemapper。该模式使用系数文件来创建thin池,这些池用于镜像和容器快照。并且这些模式是开箱即用的,无需额外配置。不过,不建议在产品部署中使用loop-lvm模式。

  可以通过docker info命令来查看是否使用了该模式。

  1. $ sudo docker info
  2. Containers: 0
  3. Images: 0
  4. Storage Driver: devicemapper
  5. Pool Name: docker-202:2-25220302-pool
  6. Pool Blocksize: 65.54 kB
  7. Backing Filesystem: xfs
  8. [...]
  9. Data loop file: /var/lib/docker/devicemapper/devicemapper/data
  10. Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata
  11. Library Version: 1.02.93-RHEL7 (2015-01-28)
  12. [...]

  通过上面输出可得出,Docker host使用devicemapper存储驱动。并且,还使用了loop-lvm模式,因为/var/lib/docker/devicemapper/devicemapper下有Data loop file和Metadata loop file这两个文件。这些都是loopback映射的稀疏文件。

在产品中配置direct-lvm模式

  产品部署中应该使用direct-lvm模式,该模式使用块设备来创建thin池。下面的步骤描述了如何在Docker host使用direct-lvm模式得devicemapper存储驱动。

  注意:如果你已经在Docker host上运行了Docker daemon,并且有一些想保存的镜像,那么在执行以下步骤之前,把它们push到Docker Hub,或者你的私有Docker Registry。

  以下步骤会创建一个逻辑卷,配置为thin池,用作后端的存储池。假设你有一个稀疏块设备/dev/xvdf,并且该设备有足够的空间来完成这个任务。在你的环境中,设备标识符和卷的大小可能不同,在你执行下面过程时,你应该替换为适合你环境的值。另外,以下的步骤应该在Docker Daemon停止的时候来执行。

   1) 进入Docker host,并停止Docker daemon;

   2) 安装LVM2和thin-provisioning-tools安装包;

   LVM2安装包提供了用户空间的工具,用于管理逻辑卷。

   thin-provisioning-tools用于激活和管理池。

  1. # on Ubuntu
  2. $ sudo apt -y install lvm2
  3. # On CentOS
  4. $ sudo yum install -y lvm2

   3) 创建一个物理卷/dev/xvdf来替换块设备;

  1. $ pvcreate /dev/xvdf

   4) 创建卷组docker;

  1. $ vgcreate docker /dev/xvdf

   5) 创建名为thinpool和thinpoolmeta的虚拟卷;

   在该示例中,数据大小是docker卷组大小的95%,留下这些空闲空间,是用于数据或元数据的自动扩容。

  1. $ lvcreate --wipesignatures y -n thinpool docker -l 95%VG
  2. $ lvcreate --wipesignatures y -n thinpoolmeta docker -l 1%VG

   6) 把池转换为thin池;

  1. $ lvconvert -y --zero n -c 512K --thinpool docker/thinpool --poolmetadata docker/thinpoolmeta

   7) 通过lvm文件来配置thin池的自动扩容;

  1. $ vi /etc/lvm/profile/docker-thinpool.profile

   8) 指定thin_pool_autoextend_threshold值;

   该值是lvm尝试扩容到可用空间时,当前已空间使用量的百分比(100=禁止)。

  1. thin_pool_autoextend_threshold = 80

   9) 修改thin_pool_autoextend_percent;

   该值是thin池要扩容的百分比(100=禁止)。

  1. thin_pool_autoextend_percent = 20

   10) 检查上面的步骤,你的docker-thinpool.profile文件应该是类似下面的内容:

   一个/etc/lvm/profile/docker-thinpool.profile示例文件:

  1. activation {
  2. thin_pool_autoextend_threshold=80
  3. thin_pool_autoextend_percent=20
  4. }

   11) 应用新的lvm profile文件;

  1. $ lvchange --metadataprofile docker-thinpool docker/thinpool

   12) 确认lv是否被修改;

  1. $ lvs -o+seg_monitor

   13) 如果之前Docker daemon被启动过,那么需要将之前的graph driver目录给挪走;

   移动graph driver会删除所有的镜像容器和卷。下面的命令将/var/lib/docker的内容移动到另一个目录中。

  1. $ mkdir /var/lib/docker.bk
  2. $ mv /var/lib/docker/* /var/lib/docker.bk

   14) 使用特殊的devicemapper选项来配置Docker daemon;

   有两种方法来配置Docker daemon的devicemapper存储驱动。你可以运行daemon时加上以下参数:

  1. --storage-driver=devicemapper \
  2. --storage-opt=dm.thinpooldev=/dev/mapper/docker-thinpool \
  3. --storage-opt=dm.use_deferred_removal=true \
  4. --storage-opt=dm.use_deferred_deletion=true

   也可以在daemon配置文件中配置,如默认的配置文件/etc/docker/daemon.json中,可如下配置:

  1. {
  2. "storage-driver": "devicemapper",
  3. "storage-opts": [
  4. "dm.thinpooldev=/dev/mapper/docker-thinpool",
  5. "dm.use_deferred_removal=true",
  6. "dm.use_deferred_deletion=true"
  7. ]
  8. }

   注意:总是使用dm.use_deferred_removal=true和dm.use_deferred_deletion=true选项,以防止无意地泄露映射资源信息。

   15) (可选的)如果使用了systemd,并且修改了daemon配置文件,需要重载systemd信息;

  1. $ systemctl daemon-reload

   16) 重启Docker daemon。

  1. $ systemctl start docker

  当你启动Docker daemon后,确保一直监控者thin池和卷组的可用空间。当卷组自动扩容时,可能会占满所有空间。可以使用lvs或lvs -a命令来监控逻辑卷,可以查看到数据和元数据的大小。另外,还可以使用vgs命令来监控卷组的可用空间。

  当到达阈值后,日志会显示thin池的自动扩容信息,可使用以下命令来查看日志:

  1. $ journalctl -fu dm-event.service

  当你确认你的配置文件无误时,就可以删除之前的备份目录了。

  1. $ rm -rf /var/lib/docker.bk

  你还可以使用dm.min_free_space参数。该值保证党可用空间到达或者接近最小值时,操作失败会有提示。可以查看更多的驱动选项

检查devicemapper结构体

  可以通过lsblk命令查看devicemapper存储驱动创建的pool相关的设备文件。

  1. $ sudo lsblk
  2. NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
  3. xvda 202:0 0 8G 0 disk
  4. └─xvda1 202:1 0 8G 0 part /
  5. xvdf 202:80 0 10G 0 disk
  6. ├─vg--docker-data 253:0 0 90G 0 lvm
  7. └─docker-202:1-1032-pool 253:2 0 10G 0 dm
  8. └─vg--docker-metadata 253:1 0 4G 0 lvm
  9. └─docker-202:1-1032-pool 253:2 0 10G 0 dm

  下图显示了上面示例的镜像信息。

  

  图中,pool的名称叫做Docker-202:1-1032-pool,横跨data和metadata两个设备。devicemapper的pool名称格式为:

  1. Docker-MAJ:MIN-INO-pool

  MAJ,MIN和INFO分别表示major、minor设备号,和inode号。

  有两个主要的目录。/var/lib/docker/devicemapper/mnt目录包含镜像和容器层的映射点。/var/lib/docker/devicemapper/metadata目录为每个镜像层和容器快照都对应了一个文件,文件以JSON格式保存每个快照的元数据。

Device Mapper和Docker性能

  理解按需分配和copy-on-write可以让你对容器性能有个整体的了解。

按需分配性能影响

  devicemapper存储驱动在按需分配操作时会给容器分配一个新的块。也就是说每次应用向容器某个位置写数据时,一个或多个块就会从池里面分配,并且映射给容器。

  所有的块都是64KB大小,一个小于64KB的写请求也会导致分配64KB的块,大于64KB的写请求就要求多个64KB的块。这会影响容器的性能,尤其容器有很多小的写请求时。然而,一旦一个块分配给容器后,后续的读写都会直接操作这个新分配的块。

Copy-on-write性能影响

  每次容器第一次更新已有数据时,devicemapper存储驱动都会执行copy-on-write操作,该操作会将数据从镜像快照拷贝到容器快照。这在容器性能上有着明显的影响。

  所有的copy-on-write操作都有64KB的粒度。因此,要修改1GB文件的32KB内容时,只需要拷贝64KB大小的块到容器即可。如果要拷贝整个大文件到容器层时,该特性较于文件层的copy-on-write操作有着明显的性能优势。

  然而,实际中,如果容器执行大量的小的写请求时(<64KB),devicemapper性能差于AUFS。

其他device mapper性能注意事项

  还有一些点会影响devicemapper存储驱动的性能。

   ▶模式。Docker运行devicemapper存储驱动的默认模式是loop-lvm。该模式使用稀疏文件,并且性能堪忧。在产品中不建议使用该模式,产品环境中建议使用direct-lvm,这样存储驱动就可以直接写向块设备。

   ▶高速存储。为了更好的性能,可以将Data文件和Metadata文件放在高速存储(如SSD)上。当然,也可以直接连接到SAN或NAS array上。

   ▶内存使用。devicemapper不是内存最高效的Docker存储驱动。启动同一个容器的n份拷贝需要将其文件大小的n份拷贝加载到内存中,这对于Docker host的内存有一定影响。因此,devicemapper存储驱动可能不是Pass或其他高密度用例的最优方案。

  最后一点,数据卷提供了更好和可预测的性能。因此,应该将负载高的写请求写到数据卷中。

Docker存储驱动之Device Mapper简介的更多相关文章

  1. Docker存储驱动Device Mapper,Overlay,AUFS

    Docker存储驱动之Device Mapper简介 - BookShu - 博客园https://www.cnblogs.com/styshoo/p/6528762.html Docker存储驱动之 ...

  2. Docker存储驱动之OverlayFS简介

    简介 OverlayFS是一种和AUFS很类似的文件系统,与AUFS相比,OverlayFS有以下特性: 1) 更简单地设计: 2) 从3.18开始,就进入了Linux内核主线: 3) 可能更快一些. ...

  3. Docker存储驱动之总览

    简介 本文会介绍Docker存储驱动的特性,别列出现在已经支持的存储驱动,最后,会介绍如果选型适合你的存储驱动. 可插拔的存储驱动架构 Docker的存储驱动架构是可插拔的,可以让你很方便的将适合你环 ...

  4. 聊一聊docker存储驱动

    目录 镜像的分层特性 容器读写层的工作原理 写时复制 用时配置 Docker存储驱动 AUFS OverlayFS Devicemapper 常用存储驱动对比 AUFS VS OverlayFS Ov ...

  5. DOCKER学习_006:Docker存储驱动

    一 镜像的分层特性 在说docker的文件系统之前,我们需要先想清楚一个问题.我们知道docker的启动是依赖于image,docker在启动之前,需要先拉取image,然后启动.多个容器可以使用同一 ...

  6. docker 存储驱动(storage driver)知识总结

    http://www.sohu.com/a/101016494_116235 一,先看docker镜像是如何构建和存储. 下面是ubuntu:15.04的镜像分层.一共是4层,每一层都由一些只读并且描 ...

  7. 有容云-【原理】Docker存储驱动之AUFS

    编者按:今天聊一聊Docker的Image(镜像)与Container(容器)的存储以及存储驱动之AUFS.   Docker存储驱动简介 Docker内置多种存储驱动,每种存储驱动都是基于Linux ...

  8. docker存储驱动

    http://www.sohu.com/a/101016494_116235 https://success.docker.com/article/compatibility-matrix Red H ...

  9. Docker存储驱动之AUFS简介

    简介 AUFS是曾是Docker默认的首选存储驱动.它非常稳定.有很多真实场景的部署.很强的社区支持.它有以下主要优点: 极短的容器启动时间. 有效的存储利用率. 有效的内存利用率. 虽然如此,但由于 ...

随机推荐

  1. 验证浏览器是否安装已flash插件的js脚本

    function flashChecker() { var hasFlash = 0; //是否安装了flash var flashVersion = 0; //flash版本 if(document ...

  2. 分析NGINX 健康检查和负载均衡机制

    nginx 是优秀的反向代理服务器,这里主要讲它的健康检查和负载均衡机制,以及这种机制带来的问题.所谓健康检查,就是当后端出现问题(具体什么叫出现问题,依赖于具体实现,各个实现定义不一样),不再往这个 ...

  3. JavaScript Window.document对象

    一.找到元素: docunment.getElementById("id"):根据id找,最多找一个:    var a =docunment.getElementById(&qu ...

  4. ArcGIS三种方式打断相交线------拓扑法

    拓扑法:有多个layer图层相交线,选用拓扑法,将多个图层相交线打断. 新建拓扑结构: (1)单击新建"Nfg.gdb"数据库文件: (2)单击新建"XX"集合 ...

  5. .net 网站应对压力的一些方案总结

    开年比较空,抽时间写个博文,总结下自己工作里的一些应对网站访问压力的技术方案. 自己项目现在大概一天50W的pv.已从前端到后端的顺序总结下自己用的一些方案. 一. 前端页面: 1.首先减少资源的大小 ...

  6. 自学javaee程序员之路--ssm的小项目(一)

    大家好~我叫王聪,缩写是WC(不是厕所!不是厕所!).是一名某内陆大四的学生.这两个月自学了javaee---关于web的一些心得,分享记录一下.建立这个博客的目的是望各位前辈学长指正批评~~也是建立 ...

  7. Android实现先横向横线展现在纵向拉开图片

    前段时间产品那边让我做一个动画,要求是先以横线的方式横向展开,在纵向展示图片,最后展示几秒动画在原路返回,随后我在网上查找资料,发现这方面的记录很少,最后自己写了一个 后期还会慢慢改进: 转载请说明出 ...

  8. 使用express.js框架一步步实现基本应用以及构建可扩展的web应用

    最近过年在家有点懈怠,但是自己也不断在学习新的前端技术,在家琢磨了express.js的web框架. 框架的作用就是提高开发效率,快速产出结果.即使不使用框架,我们也会在开发过程中逐渐形成构成框架. ...

  9. C#剪贴板对文件的复制、粘贴操作

    1.把文件加到剪贴板: System.Collections.Specialied.StringCollection files=new System.Collections.Specialied.S ...

  10. Exception in thread "main" org.hibernate.HibernateException: save is not valid without active transaction

    在spring4+hibernate4整合过程中,使用@Transactional注解事务会报"Exception in thread "main" org.hibern ...