写在前面:html
因为红帽在Linux界的影响力,相信不少朋友在测试和生产系统用的是RedHat或者CentOS系统,此次我在CentOS系统上遇到了一个颇有意思的故障,经过此次故障的缘由分析及解决,特地写了这篇文章分享给你们。git
咱们在CentOS上部署了一套Docker系统,运行了一段时间后,忽然发现全部容器运行异常,同时宿主机内核报磁盘I/O错误:github
看到问题的第一反映是查看磁盘状态和空间使用状况,发现系统的根目录已经用完:docker
咱们知道,Docker默认的存储目录是在/var/lib/docker/下,同时咱们也知道,能够经过使用-g, --graph=”/var/lib/docker” 参数修改Docker 默认存放路径。知道了问题后,咱们能够经过挂载一个大硬盘到系统,并将Docker的目录更改成新挂载到硬盘上:服务器
我将Docker的存储目录设置到刚才新增长的/data目录下,可是原来的镜像和容器都找不到了,由于路径改了。原来的镜像是在/var/lib/docker/devicemapper/devicemapper/{data,metadata},转移文件后继续运行Docker服务,这样咱们就有了一个300G的大房子给Docker们用了。app
你们觉得事情到了这里就完结了么?其实我也想,可是我顺便折腾了一下,因而又发生了接下来的事情。说我手贱也好,瞎折腾也罢,导入一堆容器镜像和运行一堆容器后,系统又光荣告诉我全部的容器根目录所有变成了只读,宿主机内核一样报磁盘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的存储空间使用,以下图所示:spa
当有实际读写后,这些存储块将在存储池中被标记为已使用(或者从池中拿走)。当实际读写的块容量大于池的容量时,容器的运行空间不足,因此报I/O错误。
Device Mapper存储驱动很是方便,你不须要作任何安装部署即可以使用:如建立额外的分区来存储 Docker 容器,或者创建LVM。然而它也有两个缺点:
• 存储池会有一个默认 100GB 的容量,知足不了大存储的需求。
• 它将会被稀疏文件所支持(精简配置,一开始基本不占用空间,只有当实际须要写的时候才会使用磁盘的存储块)但性能较差。
针对这些问题,有两个解决方案:
1. 使用更大的文件/磁盘/逻辑卷建立data文件:
2. 经过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错误的缘由已经完美解决,但愿这篇文章能帮到在使用过程当中遇到相同问题的朋友。