- CLI
- 开发工具
【免费下载链接】cli
The Docker CLI
docker diff(即docker container diff)用于列出容器自创建以来文件系统上发生的全部变更,是 Docker CLI 提供的轻量级容器变更审计工具。本文基于 Docker CLI 官方参考文档与源码实现,完整讲解该命令的语法、三种变更类型(A/C/D)的语义、输出格式与自定义模板能力,并深入源码与测试用例揭示其底层调用链,帮助你掌握容器文件系统变更的排查与调试方法。
命令概述与别名
docker diff的功能定位是「检查容器文件系统上文件或目录的变更」,官方描述为:
Inspect changes to files or directories on a container's filesystem
该命令是docker container diff的顶层快捷别名,二者完全等价。在 container 子命令文档 中,diff被列为docker container的 24 个子命令之一。
| 别名 | 等价命令 |
|---|---|
docker container diff | 容器子命令的规范形式 |
docker diff | 顶层快捷别名 |
从源码看,该命令通过RegisterLegacy(newDiffCommand)注册为顶层命令(见 cli/command/container/cmd.go),同时被挂载到docker container子命令树下(cli/command/container/cmd.go)。
三种变更类型:A / D / C
docker diff只报告变更的类型和路径,不展示文件内容差异。自容器创建以来,文件系统上的每次变更都会被归为以下三类之一:
| 符号 | 英文含义 | 中文说明 |
|---|---|---|
A | A file or directory was added | 新增了文件或目录 |
D | A file or directory was deleted | 删除了文件或目录 |
C | A file or directory was changed | 修改了文件或目录(内容、权限、属主等元数据变化均属此类) |
在源码层面,这三种类型对应 diff_test.go 中使用的container.ChangeModify(C)、container.ChangeAdd(A)、container.ChangeDelete(D)三类FilesystemChange枚举值,并通过formatter_diff.go中Type()方法的Kind.String()输出为单字符符号(见 cli/command/container/formatter_diff.go)。
理解要点:
C是范围最广的一类,既包括文件内容的修改,也包括权限位、属主、链接等元数据层面的变化——正如 nginx 示例中/dev/console、/run等目录被标记为C一样,容器启动过程对设备节点和运行目录的触碰都会被计入。- 该命令对比的是容器自身被创建时的基线文件系统(即由镜像各层叠加而成的初始状态),而不是与某个历史快照对比。
指定容器的三种方式
docker diff接受一个容器参数,可以用以下任一方式标识:
- 完整容器 ID:如
docker diff 1fdfd1f54c1b30a87b2a1e1d8a2e0f9a3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f(64 位十六进制)。 - 短容器 ID:如
docker diff 1fdfd1f54c1b,只要前缀能唯一区分即可。 - 容器名称:通过
docker run --name <name>显式指定的名称。
命令定义为Use: "diff CONTAINER",并通过cli.ExactArgs(1)强制要求恰好一个位置参数,多传或少传都会报错(见 cli/command/container/diff.go)。同时该命令注册了容器名的 Shell 补全函数completion.ContainerNames(dockerCLI, false),在 bash/zsh/fish 等 shell 中输入docker diff后按 Tab 即可自动补全容器 ID 或名称(cli/command/container/diff.go)。
实战示例:检查 nginx 容器的文件系统变更
官方文档给出的经典示例是检查一个 nginx 容器启动后的变更:
$ docker diff 1fdfd1f54c1b C /dev C /dev/console C /dev/core C /dev/stdout C /dev/fd C /dev/ptmx C /dev/stderr C /dev/stdin C /run A /run/nginx.pid C /var/lib/nginx/tmp A /var/lib/nginx/tmp/client_body A /var/lib/nginx/tmp/fastcgi A /var/lib/nginx/tmp/proxy A /var/lib/nginx/tmp/scgi A /var/lib/nginx/tmp/uwsgi C /var/log/nginx A /var/log/nginx/access.log A /var/log/nginx/error.log输出解读:
/dev、/run、/var/lib/nginx/tmp、/var/log/nginx等目录标记为C,表明容器运行时对它们进行了写操作;/run/nginx.pid、各tmp子目录、access.log、error.log标记为A,是 nginx 进程运行时新创建的文件;- 本示例中未出现
D,说明没有文件被删除。
这组输出也印证了该命令的典型用途:验证容器内进程的实际落盘行为——例如排查日志是否真的写入容器、临时文件目录是否按预期创建,比进容器逐目录翻看更高效。
输出格式与自定义模板
默认输出
默认输出为两列:类型符号 + 路径,即源码中的默认格式{{.Type}} {{.Path}}(cli/command/container/diff.go)。
表格模式与模板字段
底层 formatter 定义了两种输出形态(见 cli/command/container/formatter_diff.go):
默认表格格式:
table {{.Type}}\t{{.Path}},带表头CHANGE TYPE与PATH,即:CHANGE TYPE PATH C /var/log/app.log A /usr/app/app.js D /usr/app/old_app.js可用模板字段:
{{.Type}}(变更类型符号)与{{.Path}}(变更路径)。
newDiffFormat的实现逻辑是:当传入的格式为table时返回默认表格格式,否则将用户提供的字符串原样作为 Go template 使用(cli/command/container/formatter_diff.go)。对应的单测 formatter_diff_test.go 验证了三种输出形态:
| 格式 | 输出效果 |
|---|---|
table | 带CHANGE TYPE/PATH表头的两列表格 |
table {{.Path}} | 只输出路径列,表头为PATH |
{{.Type}}: {{.Path}} | 自定义分隔符,如C: /var/log/app.log |
底层调用链与实现原理
整个命令的执行路径非常简洁清晰(见 cli/command/container/diff.go):
docker diff <container> └─ newDiffCommand: 解析参数(ExactArgs(1)) └─ runDiff(ctx, dockerCLI, containerID) ├─ Client().ContainerDiff(ctx, containerID, client.ContainerDiffOptions{}) │ └─ 通过 Docker Engine API 的 /containers/{id}/changes 端点获取变更列表 └─ diffFormatWrite(diffCtx, res) └─ 遍历 res.Changes,按 {{.Type}} {{.Path}} 逐行输出关键实现细节:
- CLI 本身不计算变更,而是调用 Docker API 客户端方法
ContainerDiff(cli/command/container/diff.go),由 dockerd 对容器存储层的变更集合(包括可写层内容变更与元数据变更)进行汇总; - 返回的
client.ContainerDiffResult包含[]container.FilesystemChange列表,每个元素由Kind(A/D/C)与Path(绝对路径)组成; - 输出阶段使用
formatter.Context统一渲染,diffFormatWrite遍历每条变更并调用格式化函数逐行输出(cli/command/container/formatter_diff.go),这保证了与docker ps、docker inspect等命令一致的模板机制。
测试用例验证
仓库为docker diff提供了完整的两类单元测试(见 cli/command/container/diff_test.go):
TestRunDiff:通过fakeClient注入包含三种变更类型的模拟响应,断言输出为三行C /path/to/file0、A /path/to/file1、D /path/to/file2,验证了「类型 + 路径」的输出契约与 A/C/D 三种类型的渲染;TestRunDiffClientError:模拟 API 调用返回错误,断言错误被原样向上传播,验证错误处理路径。
结合 cli/command/container/client_test.go 中fakeClient.ContainerDiff的实现可以看到,测试通过注入containerDiffFunc完全隔离了 API 层,专注于验证 CLI 层的参数解析、调用与格式化逻辑。
常见使用场景
- 验证容器内程序的写入行为:启动容器后立即执行
docker diff,可以快速确认进程是否写入了预期路径(如日志、PID 文件、缓存目录)。 - 排查异常写盘:当容器磁盘占用异常时,
docker diff能快速定位新增或膨胀的目录,辅助判断是业务数据还是进程残留。 - 结合
docker commit制作镜像:docker diff列出的变更正是docker commit将打包进新镜像的内容;先用diff审查变更清单,可避免把临时文件、日志等不必要内容带入镜像。 - 容器调试与取证:只读容器(如
--read-only)中运行的程序若尝试写盘,可以通过docker diff观察实际发生的写操作。
需要说明的边界:docker diff展示的是变更路径清单而非内容级差异(内容差异请用docker cp导出后自行比对),且无法区分同一路径上的多次连续修改——它只报告当前状态相对创建基线的最终差异。
延伸阅读
- 官方命令参考:docs/reference/commandline/container_diff.md
- 命令实现源码:cli/command/container/diff.go、cli/command/container/formatter_diff.go
- 单元测试:cli/command/container/diff_test.go、cli/command/container/formatter_diff_test.go
- 容器子命令总览:docs/reference/commandline/container.md
- 相关命令:
docker commit(基于容器变更创建镜像)、docker cp(复制容器文件)、docker export(导出容器文件系统为 tar 归档),参见 container 子命令目录。
- CLI
- 开发工具
【免费下载链接】cli
The Docker CLI
相关推荐
tldr 命令速查实战:深入理解 `docker container diff` 容器文件系统变更检查
tldr 命令速查实战:深入理解 docker container diff 容器文件系统变更检查 本指南以 tldr 仓库中的 docker containe
文档教程知识库agentic-awesome-skills 技能目录智能分类实施指南:从"未分类"混乱到关键词驱动的自动归类
agentic awesome skills 技能目录智能分类实施指南:从"未分类"混乱到关键词驱动的自动归类 导读 本文基于 agentic awesome
CLI开发工具Podman Image Diff 完全指南:深入解析镜像文件系统变更检测命令
Podman Image Diff 完全指南:深入解析镜像文件系统变更检测命令 导读 podman image diff 是 Podman 中用于 检查镜像文件
容器运行时云原生CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考