基于本文回答

播面 播面

文解图解播客视频,多维讲透八股文
0
评论

什么是 Docker 镜像的“只读层”和容器的“可读写层”?

在 Docker 的世界里,“只读层”(Read-Only Layer)“可读写层”(Read-Write Layer) carbon 是理解 Docker 镜像和容器工作原理的核心概念。

它们共同构成了 Docker 的 联合文件系统(UnionFS)。简单来说:镜像(Image)是只读的,而容器(Container)是在镜像之上加了一个可读写的层。

下面我们用通俗的语言和图解来详细拆解这两个概念。


1. 什么是 Docker 镜像的“只读层”?

当你编写 Dockerfile 并构建镜像时,每一条指令(如 FROM, RUN, COPY)都会生成一个“层”(Layer)。这些层堆叠在一起,就形成了 Docker 镜像。

  • 本质:这些层是绝对只读的(Read-Only)。一旦镜像构建完成,任何人都无法修改这些层中的内容。
  • 共享机制:因为它们是只读的,所以可以被安全地共享。例如,如果你有 10 个运行 Ubuntu 的容器,它们在宿主机上其实共享同一份 Ubuntu 镜像的只读层,这极大地节省了磁盘空间。
  • 比喻:就像一本已经印刷出版的书。书里的字(代码、系统文件)是固定死的,你不能直接在印刷好的书页上改字。

2. 什么是容器的“可读写层”?

当你运行一个容器时(使用 docker run 命令),Docker 会在镜像的“只读层”之上,最顶端添加一个薄薄的新层。这个层被称为“容器层”(Container Layer)“可读写层”(Read-Write Layer)

  • 本质:所有对容器运行时的修改(比如写日志、创建新文件、修改已有配置、删除文件)都只会发生在这个可读写层中。
  • 生命周期:这个可读写层是与容器同生共死的。当你删除容器(docker rm)时,这个可读写层也会被彻底删除,里面产生的数据也会丢失(除非使用了数据卷 Volume)。
  • 比喻:就像在刚才那本印刷书上盖了一张透明的塑料胶片(写字板)。你可以在胶片上用马克笔写字、画画。从上面看下去,书的内容和你的涂鸦融为一体,但你其实没有伤到书本一分一毫。

3. 它们是如何协同工作的?(写时复制 - Copy-on-Write)

既然镜像层是只读的,那如果我在容器里修改一个已有的镜像文件,会发生什么?

这里引入了 Docker 核心的 写时复制(Copy-on-Write, CoW) 机制:

  1. 读取文件(Read):容器需要读取文件时,会从上往下在各层中寻找。如果在最顶部的可读写层找不到,就去下面的只读镜像层找,直接读取。
  2. 修改文件(Write/Modify)
    • 如果容器想要修改一个来自只读层的文件(比如 /etc/nginx/nginx.conf)。
    • Docker 会自动将这个文件从底部的只读层复制一份到顶部的可读写层
    • 然后,容器在可读写层对这个副本进行修改。
    • 底部的原始文件依然完好无损,但对于容器来说,它只能看到可读写层中修改后的新版本(新版本遮挡了旧版本)。
  3. 删除文件(Delete)
    • 如果容器要删除一个只读层的文件,Docker 并不会真的从只读层删掉它(因为删不掉)。
    • Docker 会在可读写层创建一个特殊的“遮挡标记”(Whiteout file),让容器觉得这个文件已经被删除了。

4. 对比总结表

特性 镜像的“只读层” (Image Layers) 容器的“可读写层” (Container Layer)
读写属性 只读 (Read-Only) 可读写 (Read-Write)
数量 可以有多个层(堆叠) 只有一个(位于最顶层)
共享性 多个容器可共享同一份镜像层 每个容器独占自己的可读写层
生命周期 永久保存,除非手动删除镜像 随容器创建而创建,随容器销毁而销毁
占用空间 通常较大(包含操作系统、依赖库等) 通常非常小(仅保存运行时的增量修改)

5. 最佳实践启示

理解了这两个概念,在实际开发中会有以下指导意义:

  1. 不要在容器的可读写层保存重要数据:因为容器一旦重建,可读写层的数据就没了。重要数据(如数据库文件、用户上传的图片)应该挂载到 Volume(数据卷) 中,绕过可读写层,直接写在宿主机上。
  2. 避免在容器运行期间产生大量垃圾文件:因为可读写层会随着写入的数据增多而变大,导致容器臃肿。
  3. 合理构建 Dockerfile:在构建镜像时,尽量把清理垃圾的命令(如 rm -rf /var/lib/apt/lists/*)和安装命令写在同一条 RUN 指令中,否则清理命令只会发生在新的只读层,而无法减小前一个只读层的大小。