容器删了数据就没了?先搞懂可写层

为什么容器里改的东西,容器一删就没了?小够讲清镜像分层、写时复制和可写层:只读的是模板,能写的是临时层,理解了它,数据卷的选择和镜像优化就顺理成章了。

我是小够,只推够用的 VPS。

上一篇讲了数据存哪,今天补一个原理:为什么容器里写的东西,容器删了就没? 搞懂这个,数据卷、镜像分层、镜像优化这些概念全通了。

镜像是千层饼

Docker 镜像不是一整块,而是一层一层叠起来的。每一层对应 Dockerfile 里的一条指令:

FROM nginx:alpine          # 第1层:底子
COPY . /usr/share/nginx/html  # 第2层:你的网站文件

docker history 镜像名 能看到每一层,每层有多大、哪条指令产生的,一目了然。层的好处是复用:十个应用都用 nginx:alpine 做底,底子那层在硬盘上只存一份,省空间。拉新镜像时,已经有的层不用重复下载,这就是为什么第二次 docker pull 总是很快——Already exists 的那些层就是复用的。

关键:镜像的每一层都是只读的,谁也改不了。只读是刻意的设计,不是限制。

容器 = 镜像 + 一层可写的膜

容器跑起来时,Docker 在镜像最上面盖了一层"可写层"。你在容器里新建、修改文件,都写在这层膜上。镜像本身纹丝不动,十个容器共用一个镜像,各写各的膜,互不干扰。

容器删掉,这层膜跟着撕掉——你在里面写的东西当然就没了。而镜像还在,下次 docker run 又是一张干干净净的膜。这就是"容器天生一次性"的由来。

写时复制:改文件时发生了什么

你以为的"修改文件",实际是这样:假设你要改镜像里的 /etc/nginx/nginx.conf,Docker 会先把这个文件从只读层复制到可写层,再让你改。这叫写时复制(Copy-on-Write,CoW)。

推论:别在可写层里频繁改大文件。比如在容器里解压个几个 GB 的包,复制+写入双倍 IO,小机器会明显变慢。大文件操作放构建时(Dockerfile 的 RUN)做,别在运行的容器里做。数据库这种持续写的应用,更应该把数据目录挂数据卷,绕开可写层——性能更好,也更符合设计。

可写层多大?看一眼

docker ps -s

多了一列 SIZE,显示每个容器的可写层大小。正常应用的可写层应该很小(几 MB 到几十 MB),如果看到几个 GB,说明有东西在往容器里写数据——八成是日志或上传文件没挂数据卷,赶紧整改。docker system df -v 能看到更详细的空间占用。

所以数据卷是什么

数据卷就是绕开这层膜,把数据直接写到主机管理的、容器删了也不受影响的地方。上一篇讲的几种存法,本质都是这个思路。理解了可写层,数据卷的选择就顺理成章了:需要持久化、需要大 IO 的,统统走数据卷;可写层只留给"临时、少量"的东西。

镜像优化:层数和顺序的讲究

理解了分层,就能看懂镜像优化的道理:

  1. 少写 RUN:每条 RUN 是一层,合并成一条,层数少体积小。
  2. 不常变的放前面:Dockerfile 前面的层更容易被缓存复用。依赖安装(npm install)放前面,业务代码 COPY 放后面,这样改代码重建时不用重装依赖。
  3. 构建完删临时文件:同一条 RUN 里下载、解压、清理一条龙,别分开写——分开写的话,删掉的文件其实还在之前的层里占着空间,只是"看不见"了。
# 不好:下载的东西删了也占着之前层的空间
RUN curl -o pkg.tar.gz https://example.com/pkg.tar.gz
RUN tar xzf pkg.tar.gz && rm pkg.tar.gz

# 好:同一层里下载解压清理
RUN curl -o pkg.tar.gz https://example.com/pkg.tar.gz \
 && tar xzf pkg.tar.gz \
 && rm pkg.tar.gz

两个实用推论

第一,容器里尽量别存重要东西。 需要持久化的走数据卷,这是设计哲学,不是 bug。每次想在容器里存东西,先问自己:容器删了这个还在吗?

第二,docker commit 能把膜固化成新镜像,但别依赖它。 有人配好容器后 docker commit 存个镜像当备份:

docker commit 容器名 myapp:backup-20250709

能用,但不可重复、不可审计——三个月后你根本记不得这个镜像里改了啥。正确姿势是把配置写成 Dockerfile 或 compose 文件,环境即代码。commit 只适合应急(比如"这个调好的环境我先存一版"),不适合当流程。

一句话总结

镜像只读、容器可写层临时、数据卷持久。记住这三句,Docker 的数据问题、镜像优化问题,就再也不会懵了。