Ubuntu容器说明
回顾一下,容器只不过是一个正在运行的进程,它还应用了一些附加的封装功能,以使其与主机和其他容器保持隔离。而镜像包含运行应用程序所需的所有内容——代码或二进制文件、运行时、依赖项以及所需的任何其他文件系统对象。
实际上在实际使用中,我们使用镜像创建容器,非常像使用一个新的系统镜像(这里的镜像包含了运行程序的所有内容)来安装系统(创建容器)
有镜像才能创建容器,这是根本前提
那么这些容器的关系是什么情况,如下图

推荐 Ubuntu,因为 CentOS 太大了
貌似现在你在 Ubuntu 装 docker,就会自带一个 Ubuntu 的镜像上的 Ubuntu 容器
Docker镜像加载原理
镜像基本
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件,这个打包好的运行环境就是 image 镜像文件。只有通过这个镜像文件才能生成 Docker 的镜像实例。
以我们的pull为例,在下载的过程中我们可以看到docker的镜像好像是在一层一层的在下载

发现镜像是一层一层的在下载的,镜像是分层的,这与docker的镜像加载原理有关。
UnionFS 联合文件系统
Union文件系统是一种分层、轻量级并且高性能的文件系统,它的核心功能是将多个独立的文件系统(目录)“联合” 挂载到同一个目录下,形成一个逻辑上的单一文件系统。用户操作这个挂载点时,会感觉访问的是一个完整的文件系统,而实际数据可能分布在多个底层目录中。
Union文件系统是 Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
而且它一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
核心特性
- 分层存储:支持将多个目录(称为 “层”)按顺序叠加,每层可以是只读或可写的。
- 写时复制(Copy-on-Write,CoW):当修改只读层中的文件时,系统会先将该文件复制到可写层再进行修改,避免直接修改底层只读数据,节省空间并保证底层数据的安全性。
- 透明性:用户无需关心数据实际存储在哪个层,操作逻辑与单一文件系统一致。
我们怎么理解其中的工作原理呢
假设存在三个层,从上到下依次为:
- 可写层(Writable Layer)
- 只读层 1(Read-only Layer 1)
- 只读层 2(Read-only Layer 2)
当用户访问或修改文件时:
- 读取文件:系统从顶层开始向下查找,找到第一个包含该文件的层并返回内容。
- 修改文件:若文件在只读层,系统先将其复制到可写层,再在可写层中修改;若文件已在可写层,则直接修改。
- 删除文件:并非真正删除底层文件,而是在可写层中标记 “删除”,后续查找时会忽略底层的该文件。
Docker镜像分层的特点
Docker 通过 UnionFS(或其变种,如 AUFS、OverlayFS、Device Mapper 等)实现镜像层的联合挂载,使多个只读层和容器可写层看起来像一个完整的文件系统。
因为 Docker 镜像的设计直接依赖 UnionFS 的分层特性,其核心思想是将镜像拆分为多个只读层,通过复用和叠加实现高效存储与分发。
镜像分层的构成
基础层:通常是操作系统的基础镜像(如
ubuntu:20.04
),包含内核、系统工具等基础文件。中间层:通过
Dockerfile
指令(如RUN
、COPY
、ADD
)生成的层,每个指令对应一个只读层。例如:1
2
3FROM ubuntu:20.04 # 基础层
RUN apt-get update # 中间层1
COPY app.py /app/ # 中间层2顶层:镜像本身没有可写层,只有当容器启动时,Docker 才会在镜像的所有只读层之上添加一个可写层(容器层),容器内的所有修改(如新建、删除、修改文件)都只发生在这一层。
关键特性
- 只读性:镜像的所有层均为只读,确保镜像的一致性和可复用性。多个容器可以共享同一镜像的底层,节省磁盘空间。
- 层复用:不同镜像若包含相同的中间层(如都执行过
apt-get update
),Docker 会只存储一份该层,避免重复存储。 - 与容器的关系:容器启动时,Docker 在镜像层之上添加可写层,容器的修改仅保存在可写层。当容器删除时,可写层被清理,镜像层不受影响。
所有的 Docker镜像都起始于一个基础镜像层,进行修改或增加新的内容时,就会在当前的镜像层之上,创建新的镜像层。
假设有一个基于 ubuntu:20.04
的镜像,包含 3
个只读层:
- 基础层(ubuntu 系统文件)
- 层 1(
RUN apt-get install python3
) - 层 2(
COPY app.py /app/
)
- 当启动容器时,添加可写层(层 4)。
- 若容器内执行
echo "hello" >> /app/app.py
,系统会将层 2 中的app.py
复制到层 4,再修改层 4 中的文件。 - 若容器内删除
/app/app.py
,系统仅在层 4 中标记该文件为 “删除”,层 2 中的文件实际仍存在。
镜像加载原理
docker 的镜像实际上由一层一层的文件系统组成,上面说过了这种层级的文件系统就是UnionFS,这个系统决定了 Docker 镜像的分层特性。
bootfs(boot file system)
主要包含
bootloader
和 kernel
,bootloader
主要是引导加载 kernel
,Linux 刚启动时会加载
bootfs
文件系统,在 Docker
镜像的最底层是引导文件系统 bootfs 。这一层与我们典型的 Linux /
Unix 系统是一样的,包含 boot 加载器和内核。当 boot
加载完成之后,整个内核就都在内存中了,此时内存的使用权已由
bootfs
转交给内核,此时系统也会卸载
bootfs
。
rootfs(root file system)
,在 bootfs 之上,包含的就是典型
Linux 系统中的 /dev,/proc,/bin,/etc
等标准目录和文件。
rootfs 就是各种不同的操作系统发行版,比如 Ubuntu,Centos等等。
这两个文件系统是 linux 内核的核心,这也是为什么 docker 镜像加载 Ubuntu才不到80兆,就是因为只有linux的核心内容,只包含最基本的命令、工具和程序库。
而镜像分层最大的一个好处就是资源共享,这样就很好,大部分基础镜像做好了的内容,其他镜像也可以由相同的 base 镜像构建而来,docker 只需要在磁盘上保留一份 base 镜像,再在内存中保留一份镜像,就可以为所有容器服务了,而且镜像的每一层都可以被共享。
而 Docker 镜像是制度的,是因为 docker 镜像得到每一层都是只读的,叫只读层,而容器层是可写的,容器层会在容器启动的时候,以可写层的方式加载到镜像的顶部。容器层之下都叫镜像层。
Docker
镜像并非一个单一文件,而是由多个只读层(Layer)
和元数据(Metadata) 组成,存储在 Docker
的本地仓库(通常位于 /var/lib/docker/
目录下)。
镜像层(Layer)
- 每个镜像层对应
Dockerfile
中的一条指令(如RUN
、COPY
、ADD
等),是一组文件和目录的集合,以tar 包形式存储(解压后为具体文件)。 - 每层都有一个唯一的 SHA256 哈希值作为标识,例如
a1b2c3...
,存储路径通常为/var/lib/docker/overlay2/[哈希值]/diff/
(不同存储驱动路径略有差异)。 - 层与层之间通过父层指针关联,形成一个链式结构(如层 3 的父层是层 2,层 2 的父层是层 1,以此类推)。
元数据(Metadata)
- 镜像配置文件(Config):记录镜像的基础信息,如层的顺序、默认启动命令(
CMD
)、环境变量、入口点(ENTRYPOINT
)等,以 JSON 格式存储,路径通常为/var/lib/docker/image/overlay2/imagedb/content/sha256/[镜像ID]
。 - 镜像索引(Index):用于关联不同平台的镜像(如 amd64、arm64 架构),确保拉取时匹配当前系统架构。
- 层元数据:记录层的父层哈希、创建时间、大小等信息,帮助 Docker 识别层的依赖关系。
镜像加载的流程可以按照如下理解,当通过
docker run [镜像名]
启动容器时,Docker
会按以下步骤加载镜像并准备文件系统:
解析镜像元数据
- Docker 首先根据镜像名(或 ID)查找本地仓库中的镜像配置文件,确认镜像包含的所有层及其顺序(从基础层到顶层)。
- 检查所有层是否已本地存在:若缺失,会从远程仓库拉取(通过哈希值匹配,仅拉取缺失层)。
准备联合挂载的目录结构
- 确定 lowerdir:将镜像的所有层按顺序组合为 OverlayFS
的
lowerdir
(例如,基础层在最底部,后续层依次叠加)。 - 创建 upperdir 和
workdir:在容器专属目录下生成可写层(
upperdir
)和工作目录(workdir
),路径通常为/var/lib/docker/overlay2/[容器ID]/upper/
和/work/
。 - 创建
mergedir:生成联合挂载的入口目录(
mergedir
),路径通常为/var/lib/docker/overlay2/[容器ID]/merged/
,容器内的/
根目录即映射到此处。
- 确定 lowerdir:将镜像的所有层按顺序组合为 OverlayFS
的
执行联合挂载
通过 OverlayFS 的挂载命令(如
mount -t overlay overlay -o lowerdir=...,upperdir=...,workdir=... mergedir
)将lowerdir
(镜像层)、upperdir
(可写层)联合挂载到mergedir
。此时,容器通过
mergedir
访问的文件系统,是所有镜像层和可写层的逻辑合并,用户无需关心文件实际存储在哪个层。
处理文件操作(基于 CoW 机制)
容器运行时对文件的操作(读、写、删)会触发 OverlayFS 的写时复制机制:
读取文件:从
mergedir
访问文件时,OverlayFS 会从lowerdir
顶层开始向下查找,找到第一个包含该文件的层并返回内容。修改文件:若文件在
lowerdir
(镜像层)中,OverlayFS 会先将文件复制到upperdir
(可写层),再修改upperdir
中的副本,原镜像层文件不变。删除文件:若删除
lowerdir
中的文件,OverlayFS 会在upperdir
中创建一个特殊的 “删除标记”(whiteout 文件),后续查找时会忽略lowerdir
中的该文件,实际镜像层文件未被删除。
Docker 镜像加载的核心是通过联合文件系统(如 OverlayFS)将多个只读镜像层联合挂载为一个逻辑文件系统,并通过写时复制(CoW)机制确保容器修改仅作用于顶层可写层,不影响底层镜像。