🐳 深入剖析Docker内部:Overlay2文件系统结构完美分析

大家好!今天,我们将亲眼看看Docker容器是如何实际存储和管理数据的,探索其“魔法”般的结构。

许多人使用容器,但常常好奇实际文件在哪里、如何存在。Docker使用一种名为OverlayFS (Overlay Filesystem)的技术,将多个层组合起来,使其看起来像一个单一的文件系统。

今天,我们不谈复杂的理论,而是直接敲击命令,从宿主操作系统的角度彻底检查容器的内部结构。准备好了吗?🚀

image



1. 准备实验对象:运行容器 🏃‍♂️

首先,让我们运行一个要分析的容器。我们将使用轻量且快速的nginx:alpine镜像。

# 运行Nginx容器(后台模式)
docker run -d -p 80:80 --name nx nginx:alpine

现在Nginx正在隔离环境中运行。但从Linux内核的角度来看,这只是一个进程而已。


2. Overlay的秘密地图:检查Mountinfo 🗺️

现在是最关键的一步!让我们通过/proc文件系统,检查这个进程所看到的的文件系统是如何实际组装起来的。

# 从相应的挂载信息中查询overlayfs设置值
mount | grep rootfs

输出示例:

overlay on /var/lib/docker/rootfs/overlayfs/fea3c44e1dee873838bee303f32d739164f9a34e6a38b3c99ccc3c6e88f7e702 type overlay (rw,relatime,lowerdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/9/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/8/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/7/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/6/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/5/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/4/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/3/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/2/fs:/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs,upperdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/10/fs,workdir=/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/10/work,nouserxattr)

执行此命令会得到一个相当长的字符串,但你只需要找到其中的4个关键关键词

  • 🔒 LowerDir(镜像层):这是只读的。我们下载的nginx:alpine镜像由多层组成,这些层就属于这里。它们绝不会被修改。
  • ✏️ UpperDir(容器层):这是可读写的区域。当你在容器内创建或修改文件时,实际上只存储在这里
  • ⚙️ WorkDir:这是OverlayFS内部用于文件合并操作的临时空间。
  • 👀 Merged(合并视图):这是紧跟在’overlay on’后面的、以/var/lib/docker/rootfs/overlayfs/开头的路径。它是LowerDir和UpperDir合并后,我们所看到的最终结果。容器所看到的/(根)目录就是它。

3. 从宿主到容器内部的“瞬间移动” 🚪

我们能否在不连接到容器(docker exec)的情况下,从宿主查看容器的文件?是的,可以!Linux内核在/proc目录中设置了一个后门。

# 方法1:通过容器ID检查Merged目录
ls -al /var/lib/docker/rootfs/overlayfs/$(docker inspect -f '{{.Id}}' nx)

# 方法2:利用PID直接进入容器的RootFS(推荐!)
ls -al /proc/$(docker inspect -f '{{.State.Pid}}' nx)/root/

/proc/[PID]/root/路径就像一个连接到该进程所看到的根目录的符号链接。进入这里,你会看到与使用`docker exec`进入时完全相同的文件。是不是很神奇?✨


4. 数据写入实验与验证 🧪

宿主所看到的视图和容器所看到的视图真的连接在一起吗?为了测试,我们将在宿主上创建一个文件。

# 在宿主上创建文件到容器的Root路径
echo test1234 > /proc/$(docker inspect -f '{{.State.Pid}}' nx)/root/test.txt

现在,如果你在容器内部检查,test.txt文件就会神奇地出现。这个实验意义重大:容器文件系统并非一个独立的封闭空间,而是宿主文件系统中特定路径的组合展示


5. 我的文件实际在哪里?💾

我们刚刚创建的test.txt文件必须物理地存在于宿主硬盘的某个位置。它究竟存储在哪里了呢?

# 查找test.txt文件的实际物理位置
find /var/lib/docker/rootfs/ -name test.txt

搜索结果将显示类似/var/lib/docker/overlay2/[哈希值]/diff/test.txt的路径。

这个路径与我们在第3步中确认的UpperDir路径一致!也就是说,我们直接验证了OverlayFS的工作原理:“镜像(LowerDir)保持不变,所有更改都单独存储在UpperDir中”


🎯 结论与总结

通过今天的实践,我们了解了以下事实:

  1. 容器不是一个魔术盒,而是一个Linux进程。
  2. 容器的文件系统是只读镜像(Lower)之上叠加可写层(Upper)后合并(Merged)而成的。
  3. 删除容器时,UpperDir也会被删除,因此其中写入的数据也会随之丢失。(这就是为什么需要卷!)

理解这些原理将帮助您更好地理解Docker镜像构建速度和容器启动速度为何如此之快,以及如何有效地管理存储。

祝您今天容器航行愉快!⛵️



Comments

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注