Skip to content
60.Docker»10.文件系统»LV020-镜像分层.md

LV020-镜像分层

一、概述

1. 镜像分层的简单直观体现

在执行 docker pull 时,会发现多个 Pull complete 字样,就能体现分层,如果是一个文件,只会有一个 Pull complete 。

shell
  /workspace git:(main) docker pull alpine
Using default tag: latest
latest: Pulling from library/alpine
2d35ebdb57d9: Pull complete
Digest: sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
Status: Downloaded newer image for alpine:latest
docker.io/library/alpine:latest

  /workspace git:(main) docker pull redis
Using default tag: latest
latest: Pulling from library/redis
abe1fea37542: Pull complete
3db098501f01: Pull complete
ca19d868f352: Pull complete
b89fb5fc7ff7: Pull complete
4b4668b306bc: Pull complete
4f4fb700ef54: Pull complete
71e796aaa0ba: Pull complete
Digest: sha256:4521b581dbddea6e7d81f8fe95ede93f5648aaa66a9dacd581611bf6fe7527bd
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest

2. 文件系统

文件系统是计算机系统中用于组织和管理数据存储的一种方式。它定义了数据如何存储、命名、访问和修改的方式。如 Windows 自带的 NTFS、FAT32、EXFAT,和 Linux 中的 EXT3、EXT4 这种的。

前面已经在 Docker 中,使用的是联合文件系统 UnionFileSystem,它是一种分层的高性能文件系统,其实是一种为 Linux 操作系统设计的用于把多个文件系统『联合』到同一个挂载点的文件系统服务。docker 用它作为镜像的存储方式,使得容器在运行时只需存储修改过的部分,而不是整个文件系统,从而节省存储空间并提高效率。

目前 docker 支持的联合文件系统有很多种,包括:AUFS、overlay、overlay2、DeviceMapper、VSF 等

二、分层存储简介

Docker 镜像是由一系列分层存储(Layered Storage)构成的,这种特殊的设计使得 Docker 镜像在管理和传输时具备高效性和灵活性。

1. 分层存储的概念

分层存储是 Docker 镜像的核心组成原理之一。每个 Docker 镜像由多个层叠加而成,每一层代表一个文件系统的快照。这些层共同构成了一个完整的镜像文件系统。

每个镜像层都是只读的,当容器运行时,会在镜像层之上再添加一个可写层,用于容器的写操作。这样,多个容器可以共享同一个只读的镜像层,同时拥有各自的可写层,实现资源的高度共享和隔离。

分层存储的设计使得 Docker 镜像具有可复用性,相同的镜像层可以被多个镜像共享,节省了存储空间,同时降低了镜像的传输时间,提高了镜像的传输效率。

2. 分层存储的优势

分层存储在 Docker 中发挥着重要的作用。它带来了以下几个主要优势:

镜像的复用:由于分层存储,当多个镜像共享相同的基础层时,它们只需要在本地存储中保存一份基础层的副本,避免了重复存储相同内容的问题。这样的设计显著降低了镜像的存储需求。

镜像的传输效率:镜像的分层存储结构允许只传输更改的层,而不是整个镜像,因此在传输时只需要传输更新的部分,减少了传输的数据量,提高了传输效率。

镜像的版本管理:每个镜像层都是只读的,当需要更新或修改镜像时,只需新增一层来覆盖原有的层,这样就可以实现版本管理,方便用户管理和回滚镜像的不同版本。

3. 为什么可以节省空间?

分层存储节省存储空间的原理在于镜像的分层结构。当多个镜像共享相同的基础层时,它们只需要在本地存储中保存一份基础层的副本,因为这些镜像所依赖的基础层是只读的,不会发生变化。

例如,假设有两个镜像 A 和 B,它们都使用了相同的基础镜像 C 作为底层。在存储中,镜像 A 和 B 分别保存自己的特定层以及指向镜像 C 的指针。实际上,镜像 C 的内容在存储中只保存一份,但由于镜像 A 和 B 都依赖于这份内容,它们共享了同一个基础层,从而节省了存储空间。

综上所述,分层存储是 Docker 镜像的核心设计之一,它通过共享、复用和增量更新的方式,实现了高效的存储和传输。

三、基本原理

简单来说就是,在 BootFS(宿主机提供)的基础上,利用 UFS 封装 RootFS,使用镜像层的数据,叠加出一个可写的容器层。即:

  • 每一层是 只读的(read-only)
  • 最上层是容器的 可写层(write layer)
  • 所有层叠加后对外表现为一个完整的文件系统

当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。

典型的 Linux 在启动后,首先将 rootfs 置为 readonly, 进行一系列检查, 然后将其切换为 “readwrite” 供用户使用。在 docker 中,起初也是将 rootfs 以 readonly 方式加载并检查,然而接下来利用 union mount 的将一个 readwrite 文件系统挂载在 readonly 的 rootfs 之上,并且允许再次将下层的 file system 设定为 readonly 并且向上叠加, 这样一组 readonly 和一个 writeable 的结构构成一个 container 的运行目录, 每一个被称作一个 Layer。如下图

在这里插入图片描述

从目录的角度来说,大致如下图所示:

在这里插入图片描述

所有对容器的改动,无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。

镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相同路径的文件,比如 /a,上层的 /a 会覆盖下层的 /a,也就是说用户只能访问到上层中的文件 /a。在容器层中,用户看到的是一个叠加之后的文件系统。

添加文件: 在容器中创建文件时,新文件被添加到容器层中。

读取文件: 在容器中读取某个文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后打开并读入内存。

修改文件: 在容器中修改已存在的文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改之。

删除文件: 在容器中删除文件时,Docker 也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。

只有当需要修改时才复制一份数据,这种特性被称作 Copy-on-Write。可见,容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改。这样容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享。

四、Dockerfile 与镜像分层

当构建 Docker 镜像时,每执行一条 Dockerfile 指令(如 RUNCOPYADD),Docker 就会创建一个新的 镜像层(Layer)。这些层像“积木”一样一层层叠加,最终形成完整的镜像结构。例如:

dockerfile
# 1.新镜像不再是从 scratch 开始,而是直接在 Debian base 镜像上构建。
FROM debian                
MAINTAINER wzlinux

# 2.安装 emacs 编辑器。
RUN apt-get update && apt-get install -y emacs       

# 3.安装 apache2。
RUN apt-get install -y apache2

# 4.容器启动时运行 bash。
CMD ["/bin/bash"]
img

新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。所以其实每个 RUN 都会创建新层, 可以通过 && 合并命令,减少层数并清理缓存,以此来优化 Docker 构建速度。

dockerfile
RUN apt-get update && \
    apt-get install -y curl vim && \
    rm -rf /var/lib/apt/lists/*

五、查看镜像层

1. 查看镜像详情

shell
docker image inspect myapp

2. 图形方式查看层结构

bash
docker history myapp

【例】

shell
  /workspace git:(main)  docker history ubuntu:16.04
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
b6f507652425   4 years ago   /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B        
<missing>      4 years ago   /bin/sh -c mkdir -p /run/systemd && echo 'do…   7B        
<missing>      4 years ago   /bin/sh -c rm -rf /var/lib/apt/lists/*          0B        
<missing>      4 years ago   /bin/sh -c set -xe   && echo '#!/bin/sh' > /…   745B      
<missing>      4 years ago   /bin/sh -c #(nop) ADD file: 11b425d4c08e81a3e…   135MB

参考资料:

万字长文深入理解 Docker 镜像分层原理、容器数据卷、网络通信架构(Docker 系列第 2 章,共 3 章) - 小松聊 PHP 进阶 - 博客园

docker 文件系统分层存储原理 - 塔克拉玛攻城狮 - 博客园

Docker 底层:Overlay2 文件系统原理 - 拾月凄辰 - 博客园

Docker 底层:联合文件系统 - 拾月凄辰 - 博客园

Docker 原理(图解+秒懂+史上最全)-CSDN 博客

Docker 镜像解密:分层存储与镜像构建原理-腾讯云开发者社区-腾讯云

莫道桑榆晚 为霞尚满天.