先认清两端
docker 发请求,dockerd 执行请求
命令行客户端(Command Line Interface,CLI)就是你敲的 docker 程序。守护进程(daemon)是持续在后台运行的服务;Docker 的这个服务叫 dockerd。两者是客户端/服务端关系。[1]
本页按 Linux 上常规安装的 Docker Engine 讲解:两端通过 Unix socket /var/run/docker.sock 通信。这是本机进程交换消息的端点,不需要用 IP 和端口寻址。CLI 经它发送 Docker API 请求;API 就是服务端接受操作请求的接口。[2]
实测于 2026-11-04,Docker Engine 29.1.3:维护者的 Linux 机器上存在这个 socket,记录如下。
权限、属主、日期保留自原始记录,你的机器会不同。这条命令只查看文件信息;端点存在不等于当前用户一定有权连接。
先复习三个对象:镜像(image)是创建容器的只读模板;容器(container)是从模板创建的运行实例;镜像仓库服务(Registry)存放和分发镜像。本页用的公共服务是 Docker Hub。[1]
本地有 hello-world 镜像,但 dockerd 没启动。只有 docker 命令能用,能运行容器吗?先写下理由。
不能。CLI 需要把请求交给服务端。镜像存在也无法代替 dockerd;CLI 仍要先建立连接,后台再安排容器启动。
深入一点:所有安装都用这个 socket 吗?
这里限定的是 Linux 常规安装的本机默认连接。Rootless(以非 root 用户运行服务端)、Docker Desktop 或远程连接可能使用不同端点。客户端和服务端也可以在不同机器上;本页图中的“本地镜像”始终指服务端机器上的镜像。Rootless 文档、Desktop for Linux 文档。
沿请求走八步
没有镜像时,先拉取再创建
前提:dockerd 可连接、网络可用,本地还没有 hello-world 镜像。省略标签时使用 :latest;标签是镜像名称后的版本标记,latest 是默认标签名。下图是控制流程示意,令牌表示当前请求、镜像或输出,不表示同一份数据一直传递。[3]
- CLI → dockerd。你执行
docker run hello-world,CLI 通过本机 socket 发出请求。 - 检查本地镜像。dockerd 查找
hello-world:latest。先猜:本地没有镜像,下一步能直接启动吗? - 未命中。服务端告知 CLI 镜像不存在;默认策略允许 CLI 接着请求服务端拉取。先猜:镜像从终端、Docker Hub,还是 runc 获取?
- 从 Docker Hub 拉取。服务端下载所需镜像,保存到本地。拉取是下载镜像,还没运行程序。
- 交给 containerd。dockerd 用镜像创建容器,并通过 containerd 管理它的创建、启动和停止。这一整段叫容器的“生命周期”。
- 通过 runc 启动进程。默认运行时 runc 建立容器运行环境、启动镜像指定的程序。本例程序打印问候语。
- 输出回到终端。程序的标准输出(stdout)经后台传给 CLI,CLI 显示出来。此处是输出路径示意,省略内部转发细节。
- 进程退出,容器停止。hello-world 完成任务就退出。此次没有
--rm,停止的容器记录和镜像都还在。
CLI 发起请求,dockerd 处理请求;Docker Hub 交付镜像,容器在服务端机器上运行。读取本地镜像不需要每次访问 Hub。
深入一点:containerd 与 runc 各管什么?
containerd 负责容器生命周期管理,默认通过 runc 这个底层运行时启动容器进程。实际还有中间的 shim 进程;这里省略它。隔离机制、资源限制、这些进程如何协作,留到模块 7。[4]
打印问候语后,终端提示符回来了。没有加 --rm,最合理的解释是什么?
提示符回来只说明前台命令结束。后台服务不因此退出;容器停止也不等于删除。
Hub 分发镜像;容器在你的服务端机器上运行,没有“收回”这一步。
hello-world 打印后返回退出码 0,表示成功。程序结束,容器进入停止状态;默认保留容器记录,可以用 docker ps -a 看见。
在自己的机器验证
运行两次,看一份镜像和两个容器
实测于 2026-11-04,Docker Engine 29.1.3,由维护者提供;Linux 内核 7.3.0-1004-oem,x86_64。测试前 hello-world 镜像已在本地,因此本次两次运行都没有拉取日志。
请用有权连接 Docker 的终端。若机器要求管理员权限,按你的安装方式使用 sudo docker …。镜像首次下载需要联网。默认运行可能创建容器网络接口、触发 Docker 的网络规则;本实验不发布端口、不使用 --privileged,也不挂载宿主机目录。
1. 第一次运行:本地已有镜像,直接打印问候语
下面保留第一次运行的完整输出。没有 Unable to find image 或下载进度,因为本地已经有镜像。
2. 第二次运行,再查看容器和镜像
维护者提供的第二次输出只记录了开头两行,下面原样保留;这不表示程序只会打印这两行。
docker ps 默认只显示运行中的容器,-a 包括已停止的;--filter ancestor=hello-world 筛选使用该镜像的容器。上面两个不同的 CONTAINER ID 对应两次新建的容器,状态都是 Exited (0),表示程序正常结束。[6]
docker images 是 docker image ls 的别名。Docker 29 的列表按 IMAGE / ID / DISK USAGE / CONTENT SIZE / EXTRA 展示;hello-world:latest 合并了名称与标签,镜像 ID 为 c567b231f656。DISK USAGE 和 CONTENT SIZE 分别是磁盘占用和镜像内容大小,不能再寻找旧版的 SIZE 列。[5]
EXTRA 里的 U 表示镜像正被容器使用;这里两个容器虽然已停止,仍引用镜像。WARNING 提醒该输出面向人阅读,脚本应使用 --format,并非实验失败。
容器 ID、名字、时间、镜像 ID 与大小均保留自原始记录,你的机器会不同。请观察容器 ID 不同、镜像仍在这两个结果,不必为了重现实验而删除已有镜像。
旧教程为什么写 REPOSITORY / TAG / SIZE?
旧版列表将名称与标签分列,大小使用 SIZE;Docker 29 默认改用新视图。参见 Docker 29 发布说明和 docker image ls 文档。
缺镜像时会出现什么?看 Alpine 首次拉取实测
hello-world 在测试前已在本地,没有记录到它的首次拉取。下面采用同次测试中 alpine:3.21 的真实记录,实测于 2026-11-04,Docker Engine 29.1.3。--network none 只限制容器联网,宿主机拉取镜像仍需访问 Docker Hub。
Unable to find image → Pulling from → Pull complete → Digest → Status: Downloaded newer image 是拉取证据。最后的 only-A 是容器程序输出;完整的文件隔离实验见上一页。
3. 自行练习:让新容器退出后自动删除
这是供你继续验证的练习命令,不在上面的实测记录中。--rm 表示退出后自动删除该容器。运行后应仍看到 Hello from Docker!;检查容器列表是否没有多留第三个容器、镜像是否仍在。前两次的容器不受影响。[3]
如果实验没走到问候语,该检查哪段?
- 连接或权限错误:先确认 CLI 能连接服务端;这是图中的第一段。
- 拉取超时、访问仓库失败:检查服务端访问 Docker Hub 的网络;不是程序正常退出。
docker ps看不到 hello-world:它可能已停止,用docker ps -a看;若加了--rm,记录已自动删除。
本实验会保留前两次的容器与下载的镜像。若需清理,只对你刚记下的两个容器 ID 执行 docker rm 容器ID(将中文替换为实际 ID),不要批量删除已有容器。删除容器文档。
改变一个条件
第二次跳过拉取,仍然新建容器
本地已有同一镜像、使用默认拉取策略时,第二次直接复用镜像。它仍会创建一个新容器、启动程序、返回输出,然后停止。run 每次创建新实例。[3]
如果断开互联网,但 dockerd 正常且本地保留 hello-world 镜像,再运行同一命令会怎样?说出会跳过和仍要执行的部分。
这个程序无需联网,默认策略也不再拉取,所以仍可创建、运行并输出问候语。跳过的是 Hub 拉取;CLI → dockerd、创建与启动、返回输出、退出都仍会发生。
首次:本地未命中
多了下载镜像的路径,镜像保存下来。
再次:本地已命中
镜像相同,容器 ID 不同;A 与 B 都停止。
深入一点:为什么 latest 不表示每次都更新?
latest 只是一个标签名,不保证自动更新。docker run 默认的拉取策略是 --pull=missing,即仅在缺镜像时拉取。显式指定 --pull=always 会请求拉取;本页的第二次运行预测仅针对默认策略。[3]
读懂那四句话
问候语里的四步,是程序自己的说明
上面的第一次运行实测保留了完整问候语,包含四步说明。它们来自 hello-world 官方源码中的固定消息;四条各抓住一个动词,对照下方解读。
- contacted:客户端联系服务端。对应动画第 1 步;你敲命令,后台接到请求。
- pulled:服务端从 Hub 拉取镜像。对应第 2–4 步的缺镜像分支;先查本地,缺少才拉取。
- created:从镜像创建容器并运行程序。对应第 5–6 步;程序生成你正在读的消息。
- streamed:服务端把输出传给客户端,客户端显示到终端。对应第 7 步;输出来自容器中的程序。
这段消息是写在程序里的固定文字,不能当成拉取日志。第二次也会打印其中的“pulled”,即使本次没有拉取。应结合问候语之前是否出现拉取信息,以及本地镜像和容器列表判断。退出、停止是动画补充的第 8 步。[7]
第二次输出仍有 pulled。请解释:为什么不能凭这一个词就断言“每次都重新下载镜像”?
这句话来自程序内固定消息,不是服务端本次拉取的审计记录。默认只在缺镜像时拉取;本地已有镜像时仍会新建容器,打印同一段文字。
- 区分了固定消息与实际拉取日志
- 说明了默认策略只在缺镜像时拉取
- 指出第二次仍会新建并运行容器
两次运行之间未删除镜像,也没有改参数。第二次没有拉取日志,却又打印了问候语。哪组结果与此一致?
镜像可以复用;但 run 每次新建容器,所以容器 ID 应不同。
镜像是可重复使用的只读模板,创建容器不会把它消耗掉。
默认策略命中本地镜像就跳过拉取。两次 run 创建两个不同的容器;两个程序都正常退出。未加 --rm,两个停止的记录都保留。
现在你应能复述整条链路
- CLI → dockerd → 查本地 → 缺少就从 Hub 拉取 → containerd / runc 创建启动 → 输出回终端 → 进程退出、容器停止。
- CLI 与 dockerd 是客户端/服务端;Linux 常规安装默认用
/var/run/docker.sock。 - 再次运行默认复用镜像,但创建新容器;用
docker images与docker ps -a验证。 - 固定输出的四步是概述,不能证明本次一定拉取;
--rm才让本次容器在退出后自动删除。
来源与核查范围
核查日期:2026-11-03;讲解范围:Docker Engine 29.x、Linux 常规安装、默认运行时与拉取策略。终端记录引用维护者在 Issue #10 提供的 2026-11-04 实测(Docker Engine 29.1.3,Linux 7.3.0-1004-oem,x86_64);hello-world 在测试前已在本地。延伸练习命令另行标明。
- Docker overview:客户端/服务端架构,镜像、容器与 Registry。
- dockerd:Daemon socket option:Linux 默认 Unix socket 与服务端连接方式。
- docker container run:新建并运行容器、默认标签、缺镜像后的请求、--pull 与 --rm。
- Alternative container runtimes:containerd 管理生命周期、默认使用 runc。
- docker image ls:docker images 别名与镜像列表;另见 Docker 29 发布说明的默认视图变更。
- docker container ls:docker ps、-a 和 ancestor 筛选。
- hello-world 官方 hello.c:固定消息、写入标准输出并返回 0;用于解读实测输出中的四步。
- Linux post-installation steps:运行 hello-world 后打印消息并退出。