当前位置: 首页 > news >正文

第二部分-Docker核心原理——09. 联合文件系统(UnionFS)

09. 联合文件系统(UnionFS)

1. UnionFS 概述

联合文件系统(Union File System)是一种将多个目录(分支)合并成单个视图的文件系统。它是 Docker 镜像分层和容器高效运行的核心技术。

┌─────────────────────────────────────────────────────────────┐ │ UnionFS 工作原理 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 统一视图 │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ merged/ │ │ │ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │ │ │ a │ │ b │ │ c │ │ d │ │ e │ │ │ │ │ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │ │ │ └─────────────────────────────────────────────────────┘ │ │ ▲ │ │ │ 合并 │ │ ┌─────────────────┼─────────────────┐ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Layer 3 │ │ Layer 2 │ │ Layer 1 │ │ │ │ (容器层) │ │ (镜像层) │ │ (镜像层) │ │ │ │ 可读写 │ │ 只读 │ │ 只读 │ │ │ │ │ │ c, e │ │ a, b │ │ │ │ d │ │ │ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘

2. 常见的 UnionFS 实现

文件系统特点适用场景
OverlayFS内核原生,性能好,稳定Linux 内核 3.18+,Docker 默认
Overlay2OverlayFS 改进版,支持更多层Linux 内核 4.0+,推荐
AUFS功能丰富,但未进内核主线Ubuntu/Debian 旧版本
devicemapper基于块设备,性能较差CentOS/RHEL 旧版本
btrfs子卷快照,功能强大特殊需求
zfs功能强大,适合大型存储特殊需求

3. OverlayFS 架构

3.1 OverlayFS 组成部分

┌─────────────────────────────────────────────────────────────┐ │ OverlayFS 组成 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ mount point │ │ │ │ (merged) │ │ │ └─────────────────────────────────────────────────────┘ │ │ ▲ │ │ │ │ │ ┌───────────────────────┼───────────────────────┐ │ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │ lowerdir │ │ lowerdir │ ... │ upperdir │ │ │ │ (只读) │ │ (只读) │ │ (可写) │ │ │ │ 层 1 │ │ 层 2 │ │ 容器层 │ │ │ └───────────┘ └───────────┘ └───────────┘ │ │ │ │ workdir: 用于原子操作的工作目录 │ │ │ └─────────────────────────────────────────────────────────────┘

3.2 OverlayFS 目录结构

# Docker OverlayFS 目录/var/lib/docker/overlay2/# 典型目录结构├──<layer-id-1>/# 镜像层 1│ ├── diff/# 该层的实际文件│ ├──link# 指向自身的短链接│ └── lower# 指向父层的链接├──<layer-id-2>/# 镜像层 2│ ├── diff/ │ ├──link│ └── lower ├──<container-id>/# 容器层│ ├── diff/# 容器修改的文件│ ├──link│ ├── lower# 指向所有父层│ ├── merged/# 合并后的视图(挂载点)│ └── work/# 工作目录└── l/# 短链接目录├──<short-id-1>->../<layer-id-1>/diff └──<short-id-2>->../<layer-id-2>/diff

4. OverlayFS 实践

4.1 手动创建 OverlayFS

# 创建目录结构mkdir-p/tmp/overlay/{lower,upper,work,merged}# 创建测试文件echo"Hello from lower layer">/tmp/overlay/lower/hello.txtecho"Lower file">/tmp/overlay/lower/lower.txt# 挂载 OverlayFSsudomount-toverlay overlay\-olowerdir=/tmp/overlay/lower,\upperdir=/tmp/overlay/upper,\workdir=/tmp/overlay/work\/tmp/overlay/merged# 查看合并视图ls-la/tmp/overlay/merged/cat/tmp/overlay/merged/hello.txt# 修改文件(会写入 upperdir)echo"Modified in container">>/tmp/overlay/merged/hello.txt# 查看 upperdir 内容ls-la/tmp/overlay/upper/# 多层 lowerdir(多个只读层)mkdir-p/tmp/overlay/{lower1,lower2,upper2,work2,merged2}echo"Layer 1">/tmp/overlay/lower1/file1.txtecho"Layer 2">/tmp/overlay/lower2/file2.txtsudomount-toverlay overlay\-olowerdir=/tmp/overlay/lower2:/tmp/overlay/lower1,\upperdir=/tmp/overlay/upper2,\workdir=/tmp/overlay/work2\/tmp/overlay/merged2ls/tmp/overlay/merged2/# file1.txt file2.txt# 卸载sudoumount/tmp/overlay/merged

4.2 查看 Docker OverlayFS

# 查看存储驱动dockerinfo|grep-A3"Storage Driver"# 查看镜像层dockerhistoryubuntu:20.04# 查看镜像层目录IMAGE_ID=$(dockerinspect--format='{{.Id}}'ubuntu:20.04)ls-la/var/lib/docker/overlay2/# 查看镜像层关系find/var/lib/docker/overlay2-name"lower"-execcat{}\;# 查看容器层CONTAINER_ID=$(dockerrun-dubuntu:20.04sleep3600)ls-la/var/lib/docker/overlay2/$(dockerinspect-f'{{.GraphDriver.Data.MergedDir}}'$CONTAINER_ID)# 查看容器修改的文件find/var/lib/docker/overlay2/$(dockerinspect-f'{{.GraphDriver.Data.UpperDir}}'$CONTAINER_ID)-typef

5. 写时复制(Copy-on-Write)

5.1 CoW 原理

┌─────────────────────────────────────────────────────────────┐ │ 写时复制(CoW)原理 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 读取文件 /etc/hosts │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 容器层 (upper) 查找 → 未找到 │ │ │ │ 镜像层 (lower) 查找 → 找到,直接返回 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ 修改文件 /etc/hosts │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 1. 从镜像层读取原文件 │ │ │ │ 2. 复制到容器层(upperdir) │ │ │ │ 3. 在容器层修改 │ │ │ │ 4. 后续读取从容器层读取 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ 删除文件 │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 在容器层创建 .wh. 文件标记删除 │ │ │ │ 白色文件(whiteout)遮蔽下层文件 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘

5.2 CoW 演示

# 启动容器dockerrun-it--nametestubuntu:20.04bash# 容器内操作root@container:/# cat /etc/hostsroot@container:/# echo "test" > /test.txtroot@container:/# rm /etc/hostsroot@container:/# exit# 查看容器层变化CONTAINER_ID=$(dockerps-a-q--filtername=test)UPPER_DIR=$(dockerinspect-f'{{.GraphDriver.Data.UpperDir}}'$CONTAINER_ID)# 查看新增的文件ls-la$UPPER_DIR/# 会看到 test.txt 和 .wh.hosts(删除标记)# 查看镜像层和容器层差异dockerdiff$CONTAINER_ID# C /etc/hosts # 修改# A /test.txt # 新增

6. Docker 镜像分层

6.1 镜像层结构

# Dockerfile 示例 FROM ubuntu:20.04 # 第1层:基础镜像 RUN apt-get update # 第2层:更新软件源 RUN apt-get install -y nginx # 第3层:安装 nginx COPY index.html /var/www/html/ # 第4层:复制文件 CMD ["nginx", "-g", "daemon off;"] # 第5层:启动命令
# 查看构建过程中的层dockerbuild --no-cache-tmyapp.# 查看镜像层dockerhistorymyapp# 查看每层大小dockerhistory--no-trunc myapp|awk'{print $1, $2, $5}'# 查看镜像的 rootfs 信息dockerinspect myapp|grep-A20"RootFS"

6.2 层共享与复用

# 多个镜像共享基础层REPO1=$(dockerpull ubuntu:20.04|grepDigest)REPO2=$(dockerpull ubuntu:20.04|grepDigest)# 两个镜像共享相同的层,不会重复存储# 查看共享的层dockerimage inspect ubuntu:20.04|grep-A10"GraphDriver"# 删除镜像时,只有没有被其他镜像引用的层才会被删除dockerrmi ubuntu:20.04

7. 层缓存优化

7.1 优化 Dockerfile

# ❌ 不好:每次都会重新执行 FROM ubuntu:20.04 RUN apt-get update RUN apt-get install -y python3 nginx COPY . /app # ✅ 好:利用层缓存 FROM ubuntu:20.04 # 先复制依赖文件 COPY requirements.txt /app/ # 安装依赖(只有 requirements.txt 变化才重新安装) RUN apt-get update && apt-get install -y python3 nginx # 最后复制代码 COPY . /app # ✅ 更好:合并命令减少层数 FROM ubuntu:20.04 RUN apt-get update && \ apt-get install -y python3 nginx && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* COPY . /app

7.2 查看层缓存

# 构建时查看缓存使用dockerbuild --no-cache-tmyapp.dockerbuild-tmyapp.# 第二次构建会使用缓存# 强制使用缓存dockerbuild --cache-from myapp-tmyapp:v2.# 查看构建历史dockerhistory--no-trunc myapp

8. Docker 存储驱动配置

# 查看当前存储驱动dockerinfo|grep"Storage Driver"# 配置存储驱动 /etc/docker/daemon.json{"storage-driver":"overlay2","storage-opts":["overlay2.override_kernel_check=true","overlay2.size=20G"]}# 重启 Dockersudosystemctl restartdocker# 查看存储驱动详情dockersystem info|grep-A10"Storage Driver"

9. 常用命令总结

操作命令
查看存储驱动docker info
查看历史docker history
查看层变化docker diff
查看镜像文件docker inspect
清理未使用层docker system prune
查看层大小docker system df

10. 实战演练

# 1. 创建测试 Dockerfilecat>Dockerfile<<EOF FROM alpine:latest RUN echo "Layer 1" > /layer1.txt RUN echo "Layer 2" > /layer2.txt RUN echo "Layer 3" > /layer3.txt EOF# 2. 构建镜像dockerbuild-ttest-layers.# 3. 查看层信息dockerhistorytest-layers# 4. 查看每个层的大小dockerhistory--no-trunc test-layers|awk'{print $1, $2, $5}'# 5. 运行容器并修改dockerrun-d--nametest-container test-layerssleep3600# 6. 查看容器修改dockerexectest-containertouch/container-file.txtdockerdifftest-container# 7. 查看存储目录CONTAINER_ID=$(dockerinspect-f'{{.Id}}'test-container)ls-la/var/lib/docker/overlay2/$CONTAINER_ID/diff/# 8. 清理dockerrm-ftest-containerdockerrmi test-layers

11. 常见问题

Q1: 为什么 OverlayFS 层数有限制?

OverlayFS 内核限制最多 128 层,Docker 默认最多 128 层镜像。

Q2: 如何查看层是否被共享?

# 使用 docker image inspect 查看 layer iddockerimage inspect--format='{{.RootFS.Layers}}'image1dockerimage inspect--format='{{.RootFS.Layers}}'image2

Q3: 容器修改的文件在哪里?

在容器的 upperdir 目录中:/var/lib/docker/overlay2/<container-id>/diff/

12. 小结

  • UnionFS实现镜像分层和写时复制
  • Overlay2是 Docker 默认存储驱动
  • 镜像由多层只读层组成
  • 容器添加一层可写层(upperdir)
  • 写时复制(CoW)节省存储空间
  • 层共享减少镜像存储和传输
  • 合理组织 Dockerfile 利用层缓存
  • 注意层数限制(128层)

http://www.jsqmd.com/news/780926/

相关文章:

  • Valyu AI Skills:为AI智能体注入多源信息检索与处理能力
  • 别再只发脉冲了!用STM32串口玩转MKS SERVO57D闭环步进电机,保姆级MODBUS-RTU配置教程
  • 游戏开发中的3D物理模拟与运动轨迹生成技术
  • Cortex-M0+移位与逻辑运算指令优化指南
  • Qt5.7.1项目里,不用QTextToSpeech,怎么用Windows自带的SAPI.SpVoice实现TTS?
  • 大语言模型并行训练与跨语言推理核心技术解析
  • 大语言模型行为评估:上下文一致性与事实准确性实践
  • ECS架构解析:从数据驱动到游戏开发实战
  • 第二部分-Docker核心原理——11. 容器存储原理
  • Python 开发者五分钟上手 Taotoken 多模型调用教程
  • Arm CoreLink MHU-320AE寄存器编程与安全机制详解
  • PINGPONG基准:评估AI模型多语言代码理解能力
  • 强化学习在物理奥赛解题中的应用与优化
  • ARM VCMLA指令解析:向量复数乘加的硬件加速技术
  • LangChain生态实战指南:从Awesome列表到AI应用开发
  • 嵌入式开发避坑:W25Q64 Flash跨页读写代码实战(附完整C语言示例)
  • G-Helper深度解析:华硕笔记本性能调优的轻量化终极解决方案
  • 08-MLOps与工程落地——特征存储:Hopsworks
  • 避开这些坑!在Windows和Linux上编译open62541 OPC UA项目的完整指南
  • 【AI 健康毕设】基于可穿戴传感数据的睡眠质量分析与改善建议系统:PyTorch、FastAPI、Vue、MySQL
  • spacy-llm:将大语言模型无缝集成到spaCy NLP框架的工程实践
  • 多语言代码转换数据集构建与评估实践
  • 多智能体强化学习中的上下文合作机制解析
  • CasaOS应用商店深度解析:从Docker Compose原理到社区贡献实战
  • 数据清洗实战:用OpenRefine的‘文本归类’和‘自定义归类’功能,5分钟清理上万条用户标签
  • PRIS框架:智能优化文本到视觉生成的提示工程
  • 嵌入式图像处理利器SharpClaw:i.MX平台硬件加速实战
  • ARM架构TRBE跟踪缓冲区机制与时间戳处理详解
  • 2026年4月工业省电空调品牌推荐,服务好的工业省电空调供应商 - 品牌推荐师
  • 从继电器到可控硅:用2N6073B改造你的220V交流灯控项目,附完整Arduino驱动代码