【深析】 Docker Desktop 中的容器文件系统:OverlayFS vs Containerd Snapshots

引言

在使用 Docker Desktop 运行容器时,开发者经常会遇到各种复杂的文件系统路径。特别是当我们通过 -v 参数挂载本地目录时,Docker 会创建一系列复杂的存储结构。

本文将通过一个具体的 LocalAI 容器案例,深入解析 Docker Desktop 中两个不同存储路径的区别和原理。

先说结论

简单说,这两个路径是Docker在后台"干活"留下的不同痕迹。
第一个是Containerd运行时管理的,相当于容器的"原始底稿",就是你挂载卷之前容器里自带的/build目录内容。

第二个是Docker自己管理的,相当于运行时的"工作现场",包含了卷挂载后的状态。

但其实这两个路径你都不用管,因为你的数据实际上在Windows的D:/localai-data/里。容器里的/build只是映射过来的窗口,你在Windows那边改了文件,容器里就能看到。
所以记住:你的模型和配置都在D盘那个文件夹,Docker内部那些复杂路径不用操心。

案例背景

我运行了以下命令启动一个 LocalAI 容器:

bash 复制代码
docker run -d --name local-ai --gpus all -p 8080:8080 \
  -v D:/localai-data/models:/build/models \
  -v D:/localai-data/config:/build/config \
  localai/localai:latest-gpu-nvidia-cuda-12

然后发现在 WSL 中有两个看起来相似但不同的路径:

  1. Containerd 快照路径:

    复制代码
    \\wsl.localhost\docker-desktop\mnt\docker-desktop-disk\data\desktop-containerd\daemon\io.containerd.snapshotter.v1.overlayfs\snapshots\284\fs\build
  2. Docker OverlayFS 路径:

    复制代码
    \\wsl.localhost\docker-desktop\mnt\docker-desktop-disk\data\docker\rootfs\overlayfs\f0e048f2205532a006623a8e02fef1534a391646e84047a51ffb5f560a616967\build

技术架构深度解析

1. Docker Desktop 的存储架构

Docker Desktop 在 Windows 上使用 WSL 2 运行时,采用了多层存储架构:

复制代码
Windows 主机
    ├── WSL 2
    │   ├── Docker Desktop VM
    │   │   ├── Containerd 运行时
    │   │   └── Docker 守护进程
    │   └── 用户 WSL 发行版
    └── Windows 文件系统

2. Containerd 快照路径解析

路径:
\\wsl.localhost\docker-desktop\mnt\docker-desktop-disk\data\desktop-containerd\daemon\io.containerd.snapshotter.v1.overlayfs\snapshots\284\fs\build

这是 Containerd 的 OverlayFS 快照层

结构分解:

  • desktop-containerd/daemon/ - Containerd 守护进程数据目录
  • io.containerd.snapshotter.v1.overlayfs/ - OverlayFS 快照管理器
  • snapshots/284/ - 第 284 号快照(随机ID)
  • fs/ - 容器的根文件系统
  • build/ - 容器内的 /build 目录

特点:

  1. Containerd 管理:由 Containerd 容器运行时直接管理
  2. 快照机制:使用 OverlayFS 的快照(snapshot)功能
  3. 原始容器视图:展示容器挂载卷之前的原始文件系统状态
  4. 可写层基础:是容器可写层(upperdir)的基础

验证命令:

bash 复制代码
# 查看容器在 Containerd 中的信息
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  redcoolbeans/dockercontainerspy containerd-list

# 在容器内查看 OverlayFS 挂载
docker exec local-ai mount | grep overlay

3. Docker OverlayFS 路径解析

路径:
\\wsl.localhost\docker-desktop\mnt\docker-desktop-disk\data\docker\rootfs\overlayfs\f0e048f2205532a006623a8e02fef1534a391646e84047a51ffb5f560a616967\build

这是 Docker 的 OverlayFS 存储驱动层

结构分解:

  • docker/rootfs/overlayfs/ - Docker 的 OverlayFS 存储驱动目录
  • f0e048f220... - 64 位随机哈希,标识特定容器层
  • build/ - 容器内的 /build 目录

特点:

  1. Docker 管理:由 Docker 守护进程管理
  2. 存储驱动:使用 Docker 的 OverlayFS 存储驱动
  3. 联合挂载:可能包含多个镜像层的联合视图
  4. 运行时状态:包含卷挂载后的文件系统状态

验证命令:

bash 复制代码
# 查看容器的存储驱动信息
docker inspect local-ai --format='{{.GraphDriver}}'

# 查看具体的存储驱动数据
docker inspect local-ai --format='{{json .GraphDriver.Data}}'

关键区别对比

特性 Containerd 快照路径 Docker OverlayFS 路径
管理者 Containerd 运行时 Docker 守护进程
用途 容器基础镜像快照 容器运行时的存储层
数据内容 挂载卷前的原始数据 可能包含运行时修改
持久性 基础层,通常只读 包含可写层
生命周期 与镜像层关联 与容器实例关联

实际操作验证

1. 查看容器存储详情

bash 复制代码
# 获取容器 ID
docker ps -qf "name=local-ai"

# 查看容器详情
docker inspect local-ai | grep -A 10 -B 5 "GraphDriver"

# 输出示例:
# "GraphDriver": {
#   "Data": {
#     "LowerDir": "/var/lib/docker/overlay2/xxx/diff:/var/lib/docker/overlay2/yyy/diff",
#     "MergedDir": "/var/lib/docker/overlay2/zzz/merged",
#     "UpperDir": "/var/lib/docker/overlay2/zzz/diff",
#     "WorkDir": "/var/lib/docker/overlay2/zzz/work"
#   },
#   "Name": "overlay2"
# }

2. 在容器内验证挂载

bash 复制代码
# 进入容器
docker exec -it local-ai sh

# 查看 /build 目录结构
ls -la /build/
# 应该看到 models 和 config 目录

# 查看挂载信息
mount | grep /build
# 输出应显示从 Windows 主机挂载的目录

3. 查看卷映射

bash 复制代码
# 查看容器的卷挂载
docker inspect local-ai --format='{{json .Mounts}}'

# 输出应显示:
# [
#   {
#     "Type": "bind",
#     "Source": "/mnt/d/localai-data/models",
#     "Destination": "/build/models",
#     "Mode": "",
#     "RW": true,
#     "Propagation": "rprivate"
#   },
#   {
#     "Type": "bind",
#     "Source": "/mnt/d/localai-data/config",
#     "Destination": "/build/config",
#     "Mode": "",
#     "RW": true,
#     "Propagation": "rprivate"
#   }
# ]

为什么有两个相似的 /build 目录?

1. 挂载覆盖机制

当使用 -v 参数挂载卷时,Docker 使用了 Linux 的挂载覆盖机制:
容器 /build 原始目录
卷挂载点
Windows D:/localai-data/models
Windows D:/localai-data/config
容器运行时视图 /build

2. 两个路径的实际关系

  1. Containerd 路径

    • 包含容器镜像中原始的 /build 目录内容
    • 在卷挂载前存在
    • 卷挂载后,这个目录被隐藏(masked)
  2. Docker OverlayFS 路径

    • 是运行时视图的一部分
    • 可能包含容器修改的元数据
    • 实际的卷挂载在这个视图之上

3. 验证实验

bash 复制代码
# 1. 停止容器
docker stop local-ai

# 2. 查看 Containerd 路径
# 此时应该能看到原始的 /build 目录内容

# 3. 启动容器
docker start local-ai

# 4. 再次查看 Containerd 路径
# 由于卷已挂载,原始 /build 目录被隐藏

实际影响和注意事项

开发者需要知道的

  1. 数据存储位置

    • 你的模型文件实际存储在 D:/localai-data/models/
    • Docker 容器内的路径只是映射视图
  2. 性能考虑

    • Windows → WSL → Docker 的三层转换有一定性能开销
    • 大量小文件操作时尤其明显
  3. 备份策略

    • 备份 D:/localai-data/ 目录即可
    • 不需要备份 Docker 内部的路径
  4. 权限问题

    • Windows 和 Linux 文件权限系统不同
    • 可能需要配置 WSL 的权限映射

优化建议

  1. 使用 Docker 卷替代绑定挂载:

    bash 复制代码
    docker volume create localai-models
    docker volume create localai-config
    
    docker run -d --name local-ai \
      -v localai-models:/build/models \
      -v localai-config:/build/config \
      localai/localai:latest-gpu-nvidia-cuda-12
  2. 性能优化配置

    json 复制代码
    // 在 Docker Desktop 设置中添加
    {
      "wslEngineEnabled": true,
      "wslEngineOptimization": "performance"
    }
  3. 监控存储使用

    bash 复制代码
    # 查看容器存储使用
    docker system df
    
    # 查看详细存储信息
    docker system df -v

高级调试技巧

1. 查看 OverlayFS 层次结构

bash 复制代码
# 在 WSL 中
wsl -d docker-desktop

# 查找容器相关层
find /var/lib/docker/overlay2 -name "lower" -type f | xargs grep -l "local-ai"

# 查看层关联
cat /var/lib/docker/image/overlay2/layerdb/mounts/*/mount-id

2. 使用 dive 工具可视化

bash 复制代码
# 安装 dive
docker run --rm -it \
  -v /var/run/docker.sock:/var/run/docker.sock \
  wagoodman/dive:latest localai/localai:latest-gpu-nvidia-cuda-12

3. 文件系统事件监控

bash 复制代码
# 在容器运行时监控文件访问
docker run --rm -it \
  --pid=container:local-ai \
  --cap-add SYS_PTRACE \
  alpine sh -c "apk add strace && strace -p 1 -e trace=file"

常见问题解答

Q1: 为什么我的文件修改在容器内看不到?

A: 检查 Windows 到 WSL 的文件系统同步,可能需要重启 Docker Desktop 或 WSL。

Q2: 如何清理这些临时文件?

A: 使用 docker system prune -a 清理,但注意这会删除所有未使用的资源。

Q3: 这两个目录哪个是"真实"的?

A: 都不是"真实"的数据存储位置。你的真实数据在 Windows 的 D:/localai-data/ 目录中。

Q4: 如何提高文件访问性能?

A: 考虑将数据放在 Linux 文件系统中,或使用 Docker 卷而非绑定挂载。

总结

通过深入分析这两个路径,我们可以理解 Docker Desktop 的复杂存储架构:

  1. 分层管理:Docker 使用多层存储(镜像层、容器层、挂载层)
  2. 运行时抽象:Containerd 和 Docker 守护进程协同工作
  3. 挂载覆盖:绑定挂载会覆盖容器内的原始目录
  4. 性能优化:理解架构有助于优化存储性能

对于大多数开发者,关键要点是:

  • 理解你的数据实际存储位置
  • 使用适当的挂载方式
  • 定期清理不再使用的存储资源
  • 监控存储使用情况

掌握这些底层知识,可以帮助你更有效地使用 Docker,并能在出现问题时快速定位和解决存储相关问题。

相关推荐
无限大.2 小时前
为什么“容器化“技术很重要?——从虚拟机到 Docker
运维·docker·容器
眠りたいです2 小时前
Docker:Docker Network容器之间及容器与外部世界的通信桥梁
运维·docker·容器·docker网络
幺零九零零2 小时前
Docker底层-OverlayFS
运维·docker·容器
goodlook01232 小时前
监控平台搭建-监控指标展示-Grafana篇(五)
java·算法·docker·grafana·prometheus
java_logo2 小时前
Apache Flink Docker 容器化部署指南
docker·flink·apache·apache flink·apache flink部署·flink部署文档·flink部署教程
深耕AI2 小时前
【Docker Desktop for Windows】 两个 volumes 目录的区别
windows·docker·容器
深耕AI2 小时前
【手搓 Docker 卷 volumes】从 `docker volume create` 到落盘位置的最后1公里
运维·docker·容器
鸠摩智首席音效师2 小时前
如何在 Linux 中使用 fallocate 命令 ?
linux·运维·服务器
雨大王5122 小时前
如何选择汽车制造数字化服务商?关键指标与实战案例解析
大数据·运维·人工智能