Docker / 命令文档

Docker 镜像核心(面试向)

$ docker 9 个章节 Linux 命令详解

概述

本篇是「Docker 面试核心」系列第四篇,讲镜像体系:分层与 Layer、Tag 与 Digest、构建缓存、Base Image 选型、镜像瘦身、多阶段构建与私有仓库。镜像题的本质是「分层 + 内容寻址」八个字。

所有命令与输出均在 Docker 26.1.4(overlay2)实测。

镜像分层与 Layer

镜像是一组只读层的堆叠,容器在其顶部挂一个可写层。每层只存与父层的差异(overlay2 的 lowerdir/upperdir 机制)。

$ docker history busybox:latest --format "{{.CreatedBy}}\t{{.Size}}"
BusyBox 1.38.0 (glibc), Debian 13        4.45MB

$ docker images --format "{{.Repository}}:{{.Tag}} {{.Size}}"
kb-multi:latest 4.45MB
busybox:latest 4.45MB
alpine:3.19 7.4MB

面试要点:

  • 分层的价值:共享(busybox 与 kb-multi 共用同一基础层,磁盘只存一份)、缓存(构建复用)、传输(pull/push 只传本地没有的层)
  • 容器可写层是 copy-on-write:改文件 = 从下层复制到可写层再改;删除下层文件只是在可写层打「删除标记」,磁盘不会变小
  • 查看每层来源与大小用 docker history,排查「镜像为什么这么大」的第一入口

Tag 与 Digest

Tag 是可变的人类友好指针(可被重新 push 覆盖);Digest 是内容的 sha256(不可变),生产拉取要能落到 Digest。

$ docker images --digests --format "table {{.Repository}}:{{.Tag}}\t{{.Digest}}\t{{.Size}}"
REPOSITORY:TAG   DIGEST                                                                    SIZE
busybox:latest   sha256:dc2d74b28e4cf8984fa52af1f39bc7c3d9c73760b41a74d629f5d11b1ab28616   4.45MB
alpine:3.19      sha256:6baf43584bcb78f2e5847d1de515f23499913ac9f12bdf834811a3145eb11ca1   7.4MB

# 同一镜像可打多个 Tag,都指向同一镜像 ID;删一个 Tag 不删数据
$ docker tag busybox:latest busybox:kb-demo && docker rmi busybox:kb-demo
tag 删除后真身仍在: 1 个镜像 ID

面试要点:

  • latest 只是默认 Tag 不代表最新版本,且可被覆盖 —— 生产禁止依赖 latest 做部署(不可复现、无法回滚定位)
  • Digest 引用格式 repo@sha256:...,同一次构建产物永远一致,适合审计与回滚
  • Tag 与 Digest 的关系:Tag 指向 manifest,manifest 记录 digest 与层清单;重新 push 同 Tag 会让本地旧 Tag 的 digest 校验失败

构建缓存 Cache

构建是逐层缓存的:父层不变 + 指令不变 + COPY 源不变 + ARG 值不变 → 直接 CACHED。输入一变,该层及之后全部重建(实测见 Dockerfile 篇)。

$ docker build -t kb-test:1.0 /tmp/kb_build 2>&1 | grep -c CACHED
3

面试要点:

  • 缓存命中三要素:父层 ID、指令文本、(COPY/ADD 的)文件内容校验和
  • 工程实践:基础镜像/系统依赖层在前,应用代码层在后;先 COPY 依赖清单装依赖,再 COPY 代码
  • 缓存不是免费的:RUN 里下载的包在缓存层里照样占磁盘;docker builder prune 清构建缓存

Base Image 选择

基础镜像大小特点适用
alpine~7.4MBmusl libc,包管理 apk追求极小体积,注意 musl 兼容性
busybox~4.45MB只有 BusyBox 工具集极简工具容器、调试
debian-slim~75MBglibc,兼容性最好Python/C 等依赖 glibc 的应用
distroless~20MB无 shell 无包管理器生产安全基线,攻击面最小

面试要点:

  • alpine 的经典坑:musl 与 glibc 的 DNS 解析行为差异、C 扩展需重编译;Python 项目常从 alpine 迁回 slim
  • 选型公式:兼容性优先选 slim/distroless,体积极端敏感才上 alpine
  • FROM scratch + 静态编译二进制是体积与安全的双重最优解(Go 生态标配)

镜像大小优化

四个手段,按收益排序:小基础镜像、多阶段构建、合层清理、.dockerignore。

# 反面教材:两层各留 100MB 安装缓存
RUN apt-get update && apt-get install -y big-pkg
RUN apt-get clean                      # 没用!删除发生在新的一层

# 正确:同层内完成安装与清理
RUN apt-get update && apt-get install -y --no-install-recommends big-pkg \
    && rm -rf /var/lib/apt/lists/*

面试要点:

  • 「删除文件不会缩小镜像」是层模型的推论,也是高频考题;清理必须与产生垃圾的安装写在同一条 RUN
  • --no-install-recommends(apt)/ --no-cache(apk)/ pip 无缓存参数配合使用
  • 构建上下文过大(node_modules 被打包)会拖慢 build,.dockerignore 是第一道优化

多阶段构建

把「编译环境」和「运行环境」拆成两个 FROM:构建阶段产生的工具链、源码、缓存不会进入最终镜像,只 COPY 走产物。

FROM alpine:3.19 AS build
RUN echo "artifact-v1" > /tmp/artifact.txt

FROM busybox:latest
COPY --from=build /tmp/artifact.txt /artifact.txt
CMD ["cat", "/artifact.txt"]
$ docker build -t kb-multi /tmp/kb_multi 2>&1 | grep "writing image"
#9 writing image sha256:58ad04c4... done

$ docker run --rm kb-multi
artifact-v1

$ docker images kb-multi --format "{{.Size}}"
4.45MB        # 最终镜像只含 busybox + 产物,alpine 构建层全部留在构建阶段

面试要点:

  • 收益场景:C/Go 编译链、Java 的 JDK 构建层(几百 MB)不进运行镜像,最终镜像只带 JRE 与 jar
  • COPY --from=build 是层间复制;--target 可只构建到某个阶段(调试用)
  • 多阶段不影响缓存逻辑,各阶段独立缓存;命名阶段(AS build)便于引用

私有镜像仓库

自建 Registry(registry:2 官方镜像)实现 push/pull 内网分发,实测全流程:

# 1) 起私有仓(生产可用 Harbor 获得权限管理与扫描)
$ docker run -d --name kb-registry -p 5000:5000 registry:2

# 2) 打 Tag(地址 = registry/仓库:tag)并推送
$ docker tag alpine:3.19 localhost:5000/alpine:kb
$ docker push localhost:5000/alpine:kb
kb: digest: sha256:7dc2e94aced06294d5d6d11c91154d4ea696b13a184e9f8b341535257be69e02 size: 527

# 3) 删除本地后从私有仓拉回
$ docker rmi localhost:5000/alpine:kb && docker pull localhost:5000/alpine:kb
localhost:5000/alpine:kb

# 4) 查询仓库清单(Registry V2 API)
$ curl -s http://127.0.0.1:5000/v2/_catalog
{"repositories":["alpine"]}

面试要点:

  • localhost/127.x 上的 Registry 默认按非安全仓处理(HTTP 可用);非回环地址需要 insecure-registries 配置或配 TLS 证书
  • 私有仓的价值:内网分发快、版本自主可控、配合 CI 实现镜像晋级(dev → staging → prod)
  • Harbor = registry + RBAC + 镜像扫描 + 复制同步,企业标准答案;直连仓库拉取慢时配 registry-mirrors

高频面试题速答

问题一句话答案
镜像为什么要分层层可共享、可缓存、可增量传输,是 Docker 存储与分发效率的基础
Tag 和 Digest 区别Tag 可变可覆盖,Digest 是内容 sha256 不可变,生产固定版本用 Digest
为什么删文件镜像不变小只读层不可修改,删除只是可写层里的标记,须同层安装+清理或重建
容器写文件会影响镜像吗不会,写入发生在容器可写层(copy-on-write),镜像始终只读
多阶段构建解决什么问题编译工具链与产物分离,最终镜像只含运行必需物,体积与攻击面双降
latest Tag 有什么风险可变且语义模糊,重拉可能变镜像,导致不可复现部署与难定位回滚
私有仓库怎么搭registry:2 一行起服务,Harbor 加权限与扫描;非回环地址需 TLS 或 insecure-registries
镜像瘦身四板斧小基础镜像、多阶段构建、同层安装+清理、.dockerignore 减上下文

同分类 · 其他命令文档