浅谈 Docker容器运行时挂盘问题

社区编辑2018-05-18 14:42

在以往的虚拟机使用中,空间不够时,再挂载一块磁盘上去是很常见的需求处理方式,而在容器使用时,如果想要在其运行时挂载一块盘,却并非一件易事。
 

容器磁盘的相关知识

 
Docker容器使用 volume,可以简单分为两种:
 

1. 使用宿主机本地目录

 
使用方式形如-v /host_absolute_path:/container_absolute_path
 

2. 使用 docker volume

 
使用方式形如-v volume_name:/container_absolute_path
 

本地卷
 
本地卷又可以进一步细分文件系统类型,如:
 
docker volume create –driver local –opt type=tmpfs –opt device=tmpfs –opt o=size=100m,uid=1000 –name foo
docker volume create –driver local –opt type=btrfs –opt device=/dev/sda2 –name foo

 
远程卷
 
支持远程卷的插件包括:convoy、flocker、glusterFs等等,这里不再详细展开。
 
本人一直从事网易云基础服务(蜂巢)相关项目的研发工作,在实际的项目中,远程卷管理实际是由上层编排工具 kubernetes来创建和管理的,Docker使用的是块设备已挂载的目录。
 
因此本文主要侧重于对第一种类型的介绍,即,如何在容器运行时挂载宿主机本地目录。
 

运行时挂载目录实现

 

1. 关键问题

 

○ 块设备挂载问题
 
块设备可以简单分为两类:容器启动前挂在宿主机上的块设备、启动后的挂载盘,前者在容器中是可见的,而后者因为容器的隔离性则不可见。因此,这里需要一些措施,将宿主机新增的块设备在容器中可见。
 
本文将利用宿主机和容器共享内核的特性,通过从宿主机传递块设备的主次设备号到容器中来打破这种隔离。
 

○ 文件系统识别问题
 
因为 Mount namespace的隔离,宿主机的目录对容器而言是没有实际意义的。这里搭配上面传入的块设备,通过挂载块设备、引入块设备根目录,使得传入的路径变得有意义。
 

○ 容器权限控制问题
 
挂载路径需要 mount权限,容器默认是没有该权限的,当然,可以通过 –privileged、cap add等手段在启动时赋予权限,但是对运行时的容器无法新增(权限)。而且有些产品,如网易云基础服务(蜂巢),出于安全考虑,并没有开放太多权限,为了规避权限问题,这里采用 docker-enter在运行中的容器里执行命令。(https://github.com/jpetazzo/nsenter.git )
 

2. 实现演示

 
下面主要通过一些简单的脚本命令,从原理出发,显示实现运行时挂载新增盘的路径(以下操作基于docker 1.12.0)
 
一些准备工作:在宿主机上挂一块新盘到 /root/new-disk,新盘在容器中不可见。
 
接下来,我们要实现挂载宿主机 /root/new-disk/container1目录到容器 test的 /data目录,以下是操作过程:
 
① 宿主机获取必要信息
 
为了在容器中挂载,需要从宿主机获取必要信息,这里给出本人测试的一些参考结果:DEV: /dev/vdf1; DEVDEC: 254 81; SUBROOT: ; SUBPATH: /root/new-disk/container1。下面是需要在宿主机上运行的命令:
 

FILESYS=$(df -P /root/new-disk/container1 | tail -n 1 | awk '{print $1}')

while read DEV MOUNT JUNK; do
if [ "$DEV"x == "$FILESYS"x ]; then
break
fi
done < /proc/mounts while read A B C SUBROOT MOUNT JUNK; do if [ "$MOUNT"x == "$FILESYS"x ]; then break fi done < /proc/self/mountinfo SUBPATH=$(echo /root/new-disk/container1 | sed s,^$FILESYS,,) DEVDEC=$(printf "%d %d" $(stat --format "0x%t 0x%T" $DEV)) # 块设备主次设备号 

 
② 在容器中挂载该盘及其目录
 
在容器中运行以下命令,就可以在 test容器 /data目录下,看到宿主机 /root/new-disk/container1目录下的内容。
 

docker-enter test sh -c "mknod --mode 0600 $DEV b $DEVDEC" # 用主次设备号新建块设备
docker-enter test mkdir /tmpmnt
docker-enter test mount $DEV /tmpmnt
docker-enter test mkdir -p /data
docker-enter test mount -o bind /tmpmnt/$SUBROOT/$SUBPATH /data # 用相对块设备根目录的路径来挂载宿主机路径
docker-enter test unmount /tmpmnt
docker-enter test rmdir /tmpmnt

 
注:本文的写作对以下链接中的文章亦有参考 https://jpetazzo.github.io/2015/01/13/docker-mount-dynamic-volumes/