Sunday面试指南

Docker 镜像和容器有什么区别?分层构建为什么能复用缓存?

🧑‍💻 面试官:Docker 镜像和容器有什么区别?

🙋‍♂️ 我:镜像是模板,容器是运行起来的镜像。

🧑‍💻 面试官:我在容器里改一个文件,会把镜像也改掉吗?

🙋‍♂️ 我:不会,容器有自己的可写层。

🧑‍💻 面试官:那 Dockerfile 前面复制了整个项目,后面安装依赖,改一句业务代码为什么连依赖都重新装了?

先把「镜像的只读层」「容器的可写层」「构建缓存的输入」分开,才能解释运行与构建两种行为。

面试速答(60 秒版)

镜像包含运行需要的文件系统内容与配置,通常由可复用的只读层组成。容器是基于镜像创建的运行实例,有自己的可写层,并使用隔离与资源限制等运行机制。

容器写文件通常改变自己的层或挂载的数据,不会直接修改原镜像;需要持久化的数据应使用合适的卷等方案。

构建缓存则依据指令和相关输入判断是否能复用。经常变化的源码放得太早,会让后续步骤重新执行。实际构建会先复制依赖描述与 lockfile、安装依赖,再复制源码;再用多阶段构建把编译工具留在构建阶段,只把必要产物放进运行镜像。

缓存不是可复现性的保证,还要管理基础镜像、依赖与构建输入。

两个容器复用镜像只读层,各自拥有可写层,外部卷负责持久化

知识点详解:容器写入和镜像构建,用的是不同的层

镜像相同,不代表运行状态相同

假设同一个镜像启动两个容器。它们可以复用镜像内容,但各自有运行进程、配置与可写层。甲容器改了日志文件,乙不会因此自动看到。

如果两者挂载同一个外部卷,则共享行为来自挂载的数据,不是镜像被修改。容器删除后,未持久化的可写层数据可能随之丢失。

因此,镜像版本解决「运行什么代码」,卷或外部存储解决「数据怎样留下来」。容器也不是完整虚拟机,通常共享宿主内核;隔离能力要按实际平台与运行配置理解。

层能复用,但下层内容不会被免费抹掉

镜像的多个层共同组成最终文件系统视图。容器在上面增加可写层,修改来自下层的文件时,按存储驱动等机制形成自己的内容。

如果一个构建步骤把大文件放进镜像,后续步骤又删除,最终视图可以不显示它,但早先层里的内容不一定因此从镜像分发大小中消失。

所以避免先把不需要的文件复制进去。构建上下文用 .dockerignore 排除无关内容;密钥不要复制到层里再删,也不要误以为后面看不见就从历史中彻底消失。

缓存命中看输入,而不是文件名没变

假设先复制全项目,再安装依赖。源码一变化,前面的复制输入变了,后续安装依赖也可能重新执行。

把依赖描述与 lockfile 先复制,再安装,最后放业务源码,可以让只改源码时更容易复用安装步骤。具体缓存还受构建参数、基础镜像和构建器行为影响。

COPY 等相关步骤会检查输入;单纯修改文件 mtime 不是官方所说的缓存失效依据。RUN 也不会自动检查远端包仓库是否更新,所以缓存命中不证明拿到的是最新包。需要更新时,应明确安排刷新与版本策略。

多阶段构建减少带入运行环境的东西

假设 TypeScript 项目需要编译,或 Python 项目要构建安装产物。构建阶段可以准备编译工具和依赖,运行阶段只复制实际需要的结果。

两种语言的产物与运行依赖不同,不能机械复制一个 Dockerfile。Python 扩展还要注意平台与动态库兼容,TS 编译产物也可能仍需要运行时包。

验收时检查最终镜像内容、大小、启动与健康状态,再做一次无缓存构建比较结果。更小是收益,但不能为了小到删掉必需库;更快构建也不能掩盖依赖漂移。

本题机制参考:Docker Image Layers、Docker Cache Invalidation、Docker Multi-stage Builds。

面试官继续追问

容器删除以后数据一定没了吗?

不是。外部卷等持久化数据可以独立存在,容器可写层与挂载存储要分清。

每个 Dockerfile 指令都增加一个文件系统层吗?

不能这样概括。某些指令改变镜像配置或元数据,文件系统变化与镜像历史条目也不完全等同。

用了 lockfile 就完全可复现吗?

不一定。基础镜像、平台、运行时、远端资源和安装脚本也可能影响结果,需要控制完整输入。

面试速记卡

  • 镜像是可复用内容与配置;容器是运行实例。
  • 镜像只读层与容器可写层分开。
  • 持久数据依靠合适存储,不依赖临时可写层。
  • 稳定依赖输入在前,易变源码在后。
  • 多阶段只带必要产物;缓存不等于可复现。

公司面试真题

这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。

浏览公司面试真题 →
简历汪永久免费在线制作简历,模板直接套用、导出无水印,永久免费、下载免费,不需要付费解锁任何功能。去写简历