极兔一面:Dockerfile如何优化?注意:千万不要只说减少层数

news2025/2/26 9:32:48

说在前面

在40岁老架构师 尼恩的读者交流群(50+)中,面试题是一个非常、非常高频的交流话题。

最近,有小伙伴面试极兔时,遇到一个面试题:

如果优化 Dockerfile?

小伙伴没有回答好,只是提到了减少镜像层数。

一般来说,面试的小伙伴,大部分都会说

  1. 使用更小的基础镜像, 比如 alpine.
  2. 减少镜像层数, 比如 使用 && 符号将命令链接起来。
  3. 给基础镜像打上 安全补丁

但这些,其实都是单点的优化。优化 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.10.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

可以看到,

  1. 每层 layer 都使用 cache ( ---> Using cache) ,并未重新构建。
  2. 我们可以通过 docker image ls |grep demo 看到, demo:0.0.1demo: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

可以看到 ,

  1. 第二层仍然使用 cache
  2. 但是第三层已经生成了新的 hash 了
  3. 虽然第四层的操作没有变更,但是由于上层的镜像已经变化了,所以第四层本身也发生了变化。

注意: 每层在 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

可以看到,

  1. 虽然和 3rd build 使用的 Dockerfile 相同, 但由于没有缓存,每一层都是重新 build 的。
  2. 虽然 demo:0.0.3demo:0.0.4 在功能上是一致的。但是 他们的 layer 不同, 从根本上来说,他们是不同的镜像。

结论

1. 合理分层、构建一个精良的基础镜像

  1. 一个相对固定的 build 环境
  2. 善用 cache
  3. 构建 自己的基础镜像:其中就包括了
    a. 安全补丁
    b. 权限限制
    c. 基础库依赖安装
    d. 等…

2. 精简为美:一屋不扫何以扫天下

  1. 使用 .dockerignore 保持 context 干净
  2. 容器镜像环境清理
    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 高并发 配置 (一文全懂)》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/347549.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

01背包问题 AcWing(JAVA)

有 N件物品和一个容量是 V的背包。每件物品只能使用一次。 第 i件物品的体积是 vi,价值是 wi。 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。 输入格式 第一行两个整数,N&#xff…

数据结构与算法(一):概述

数据结构学了有一年左右的时间了,但是一直没有详细地总结一下,现在回想起来,感觉有些内容忘记了。所以接下来一段时间我将重新归纳总结一下,算是温故而知新了。 一、数据结构 1、定义 数据结构是计算机存储、组织数据的方式。在…

excel学习笔记-导入外部文件,报错,数值格式变换,日期格式的转化,求和快捷键,冻结窗格

这里写目录标题一、导入外部文件1.导入csv文件2.导入txt文件3.修改txt内容,需要刷新才能看见更改二、报错三、数值格式变换四、日期格式的转化五、ALT ,求和快捷键六、冻结窗格一、导入外部文件 1.导入csv文件 2.导入txt文件 3.修改txt内容,…

内网渗透(三十)之横向移动篇-利用远控工具向日葵横向移动

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

力扣sql简单篇练习(十六)

力扣sql简单篇练习(十六) 1 产品销售分析|| 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 SELECT p.product_id,sum(s.quantity) total_quantity FROM Product p INNER JOIN Sales s ON p.product_ids.product_id GROUP BY p.product_id1.3 运行截…

explain 每个列的含义

官网传送门:https://dev.mysql.com/doc/refman/5.7/en/explain-output.html 实例表 DROP TABLE IF EXISTS actor;CREATE TABLE actor (id int(11) NOT NULL,name varchar(45) DEFAULT NULL,update_time datetime DEFAULT NULL,PRIMARY KEY (id)) ENGINEInnoDB DEFA…

【Java项目】基于Java+MySQL+Tomcat+maven+Servlet的个人博客系统的完整分析

✨哈喽,进来的小伙伴们,你们好耶!✨ 🛰️🛰️系列专栏:【Java项目】 ✈️✈️本篇内容:个人博客系统前后端分离实现! 🚀🚀个人代码托管github:博客系统源码地址&#xff…

maven解决包冲突简单方式(插件maven helper | maven指令)

文章目录使用idea插件maven helper使用maven指令在Java开发中,常常会遇到不同jar包之间存在冲突的情况,这可能会导致编译错误、运行时异常等问题。 使用idea插件maven helper 在idea安装插件maven helper 安装重启完之后点击pom文件,有一个De…

元宇宙基础设施:WEB 3.0 chain33 优势分析

WEB 3.0 chain33 优势分析 一、与以太坊兼容(优势:兼容及开发成本低百倍) 使用 Solidity 开发智能合约并部署在 EVM 虚拟机上是目前以太坊生态主流的开发 模式,目前看来不兼容以太坊或不能与以太坊跨链都可能是不归路,…

AcWing语法基础课笔记 第一章 C++入门及简单的顺序结构

第一章 C入门及简单的顺序结构 编程是一种控制计算机的方式,和我们平时双击打开文件、关机、重启没有任何区别。 ———闫学灿 C中常用的变量类型 和所占字节大小 输出变量地址符: 软件环境 作业的评测与提交 在线练习地址:www.acwing.com …

数据库设计的基本步骤

分步设计法遵循自顶向下、逐步求精的原则,将数据库设计过程分解为若干相互独立又相互依存的阶段,每一阶段采用不同的技术与工具,解决不同的问题,从而将问题局部化,减少了局部问题对整体设计的影响。目前,此…

aws Distro for OpenTelemetry 可观测性workshop记录

参考资料 https://aws-otel.github.io/docs/introductionhttps://aws-otel.github.io/docs/introduction aws distro for opentelemetry 官方提供了不同语言不同使用场景下完善的使用实例和相关配置。 AWS Distro for OpenTelemetrics 由以下部分组成,用于向后端…

用户认证概述

文章目录一、用户身份认证1.1 单一服务器模式1.2 SSO(Single Sign On)模式1.3 Token模式二、JWT令牌2.1 JWT 令牌说明2.2 JWT令牌的组成2.3 JWT 问题和趋势2.4 JWT 测试一、用户身份认证 1.1 单一服务器模式 一般过程如下: 用户向服务器发送…

【编程基础之Python】5、安装Python第三方模块

【编程基础之Python】5、安装Python第三方模块安装Python第三方模块为什么需要安装第三方模块Python包管理器介绍pippip installpython -m pip installcondaconda install在Windows环境中安装Python模块安装numpy安装pandas安装matplotlib在Linux环境中安装Python模块在PyCharm…

线程的创建

1. 多线程常用函数 1.1 创建一条新线程pthread_create 对此函数使用注意以下几点: 线程例程指的是:如果线程创建成功,则该线程会立即执行的函数。POSIX线程库的所有API对返回值的处理原则一致:成功返回0,失败返回错误…

NLP实践——知识图谱问答模型FiD

NLP实践——知识图谱问答模型FiD0. 简介1. 模型结构2. 召回3. 问答4. 结合知识的问答0. 简介 好久没有更新了,今天介绍一个知识图谱问答(KBQA)模型,在此之前我一直在用huggingface的Pipeline中提供的QA模型,非常方便但…

低代码和零代码的有什么不同?如何区分?

低代码开发平台和零代码平台的区别是什么?一个例子就能讲清楚! 周末你外出露营,在野外需要搭一个帐篷。有两种方法: 一种是最原始的搭帐篷方法,即有隔水布、外账、内账、营柱骨架等等......另一种是直接“封装好”的…

OpenCV-PyQT项目实战(5)项目案例01:图像模糊

欢迎关注『OpenCV-PyQT项目实战 Youcans』系列,持续更新中 OpenCV-PyQT项目实战(1)安装与环境配置 OpenCV-PyQT项目实战(2)QtDesigner 和 PyUIC 快速入门 OpenCV-PyQT项目实战(3)信号与槽机制 …

PySpark实战一之入门

1、PySpark的编程模型 分三个模块: 数据输入:通过SparkContext对象,完成数据输入 数据处理计算:输入数据后得到RDD对象,对RDD对象的成员方法进行迭代计算 数据输出:最后通过RDD对象的成员方法&#xff0…

互联网行业固定资产智能化解决方案为企业降本增效

互联网行业的固定资产数量和种类往往比较多,来源可能是租赁、购入、调拨等。主要分为:办公设备、电子设备、服务器等。固定资产是互联网企业的重要资产之一,是企业持续经营的物质基础。因此,对于实物资产的管理尤为重要。 互联网…