☰
Docker CLI `docker diff` 命令完全指南:剖析容器文件系统变更
2026/10/10 1:29:54 网站建设 项目流程
  • CLI
  • 开发工具

【免费下载链接】cli

The Docker CLI

项目地址:https://gitcode.com/gh_mirrors/cli5/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只报告变更的类型和路径,不展示文件内容差异。自容器创建以来,文件系统上的每次变更都会被归为以下三类之一:

符号英文含义中文说明
AA file or directory was added新增了文件或目录
DA file or directory was deleted删除了文件或目录
CA 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接受一个容器参数,可以用以下任一方式标识:

  1. 完整容器 ID:如docker diff 1fdfd1f54c1b30a87b2a1e1d8a2e0f9a3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f(64 位十六进制)。
  2. 短容器 ID:如docker diff 1fdfd1f54c1b,只要前缀能唯一区分即可。
  3. 容器名称:通过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):

  1. TestRunDiff:通过fakeClient注入包含三种变更类型的模拟响应,断言输出为三行C /path/to/file0、A /path/to/file1、D /path/to/file2,验证了「类型 + 路径」的输出契约与 A/C/D 三种类型的渲染;
  2. TestRunDiffClientError:模拟 API 调用返回错误,断言错误被原样向上传播,验证错误处理路径。

结合 cli/command/container/client_test.go 中fakeClient.ContainerDiff的实现可以看到,测试通过注入containerDiffFunc完全隔离了 API 层,专注于验证 CLI 层的参数解析、调用与格式化逻辑。

常见使用场景

  1. 验证容器内程序的写入行为:启动容器后立即执行docker diff,可以快速确认进程是否写入了预期路径(如日志、PID 文件、缓存目录)。
  2. 排查异常写盘:当容器磁盘占用异常时,docker diff能快速定位新增或膨胀的目录,辅助判断是业务数据还是进程残留。
  3. 结合docker commit制作镜像:docker diff列出的变更正是docker commit将打包进新镜像的内容;先用diff审查变更清单,可避免把临时文件、日志等不必要内容带入镜像。
  4. 容器调试与取证:只读容器(如--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

项目地址:https://gitcode.com/gh_mirrors/cli5/cli
点击查看免费下载
上一篇:Sherpa-NCNN实时语音识别原理:从VAD检测到流式解码的核心技术揭秘
下一篇:Hetty Scope API:编程方式管理规则

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询