← 全部笔记

Docker · 1 基础概念

一句命令,谁在替你运行容器?

docker run hello-world 把请求交给后台的 dockerd;镜像准备好后,它安排启动容器,把程序输出送回你的终端。

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

请求交给后台,输出回到终端 CLI 发送请求给 dockerd。dockerd 准备镜像并启动容器。容器产生输出,经 dockerd 回到 CLI;进程退出后容器停止。 CLI 客户端你的终端 dockerd后台服务端 发送请求 返回输出 准备镜像、启动 容器中的程序打印 → 退出 → 停止 后台转回输出
图 1 命令行负责发请求;后台准备镜像、启动容器,并转回程序输出。[1]

先认清两端

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,记录如下。

ls -l /var/run/docker.sock
srw-rw---- 1 root docker 0 Nov 2 18:47 /var/run/docker.sock

权限、属主、日期保留自原始记录,你的机器会不同。这条命令只查看文件信息;端点存在不等于当前用户一定有权连接。

先复习三个对象:镜像(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]

首次运行 hello-world 的八步链路 CLI 请求 dockerd;检查本地镜像未命中;从 Docker Hub 拉取;containerd 管理容器的创建启动,runc 建立运行环境启动进程;输出经 dockerd 返回 CLI;进程退出后容器停止。 docker CLI客户端 dockerd服务端 请求 本地镜像 查找 首次查找:未命中 Docker HubRegistry 互联网上的服务 请求拉取 镜像 containerd管理生命周期 安排创建、启动 runc启动进程 调用 建立环境并启动 容器边界 hello-world程序打印消息 输出回后台 回终端 进程退出 · 容器停止镜像仍保留容器记录仍在
  1. CLI → dockerd。你执行 docker run hello-world,CLI 通过本机 socket 发出请求。
  2. 检查本地镜像。dockerd 查找 hello-world:latest。先猜:本地没有镜像,下一步能直接启动吗?
  3. 未命中。服务端告知 CLI 镜像不存在;默认策略允许 CLI 接着请求服务端拉取。先猜:镜像从终端、Docker Hub,还是 runc 获取?
  4. 从 Docker Hub 拉取。服务端下载所需镜像,保存到本地。拉取是下载镜像,还没运行程序。
  5. 交给 containerd。dockerd 用镜像创建容器,并通过 containerd 管理它的创建、启动和停止。这一整段叫容器的“生命周期”。
  6. 通过 runc 启动进程。默认运行时 runc 建立容器运行环境、启动镜像指定的程序。本例程序打印问候语。
  7. 输出回到终端。程序的标准输出(stdout)经后台传给 CLI,CLI 显示出来。此处是输出路径示意,省略内部转发细节。
  8. 进程退出,容器停止。hello-world 完成任务就退出。此次没有 --rm,停止的容器记录和镜像都还在。
图 2 首次缺少镜像时先拉取,再由本地运行链路启动程序;输出后退出是预期行为。图中省略 shim 等内部环节,箭头不表示精确的 API 调用或耗时。[3] [4]

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 或下载进度,因为本地已经有镜像。

docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/

2. 第二次运行,再查看容器和镜像

维护者提供的第二次输出只记录了开头两行,下面原样保留;这不表示程序只会打印这两行。

docker run hello-world # 第二次

Hello from Docker!
This message shows that your installation appears to be working correctly.
docker ps -a --filter ancestor=hello-world
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
842ca6b55ea5 hello-world "/hello" Less than a second ago Exited (0) Less than a second ago hopeful_benz
c19fd4a753b7 hello-world "/hello" Less than a second ago Exited (0) Less than a second ago epic_murdock
docker images hello-world
WARNING: This output is designed for human readability. For machine-readable output, please use --format.
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
hello-world:latest c567b231f656 25.9kB 9.49kB U

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。

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 是容器程序输出;完整的文件隔离实验见上一页。

3. 自行练习:让新容器退出后自动删除

docker run --rm hello-world
docker ps -a --filter ancestor=hello-world
docker images hello-world

这是供你继续验证的练习命令,不在上面的实测记录中。--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、创建与启动、返回输出、退出都仍会发生。

首次:本地未命中

查找 → 拉取镜像 → 创建容器 A → 运行、退出

多了下载镜像的路径,镜像保存下来。

再次:本地已命中

查找 → 跳过拉取 → 创建容器 B → 运行、退出

镜像相同,容器 ID 不同;A 与 B 都停止。

图 3 两次运行只改变“本地是否有镜像”,复用模板仍会创建不同实例。
深入一点:为什么 latest 不表示每次都更新?

latest 只是一个标签名,不保证自动更新。docker run 默认的拉取策略是 --pull=missing,即仅在缺镜像时拉取。显式指定 --pull=always 会请求拉取;本页的第二次运行预测仅针对默认策略。[3]

读懂那四句话

问候语里的四步,是程序自己的说明

上面的第一次运行实测保留了完整问候语,包含四步说明。它们来自 hello-world 官方源码中的固定消息;四条各抓住一个动词,对照下方解读。

  1. contacted:客户端联系服务端。对应动画第 1 步;你敲命令,后台接到请求。
  2. pulled:服务端从 Hub 拉取镜像。对应第 2–4 步的缺镜像分支;先查本地,缺少才拉取。
  3. created:从镜像创建容器并运行程序。对应第 5–6 步;程序生成你正在读的消息。
  4. streamed:服务端把输出传给客户端,客户端显示到终端。对应第 7 步;输出来自容器中的程序。

这段消息是写在程序里的固定文字,不能当成拉取日志。第二次也会打印其中的“pulled”,即使本次没有拉取。应结合问候语之前是否出现拉取信息,以及本地镜像和容器列表判断。退出、停止是动画补充的第 8 步。[7]

第二次输出仍有 pulled。请解释:为什么不能凭这一个词就断言“每次都重新下载镜像”?

这句话来自程序内固定消息,不是服务端本次拉取的审计记录。默认只在缺镜像时拉取;本地已有镜像时仍会新建容器,打印同一段文字。

  • 区分了固定消息与实际拉取日志
  • 说明了默认策略只在缺镜像时拉取
  • 指出第二次仍会新建并运行容器

两次运行之间未删除镜像,也没有改参数。第二次没有拉取日志,却又打印了问候语。哪组结果与此一致?

镜像可以复用;但 run 每次新建容器,所以容器 ID 应不同。

镜像是可重复使用的只读模板,创建容器不会把它消耗掉。

默认策略命中本地镜像就跳过拉取。两次 run 创建两个不同的容器;两个程序都正常退出。未加 --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 在测试前已在本地。延伸练习命令另行标明。

  1. Docker overview:客户端/服务端架构,镜像、容器与 Registry。
  2. dockerd:Daemon socket option:Linux 默认 Unix socket 与服务端连接方式。
  3. docker container run:新建并运行容器、默认标签、缺镜像后的请求、--pull 与 --rm。
  4. Alternative container runtimes:containerd 管理生命周期、默认使用 runc。
  5. docker image ls:docker images 别名与镜像列表;另见 Docker 29 发布说明的默认视图变更。
  6. docker container ls:docker ps、-a 和 ancestor 筛选。
  7. hello-world 官方 hello.c:固定消息、写入标准输出并返回 0;用于解读实测输出中的四步。
  8. Linux post-installation steps:运行 hello-world 后打印消息并退出。