← 全部笔记

Docker · 1 基础概念

镜像存进仓库,一个镜像能起多个容器

仓库负责分发,镜像提供程序和初始文件,容器负责实际运行。

约 15 分钟 · 截至 2026-11-03,Docker Engine 29.x · 含维护者实测记录

图 1 拉取镜像不会启动程序;同一镜像可以创建多个独立容器。
学完你能做到
  • 各用一句话定义镜像、容器、仓库,并画出一对多关系。
  • 拆开 docker.io/library/nginx:1.27,补全省略的默认值。
  • 把 pull、run、commit、push、rm、rmi 放到对象之间的正确方向。
  • 预测删除镜像或容器后,另一方和远端副本是否还在。

先建立模型

镜像提供起点,容器各自保存变化

镜像(Image)是运行程序所需文件与配置的只读包。它本身不会运行。容器(Container)是从镜像创建的运行实例,包含隔离的进程环境和自己的文件变化;程序退出后,容器也可以处于停止状态。

仓库(Registry,镜像仓库服务)保存并分发镜像。你先把镜像拉到本地,再从本地镜像创建容器。容器运行期间,通常无需持续连接仓库。

从同一镜像创建 A、B。A 在自己的文件系统里写了 /note(未使用共享存储)。B 会自动看到这个文件吗?镜像会改变吗?先写下理由。

都不会。每个容器有自己的可写层,也就是记录本容器文件变化的地方。A 的变化只属于 A,既不回写镜像,也不自动传给 B。

两份容器变化,共用同一镜像A 的可写层含 note,B 的可写层没有 note。下方同一只读镜像为两个容器提供初始文件,A 的写入不改变镜像。 容器 A容器 B 自己的可写层有 /note 自己的可写层无 /note 提供初始文件 同一个只读镜像 · 内容未改变
图 2 同源容器共享只读起点,各自保存文件变化;图中不含共享存储。
如果你懂“类和实例”,这个类比能用到哪里?

“一个类能创建多个实例”能帮助理解镜像的一对多关系。但容器还包含进程环境和自己的可写层。删除实例后再创建一个,旧容器的文件变化不会自动回来。镜像本身由多层组成,分层机制留到模块 2。

你只执行了一次 docker pull nginx:1.27。现在能推出什么?

pull 负责下载;创建并启动新容器是 run 的工作。

拉取是获取本地副本,不是从远端搬走镜像。

pull 的终点是本地镜像。要运行程序,还需要从镜像创建容器;远端镜像仍可供别人拉取。

读懂你要拉什么

镜像引用写出位置、名字和标签

镜像引用是命令里用来指定镜像的名字。把 docker.io/library/nginx:1.27 按斜杠和冒号拆开:

docker.io /仓库地址 · Registry
library /命名空间 · Namespace
nginx :名字 · Repository
1.27标签 · Tag
图 3 前三段定位一个镜像集合,标签选择该集合中的一个版本或变体。

命名空间(Namespace)通常是用户或组织名;Docker Hub 的 library 用于官方镜像。镜像集合(Repository)把相关镜像归在一个名字下,如 library/nginx。一个 Registry 可管理很多这样的集合。

标签(Tag)是指向镜像的可变名字。这里的 1.27 用于拆解引用,不表示推荐部署这个版本。latest 也只是一个标签,不保证版本号最大。

Docker Hub 常见简写的补全
你写的引用补全后的含义补上什么
nginx:1.27docker.io/library/nginx:1.27地址、官方命名空间
nginxdocker.io/library/nginx:latest再补标签 latest
team/webdocker.io/team/web:latest已有 team,不再补 library
其他仓库地址有端口时怎么读?

registry.example.com:5000/team/web:v1 中,:5000 是仓库服务端口,最后的 :v1 才是标签。library 是 Docker Hub 官方镜像的约定,不是任意私有仓库都会提供的目录。更严格的不可变引用使用摘要(digest,内容标识),留到镜像标签与版本管理一页。

命令里只写 alpine。请补全四段,不运行命令也能回答。

地址 / 命名空间 / 名字 : 标签

完整引用是 docker.io/library/alpine:latest。省略字段会采用默认值,名字 alpine 本身必须给出。

把动作放回图上

四条命令连对象,两条命令删本地对象

run 每次创建并启动一个新容器。默认情况下,本地缺少所需镜像时,它会先拉取;本地已有时通常直接使用,并不总会检查远端更新。

命令在三对象之间的方向pull 从仓库到本地镜像,push 反向;run 从本地镜像创建容器,commit 从容器生成新镜像;rmi 删除本地镜像或标签,rm 删除本地容器。 仓库 Registry pull ↓ push ↑ 本机管理的对象 本地镜像(只读) run ↓ commit ↑新镜像 容器(可写) rm → 删除容器 rmi → 删除本地镜像 / 标签
  1. pull:把仓库里的镜像下载到本地,远端保留。
  2. run:从本地镜像创建并启动新容器。先猜:保存容器里的文件变化,箭头应往哪里走?
  3. commit:从容器的变化创建新镜像,原镜像不被改写。
  4. push:把本地镜像上传到有写入权限的仓库目标。
  5. rm:删除指定容器,不删除源镜像。先猜:清理本地镜像要用什么命令?会删除远端吗?
  6. rmi:处理本地镜像或标签,不删除远端。图中箭头汇总六个动作,并非要求依次执行的脚本。
图 4 commit 生成新的本地镜像;要分发给别人,还要另做 push。

commit 把容器的文件变化与配置保存为新镜像。原镜像和原容器都保留。push 上传镜像,不上传活着的进程;实际推送还需要目标名字以及仓库写入权限。

commit 会保存“运行现场”吗?

它不保存进程内存,也不包含挂载数据卷里的数据。数据卷是独立于容器可写层的数据存储,模块 4 会展开。可重复构建通常用 Dockerfile(描述构建步骤的文本文件);这里学习 commit 只是为了理解方向。

先选命令,再选正确的箭头。可点击,也可用 Tab 和 Enter 完成。

  • docker pull
  • docker run
  • docker commit
  • docker push
仓库 → 本地镜像
本地镜像 → 仓库
本地镜像 → 新容器
容器 → 新的本地镜像

pull / push 负责本地与远端的镜像传递;run 创建容器,commit 创建新镜像。四条命令都不删除源对象。rm / rmi 的删除目标在后面再练。

动手 · 约 4 分钟

两次 run 验证:文件变化属于容器

实测记录

实测于 2026-11-04,Docker Engine 29.1.3,由维护者提供;Linux 内核 7.3.0-1004-oem,x86_64。复现前需要 Docker 已启动,当前用户可执行 Docker。

在宿主机终端按顺序执行。实验会下载镜像、占用本地磁盘,并创建临时容器;使用 --network none 禁用实验容器联网,不映射端口、不挂载宿主机目录。拉取镜像仍需宿主机能访问 Docker Hub。

alpine:3.21 是本实验使用的 Linux 镜像引用。镜像中的 sh -c 用来执行引号内的 Linux 命令。--rm 表示程序退出后自动删除这个容器,保留镜像。

1. 创建 A:缺少镜像时自动拉取,再写入文件

实测前没有 alpine:3.21。直接运行第一条命令,Docker 先拉取镜像,随后在 A 内写入并读取文件。

docker run --rm --network none alpine:3.21 sh -c 'echo only-A > /tmp/docker-object-note; cat /tmp/docker-object-note'
Unable to find image 'alpine:3.21' locally
3.21: Pulling from library/alpine
759efa4719c9: Pulling fs layer
759efa4719c9: Download complete
759efa4719c9: Pull complete
17fa8a0c9d71: Download complete
8552e842adc1: Download complete
Digest: sha256:ee16d92f96db08d9d2d07da48555a27f3a69d8f0759021dbc601195ed0d2b5bd
Status: Downloaded newer image for alpine:3.21
only-A

从 Unable to find image、Pulling from、Pull complete 到 Digest、Status: Downloaded newer image,这些才是本次拉取的日志。最后的 only-A 来自容器内的 cat;随后 A 退出并由 --rm 自动删除。

2. 创建 B:检查相同路径

docker run --rm --network none alpine:3.21 sh -c 'if test -e /tmp/docker-object-note; then echo found; else echo absent; fi'
absent

B 打印 absent,说明它没有 A 写入的文件。B 退出后也自动删除。

3. 两个容器删除后,镜像仍在

docker image ls alpine:3.21
WARNING: This output is designed for human readability. For machine-readable output, please use --format.
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
alpine:3.21 ee16d92f96db 12.2MB 3.71MB

IMAGE 列的 alpine:3.21 把名称和标签放在一起;ID 是镜像标识。DISK USAGE 与 CONTENT SIZE 分别显示磁盘占用与镜像内容大小;没有单独的 SIZE 列。EXTRA 本次为空;若出现 U,表示镜像正被容器使用,停止的容器也算引用。

摘要、ID、大小和拉取进度均保留自原始记录,你的机器会不同。应关注 only-A → absent,以及容器删除后镜像仍然存在。若拉取失败,先检查网络、权限或服务是否启动。

Docker 29 的镜像列表为什么和旧教程不同?

Docker 29 默认改用新视图,表头为 IMAGE / ID / DISK USAGE / CONTENT SIZE / EXTRA。前面的 WARNING 是可读性提示,并非拉取失败;脚本读取请用 --format。旧版本示例把 REPOSITORY、TAG、SIZE 分列展示。参见 Docker 29 发布说明与 docker image ls 文档。

B 为什么打印 absent?请同时解释“镜像仍在”和“A 写过的文件不在”这两件事。

A 的文件写在 A 的可写层里,镜像没改变。A 退出时 --rm 删除 A 和它的可写层。B 由原镜像重新创建,所以没有 A 的文件。删除容器不会删除镜像。

  • 提到 A 的可写层独立于镜像
  • 提到 --rm 自动删除退出的容器
  • 提到 B 从原镜像创建,而不是接着 A 的文件运行
可选清理:不再需要这个实验镜像时

只在确认其他工作不依赖这个本地引用时执行 docker rmi alpine:3.21。它只处理本地镜像或标签,远端镜像还在。若提示容器引用等冲突,先保留它,不要为实验加 -f 强删。

删除的边界

删除要看目标,也要看是否有引用

docker rm 容器名 删除容器及其可写层。普通 rm 通常要求容器已停止;停止只是进程退出,容器对象和文件变化仍然保留。docker rmi 镜像引用 删除本地镜像或取消标签,不删除远端。

① 删除停止的 A
镜像仍在容器 A

B 仍在,A 自己的文件变化丢失。可以从原镜像再创建 C。

② A 还引用唯一标签镜像
镜像仍在A 仍在

按镜像 ID 尝试普通 rmi,可能因引用被拒绝;停止的容器也可能阻止删除。命令失败不等于 A 被删除。

③ 无容器引用,仅一个标签
本地镜像远端仍在

成功 rmi 后本地没有这个镜像。以后还能 pull;没有容器被顺带删除。

图 5 本地删除不会传到远端;存在引用时,要先判断删除是否成功。

只看命令的目标,不实际执行。把四个删除动作分到对应对象。

  • docker rm A
  • docker rmi alpine:3.21
  • docker rm B
  • docker rmi nginx:1.27
容器 → 删除容器及其可写层
本地镜像 / 标签 → 删除或取消引用

rm 的目标是容器;rmi 的目标是本地镜像或标签。分类表示命令意图,执行能否成功还要检查停止状态和引用冲突。

为什么 rmi 成功也不一定释放全部空间?

一个镜像可以有多个标签。删除某个标签可能只是取消这个名字;其他标签或容器仍可引用相同内容。不要把“名字消失”理解成“所有底层数据立即消失”。这里不使用强制删除;具体空间回收和存储后端留到后续模块。

A 已停止但还没删除。你执行 rmi 后收到引用冲突。此时 A 的文件变化、本地镜像、远端镜像分别还在吗?

这个场景中三者都还在。A 停止不等于删除,文件变化仍留在可写层;rmi 被拒绝,本地镜像未删;远端也不受本地删除影响。若确实不再需要 A 的文件,应先删除 A,再处理镜像引用。

只删除停止的 A,B 由同一镜像创建且仍在运行。镜像原本来自 Docker Hub。成功 rm A 后哪组状态正确?

rm 的目标是容器,删除镜像要另做 rmi。

共享起点不等于共享生命周期。B 是另一个独立容器。

rm A 只移除 A 及其可写层。B 的运行和可写层不受影响,本地镜像保留,仓库中供分发的镜像也保留。

合上页面前

用四句话还原整张图

同事说:“把正在跑的容器 push 给我,我启动后就能看到你刚写的文件。”你会怎样用三种对象解释正确做法?

push 的对象是镜像。若文件只在容器可写层里,直接 push 原镜像不会带上它。可以先用 commit 把容器变化保存为新镜像,再用有权限的目标名字 push。同事 pull 新镜像并 run 后才得到包含那些文件的新容器,进程内存不会被转移。

  • 区分镜像分发与容器运行
  • 指出 commit 产生新镜像,push 上传镜像
  • 指出新容器不等于原进程的运行现场

来源与核对范围

截至 2026-11-03,Docker Engine 29.x。以下为核对概念和 CLI 行为的官方文档;示意图不表示磁盘上的具体存储布局。实验引用维护者在 Issue #10 提供的 2026-11-04 记录(Docker Engine 29.1.3,Linux 7.3.0-1004-oem,x86_64)。

  1. What is an image?:镜像只读、不可变与分层概念。
  2. What is a registry?:Registry 与 repository 的区别。
  3. docker image tag:引用格式、docker.io / library / latest 默认值。
  4. docker container run、Running containers:创建运行、默认拉取策略、--rm 与 none 网络。
  5. docker image pull、docker image push:下载、上传与仓库凭据。
  6. docker container commit:生成新镜像,不保存挂载卷数据。
  7. docker container rm、docker image rm:删除目标、标签与引用限制。Explore the Images view 同时说明运行或停止的容器对删除镜像的限制。
  8. Storage:每个容器独立的可写层及其删除边界。
  9. docker image ls 与 Docker Engine 29.0 发布说明:镜像列表、格式化输出与默认视图变更。