Whiteout
在AUFS / Overlay2(联合文件系统)的语境下,Whiteout(白化文件 / 遮蔽标记)是一个极其聪明的“障眼法”。
既然我们前面说了,底层的那些图层是绝对只读、不能修改、也不能删除的,那就会引发一个悖论:
“如果我在容器里,把底层自带的一个文件给『删了』,AUFS 该怎么表现出来?”
底层的物理文件根本删不掉,但用户在容器里执行了rm -rf file.txt,再次输入ls时,这个文件确实消失了。
这个让文件“凭空消失”的魔术,就是靠Whiteout来实现的。
1. 大白话原理:涂改液与小贴纸
继续用 PS 图层或者透明胶片的比喻:
底层系统(只读层)上画了一个大苹果(file.txt)。因为底层不能动,你没办法拿橡皮擦把这个苹果擦掉。
这时候,如果你在容器里执行了“删除”操作,AUFS 就会在最上面的顶层(你的透明胶片上),在对应大苹果的那个位置,啪叽贴上一张不透明的白色小贴纸(这个贴纸就叫 Whiteout 文件)。
当用户从上往下看(也就是在容器里执行ls)时,因为视线被最上层的白色贴纸挡住了,底层的那个大苹果就被“遮蔽”了。对用户来说,这个文件就像是被彻底删除了。
2. 内核视角:Whiteout 到底长啥样?
在底层的 Linux 文件系统里,这个“白色小贴纸”可不是闹着玩的,它有具体的存在形式:
- 在 AUFS 中:如果你删除了一个叫
foo的文件,AUFS 会在可写层创建一个隐藏文件,名字叫.wh.foo(前缀了.wh.)。当 AUFS 驱动扫描到有.wh.开头的文件时,就会明白:“哦,这个文件被用户咔嚓了,对用户隐藏它。” - 在 Overlay2 中(现代 Docker 的标配):它的实现更底层,会创建一个主次设备号为 0:0 的字符设备(Character Device)。内核一看到这个特殊的设备节点,就会自动在虚拟目录里把对应的底层文件给隐藏掉。
3. 为什么我们要关心 Whiteout?(避坑指南)
这个机制虽然精妙,但它带来了一个非常反直觉的“大坑”,这也是为什么很多初学者打包 Docker 镜像时,镜像体积会莫名其妙爆掉的原因:
❌ 错误的减肥姿势:
你在写 Dockerfile 的时候:
- 第一步(第一层):下载了一个1 GB1\text{ GB}1GB的压缩包并解压安装。
- 第二步(第二层):顺手执行了
rm -rf big_file.tar.gz。
你以为你把镜像体积缩减了1 GB1\text{ GB}1GB?完全没有!
因为第一步的1 GB1\text{ GB}1GB已经写死在只读层了。你第二步的删除动作,只是在可写层建了一个几字节的Whiteout 贴纸把拉链包遮住了。这个1 GB1\text{ GB}1GB的死权重依然结结实实地躺在你的硬盘里,并随着镜像一起被传输。
正确的减肥姿势:
必须在同一步(同一个RUN指令)里,把下载、解压、删除一气呵成:
RUN wget http://example.com/big.tar.gz && tar -xvf big.tar.gz && rm big.tar.gz因为在同一个 Layer 内部,文件还没沉淀到下层的“只读层”,这时候删除是真的物理删除,不会产生 Whiteout 贴纸。
总结
Whiteout 就是联合文件系统里的“掩耳盗铃”。因为底层删不掉,所以内核选择在最上层糊一层“白漆”(Whiteout),骗过用户的眼睛。
