2-2-7-devicemapper相关生产故障
由于红帽在Linux界的影响力 , 相信很多朋友在测试和生产系统用的是RedHat或者CentOS系统 , 这次我在CentOS系统上遇到了一个很有意思的故障 , 通过这次故障的原因分析及解决 , 特意写了这篇文章分享给大家。
我们在CentOS上部署了一套Docker系统 , 运行了一段时间后 , 突然发现所有容器运行异常 , 同时宿主机内核报磁盘I/O错误 :

看到问题的第一反映是查看磁盘状态和空间使用情况 , 发现系统的根目录已经用完 :

我们知道 , Docker默认的存储目录是在/var/lib/docker/下 , 同时我们也知道 , 可以通过使用-g, --graph="/var/lib/docker" 参数修改Docker 默认存放路径。知道了问题后 , 我们可以通过挂载一个大硬盘到系统 , 并将Docker的目录更改为新挂载到硬盘上 :

我将Docker的存储目录设置到刚才新增加的/data目录下 , 但是原来的镜像和容器都找不到了 , 因为路径改了。原来的镜像是在/var/lib/docker/devicemapper/devicemapper/{data , metadata}, 转移文件后继续运行Docker服务 , 这样我们就有了一个300G的大房子给Docker们用了。
大家以为事情到了这里就完结了么?其实我也想 , 但是我顺便折腾了一下 , 于是又发生了接下来的事情。说我手贱也好 , 瞎折腾也罢 , 导入一堆容器镜像和运行一堆容器后 , 系统又光荣告诉我所有的容器根目录全部变成了只读 , 宿主机内核同样报磁盘I/O错误 , 一开始我以为data目录又被写满了 , 但是用df –Th命令查看后 , 发现目录还有很多空间 :

但是残酷的现实是 , 只用了不到一半的空间后 , 所有的容器就全部出现异常了 , 这是我祭出了经典三板斧 : 重启容器 , 重启Docker服务 , 重启服务器。然并卵 , 容器还是运行异常。通过在网上爬了一堆资料 , 在http://jpetazzo.github.io/2014/01/29/docker-device-mapper-resize/上查到 , CentOS默认用的是Device Mapper作为容器的存储驱动的 , 大家可以用dockers info命令查看 , Docker服务启动时默认会在/var/lib/docker/devicemapper/devicemapper/目录创建一个100G ( 由于1000和1024换算的关系 , 系统实际显示的是107.4G , 其他数字亦同 ) 的data文件 , 然后启动的容器的所有变更的数据全部保存到这个data文件中;也就是说当容器内产生的相关data数据超过100G后容器就再也没有多余的空间可用 , 从而导致所有容器的根目录变为只读!同时它会限制每个容器最大为 10GB 。太坑爹了有木有 , 给了大房子只能用100G!

为了找到根本原因 , 我们需要了解Device Mapper存储驱动的原理 : Device Mapper存储驱动是以精简配置的方式运行的 , 它实际上是目标块设备的快照。
Docker启动时会设置一个100G的sparse文件 ( /var/lib/docker/devicemapper/devicemapper/data , 元数据为/var/lib/docker/devicemapper/devicemapper/metadata ) , 并将其作为Device Mapper的存储池 , 而所有容器都从该存储池中分配默认10G的存储空间使用 , 如下图所示 :

当有实际读写后 , 这些存储块将在存储池中被标记为已使用 ( 或者从池中拿走 ) 。当实际读写的块容量大于池的容量时 , 容器的运行空间不足 , 所以报I/O错误。
Device Mapper存储驱动非常方便 , 你不需要做任何安装部署便可以使用 : 如创建额外的分区来存储Docker容器 , 或者建立LVM。然而它也有两个缺点 :
存储池会有一个默认
100GB的容量 , 满足不了大存储的需求。它将会被稀疏文件所支持 ( 精简配置 , 一开始基本不占用空间 , 只有当实际需要写的时候才会使用磁盘的存储块 ) 但性能较差。
针对这些问题 , 有两个解决方案:
使用更大的文件/磁盘/逻辑卷创建
data文件
通过
Docker启动参数的--storage-opt选项来限制每个容器初始化的磁盘大小 , 如-storage-opt dm.basesize=80G这样每个容器启动后 , 根目录的总空间就是80G。
但是我总觉得这样的解决方式不够优雅 , 需要多步操作才能满足需求 , 同时 , 容器的空间还是被限制的 , 只是限制的大小变化而已。那有没有更好的办法呢? 让我们继续来爬资料 , 在Docker的官方网站上 : https://docs.docker.com/engine/reference/commandline/dockerd/

Docker在存储驱动方面支持 AUFS、Device Mapper、Btrfs、ZFS、 Overlay 、Overlay2等多址方式 , 现由于AUFS并未并入内核 , 目前只有Ubuntu系统上能够使用aufs作为docker的存储引擎 , 而在CentOS系统上默认使用Device Mapper ,但是幸运的是 , 在Linux内核3.18.0以上的版本 , 是可以原生支持Overlay驱动方式的 , Overlayfs跟AUFS很像 , 但是性能比AUFS好 , 有更好的内存利用。
Docker通过-s参数选择存储驱动 , 通过-s=overlay , 我们将存储驱动器设置为Overlay方式 , 再重启Docker应用。

大家可以看到 , 现在Docker已经是使用了OverlayFS(这里大家要注意 , 如果系统有存储的镜像和运行的容器 , 更改存储驱动后将都不可用 , 请先行备份)。
通过修改为OverlayFS , Device Mapper的存储池容量限制及单个容器运行最大空间限制统统没有了 , 同时Overlay的读写性能也好于Device Mapper , 只需通过-s=overlay一个参数即可优雅的使用更好的文件系统来运行容器。
至此 , 容器运行时I/O错误的原因已经完美解决 , 希望这篇文章能帮到在使用过程中遇到相同问题的朋友。
转载于: CentOS系统故障 | 一桩"血案"引发的容器存储驱动比较 - 有容云 - 博客园 (cnblogs.com)