详解docker中容器devicemapper设备的挂载流程
事故起因
版本说明:本文中docker版本主要基于1.10版本,操作系统为centos7。devicemapper在文中缩写为dm。
某个用户的容器启动不起来,启动时候一直报错。通过docker log查看日志,可以看到报错信息如下
Timestamp: 2019-04-01 16:19:26.33690413 +0800 CST
Code: System error
Message: can't create pivot_root dir , error mkdir /export/docker/devicemapper/mnt/7a35b08cc8815ef439dfcc74fd375e2fbb618208674d6eff1ea5d0b61cc7ef1c/rootfs/.pivot_root492082001: no space left on device
对照docker源码,可以看到报错位置,就是在容器启动时候,docker会在容器中创建一个临时文件夹。而由于空间不足,因此导致报错,容器无法启动。
pivotDir, err := ioutil.TempDir(tmpDir, ".pivot_root")
if err != nil {
return fmt.Errorf("can't create pivot_root dir %s, error %v", pivotDir, err)
}
初步可以判断是由于用户将dm的根目录磁盘写满了导致。那么要解决这个问题,其实思路也就很简单,就是将容器的dm设备手动挂载起来,然后清理冗余文件后,再恢复。那么在docker中,是如何将容器的dm盘进行创建挂载的,笔者对此进行了一下梳理。
docker中容器的dm盘挂载原理
获取dm设备信息
这里我们假设要模拟的是f34cee76e36a
容器的挂载。
首先进入到docker的存储目录,可以从image的层文件夹中根据容器的id,获取mount-id。
而后从devicemapper/metadata/
文件夹中,根据mount-id,获取device_id。这里,该容器的device_id为34709。
[root@localhost docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f34cee76e36a xuxinkun/kubesql "kubesql-server" 3 weeks ago Exited (137) 3 hours ago kubesql-server
[root@localhost docker]# cat image/devicemapper/layerdb/mounts/f34cee76e36af13dbda34f78406ebe1c9a426d16e4eaae02055ace6b557b7539/mount-id
dad0ce9e1adcfe76df319443f429698eafa52dd6c6cb4fb8192f0846656f8824
[root@localhost docker]# cat devicemapper/metadata/dad0ce9e1adcfe76df319443f429698eafa52dd6c6cb4fb8192f0846656f8824
{"device_id":34709,"size":10737418240,"transaction_id":844170,"initialized":false,"deleted":false}
先看一下已经创建的dm设备列表和table。
[root@localhost docker]# dmsetup ls
cl-swap (253:1)
cl-root (253:0)
docker-253:2-3693860-e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547 (253:4)
cl-home (253:2)
docker-253:2-3693860-pool (253:3)
[root@localhost docker]# dmsetup table
cl-swap: 0 32899072 linear 8:2 2048
cl-root: 0 104857600 linear 8:2 1647560704
docker-253:2-3693860-e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547: 0 20971520 thin 253:3 34719
cl-home: 0 1614659584 linear 8:2 32901120
docker-253:2-3693860-pool: 0 167772160 thin-pool 7:1 7:0 128 32768 1 skip_block_zeroing
这其中主要要关注的是docker-253:2-3693860-pool
,也就是253:3
这个设备。后面的所有的docker容器的dm设备都是从这个pool中分出去的。
从table中可以看到一个已经创建好的,它的table是0 20971520 thin 253:3 34719
。table的格式如下
logical_start_sector num_sectors target_type target_args
开始扇区 扇区数 设备类型 设备参数
20971520的扇区数应与docker启动时为容器分配的容量相关。
其中,设备参数的34719即为容器对应的device_id。
创建dm设备
那么现在模拟容器挂载,首先创建一个dm设备。
[root@localhost docker]# dmsetup create docker-253:2-3693860-dad0ce9e1adcfe76df319443f429698eafa52dd6c6cb4fb8192f0846656f8824 --table "0 20971520 thin 253:3 34709"
然后可以查看下dm设备。
[root@localhost docker]# dmsetup ls
cl-swap (253:1)
cl-root (253:0)
docker-253:2-3693860-dad0ce9e1adcfe76df319443f429698eafa52dd6c6cb4fb8192f0846656f8824 (253:5)
docker-253:2-3693860-e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547 (253:4)
cl-home (253:2)
[root@localhost tmp]# ls /dev/dm-*
/dev/dm-0 /dev/dm-1 /dev/dm-2 /dev/dm-3 /dev/dm-4 /dev/dm-5
可以看到,新增了一个dm-5的设备。
当然,也可以在/dev/mapper下查看
挂载dm设备
现在将/dev/dm-5设备挂载到自己创建了/tmp/test目录下。
[root@localhost docker]# mount /dev/dm-5 /tmp/test
[root@localhost docker]# ll /tmp/test/
total 4
-rw------- 1 root root 64 Mar 11 15:40 id
drwxr-xr-x 16 root root 274 Apr 2 09:54 rootfs
[root@localhost docker]# ll /tmp/test/rootfs/
total 16
-rw-r--r-- 1 root root 12076 Dec 5 09:37 anaconda-post.log
lrwxrwxrwx 1 root root 7 Dec 5 09:36 bin -> usr/bin
drwxr-xr-x 4 root root 43 Mar 11 17:50 dev
drwxr-xr-x 48 root root 4096 Mar 11 17:50 etc
drwxr-xr-x 3 root root 21 Mar 11 17:38 home
lrwxrwxrwx 1 root root 7 Dec 5 09:36 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Dec 5 09:36 lib64 -> usr/lib64
drwxr-xr-x 2 root root 6 Apr 11 2018 media
drwxr-xr-x 2 root root 6 Apr 11 2018 mnt
drwxr-xr-x 2 root root 6 Apr 11 2018 opt
drwxr-xr-x 2 root root 6 Dec 5 09:36 proc
dr-xr-x--- 4 root root 140 Mar 11 17:37 root
drwxr-xr-x 12 root root 163 Mar 11 17:50 run
lrwxrwxrwx 1 root root 8 Dec 5 09:36 sbin -> usr/sbin
drwxr-xr-x 2 root root 6 Apr 11 2018 srv
drwxr-xr-x 2 root root 6 Dec 5 09:36 sys
drwxrwxrwt 7 root root 132 Mar 11 17:38 tmp
drwxr-xr-x 13 root root 155 Dec 5 09:36 usr
drwxr-xr-x 18 root root 238 Dec 5 09:36 var
可以在目录下看到rootfs文件夹,该文件夹下即为容器的所有文件。而后找出冗余文件删除即可。
卸载移除设备
最后清理恢复,卸载目录,移除dm设备。
[root@localhost docker]# umount /tmp/test
[root@localhost docker]# dmsetup remove docker-253:2-3693860-e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547
重启容器后,恢复正常。至此,该问题得以修复。
以上是手动进行容器dm设备的创建挂载流程。其实在docker中,也基本上是这样来进行操作的。具体代码可以参考graphdriver下的devmapper中的相关流程。代码这里就不再一一走读了。
docker中mnt挂载点不可见的问题
在这里顺带还解释下docker挂载的一个疑惑,就是docker中dm设备最终都会mount到devicemapper/mnt/{mount-id}/目录下。
但是在/proc/mounts
下并不可见。这是因为docker daemon单独创建了一个mount的namespace。
[root@localhost tmp]# ll /proc/1/ns/mnt
lrwxrwxrwx 1 root root 0 Mar 19 19:03 /proc/1/ns/mnt -> mnt:[4026531840]
[root@localhost tmp]# ps -fe|grep docker
root 845 23449 0 17:04 pts/1 00:00:00 grep --color=auto docker
root 23121 1 0 09:09 ? 00:00:38 /usr/bin/docker-current daemon --selinux-enabled --log-driver=journald -H tcp://127.0.0.1:5050 -H unix:///var/run/docker.sock -g /home/docker --storage-opt dm.blkdiscard=false --storage-opt dm.mountopt=nodiscard --storage-opt dm.loopdatasize=80G --storage-opt dm.loopmetadatasize=4G
[root@localhost tmp]# ll /proc/23121/ns/mnt
lrwxrwxrwx 1 root root 0 Apr 2 10:25 /proc/23121/ns/mnt -> mnt:[4026532450]
23121为docker daemon的进程id。可以通过查看该进程的mounts即可看到相应的挂载信息。
[root@localhost tmp]# cat /proc/23121/mounts|grep mnt
/dev/mapper/docker-253:2-3693860-e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547 /home/docker/devicemapper/mnt/e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547 xfs rw,relatime,nouuid,attr2,inode64,logbsize=64k,sunit=128,swidth=128,noquota 0 0
当然,也可以通过nsenter切入到docker daemon进程的mount ns下进行查看。
[root@localhost tmp]# nsenter -t 23121 -m mount -l|grep mnt
/dev/mapper/docker-253:2-3693860-e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547 on /home/docker/devicemapper/mnt/e85dd79353abe6967cbc4f968934ae078d4d9da02010033b73a3df9659e4d547 type xfs (rw,relatime,nouuid,attr2,inode64,logbsize=64k,sunit=128,swidth=128,noquota)
详解docker中容器devicemapper设备的挂载流程的更多相关文章
- 详解Docker中Image、Container与 Volume 的迁移
开源Linux 长按二维码加关注~ 上一篇:Linux Used内存到底哪里去了? 已经部署的容器化服务,也不是不需要维护的.而且,由于生产环境往往有这样那样的严格要求,往往需要些非常规操作.Imag ...
- 详解Docker 端口映射与容器互联
详解Docker 端口映射与容器互联 1.端口映射实现访问容器 1.从外部访问容器应用 在启动容器的时候,如果不指定对应的参数,在容器外部是无法通过网络来访问容器内部的网络应用和服务的. 当容器中运行 ...
- 用IDEA详解Spring中的IoC和DI(挺透彻的,点进来看看吧)
用IDEA详解Spring中的IoC和DI 一.Spring IoC的基本概念 控制反转(IoC)是一个比较抽象的概念,它主要用来消减计算机程序的耦合问题,是Spring框架的核心.依赖注入(DI)是 ...
- 详解Webwork中Action 调用的方法
详解Webwork中Action 调用的方法 从三方面介绍webwork action调用相关知识: 1.Webwork 获取和包装 web 参数 2.这部分框架类关系 3.DefaultAction ...
- 使用IDEA详解Spring中依赖注入的类型(上)
使用IDEA详解Spring中依赖注入的类型(上) 在Spring中实现IoC容器的方法是依赖注入,依赖注入的作用是在使用Spring框架创建对象时动态地将其所依赖的对象(例如属性值)注入Bean组件 ...
- 详解Spring中Bean的作用域与生命周期
摘要:在利用Spring进行IOC配置时,关于bean的配置和使用一直都是比较重要的一部分,同时如何合理的使用和创建bean对象,也是小伙伴们在学习和使用Spring时需要注意的部分,所以这一篇文章我 ...
- jQuery:详解jQuery中的事件(二)
上一篇讲到jQuery中的事件,深入学习了加载DOM和事件绑定的相关知识,这篇主要深入讨论jQuery事件中的合成事件.事件冒泡和事件移除等内容. 接上篇jQuery:详解jQuery中的事件(一) ...
- 图文详解Unity3D中Material的Tiling和Offset是怎么回事
图文详解Unity3D中Material的Tiling和Offset是怎么回事 Tiling和Offset概述 Tiling表示UV坐标的缩放倍数,Offset表示UV坐标的起始位置. 这样说当然是隔 ...
- 【转】详解C#中的反射
原帖链接点这里:详解C#中的反射 反射(Reflection) 2008年01月02日 星期三 11:21 两个现实中的例子: 1.B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内 ...
随机推荐
- Mysqlutil.JDBCutil.Dtabaseutil数据库操作工具类[批量操作]
一个用来操作数据库的常用工具类. 提供批量操作,生成建表,插入语句等 操作示例: // 1.获取连接 DataBaseUtil jdbc = new DataBaseUtil(); jdbc.getC ...
- OO第二次博客作业--第二单元总结
第一次作业 1. 设计策略 第一次作业,一共三个线程,主线程.输入线程和电梯线程,有一个共享对象--调度器(队列). 调度的策略大多集中到了电梯里,调度器反而只剩下一个队列. 2. 基于度量的分析 类 ...
- java第二次作业之一
package dama; public class person { private String name; private String sex; private int age; privat ...
- 【C++】满二叉树问题
/* 给出一棵满二叉树的先序遍历,有两种节点:字母节点(A-Z,无重复)和空节点(#).要求这个树的中序遍历.输出中序遍历时不需要输出#. 满二叉树的层数n满足1<=n<=5. Sampl ...
- flutter学习之二Material Design设计规范
前言: 最近在自学flutter跨平台开发,从学习的过程来看真心感觉不是那么一件特别容易的事.不但要了解语法规则, 还要知晓常用控件,和一些扩展性的外延知识,所以套一句古人的话“路漫漫其修远矣,无将上 ...
- nodejs实时的检测系统文件的变化(无需重启服务)
1.安装superior npm -g install supervisor 注意 superior必须全局安装,否则错误命令会提示安装到全局 2.修改启动 现在我们需要使用 supervisor a ...
- 迪杰斯特拉(Dijkstra)算法描述及理解
Dijkstra算法是一种计算单源最短无负边路径问题的常用算法之一,时间复杂度为O(n2) 算法描述如下:dis[v]表示s到v的距离,pre[v]为v的前驱结点,用以输出路径,vis[v]表示该点最 ...
- 常用sql 集合记录整理
select 'truncate table ' + Name + ';' from sysobjects where xtype='U' order by name asc; -- 查询出指定库的 ...
- 关于 RESTful API 中 HTTP 状态码的定义
最近正好使用了一会儿 Koa ,在这说一下自己对各个 请求码的见解和使用场景,懒人直接看 200.400.401.403.404.500 就可以了. 其中 2XX/3XX 其实都是请求成功,但是结果不 ...
- 70个Python练手项目列表(都有完整教程)
前言: 不管学习那门语言都希望能做出实际的东西来,这个实际的东西当然就是项目啦,不用多说大家都知道学编程语言一定要做项目才行. 这里整理了70个Python实战项目列表,都有完整且详细的教程,你可以从 ...