🐳 深入剖析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 (镜像层): 这是只读(Read-Only)的。它对应于我们下载的nginx:alpine镜像所组成的多个层。它绝不会被修改。
  • ✏️ UpperDir (容器层): 这是可写(Read-Write)区域。当您在容器内创建或修改文件时,它们实际上只存储在这里
  • ⚙️ 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

发表回复

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