使用 docker history 查看镜像分层历史
命令简介
docker history 是分析镜像分层的核心命令,它展示一个镜像的构建历史——每一条记录对应 Dockerfile 中的一条指令(或 FROM 基础镜像的每一层)。
docker history [选项] <镜像名:标签>
基本用法
# 查看 nginx 镜像的历史
docker history nginx:latest
# 输出示例
IMAGE CREATED CREATED BY SIZE
abc123def456 2 weeks ago CMD ["nginx" "-g" "daemon off;"] 0B
def456abc123 2 weeks ago STOPSIGNAL SIGQUIT 0B
789abc123def 2 weeks ago EXPOSE 80 0B
456def789abc 2 weeks ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B
123abc456def 2 weeks ago COPY docker-entrypoint.sh /usr/local/bin/ 1.2kB
...
graph TB
subgraph docker history 输出对应关系
CMD_LAYER[CMD 层<br/>SIZE: 0B]
ENV_LAYER[ENV 层<br/>SIZE: 0B]
RUN_LAYER[RUN apt-get<br/>SIZE: 132MB]
COPY_LAYER[COPY 层<br/>SIZE: 1.2kB]
FROM_LAYER[FROM ubuntu:22.04<br/>SIZE: 78MB]
subgraph Dockerfile
D1[FROM ubuntu:22.04]
D2[COPY xxx]
D3[RUN apt-get]
D4[ENV xxx]
D5[CMD xxx]
end
end
D1 --> FROM_LAYER
D2 --> COPY_LAYER
D3 --> RUN_LAYER
D4 --> ENV_LAYER
D5 --> CMD_LAYER
常用参数
–no-trunc:不截断输出
# 默认情况下,长内容会被截断
docker history nginx:latest
# CREATED BY
# COPY docker-entrypoint.sh /usr/local/bin/
# 使用 --no-trunc 显示完整指令
docker history nginx:latest --no-trunc
# CREATED BY
# COPY docker-entrypoint.sh /usr/local/bin/ # buildkit
–format:自定义输出格式
# 只显示层大小和创建指令
docker history nginx:latest --format "table {{.Size}}\t{{.CreatedBy}}"
# 输出
SIZE CREATED BY
0B CMD ["nginx" "-g" "daemon off;"]
0B STOPSIGNAL SIGQUIT
0B EXPOSE 80
132MB /bin/sh -c #(nop) ENTRYPOINT ["/docker-entrypoint.sh"]
–human:人性化显示(默认开启)
# 显示实际字节数
docker history nginx:latest --no-human
# SIZE
# 127836509 ← 实际字节数,而不是 122MB
实战:分析一个自建镜像
# 示例 Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package.json package-lock.json ./
COPY src/ ./src/
ENV NODE_ENV=production
RUN npm ci --production
EXPOSE 3000
CMD ["node", "src/index.js"]
# 构建
docker build -t myapp:latest .
# 查看历史
docker history myapp:latest --no-trunc | head -10
输出分析:
IMAGE CREATED CREATED BY SIZE
f1a2b3c4 1 min ago CMD ["node" "src/index.js"] 0B ← 空层
e5d6f7a8 1 min ago EXPOSE 3000 0B ← 空层
b9c0d1e2 1 min ago /bin/sh -c npm ci --production 52MB ← 最大的层
f3a4b5c6 1 min ago /bin/sh -c #(nop) ENV NODE_ENV=production 0B ← 空层
d7e8f9a0 1 min ago /bin/sh -c #(nop) COPY src/ ./src/ 2MB ← 应用代码
b1c2d3e4 1 min ago /bin/sh -c #(nop) COPY package*.json ./ 12KB ← 依赖清单
a5b6c7d8 1 min ago /bin/sh -c #(nop) WORKDIR /app 0B ← 空层
9f0e1d2c 2 weeks ago /bin/sh -c #(nop) CMD ["node"] 0B ← FROM 基础镜像的层
3b4a5c6d 2 weeks ago /bin/sh -c #(nop) ENTRYPOINT 0B
...
通过 history 识别常见问题
问题一:镜像体积过大——找到罪魁祸首
# 按层大小排序,找出最大的层
docker history myapp:latest --no-trunc \
--format "{{.Size}}\t{{.CreatedBy}}" | sort -rh
132MB /bin/sh -c apt-get update && apt-get install -y python3
52MB /bin/sh -c npm ci --production
78MB /bin/sh -c #(nop) FROM ubuntu:22.04
问题二:发现文件删除无效
# 问题 Dockerfile
FROM ubuntu:22.04
# 层1:下载大文件(200MB)
RUN wget https://example.com/bigfile.tar.gz && tar -xzf bigfile.tar.gz
# 层2:删除压缩包(但层1仍占200MB)
RUN rm bigfile.tar.gz
# docker history 会显示:
SIZE CREATED BY
0B CMD ["bash"]
0B RUN rm bigfile.tar.gz ← 删除操作是空层?
# ⚠️ 注意!RUN rm 虽然"删除"了文件,但层1的200MB还在
# 实际上层2只是增加了 whiteout 文件
问题三:检查缓存利用情况
# 如果基础镜像层后的第一层有 0B 但被截断了
# 可能是 FROM 指令产生的空层
docker history myapp --no-trunc | head -5
# ...
# 0B /bin/sh -c #(nop) FROM node:18-alpine@sha256:xxx
# 对比不同构建的 history
# 相同 hash 的 FROM 层 → 缓存命中
# 不同 hash → 基础镜像变了,缓存失效
Docker history 与 build cache 的关系
# 第一次构建
docker build -t myapp:v1 .
docker history myapp:v1 --no-trunc
# 修改 Dockerfile 后第二次构建
docker build -t myapp:v2 .
docker history myapp:v2 --no-trunc
# 对比两版的 history
# 如果 FROM 层 hash 相同 → 基础镜像层缓存命中
# 如果 COPY 前一层 hash 相同 → COPY 缓存命中
graph LR
subgraph 缓存命中分析
V1[myapp:v1 历史]
V2[myapp:v2 历史]
V1 --> L1[FROM 层<br/>hash: xxx]
V1 --> L2[COPY package.json<br/>hash: yyy]
V1 --> L3[RUN npm ci<br/>hash: zzz]
V1 --> L4[COPY src/<br/>hash: aaa]
V2 --> L1_2[FROM 层<br/>hash: xxx ✅ 缓存命中]
V2 --> L2_2[COPY package.json<br/>hash: yyy ✅ 缓存命中]
V2 --> L3_2[RUN npm ci<br/>hash: zzz ✅ 缓存命中]
V2 --> L4_2[COPY src/<br/>hash: bbb ❌ 缓存未命中]
end
面试追问
问:为什么有些层大小为 0B?
答:CMD、ENV、EXPOSE、LABEL、STOPSIGNAL 等指令不会修改文件系统,只修改镜像的元数据配置,所以对应的层是空层(empty_layer: true),SIZE 为 0。
问:docker history 和 docker inspect 显示的层信息有什么不同?
答:docker history 展示的是构建历史(每条 Dockerfile 指令对应一行)。docker inspect .RootFS.Layers 展示的是实际物理层(多个指令可能合并成一层,空层不占物理存储)。
总结
| 用途 | 命令 | 关键参数 |
|---|---|---|
| 查看构建历史 | docker history |
--no-trunc (完整指令) |
| 分析层大小 | docker history --format |
--human / --no-human |
| 调试缓存 | 对比不同版本 history | 看层 hash 是否相同 |
| 定位体积问题 | 按 SIZE 排序 | --format "{{.Size}}" |
一句话总结:docker history 是镜像的”X光机”,一眼看穿镜像内部结构,是排查镜像体积、分析缓存命中、理解分层结构的必备工具。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容