【容器】如何理解容器文件系统

我们在容器里,运行 df 命令,你可以看到在容器中根目录 (/) 的文件系统类型是"overlay",它不是我们在普通 Linux 节点上看到的 ext4 或者 xfs 之类常见的文件系统。

那么看到这里你肯定想问,Overlay 是一个什么样的文件系统呢,容器为什么要用这种文件系统?

在说容器文件系统前,我们先来想象一下如果没有文件系统管理的话会怎样。假设有这么一个场景,在一个宿主机上需要运行 100 个容器。

每个容器都需要一个镜像,这个镜像就把容器中程序需要运行的二进制文件,库文件,配置文件,其他的依赖文件等全部都打包成一个镜像文件。

如果没有特别的容器文件系统,只是普通的 ext4 或者 xfs 文件系统,那么每次启动一个容器,就需要把一个镜像文件下载并且存储在宿主机上。

我举个例子帮你理解,比如说,假设一个镜像文件的大小是 500MB,那么 100 个容器的话,就需要下载 500MB*100= 50GB 的文件,并且占用 50GB 的磁盘空间。

如果你再分析一下这 50GB 里的内容,你会发现,在绝大部分的操作系统里,库文件都是差不多的。而且,在容器运行的时候,这类文件也不会被改动,基本上都是只读的。特别是这样的情况:假如这 100 个容器镜像都是基于"ubuntu:18.04"的,每个容器镜像只是额外复制了 50MB 左右自己的应用程序到"ubuntu: 18.04"里,那么就是说在总共 50GB 的数据里,有 90% 的数据是冗余的。

讲到这里,你不难推测出理想的情况应该是什么样的?

没错,当然是在一个宿主机上只要下载并且存储存一份"ubuntu:18.04",所有基于"ubuntu:18.04"镜像的容器都可以共享这一份通用的部分。这样设置的话,不同容器启动的时候,只需要下载自己独特的程序部分就可以。

 

正是为了有效地减少磁盘上冗余的镜像数据,同时减少冗余的镜像数据在网络上的传输,选择一种针对于容器的文件系统是很有必要的,而这类的文件系统被称为 UnionFS。

 

UnionFS 这类文件系统实现的主要功能是把多个目录(处于不同的分区)一起挂载(mount)在一个目录下

这种多目录挂载的方式,正好可以解决我们刚才说的容器镜像的问题。比如,我们可以把 ubuntu18.04 这个基础镜像的文件放在一个目录 ubuntu18.04/ 下,容器自己额外的程序文件 app_1_bin 放在 app_1/ 目录下。然后,我们把这两个目录挂载到 container_1/ 这个目录下,作为容器 1 看到的文件系统;对于容器 2,就可以把 ubuntu18.04/ 和 app_2/ 两个目录一起挂载到 container_2 的目录下。这样在节点上我们只要保留一份 ubuntu18.04 的文件就可以了。

 

UnionFS 类似的有很多种实现,包括在 Docker 里最早使用的 AUFS,还有目前我们使用的 OverlayFS。

前面我们在运行df的时候,看到的文件系统类型"overlay"指的就是 OverlayFS。

在 Linux 内核 3.18 版本中,OverlayFS 代码正式合入 Linux 内核的主分支。在这之后,OverlayFS 也就逐渐成为各个主流 Linux 发行版本里缺省使用的容器文件系统了。

网上 Julia Evans 有个blog,里面有个的 OverlayFS 使用的例子,很简单,我们也拿这个例子来理解一下 OverlayFS 的一些基本概念。

 

How containers work: overlayfs

https://jvns.ca/blog/2019/11/18/how-containers-work--overlayfs

 

你可以先执行一下这一组命令。

###############################################

#!/bin/bash

umount ./merged

rm upper lower merged work -r

mkdir upper lower merged work

echo "I'm from lower!" > lower/in_lower.txt

echo "I'm from upper!" > upper/in_upper.txt

# `in_both` is in both directories

echo "I'm from lower!" > lower/in_both.txt

echo "I'm from upper!" > upper/in_both.txt

sudo mount -t overlay overlay \

-o lowerdir=./lower,upperdir=./upper,workdir=./work \

./merged

################################################

 

 

我们可以看到,OverlayFS 的一个 mount 命令牵涉到四类目录,分别是 lower,upper,merged 和 work,那它们是什么关系呢?

我们看下面这张图,这和前面 UnionFS 的工作示意图很像,也不奇怪,OverlayFS 就是 UnionFS 的一种实现。接下来,我们从下往上依次看看每一层的功能。

首先,最下面的"lower/",也就是被 mount 两层目录中底下的这层(lowerdir)。

在 OverlayFS 中,最底下这一层里的文件是不会被修改的,你可以认为它是只读的。我还想提醒你一点,在这个例子里我们只有一个 lower/ 目录,不过 OverlayFS 是支持多个 lowerdir 的。然后我们看"uppder/",它是被 mount 两层目录中上面的这层 (upperdir)。

在 OverlayFS 中,如果有文件的创建,修改,删除操作,那么都会在这一层反映出来,它是可读写的。接着是最上面的"merged" ,它是挂载点(mount point)目录,也是用户看到的目录,用户的实际文件操作在这里进行。

其实还有一个"work/",这个目录没有在这个图里,它只是一个存放临时文件的目录,OverlayFS 中如果有文件修改,就会在中间过程中临时存放文件到这里。

 

从这个例子我们可以看到,OverlayFS 会 mount 两层目录,分别是 lower 层和 upper 层,这两层目录中的文件都会映射到挂载点上。

从挂载点的视角看,upper 层的文件会覆盖 lower 层的文件,比如"in_both.txt"这个文件,在 lower 层和 upper 层都有,但是挂载点 merged/ 里看到的只是 upper 层里的 in_both.txt.

 

如果我们在 merged/ 目录里做文件操作,具体包括这三种:

 

第一种,新建文件,这个文件会出现在 upper/ 目录中。

 

第二种是删除文件,如果我们删除"in_upper.txt",那么这个文件会在 upper/ 目录中消失。

如果删除"in_lower.txt", 在 lower/ 目录里的"in_lower.txt"文件不会有变化,只是在 upper/ 目录中增加了一个特殊文件来告诉 OverlayFS,"in_lower.txt'这个文件不能出现在 merged/ 里了,这就表示它已经被删除了。

 

第三种是修改文件,类似如果修改"in_lower.txt",那么就会在 upper/ 目录中新建一个"in_lower.txt"文件,包含更新的内容,而在 lower/ 中的原来的实际文件"in_lower.txt"不会改变。

 

通过这个例子,我们知道了 OverlayFS 是怎么工作了。

那么我们可以再想一想,怎么把它运用到容器的镜像文件上?

 

其实也不难,从系统的 mounts 信息中,我们可以看到 Docker 是怎么用 OverlayFS 来挂载镜像文件的:

容器镜像文件可以分成多个层(layer),每层可以对应 OverlayFS 里 lowerdir 的一个目录,lowerdir 支持多个目录,也就可以支持多层的镜像文件。

在容器启动后,对镜像文件中修改就会被保存在 upperdir 里了。

 

 

在理解了容器使用的 OverlayFS 文件系统后,我们再回到开始的问题,为什么在宿主机升级之后,在容器里读写文件的性能降低了?

现在我们至少应该知道,在容器中读写文件性能降低了,那么应该是 OverlayFS 的性能在新的 ubuntu20.04 中降低了。

Linux 为了完善 OverlayFS,增加了 OverlayFS 自己的 read/write 函数接口,从而不再直接调用 OverlayFS 后端文件系统(比如 xfs,ext4)的读写接口。但是它只实现了同步 I/O(sync I/O),并没有实现异步 I/O。

在 fio 做文件系统性能测试的时候使用的是异步 I/O,这样才可以得到文件系统的性能最大值。所以,在内核 5.4 上就无法对 OverlayFS 测出最高的性能指标了。

在 Linux 内核 5.6 版本中,这个问题已经通过下面的这个补丁给解决了,有兴趣的同学可以看一下。

overlayfs: stack file operations

https://lwn.net/Articles/755889/

 

为什么要有容器自己的文件系统?

很重要的一点是减少相同镜像文件在同一个节点上的数据冗余,可以节省磁盘空间,也可以减少镜像文件下载占用的网络资源。

作为容器文件系统,UnionFS 通过多个目录挂载的方式工作。

OverlayFS 就是 UnionFS 的一种实现,是目前主流 Linux 发行版本中缺省使用的容器文件系统。

OverlayFS 也是把多个目录合并挂载,被挂载的目录分为两大类:lowerdir 和 upperdir。

lowerdir 允许有多个目录,在被挂载后,这些目录里的文件都是不会被修改或者删除的,也就是只读的;upperdir 只有一个,不过这个目录是可读写的,挂载点目录中的所有文件修改都会在 upperdir 中反映出来。

容器的镜像文件中各层正好作为 OverlayFS 的 lowerdir 的目录,然后加上一个空的 upperdir 一起挂载好后,就组成了容器的文件系统。

OverlayFS 在 Linux 内核中还在不断的完善,比如我们在这一讲看到的在 kenel 5.4 中对异步 I/O 操作的缺失,这也是我们在使用容器文件系统的时候需要注意的。

 

 

 

1. 在上面 OverlayFS 的例子的基础上,建立 2 个 lowerdir 的目录,并且在目录中建立相同文件名的文件,然后一起做一个 overlay mount,看看会发生什么?

经过实验确认,只会在merge即联合挂载点里生成一个文件名,也就是说overlay文件系统为了省存储空间是做了同名文件合并优化。

 

“merge层”相当于提供给用户进行交互的视图层;

“upper层”相当于存储实际发生变动的地方;

“lower层”是不变的,用户通过merge层视图对lower层文件的所有操作,都被重定向到“upper层”了,特殊的是删除操作,会在upper层生成一个特殊的c类型的文件来代表该文件被删。

用户交互时只能对merge层的视图来操作。

在merge层的视图中,上层覆盖下层同名文件,上下关系不只是upper和lower之间,即便在多个lower层中,也是上层覆盖下层。

 

那么,merged/in_lower.txt ”里的值有可能是"I'm from lower2!"吗?

 

 

2.aufs,是完全被废弃吗?aufs的废弃是指在内核层的废弃吗?之前安装docker时,时可以配置使用aufs 还是overlay2,也就是说内核层还未完全去除对aufs的支持吗

 aufs的代码从来就没有进入Linux内核的主干。

 

3.本文中描述的现象,一个重要的原因是容器镜像里只有rootfs,没有Linux内核,宿主机上的所有容器是共用宿主机内核的。所以,当宿主机内核版本升级后,容器镜像并没有相应的升级,也会产生这个问题,文中并没有对这个知识要点说明。

容器镜像中只有rootfs没有Linux内核是对的。

在文章里,宿主机内核升级后,无论容器的镜像是否升级,都会有这个问题。文中的问题是overlayfs引起的,和镜像中的文件没有关系。

 

4. CentOS7.x 容器节点 tmpfs文件类型与 Overlayfs文件类型的区别是什么?

tmpfs只是用来存放一些临时文件的内存文件系统,比如/tmp目录可以使用tmpfs。

 

5.假如我将一个卷(宿主机上的某个目录)挂在到容器文件系统中的某个目录,我在容器中对这个卷中的数据做读写操作,因为这个挂载路径也是容器文件系统的一部分,性能是不是也是会有影响的

如果以volume的方式挂载到容器中,那么它就不是以overlayfs的文件系统。

性能是否影响要看volume目录的位置在哪个物理磁盘上,和它共享物理磁盘的有哪些读写进程。

 

6.对当前k8s和docker的相爱相杀有什么看法?未来docker市场会被podman取代吗?如果会的话,这个过程大概要多久?

容器云平台里,k8s肯定是主流,用了k8s, 基本就不需要要docker了,启动容器的程序肯定是越简单越好。

 

 

参考

 

如何理解容器文件系统?

https://time.geekbang.org/column/article/318173

 

容器实战高手课/在实战中深入理解容器技术的本质

https://time.geekbang.org/column/intro/365

 

How containers work: overlayfs

https://jvns.ca/blog/2019/11/18/how-containers-work--overlayfs

 

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值