LabHub
学习 学习路径 课程

Docker 基础

镜像是什么,容器又是什么

在 LabHub 中继续学习

一句话总结

镜像是由只读层堆叠而成的文件树,容器则是在其上添加一个可写层后运行的普通 Linux 进程

概念图: 由只读层堆叠而成的文件树 · 普通 Linux 进程 · 从不同命名空间观察同一进程 · 用户空间文件树

为什么需要理解这一点

“容器是轻量级 VM”这种说法流传已久,但它在几乎所有实际问题中都会导致错误预测。在主机上执行 ps,可以直接看到容器内的 nginx。

48213 root      nginx
48261 systemd+  nginx

从容器内查看同样的进程,会显示为 1 nginx29 nginx。并不是有两套进程,而是从不同命名空间观察同一进程。查看内核版本会更加清楚:主机、alpine 容器和 ubuntu 容器报告的都是同一个内核。镜像提供的不是内核,而只是用户空间文件树

工作原理

各层通过联合文件系统(overlayfs)叠加。

overlayfs 的分层结构——只读镜像层上覆盖一个可写层,对外呈现为名为 merged 的单一文件树;读取从上向下查找,写入通过 copy-up 将下层文件完整复制到上层,删除则只留下白化标记,下层原件仍然存在

merged   <- 컨테이너가 보는 뷰
  ↑ upperdir  (쓰기 가능, 컨테이너 레이어)
  ↑ lowerdir  (읽기 전용, 이미지 레이어들이 겹겹이)

每一层都以内容的 SHA-256 哈希命名。相同内容会得到相同名称,因此无需重复存储,只需比较哈希便能验证完整性。镜像 ID 使用 sha256:... 也是同样的原因。

层形成时决定了什么

Dockerfile 的每条命令都会产生一层,而且**层不会被删除。**大多数实际陷阱都来自这两句话。

COPY secret.pem /tmp/secret.pem     ← 레이어 3에 파일이 들어간다
RUN ./setup.sh && rm /tmp/secret.pem  ← 레이어 4는 "지웠다" 는 기록일 뿐

最终镜像中看不到 /tmp/secret.pem,但它仍然完整存在于第 3 层。任何人都能通过 docker save 解包并取出。把秘密放进镜像后再删除,并不是真正删除。应使用构建秘密(RUN --mount=type=secret)或多阶段构建解决。

同理,镜像大小也不会减小。

RUN apt-get update && apt-get install -y build-essential   # +400MB
RUN apt-get purge -y build-essential                       # 여전히 +400MB

必须在同一个 RUN 中安装并删除,才能只留下该层的最终状态。

删除容器后仍会保留什么

删除容器时,可写层会消失。因此,在容器内创建的文件会随 docker rm 一同消失。这就是需要卷的原因。

存储位置 删除容器后 写入性能 使用场景
可写层 消失 慢(copy-up) 临时文件
匿名卷 保留(没有名称,难以查找) 意外产生的卷
命名卷 保留 DB 数据
bind mount 保持主机文件原样 开发期间的源码

“copy-up” 非常重要。在 overlayfs 中,修改下层文件时会把整个文件复制到上层。修改 1GB 文件中的一个字节,也会复制完整的 1GB。这就是数据库不能在没有卷的情况下运行于容器层的原因。

镜像名称与 Digest

标签是可变的。nginx:1.25 昨天和今天可能指向不同镜像。latest 更是如此——它并不表示“最新”,只是省略标签时使用的默认名称

nginx:1.25                                    ← 움직인다
nginx@sha256:a1b2c3...                        ← 절대 안 움직인다

生产部署应固定到 Digest。这样,在排查“昨天能用、今天不能用”时,可以先确认“昨天与今天是否使用同一个镜像”。

生产现场中的表现

使用同一基础镜像的 100 个容器会完整共享 lowerdir,因此容器创建速度快,也能节省磁盘。反过来,由于 copy-up 成本,数据库文件这类大型写入必须放入卷中

还有一点:混淆 docker savedocker export 会浪费一整天。save 导出的是包含层、标签和历史记录的镜像,export 导出的只是容器的单层文件系统。将 export 结果 import 后,ENTRYPOINT 和 ENV 都会消失。

下一项实验要做什么

亲自查看镜像列表,启动一个容器并检查日志,再通过镜像 ID 证明添加标签并不会创建新镜像。