Docker学习笔记(持续更新中~)

September 17, 2026

Docker

容器基础

容器本质上就是一个被 Linux Namespace 隔离了视图、被 Cgroups 限制了资源、基于联合文件系统(rootfs)运行的普通宿主机进程 所谓的“进入容器”或“容器间互通”,底层均是对内核 Namespace 归属的动态调整与共享

底层核心

  1. Cgroups(Control Groups,控制组) : Linux 内核提供的物理资源配额与度量机制

    • 作用: 给进程的资源使用画“边界”,限制其能够占用的 CPU 算力、内存大小、磁盘 I/O 和网络带宽等,防止单个容器吃光宿主机资源
  2. Namespace(命名空间): Linux 内核提供的资源隔离机制 作用: 为进程制造“障眼法”,让进程误以为自己独占了系统的某种全局资源(如进程 PID、网络栈、挂载点等)

    • Mount Namespace(挂载命名空间)
      • 含义: Linux 实现的第一个 Namespace 类型(标志位为 CLONE_NEWNS)。
      • 作用: 隔离进程看到的文件系统挂载点列表。在当前 Mount Namespace 内执行的 mount 或 umount 操作,仅在该命名空间内部可见,不会影响宿主机及其他命名空间。
    • PID Namespace:隔离进程编号,容器内部拥有独立的 PID 1。
    • Network Namespace:隔离网络设备与端口,独立 IP、路由表。
    • UTS Namespace:隔离主机名。
    • IPC Namespace:隔离进程间通信(共享内存、信号量)。
    • User Namespace:隔离用户与用户组 ID。

3.UnionFS(Union File System,联合文件系统): Linux 下一种分层、轻量级且高性能的文件系统技术 作用: 为进程提供“分层合并视图”,将多个目录叠加挂载到同一目录下,对外呈现为一个统一的文件系统。

  • 分层堆叠: 镜像各层以只读状态堆叠在底层,实现多容器共享基础镜像,极大节省磁盘空间并加速镜像分发。
  • 写时复制: 修改只读层的某个文件时,系统会先将该文件复制到顶部的可读写层作更改,底层镜像文件保持不变
  • 删除屏蔽: 不会真正删除, 而是在可读写层创建一个白障屏蔽标记, 向容器屏蔽该文件

目录切换的系统调用/命令

  • chroot 只改进程指针: 仅修改当前进程记录的根目录路径,旧根目录依然挂载在系统里。如果有 root 权限,通过保留的文件描述符或 fchdir 就能用 cd ../ 轻易逃逸回宿主机。

  • pivot_root 交换挂载点: 在 Mount Namespace 级别把新目录切换为根挂载点,并将旧宿主机根目录移到指定子目录。容器随后会直接 umount 卸载旧根,彻底断开与宿主机文件系统的物理连接,无法逃逸。

文件系统概念

  • rootfs(Root File System,根文件系统)----最终产物 / 运行环境实体

    • 含义: 操作系统根目录 / 下所包含的所有标准目录和文件集合(如 /bin、/etc、/proc、/lib 等)。
    • 特点: 它只包含操作系统的文件与配置(躯壳),不包含 Linux 内核(灵魂)。容器运行时挂载在根目录上的就是 rootfs,它也是“容器镜像”的物理本质
    • 实现原理: 镜像分层与合并(OverlayFS / UnionFS) -> 挂载隔离(Mount Namespace) -> 视图切换(pivot_root / chroot)
  • UnionFS(Union File System,联合文件系统)----抽象层 / 一类技术的概念统称

    • 含义: 一种为 Linux 设计的分层、轻量级的高性能文件系统。一类技术的统称

    • 作用: 支持将不同物理路径下的多个目录内容“联合挂载(Union Mount)”到同一个挂载点下,呈现为一个统一合并后的目录视图。

    • 核心机制: 分层堆叠、写时复制(Copy-on-Write)、删除标记(Whiteout)

    • AuFS(Advanced UnionFS)----早期 unionfs 的实现

      • 含义: 早期 Docker
      • 特点: 率先实现了成熟的多层堆叠与写时复制能力,但代码复杂,且始终未合并进 Linux 官方内核主线
    • OverlayFS----现代主流实现(Docker 默认 overlay2)

    • 含义: Linux 内核官方原生支持的分层文件系统(以 overlay / overlay2 存储驱动呈现)

    • 特点:

      • 内核原生: 已合入 Linux 主线,稳定且无需第三方补丁
      • 架构精简: 仅由底层只读目录 lowerdir、顶层读写目录 upperdir、工作目录 workdir 及合并视图 merged 组成
      • 高性能: 相比 AUFS,文件跳转更少,内存与 inode 消耗极低

Docker的三层结构

比喻: 只读层就像光源, 可读写层就是遮光的卡片, 透视合并光射出来就是容器最终看到的完整图案 (rootfs)

核心设计思想:从“全量复制”到“增量分层”

  • 痛点: 传统模式下,每次软件修改都要保存一份完整的 rootfs(几百 MB 到几 GB),导致存储极度浪费、版本碎片化。
  • 解法: 像 Git 一样只记录增量。每一层(Layer)只保存相比上一层发生变化的文件。
  • 技术支撑: UnionFS(联合文件系统思想),它能将多个不同物理目录“重叠挂载”到同一个目录下,合成一个统一的文件树视图。

容器 rootfs 的三层架结构

当一个容器运行起来时,存储驱动会将 /var/lib/docker/overlay2/<layer-id>/ 下的相关物理目录, 通过内核系统调用联合挂载到统一视图目录. 整个文件系统从上到下由三部分组成:

层级名称位置与挂载权内容与作用对 docker commit 的影响
可读写层 (Read-Write Layer)最顶层
rw(可读写)
存放容器运行期间所有的增、删、改。初始为空。会被提交:通过 commit 可将其固化为一个新的只读镜像层。
Init 层 (*-init Layer)中间夹层
ro+wh(只读+白障)
存放当前容器实例独立的 ip、主机名...
(如 /etc/hosts、/etc/resolv.conf、hostname)。
不会被提交:专门隔离出来,避免容器专属的临时配置污染新镜像。
只读层 (Read-Only Layers)底部所有层
ro+wh
镜像的基础层(如纯净 Ubuntu 的 5 个基础层)。
由所有从该镜像启动的容器共享。
完全不受影响:多容器共用底包,节省巨量磁盘空间。

核心操作机制:写时复制与白障(Whiteout)

只读层不可修改,那容器内发生文件改动时底层如何处理?

  • 新建文件:直接写在最顶部的“可读写层”。
  • 修改只读文件(Copy-on-Write):把底层的旧文件先复制一份到“可读写层”,然后在可读写层上修改,上层文件会遮挡下层同名文件。
  • 删除只读文件(Whiteout / 白障):
    • 底层文件是只读的,无法真删。
    • 机制:AuFS 在顶部的“可读写层”创建一个带前缀的掩码文件(例如删除 foo 时生成 .wh.foo)。
    • 效果:在最终联合挂载的视图里,.wh.foo 会把下层的 foo 彻底遮挡隐藏,应用感知上文件已被删除。

总结

Docker 镜像的精髓在于 “分层存储、联合呈现、写时复制、白障遮蔽”:

  1. 只读层保证了基础镜像的不可变性与多容器共享;
  2. Init 层巧妙解决了单机专属配置与镜像提交的解耦;
  3. 可读写层配合 Whiteout 实现了轻量级的动态修改与增量分发。

Volum机制

允许将宿主机上指定的目录或者文件,挂载到容器里面进行读取和修改操作。

  • 解决的核心矛盾:容器 rootfs 的隔离性使容器与宿主机无法直接共享文件,且容器销毁后可读写层数据即丢失。Volume 用于实现数据持久化与主机-容器数据共享。

  • 声明方式与存储位置:

    • Bind Mount(docker run -v /home:/test):直接将指定的宿主机绝对路径 /home 挂载进容器。
  • 实现时机与关键技术:

    1. 容器初始化进程(dockerinit)在开启 Mount Namespace 之后、执行 chroot(或 pivot_root)之前执行挂载。(在rootfs准备好之后, chroot执行之前, bind mount 在宿主机挂载一个Volum)
    2. 使用 Linux mount --bind(绑定挂载),本质上是将挂载点目录的 dentry 指针重定向到宿主机目录的 inode。
    3. 由于挂载发生在容器的 Mount Namespace 内,该挂载点对宿主机不可见。(类似容器内部的一个指针指向宿主机的一个地址, 而宿主机没有这个指针, 所以宿主机不可见)
  • 为何 docker commit 不会提交 Volume 内的数据:

    • docker commit 发生在宿主机空间,宿主机看不到容器内部的 Bind Mount 映射关系,只能看到可读写层下原本为了作为挂载点而创建的空目录 /test。因此,新镜像只会包含一个空的 /test 目录。
GitHub
LinkedIn
X