LV020-镜像分层
一、概述
1. 镜像分层的简单直观体现
在执行 docker pull 时,会发现多个 Pull complete 字样,就能体现分层,如果是一个文件,只会有一个 Pull complete 。
➜ /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:latest2. 文件系统
文件系统是计算机系统中用于组织和管理数据存储的一种方式。它定义了数据如何存储、命名、访问和修改的方式。如 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 指令(如 RUN、COPY、ADD),Docker 就会创建一个新的 镜像层(Layer)。这些层像“积木”一样一层层叠加,最终形成完整的镜像结构。例如:
# 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"]
新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。所以其实每个 RUN 都会创建新层, 可以通过 && 合并命令,减少层数并清理缓存,以此来优化 Docker 构建速度。
RUN apt-get update && \
apt-get install -y curl vim && \
rm -rf /var/lib/apt/lists/*五、查看镜像层
1. 查看镜像详情
docker image inspect myapp2. 图形方式查看层结构
docker history myapp【例】
➜ /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 文件系统分层存储原理 - 塔克拉玛攻城狮 - 博客园