浅谈Overlay File System的应用

本文介绍了Overlay文件系统的基本原理及其在嵌入式系统和Docker中的应用。在嵌入式系统中,Overlay通过结合只读和可读写分区提供灵活的存储解决方案;在Docker中,OverlayFS作为存储驱动,用于合并镜像的只读层和容器的可读写层,简化了容器的文件系统管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

随着Linux Kernel从3.18开始,将Overlay文件系统纳入主线开发维护,到目前为止,经过不断的进行完善和开发,Overlay文件系统的地位变得越来越重要,并且从各方面来说,其作用和优点也越来越显著。

对于Overlay的原理,网上有很多文章进行了详细介绍,此处不再赘述其原理,下面仅做大致描述,以方便读者对文章后续的理解。

Overlay基本原理

Overlay文件系统类似于堆叠的文件系统,它本身不像其他传统文件系统(jffs2、ext4、fat…)那样,直接参与磁盘空间及节点存储的划分。它更像是一个皮包公司,依赖并构建于其他传统文件系统之上,“合并”底层传统文件系统中文件内容的差异并进行“合并”,然后再向用户呈现。因此用户看到的文件系统根目录,其实是overlay“合并”的文件系统目录的“合集”。

文件系统的理解,可以参考一本书的结构:
一本书有厚有很多内容(类比为磁盘空间),而每本书前面的目录(类比为文件系统),可以帮我们准确的定位到你要查找的内容在书正文的那一页(类比为文件系统inode节点)。

为了便于理解,来一张图进行简单说明:

Overlay应用场景很多,下面仅从两个接触到的应用领域进行较详细的说明:

  • Overlay在嵌入式系统中的应用
  • Overlay在Docker中的应用

1. Overlay在嵌入式系统中的应用

众所周知,在嵌入式通信行业,尤其是需要跑Linux操作系统的嵌入式通信产品,如交换机、WiFi路由器、GPON ONU等设备中,由于系统硬件资源及成本限制,存储通常使用SPI接口的Flash,容量基本8/16/32MByte居多(Nand Flash一般存储容量大,但是运行里面的Kernel及文件系统需要先将此分区读取出来加载在内存运行,这种方式不在讨论范围),如果使用在工业级设备上的话,为了系统稳定及容灾的考虑,都会做双image备份,这就导致存储资源更加紧缺。

而制作根文件系统使用的主要有jffs2和squashfs,两者的区别在于,jffs2 的文件系统可读写,但是压缩比率较低,squash的文件系统为只读,压缩率高,所以比较普遍的做法有两种,各有优缺点,根据自已也无需求选择使用,分别如下:

  • 传统方式
  • Overlay方式

这样实现的方式,如果要实现类似恢复出厂设置的功能时,只需要将jffs2的用户数据分区内容直接删除即可。

1.1 传统方式

根文件系统制作为squash fs,而单独划分一个jffs2的数据分区,用来存储用户对设备的配置信息,在系统启动之后,自动挂载此数据分区到squash根文件系统预先创建的目录下,从而实现用户配置信息的存储。
实现脚本:

# 只需在系统家电启动的时候,启动脚本中mount此block即可,制作只读文件系统时,预先创建好/user/data路径,以mtdblock7为例进行挂载
mount -n -t jffs2 /dev/mtdblock7 -o rw,noatime,mode=0755 /user/data
1.2 Overlay方式

同样是划分两个分区,一个只读squash,一个可读写jffs2,使用overlay fs,将squash作为lower layer,jffs2作为upper layer进行挂载,然后通过pivot_root将/overlay转移为根文件系统使用。
实现脚本:

#!/bin/sh

echo "------------------------------------------- start preinit -----------------------------------------"
mount -a
echo -e "If you want to erase overlay data, please press [y] key in 3 seconds!"
read -t 3 input
if [ "$input" = "y" ]; then
  echo -e "Remove all files in /overlay/"
  rm /overlay/* -rf
fi

...
#先挂载jffs2分区
mount -n -t jffs2 /dev/mtdblock7 -o rw,noatime,mode=0755 /overlay
#将/目录作为lowerdir,/overlay作为upperdir,使用overlay方式,挂载到/mnt目录下
mount -n -t overlayfs overlayfs:/overlay -o rw,noatime,lowerdir=/,upperdir=/overlay /mnt
mount -n /proc -o noatime,--move /mnt/proc
#将根文件系统切换为/mnt目录
pivot_root /mnt /mnt/rom 
mount -n /rom/dev -o noatime,--move /dev
mount -n /rom/tmp -o noatime,--move /tmp
mount -n /rom/sys -o noatime,--move /sys
mount -n /rom/overlay -o noatime,--move /overlay
...
echo "----------------------- preinit end, and then start normal init ------------------------------------"

目前,在嵌入式系统的应用中,比较常用并且稳定运行的Linux内核版本都是2.6.x版本,而Overlay是在Linux 3.18之后内核才开始支持,因此,要想在2.6.x版本使用,就需要我们手动打补丁,网上有OpenWRT对Linux 2.6.38版本的补丁,但是目前好像无法访问了,以下为在项目中使用的参考Linux 2.6.38版本的补丁思路:

  • 内核空间支持overlay
  • 应用空间

详细补丁文件请参考:

已经打进kernel,后续有空研究一下单独列出来

2. Overlay在Docker中的应用

Docker的存储驱动有很多种,常用的比如AUFS、DeviceMapper、BtrFS、ZFS等,当然也包括OverlayFS,本文以下部分仅描述Overlay在docker中作为存储驱动的应用。

Docker的安装及原理请参考网上其他文章,在此不再赘述。

2.1 Docker镜像和容器

镜像(image)
是一个或几个只读层的堆叠“合并”,通过overlay文件系统“合并”成一个统一文件系统,从用户角度来看,隐藏了多个只读层的细节,只存在一个统一文件系统。
在这里插入图片描述
容器(Container)
跟镜像一样,也是一个或多个只读层的堆叠“合并”,只不过不同的是,最上层比镜像多了一个可读写的统一文件系统层。即:容器 = 镜像 + 可读写层;
在这里插入图片描述
下面我们通过docker overlay存储驱动测试,来验证上面的结论。

2.2 Docker overlay存储驱动测试

在Linux(本文以CentOS7为例)下安装docker之后,docker镜像的默认安装目录是/var/lib/docker,我们安装docker之后,进入这个目录,未安装任何镜像时,先查看一下这个目录结构以方便验证,如下图:

简单介绍一下较重要或者我们测试关注的几个目录:
containers:docker的容器信息存储目录;
image:docker的镜像信息存储目录;

  • image/overlay2/imagedb:镜像信息及镜像layer各个层信息;
  • image/overlay2/layerdb:镜像各个layer层的信息(imagedb sha256sum);

overlay2:docker镜像内,镜像各个layer中具体的文件内容及数据;


这时,我们随便使用docker pull ubuntu:15.04拉取一个镜像,如下:
pull ubuntu

其实从这个过程也可以看到,Pull complete一共有4个,说明这个ubuntu:15.04镜像包含了4个只读层,但是这一点我还没有仔细验证。

在这里插入图片描述
docker images查看IMAGE ID开头为d1b55fd07600,再查看image/overlay2/imagedb/content/sha256/下已存在此ID的文件,如下图:
在这里插入图片描述
我们查看一下这个文件内容,发现是一个json文件,文件末尾部分,有一个diff_ids数组,关注一下,其实这个数组正是组成ubuntu:15.04镜像的4个layer id,从数组的第一个到第四个,分别对应image从最底层到最顶层,也就是说,"sha256:3cbe18655eb6…"是最底层。如下图红色框部分:
在这里插入图片描述
既然在image/overlay2/imagedb得到了各个layer的image id,那就再进入image/overlay2/layerdb/目录去对应查找,可以发现,只找到最底层的sha256:3cbe18655eb6…这个image id,其他的3个没有,那是因为docker使用了sha256sum方式去保存这些layer及image id,则sha256:3cbe18655eb6…这个image id的上一层id是echo -n "sha256:3cbe18655eb6...[diff_ids json数组最底层id] sha256:84cc[diff_ids json数组倒数第二层id]" | sha256sum-,那么上上一层id是上一步得出的结果和diff_ids json数组倒数第三层id一起计算sha256sum得出,以此类推,如下图的方式,可以看出各个layer已经全部对应上了。
在这里插入图片描述
查看一下layerdb中的内容,发现除了最底层之外,其他每一层都有个parent文件,指向
在这里插入图片描述
同时,在每一层都有一个cache-id,cat一下得到与对应到overlay2/目录下,而overlay2目录,则是存储镜像每层实际文件与数据的地方,真正的image对应layer镜像的内容,就包含在这个目录下,里面保存的是本层与下层的diff,而在最上层,则是这些各个层的合并,这也是overlay实现的对于分层layer的“合并”,即同一文件系统部分。
在这里插入图片描述
接下来,我们运行这个容器,并在容器里面添加一个文件,进行验证,先通过docker run -i -t ubuntu:15.04 /bin/bash进入容器,创建一个文件,然后退出。如下图操作:
在这里插入图片描述
查看容器运行状态,生成了7a0b843060f9的CONTAINER ID,在container目录下可以找到此容器ID,如下:
在这里插入图片描述
查看layerdb目录,还是之前的4个image layer,此时通过docker commit -m="add overlayfs test file" -a="name" 7a0b843060f9 name/ubuntu:15.04-dev命令,将创建测试文件之后的容器,commit一下,再查看layerdb,发现已经多了一条,如下图的操作:
在这里插入图片描述
在这里插入图片描述
进行验证,确实在在diff中包含我们创建的文件,如下:
在这里插入图片描述

本人水平一般,能力有限,欢迎大家指正留言交流!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值