使用 docker history 查看镜像分层历史

使用 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?

答:CMDENVEXPOSELABELSTOPSIGNAL 等指令不会修改文件系统,只修改镜像的元数据配置,所以对应的层是空层(empty_layer: true),SIZE 为 0。

问:docker historydocker 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
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容