Docker核心技术:Docker原理之Union文件系统

云原生学习路线导航页(持续更新中)

本文是 Docker核心技术 系列文章:主要讲解了docker文件系统的基本原理,包括:docker镜像的分层结构、docker容器的rootfs核心机制、unionFS的核心原理等,并以overlayFS 为例讲解了文件系统联合的过程,最后介绍了oci标准与docker引擎架构变化

  • 希望大家多多 点赞 关注 评论 收藏,作者会更有动力继续编写技术文章

1.Docker容器镜像的创举

  • Docker容器最大的创新,就是 使用镜像分层,解决了部署时文件分发的通用难题

    • Dockerfile每一条命令都是一个镜像层,都有一个唯一的checksum。拉取每层镜像前,会先检查这个checksum是否存在,存在的话直接使用缓存,不用再拉取了

    • 假如OS基础镜像层有500MB,你每次变更业务代码只有5MB,那么部署时拉取新镜像就做到了 增量拉取,速度和资源都得到了节约

  • 为什么说是docker容器的创举?

    • 以前,比如java程序部署时,每个公司有自己的file server,把代码或jar包放到file server。在服务器上手动get或编写agent获取到jar包,然后部署。
    • 不同的应用,需要写不同的脚本实现
    • 使用docker,docker repository相当于file server,docker client相当于agent。docker pull的时候还可以增量拉取。把流程标准化了

2.Docker 容器 rootFS 的核心机制

Linux核心技术:Linux文件系统与bootFS/rootFS 可知,docker 容器文件系统中,只包括rootfs层,不包括bootfs层

2.1.分层结构(关键差异)

  • 相比于linux rootfs结构,docker rootfs 采用了分层结构:
    • 镜像层(只读):容器启动时,以镜像的 rootfs 为不可变基础层,确保镜像文件无法被运行时修改。
    • 容器层(可写):在镜像层之上叠加一个可写层(称为容器层),所有文件变更(如写入、删除)均在此层进行。

2.2.联合挂载(Union Mount)

  • Docker 使用 联合文件系统(如 Overlay2、AUFS)将多层文件系统合并为单一视图。
  • 比如使用 Overlay2 实现联合文件系统:
    • lowerdir:对应镜像的只读层。
    • upperdir:容器的可写层,记录运行时变更。
    • merged:用户看到的统一文件系统视图。

2.3.写时复制(CoW)与 用时分配

  • 读取:直接访问底层镜像文件(无性能损耗)。
  • 修改:将目标文件从镜像层复制到容器层后再修改,原镜像文件保持只读。

2.4.总结:docker容器文件系统的优势

优势 说明
镜像不可变性 镜像层始终只读,确保同一镜像启动的容器环境一致,避免依赖冲突。
资源高效利用 多个容器共享同一镜像层,减少磁盘占用(如 100 个 CentOS 容器共享一个基础层)。
快速部署 容器启动时无需复制完整镜像文件,仅需创建可写层,秒级启动。
版本追溯 通过分层机制记录每次镜像构建的变更(Dockerfile 指令对应层),便于调试和回滚。

3.UnionFS 联合文件系统详解

3.1.Union文件系统是什么

  • Union文件系统(Union File System,简称UnionFS)是一种 ​​分层叠加 的文件系统技术,它通过将多个目录或文件系统(包括只读和可写层)合并到同一挂载点,形成一个逻辑统一的文件视图。其核心特点包括:
    • 分层存储​​:文件系统由多层组成,底层通常是只读的基础文件(如Docker镜像的基础层),上层为可写层(如容器运行时修改)。
    • 写时复制(CoW):修改文件时,系统会先将文件从只读层复制到可写层进行变更,原文件保持不变,确保高效存储和隔离性。
    • 优先级叠加 :访问文件时,高层内容覆盖低层同名文件,用户看到的是最终叠加结果。

3.2.容器存储驱动

3.2.1.容器存储驱动是什么

  • 容器存储驱动是容器引擎(如 Docker、Containerd)的核心组件,负责管理容器文件系统的存储和操作
  • 核心功能包括:
    • 分层管理:实现镜像分层(只读层)与容器运行时层(可写层)的叠加。
    • 写时复制(CoW):保证镜像不可变,仅在容器层修改文件时复制数据。
    • 数据持久化:支持通过卷(Volume)或绑定挂载实现数据持久化。
    • 性能优化:根据场景选择不同驱动,平衡 I/O 性能与资源消耗。

3.2.2.容器存储驱动发展历史

  • 早期阶段(2013-2015)
    • AUFS 作为 Docker 初始驱动,解决了分层存储需求,但依赖非主线内核补丁,兼容性差。
  • 标准化阶段(2015-2017)
    • OverlayFS 进入 Linux 主线内核(3.18+),逐步取代 AUFS 成为主流。
    • Device Mapper 因支持块存储特性,成为企业级场景的选择。
  • 多样化阶段(2018 至今)
    • Btrfs/ZFS 提供高级功能(快照、压缩),但资源消耗较高。
    • Containerd 作为独立运行时,简化驱动支持(如默认放弃 AUFS)。
  • 现状:OverlayFS 2已经成为主流,容器驱动的不二之选

3.2.3.主流存储驱动特点与对比

驱动名称 支持场景 核心优点 核心缺点
AUFS Docker(仅 Ubuntu/Debian) 成熟的分层设计,节省存储空间 非主线内核支持,性能在密集 I/O 场景差
OverlayFS Docker/Containerd(Linux 3.18+) 内核原生支持,性能均衡,兼容性好 不支持页缓存共享,多层叠加性能下降
Device Mapper Docker/Containerd(RHEL/CentOS) 块级存储,适合生产环境高负载 配置复杂,需预分配存储池,空间利用率低
Btrfs Docker(社区版 Debian/Ubuntu,企业版 SLES)/Containerd 支持快照、压缩,适合开发测试 稳定性风险,修复复杂问题需重启
ZFS Docker(Linux/FreeBSD) 数据完整性校验,支持高级存储池管理 内存占用高,需专用内核模块

3.2.4.不同存储驱动优缺点对比

  • Overlay:kernel自带,不需要额外安装,使用容器只需要额外安装docker/containerd等

3.3.OverlayFS

3.3.1.OverlayFS 是什么

  • OverlayFS是一种联合文件系统,核心功能就是 通过覆盖机制实现上下层文件的联合,得到一个终态的文件系统
  • Overlay:覆盖的意思
  • OverlayFS的分层
    • lower层:对应容器的镜像层,dockerfile的每一条指令都是一个新的只读层,合并在一层统一为lower层
    • upper层:对应容器的容器可写层
    • 合并层:最终mount到容器中的层,对应容器能够看到的目录和文件。
  • OverlayFS文件覆盖策略
    • 如果一个文件 只在下层 存在,则会直接被合并
    • 如果一个文件 只在上层 存在,则会直接被合并
    • 如果一个文件 在上下层 都存在,则上层会覆盖下层,优先级更高

3.3.2.OverlayFS 文件系统练习

bash 复制代码
mkdir upper lower merged work
echo "from lower" > lower/in_lower.txt
echo "from upper" > upper/in_upper.txt
echo "from lower" > lower/in_both.txt
echo "from upper" > upper/in_both.txt
# mount
sudo mount -t overlay overlay -o lowerdir=`pwd`/lower,upperdir=`pwd`/upper,workdir=`pwd`/work `pwd`/merged
# 查看 overlay 文件系统生成结果
tree
.
├── lower
│   ├── in_both.txt
│   └── in_lower.txt
├── merged
│   ├── in_both.txt
│   ├── in_lower.txt
│   └── in_upper.txt
├── upper
│   ├── in_both.txt
│   └── in_upper.txt
└── work
    ├── index
    └── work

6 directories, 7 files

cat merged/in_both.txt 
from upper

cat merged/in_lower.txt 
from lower

cat merged/in_upper.txt 
from upper
  • mount 是 Linux 系统中用于将存储设备(如硬盘、U盘、ISO 镜像等)或远程文件系统挂载到指定目录的命令。挂载后,用户可以通过该目录访问存储设备中的内容。

    bash 复制代码
    mount -t <文件系统类型> -o <选项> <设备路径> <挂载点目录>
  • 上述 mount 命令的参数解释

    • lowerdir:只读的底层目录(可多个),文件按顺序从下到上叠加(/lower1 在最底层,/lower2 在其上)。
    • upperdir:可写的上层目录,所有修改(新增、删除、修改文件)会记录在此。
    • workdir:供 OverlayFS 内部使用的临时目录,必须与 upperdir 在同一文件系统。
    • /merged:最终的挂载点,用户在此访问合并后的文件系统视图。

3.3.3.如何进入 docker 容器的mount ns

  • 从上面的学习和练习,可以看出,docker容器的文件系统,其实就是docker image lower层+容器可写层,使用mount命令合并之后的merged文件系统。然后这个merged系统被挂载到了容器的mount ns中,所以容器进程能看到和主机不一样的文件系统
  • 那么如何进入容器的 mount ns,查看merged之后的文件系统?
    • Docker核心技术:Docker原理之Namespace 中,学习了使用nsenter命令进入ns的方法

    • nsenter -t ${pid} -m 即可进入一个进程的mount ns

    • 比如:

      bash 复制代码
      [root@VM-226-235-tencentos /proc/11913/ns]# nsenter -t 11913 -m
      -bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
      root@VM-226-235-tencentos:/#
      root@VM-226-235-tencentos:/# ls
      bin  boot  dev  docker-entrypoint.d  docker-entrypoint.sh  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var  zgy

3.3.4.如何查看一个 docker 容器的overlay细节?

  • docker inspect ${container-id},输出中GraphDriver展示了overlay细节
  • GraphDriver 是 Docker 用于管理容器镜像分层存储的核心驱动模块,负责将镜像的只读层与容器的可写层叠加为统一视图。其输出信息包括:
    • 存储驱动类型(如 overlay2、aufs),决定了分层合并的实现方式
    • 具体存储路径(如 UpperDir、MergedDir),指向容器可写层和合并后的文件系统目录
    • 镜像层 ID,记录镜像分层结构(如 LowerDir 中的多个只读层)
  • 如:

3.4.Docker 容器采用Union文件系统 + mount ns的好处

  • 采用 Union文件系统 解决了文件更新效率的问题
  • 采用 mount ns 解决了docker容器可移植性问题。
    • 一个容器,只需要把自己所依赖的所有命令、文件、软件...都放入自己的mount ns,然后将mount ns打包压缩,就可以移植到任何的可兼容系统中
    • 容器不需要关心底层os是否包含某些命令和软件,它出生就自带了

4.OCI 容器标准

4.1.OCI 标准是什么

  • docker作为先行者和商业公司,具有盈利性质,产品更新迭代很快
  • google等作为后来者,想要分市场,只能通过标准化来介入。OCI就是这样的背景下,由google牵头,找大厂背书,搞出来的一个容器标准。
    • docker小公司抵挡不住google的攻击,妥协后也接受了OCI标准,并把自己的容器运行时抽取为containerd

4.2.docker 引擎架构

  • 早期的docker架构
    • docker daemon是主进程,任何的容器进程都是docker daemon作为父进程 fork出来的子进程
    • 此时如果docker daemon自身要升级,杀掉父进程,所有的子进程就都会有问题
  • 改进后的docker架构
    • 后来引入了一个containerd组件,并引入了一个 dockershim 的概念。containerd只是一个单纯的daemon,它在创建容器进程的时候,会同时创建一个dockershim进程,该进程作为容器进程的主进程。
    • 这样 容器进程 与 containerd 解藕开了,升级containerd不会影响到dockershim,也就不会影响到具体的容器进程。而dockershim本身非常轻量级,基本不需要升级
    • 查看下容器的进程树结构,验证是否是这样的:
    • 验证是否每个容器都有一个docker shim?

5.docker问题解析

5.1.容器的1号进程是什么

  • 容器的1号进程是它的entrypoint进程
  • 为什么有些容器应用进程就是1号,有些容器应用进程不是1号呢?
    • 有些容器entrypoint直接就指定了应用进程
    • 有些容器是在entrypoint中指定一个启动脚本,由脚本拉起应用进程,所以应用进程就不是1号了
相关推荐
一水鉴天10 小时前
整体设计 逻辑系统程序 之18 Source 容器(Docker)承载 C/P/D 三式的完整设计与双闭环验证 之2
docker·架构·认知科学·公共逻辑
飞快的蜗牛11 小时前
利用linux系统自带的cron 定时备份数据库,不需要写代码了
java·docker
火星MARK11 小时前
k8s面试题
容器·面试·kubernetes
香吧香12 小时前
Docker Registry 使用总结
docker
赵渝强老师13 小时前
【赵渝强老师】Docker容器的资源管理机制
linux·docker·容器·kubernetes
haicome14 小时前
deepseek部署
docker·ragflow·deepseek 部署
乄bluefox14 小时前
保姆级docker部署nacos集群
java·docker·容器
每天进步一点_JL15 小时前
Docker 是什么?
后端·docker·容器
一叶飘零_sweeeet15 小时前
从 0 到 1 掌控云原生部署:Java 项目的 Docker 容器化与 K8s 集群实战指南
docker·云原生·kubernetes·项目部署
森林猿16 小时前
docker-compose-kafka 4.1.0
docker·容器·kafka