3.1 镜像分层存储与联合文件系统


3.1 镜像分层存储与联合文件系统

本节摘要:镜像不是一整块文件系统,而是只读层的堆叠;容器运行时只是在最上面加了一层可写层。本节是装箱车间的地基:用两条命令亲眼看分层,用一组实验验证可写层的规则,理解分层带来的存储与传输红利。

装箱之前,先拆一只箱子

主线任务开工前先拆解样本——不拆开看懂结构,后面写的装箱单就只是照抄模板。本节回答三个问题:镜像由什么构成?容器写文件写到了哪里?分层到底省了什么?

图 3-1:镜像分层与容器可写层的堆叠结构

图 3-1:镜像分层与容器可写层的堆叠结构

亲眼看见分层

空口无凭,两条命令把分层摆上桌面。第一条,history 列出一个镜像每一层是谁造的:

# 查看 nginx alpine 镜像的层历史(自下而上读) docker history nginx:1.25-alpine # IMAGE CREATED SIZE CREATED BY # 0c7ba8a3f7e2 3 weeks ago 1.4kB CMD ["nginx" "-g" "daemon off;"] # <missing> 3 weeks ago 0B EXPOSE map[80/tcp:{}] # <missing> 3 weeks ago 49MB RUN /bin/sh -c 配置并安装 nginx # <missing> 3 weeks ago 7.1MB RUN /bin/sh -c 安装运行依赖包 # <missing> 3 weeks ago 5.6MB RUN /bin/sh -c 升级并安装基础包 # <missing> 3 weeks ago 7.6MB /bin/sh -c #(nop) ADD file:xxx in /

SIZE 一列就是每一层的"重量":底层是基础系统的文件,中层是安装动作的产物,顶层往往只是元数据声明(0B 或几 KB)。第二条,inspect 直接数层数:

# 数出镜像的层数 docker inspect nginx:1.25-alpine --format "层数: {{len .RootFS.Layers}}" # 输出示例:层数: 7

"missing" 字样不是错误——那些层在公共堆场上是共享的,你本地拉货时引擎已经核验过内容摘要,直接复用了本地已有的相同层。这正是分层的传输红利:两批镜像若共享底层,提第二票时底层不用再下载

共享层的现场验证

"共享底层"不是文档里的修辞,提货日志里看得见。先提一票基于 debian 底座的镜像,再提一票同底座的自家镜像,对比两段日志:

# 第一票:底座层首次入库 docker pull myshop/base-web:1.0 # 7dbc3b3f0c1a: Pull complete <- 基础系统层,走网络下载入库 # 第二票:同底座的另一票货 docker pull myshop/base-api:2.1 # 7dbc3b3f0c1a: Already exists <- 同一底层,一字节都没走网络 # 91ef0af61f39: Pull complete <- 只有自家改动层真正下载

Already exists 就是复用的现场证据。存储一侧的账本也能查:

# 引擎的存储账本:镜像、容器、卷各占多少 docker system df # TYPE TOTAL SIZE # Images 41 3.2GB <- 共享层去重后的合计 # Containers 3 156MB # Local Volumes 6 402MB

若镜像层不共享、每票货各带全套底座,SIZE 一列早就爆了。两段输出合起来,正是"分层 + 内容寻址"在传输与存储两侧的实测证据,也是第 6 章多服务编队敢放开手脚铺开的基础。

可写层的规矩:写时复制

容器跑起来后,所有写操作落在可写层。底层文件被修改时,引擎不会动只读层,而是把文件复制到可写层再改——这个机制叫写时复制。做个实验验证容器数据的"短命"属性:

# 起一只箱子,在容器里写一个文件 docker run -d --name lab01 nginx:1.25-alpine docker exec lab01 sh -c "echo 临时数据 > /tmp/note.txt" # 确认文件存在 docker exec lab01 cat /tmp/note.txt # 输出:临时数据 # 拆箱重建同一镜像的容器 docker rm -f lab01 docker run -d --name lab02 nginx:1.25-alpine # 新箱子里那个文件还在吗? docker exec lab02 cat /tmp/note.txt # 输出:cat: can't open '/tmp/note.txt': No such file or directory

文件没了——它写在 lab01 的可写层里,拆箱即焚。这个实验直接解释了第 5 章数据卷存在的必要性:凡是需要比容器活得久的数据,都不许放可写层

另一个值得知道的细节:可写层还有"读放大"的代价。容器里高频写大量小文件会让可写层膨胀、拖慢 I/O,这也是生产规范要求"日志、缓存、数据库文件一律外挂卷"的技术原因之一。

分层还有一面容易被忽略的价值:可审计性。每一层都是一次有据可查的变更——谁在哪个基础镜像上、执行了什么命令、产生了什么文件。安全团队据此可以逐层审查一个镜像的血统:底座是官方的哪一版、中间装了什么包、最后塞进了什么文件。出了漏洞,按层定位、按层重建,比在"一整台装好的机器"里考古快得多。这也是为什么镜像扫描工具能给出精确到层的报告——分层结构天然就是审计的账本。

分层省了什么

把红利归拢成三笔账。存储账:同一镜像起一百只容器,只读层磁盘上只有一份,一百个容器各出一份薄薄的可写层——比每只容器复制整套文件系统省一到两个数量级。传输账:堆场提货按层下载,公共底层(比如大家都用的同一个操作系统底座)提一次全网复用;你推送自家新镜像时,只有改动的层会上传。构建账:第 3.3 节的层缓存机制正是分层在构建侧的应用——装箱单里没变的指令直接复用缓存层,重复构建从分钟级降到秒级。

一句话收拢:分层把"整套环境"这个庞然大物,切成了可共享、可缓存、可增量传输的小块。3.2 节去看堆场的货单规则,3.3 节学着自己写每一层。

本节要点回顾

  • 镜像是只读层的堆叠;容器 = 镜像 + 一层私有可写层,删容器即焚可写层。
  • history 看每层的来历与重量,inspect 数层数;missing 表示共享层已在本地。
  • 写时复制:改底层文件先复制到可写层再改,只读层永远不动。
  • 三笔红利账:存储共享、传输增量、构建缓存——分层是一切高效机制的共同根源。
  • 铁律:需要持久化的数据不放可写层(第 5 章的数据卷承接此题)。

分层原理在手,接下来看堆场怎么记账管货——标签、摘要与远近场规则。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U