极兔一面:Dockerfile如何优化?注意:千万不要只说减少层数
说在前面
在40岁老架构师 尼恩的读者交流群(50+)中,面试题是一个非常、非常高频的交流话题。
最近,有小伙伴面试极兔时,遇到一个面试题:
如果优化 Dockerfile?
小伙伴没有回答好,只是提到了减少镜像层数。
一般来说,面试的小伙伴,大部分都会说
- 使用更小的基础镜像, 比如
alpine. - 减少镜像层数, 比如 使用
&&符号将命令链接起来。 - 给基础镜像打上 安全补丁。
但这些,其实都是单点的优化。优化 Dockerfile 的核心是 合理分层、构建一个精良的基础镜像。
这里尼恩给大家做一下系统化、体系化的梳理,使得大家可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”。
也一并把这个题目以及参考答案,收入咱们的 《尼恩Java面试宝典》V46版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
注:本文以 PDF 持续更新,最新尼恩 架构笔记、面试题 的PDF文件,请从这里获取:码云
为什么要优化镜像
首先,回到起点。为啥要优化 镜像?优化镜像的好处是:
- 一个小镜像有什么好处: 分发更快,存储更少,加载更快。
- 镜像臃肿带来了什么问题: 存储过多,分发更慢且浪费带宽更多。
镜像的构成
其次,来看看镜像的构成。从两个维度来看:
- 俯瞰镜像: 就是一个删减版的操作系统。
- 侧看镜像: 由一层层的
layer堆叠而成

那么问题来了
应该如何优化镜像?
举个例子 docker build
- Dockerfile v1
# v1
FROM nginx:1.15-alpine
RUN echo "hello"
RUN echo "demo best practise"
ENTRYPOINT [ "/bin/sh" ]
- Dockerfile v2
# v2
FROM nginx:1.15-alpine
RUN echo "hello"
RUN echo "demo best practise"
ENTRYPOINT [ "/bin/sh" ]
1st build
全新构建
# docker build -t demo:0.0.1 .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM nginx:1.15-alpine---> 9a2868cac230
Step 2/4 : RUN echo "hello"---> Running in d301b4b3ed55
hello
Removing intermediate container d301b4b3ed55---> 6dd2a7773bbc
Step 3/4 : RUN echo "demo best practise"---> Running in e3084037668e
demo best practise
Removing intermediate container e3084037668e---> 4588ecf9837a
Step 4/4 : ENTRYPOINT [ "/bin/sh" ]---> Running in d63f460347ff
Removing intermediate container d63f460347ff---> 77b52d828f21
Successfully built 77b52d828f21
Successfully tagged demo:0.0.1
2nd build
Dockerfile 与 1st build 完全一致, 命令仅修改 build tag , 从 0.0.1 到 0.0.2
# docker build -t demo:0.0.2 .
Sending build context to Docker daemon 4.096kB
Step 1/4 : FROM nginx:1.15-alpine---> 9a2868cac230
Step 2/4 : RUN echo "hello"---> Using cache---> 6dd2a7773bbc
Step 3/4 : RUN echo "demo best practise"---> Using cache---> 4588ecf9837a
Step 4/4 : ENTRYPOINT [ "/bin/sh" ]---> Using cache---> 77b52d828f21
Successfully built 77b52d828f21
Successfully tagged demo:0.0.2
可以看到,
- 每层 layer 都使用 cache (
---> Using cache) ,并未重新构建。 - 我们可以通过
docker image ls |grep demo看到,demo:0.0.1与demo:0.0.2的 layer hash 是相同。
所以从根本上来说, 这两个镜像就是同一个镜像,虽然都是 build 出来的。
3rd build
这次, 我们将Dockerfile 02的 第三层 RUN echo "demo best practise" 变更为 RUN echo "demo best practise 02"
docker build -t demo:0.0.3 .
Sending build context to Docker daemon 4.608kB
Step 1/4 : FROM nginx:1.15-alpine---> 9a2868cac230
Step 2/4 : RUN echo "hello"---> Using cache---> 6dd2a7773bbc
Step 3/4 : RUN echo "demo best practise 02"---> Running in c55f94e217bd
demo best practise 02
Removing intermediate container c55f94e217bd---> 46992ea04f49
Step 4/4 : ENTRYPOINT [ "/bin/sh" ]---> Running in f176830cf445
Removing intermediate container f176830cf445---> 2e2043b7f3cb
Successfully built 2e2043b7f3cb
Successfully tagged demo:0.0.3
可以看到 ,
- 第二层仍然使用
cache - 但是第三层已经生成了新的 hash 了
- 虽然第四层的操作没有变更,但是由于上层的镜像已经变化了,所以第四层本身也发生了变化。
注意: 每层在
build的时候都是依赖于上册---> Running in f176830cf445。
4th build
第四次构建, 这次使用 --no-cache 不使用缓存, 模拟在另一台电脑上进行 build 。
# docker build -t demo:0.0.4 --no-cache .
Sending build context to Docker daemon 5.632kB
Step 1/4 : FROM nginx:1.15-alpine---> 9a2868cac230
Step 2/4 : RUN echo "hello"---> Running in 7ecbed95c4cd
hello
Removing intermediate container 7ecbed95c4cd---> a1c998781f2e
Step 3/4 : RUN echo "demo best practise 02"---> Running in e90dae9440c2
demo best practise 02
Removing intermediate container e90dae9440c2---> 09bf3b4238b8
Step 4/4 : ENTRYPOINT [ "/bin/sh" ]---> Running in 2ec19670cb14
Removing intermediate container 2ec19670cb14---> 9a552fa08f73
Successfully built 9a552fa08f73
Successfully tagged demo:0.0.4
可以看到,
- 虽然和
3rd build使用的Dockerfile相同, 但由于没有缓存,每一层都是重新 build 的。 - 虽然
demo:0.0.3和demo:0.0.4在功能上是一致的。但是 他们的 layer 不同, 从根本上来说,他们是不同的镜像。
结论
1. 合理分层、构建一个精良的基础镜像
- 一个相对固定的
build环境 - 善用
cache - 构建
自己的基础镜像:其中就包括了
a. 安全补丁
b. 权限限制
c. 基础库依赖安装
d. 等…
2. 精简为美:一屋不扫何以扫天下
- 使用
.dockerignore保持context干净 - 容器镜像环境清理
a. 缓存清理
b.multi stage build
尼恩提示:以上答案,所包含的技术细节比较多,具体请参见《尼恩Java面试宝典》最新版。
参考文献
docker storage driver: https://docs.docker.com/storage/storagedriver/dockerfile best practices: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/multi-stage: https://docs.docker.com/develop/develop-images/multistage-build/
推荐阅读:
《响应式圣经:10W字,实现Spring响应式编程自由》
《全链路异步,让你的 SpringCloud 性能优化10倍+》
《Linux命令大全:2W多字,一次实现Linux自由》
《网易二面:CPU狂飙900%,该怎么处理?》
《阿里二面:千万级、亿级数据,如何性能优化? 教科书级 答案来了》
《峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?》
《场景题:假设10W人突访,你的系统如何做到不 雪崩?》
《2个大厂 100亿级 超大流量 红包 架构方案》
《Nginx面试题(史上最全 + 持续更新)》
《K8S面试题(史上最全 + 持续更新)》
《操作系统面试题(史上最全、持续更新)》
《Docker面试题(史上最全 + 持续更新)》
《Springcloud gateway 底层原理、核心实战 (史上最全)》
《Flux、Mono、Reactor 实战(史上最全)》
《sentinel (史上最全)》
《Nacos (史上最全)》
《TCP协议详解 (史上最全)》
《分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)》
《clickhouse 超底层原理 + 高可用实操 (史上最全)》
《nacos高可用(图解+秒懂+史上最全)》
《队列之王: Disruptor 原理、架构、源码 一文穿透》
《环形队列、 条带环形队列 Striped-RingBuffer (史上最全)》
《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)》
《单例模式(史上最全)》
《红黑树( 图解 + 秒懂 + 史上最全)》
《分布式事务 (秒懂)》
《缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)》
《缓存之王:Caffeine 的使用(史上最全)》
《Java Agent 探针、字节码增强 ByteBuddy(史上最全)》
《Docker原理(图解+秒懂+史上最全)》
《Redis分布式锁(图解 - 秒懂 - 史上最全)》
《Zookeeper 分布式锁 - 图解 - 秒懂》
《Zookeeper Curator 事件监听 - 10分钟看懂》
《Netty 粘包 拆包 | 史上最全解读》
《Netty 100万级高并发服务器配置》
《Springcloud 高并发 配置 (一文全懂)》
相关文章:
极兔一面:Dockerfile如何优化?注意:千万不要只说减少层数
说在前面 在40岁老架构师 尼恩的读者交流群(50)中,面试题是一个非常、非常高频的交流话题。 最近,有小伙伴面试极兔时,遇到一个面试题: 如果优化 Dockerfile? 小伙伴没有回答好,只是提到了减少镜像层数。…...
SpringBoot+Vue实现酒店客房管理系统
文末获取源码 开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7/8.0 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.9 浏…...
自适应多因素认证:构建不可破解的企业安全防线|身份云研究院
打开本文意味着你理解信息安全的重要性,并且希望获取行业最佳实践来保护你所在组织的信息安全。本文将带你了解多因素认证(MFA:Multi-Factor-Authentication)对于企业信息安全的重要性以及实施方法。 多因素认证(MFA&…...
阶段二8_集合ArrayList_学生管理系统_详细步骤
一.学生管理系统案例 1.需求: 针对目前我们的所学内容,完成一个综合案例:学生管理系统! 该系统主要功能如下: 1.添加学生:通过键盘录入学生信息,添加到集合中 2.删除学生:通过键盘录…...
一篇解决Linux 中的负载高低和 CPU 开销并不完全对应
负载是查看 Linux 服务器运行状态时很常用的一个性能指标。在观察线上服务器运行状况的时候,我们也是经常把负载找出来看一看。在线上请求压力过大的时候,经常是也伴随着负载的飙高。 但是负载的原理你真的理解了吗?我来列举几个问题&#x…...
关于IDM下载器,提示:一个假冒的序列号被用来注册……idea项目文件路径报红
关于IDM下载器,提示:一个假冒的序列号被用来注册……到C:\Windows\System32\drivers\etc 修改目录下面的hosts文件(如果没有修改的权限就右键属性hosts文件修改user的权限为完全控制),在hosts里面增加以下内容…...
JVM - 高效并发
目录 Java内存模型和内存间的交互操作 Java内存模型 内存间的交互操作 内存间交互操作的规则 volatile特性 多线程中的可见性 volatile 指令重排原理和规则 指令重排 指令重排的基本规则 多线程中的有序性 线程安全处理 锁优化 锁优化之自旋锁与自适应自旋 锁优…...
中小学智慧校园电子班牌系统源码 Saas云平台模式
智慧电子班牌区别于传统电子班牌,智慧校园电子班牌系统更加注重老师和学生的沟通交流和及时数据交互。学校为每个教室配置一台智能电子班牌,一般安装于教室门口,用来实时显示学校通知、班级通知,可设置集中分布式管理,…...
记录一次服务器被攻击的经历
突然收到阿里云发过来的异常登陆的信息: 于是,急忙打开电脑查看对应的ECS服务器的记录: 发现服务器的cpu占用率异常飙升,所以可以大概断定服务器已经被非法入侵了。 通过自己的账号登陆后,发现sshd服务有异常的链接存…...
Python解题 - CSDN周赛第29期 - 争抢糖豆
本期问哥是志在必得,这本算法书我已经觊觎许久,而之前两次因为种种原因未能如愿。因此,问哥这几天花了不少时间,把所有之前在每日一练做过的题目重新梳理了一遍。苦心人,天不负,感谢官方大大! 第…...
C代码中访问链接脚本中的符号
一、目的在之前的《GNU LD脚本命令语言(一)》、《GNU LD脚本命令语言(二)》我们介绍了GNU链接脚本的知识点,基本上对链接脚本中的SECTION、REGION、以及加载地址与执行地址的关系等内容有了一定的了解。本篇主要讲解链…...
MySQL 8:MySQL索引
索引就是通过一定的算法建立数据模型,用于快速查找某一列中具有特定值的行。如果没有索引,MySQL 必须从第一条记录开始读取整个表,直到找到相关的表。表越大,查询数据所花费的时间就越多。如果表中查询的列有索引,MySQ…...
JVM详解
一,JVM 1,JVM区域划分 类装载器,运行时数据区,字节码执行引擎 2,JVM内存模型(运行时数据区) 由本地方法栈,虚拟机栈,堆,方法区,和程序计数器组成。…...
MySQL数据库调优————索引数据结构
B-TREE B-TREE数据结构 B-TREE特性 根节点的子结点个数2 < X < m,m是树的阶 假设m 3,则根节点可有2-3个孩子 中间节点的子节点个数m/2 < y < m 假设m 3,中间节点至少有2个孩子,最多3个孩子 每个中间节点包含n个关…...
visual studio 改变界面语言
在使用visual studio 2019 时,开始是英文界面,后面变成了中文界面。但是看视频教学时有的是英文界面,我就想回到英文界面,所以有切换界面语言的需要。其实操作很简单:工具-> 选项 打开界面在界面里选择环境…...
2023.2.16每日一题——1250. 检查「好数组」
每日一题题目描述解题核心解法一:数论题目描述 题目链接:1250. 检查「好数组」 给你一个正整数数组 nums,你需要从中任选一些子集,然后将子集中每一个数乘以一个 任意整数,并求出他们的和。 假如该和结果为 1&#x…...
亿级高并发电商项目-- 实战篇 --万达商城项目 八(安装FastDFS、安装Nginx、文件服务模块、文件上传功能、商品功能与秒杀商品等功能)
专栏:高并发---分布式项目 👏作者简介:大家好,我是小童,Java开发工程师,CSDN博客博主,Java领域新星创作者 📕系列专栏:前端、Java、Java中间件大全、微信小程序、微信支…...
Viper捐款7000万韩元,合计人民币是多少钱?
Viper捐款7000万韩元,合计人民币是多少钱? #2023LCK春季赛##英雄联盟# #Viper捐款7000万韩元# Viper向大田东区捐款 7000 万,成为大田荣誉协会 105 号会员。Viper选手从 2019 年开始一直向大田东区捐款,但是他不希望这件事被公开…...
前端vue实现系统拦截跳转外链并进入跳转询问界面
跳转询问界面如下图所示: 给自己挖坑的实现方式,最终解决方案请看最底下 思路:正常情况下我们有2种方式跳转外链 第一种非a标签,我们手动添加事件进行跳转 <div class"dingdan public-padding p-item" click&quo…...
【Linux】Shell(Bash)单引号、双引号、不加引号和反引号用法和区别详解
简要总结 不加引号:不会将含有空格的字符串视为一个整体输出, 如果内容中有变量等,会先把变量解析出结果,然后在输出最终内容来,如果字符串中带有空格等特殊字符,则不能完整的输出,需要改加双引号ÿ…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器
一、原理介绍 传统滑模观测器采用如下结构: 传统SMO中LPF会带来相位延迟和幅值衰减,并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF),可以去除高次谐波,并且不用相位补偿就可以获得一个误差较小的转子位…...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
