当前位置: 首页 > news >正文

Dockerfile优化指南:利用多阶段构建将Docker镜像体积减小90%

更多

如果你已经跟随我们之前的教程,亲手将自己的应用装进了Docker这个“魔法盒子”,那你可能很快就会遇到一个幸福但又尴尬的烦恼:你亲手构建的Docker镜像,竟然像一个塞满了石头和棉被的行李箱,臃肿不堪,笨重无比。

一个简单的Go或Java应用,最终的镜像体积动辄就是1GB起步。每一次docker push都像是在上传一部高清电影,CI/CD流水线因为这个“大胖子”而慢如蜗牛。

这,真的是Docker的宿命吗?我们真的要为了一份小小的“便当”,而背上一个巨大无比的“登山包”吗?

不。今天,我将带你从一个只会把东西“塞进”包里的“打包新手”,进阶为一名懂得“断舍离”和“空间魔法”的“收纳整理大师”。我们将一起学习Dockerfile的最佳实践,并解锁它的终极奥义——多阶段构建 (Multi-stage builds)。这,是一个能让你镜像体积轻松**减小90%**的“黑魔法”。


问题的根源:你的“行李箱”里,到底装了些什么?

在开始“瘦身”之前,我们得先做一次“开箱检查”,搞清楚我们的镜像,为什么会那么大。

想象一下,我们有一个极简的Go语言Web应用,代码只有一个main.go文件。

一个新手,可能会写出这样一个“直来直去”的Dockerfile:

Dockerfile

# 版本一:一个臃肿的“新手包”
FROM golang:1.19WORKDIR /appCOPY . .RUN go build -o myapp .CMD ["./myapp"]

这个Dockerfile看起来是不是很“正常”?逻辑清晰,也能成功运行。但现在,我们来构建它,并看看它的“体重”:

Bash

docker build -t myapp:v1 .
docker images myapp:v1

你会惊讶地发现,这样一个只输出“Hello World”的小程序,它的镜像体积,可能高达800MB甚至1GB

为什么会这样?我们来分析一下这个“行李箱”里到底装了什么:

  1. 一个豪华过头的“行李箱本身” (FROM golang:1.19): 我们选择的golang基础镜像,为了方便开发者,里面预装了完整的Go语言开发环境、编译器、各种工具链、甚至是一个完整的操作系统(比如Debian)。
  2. 所有乱七八糟的“原材料” (COPY . .): 我们把当前目录下的所有文件,包括源代码.go文件、git记录.git文件夹等,一股脑都塞了进去。
  3. 生产过程中产生的“垃圾” (RUN go build ...): 编译过程,会产生各种中间文件。
  4. 最终我们想要的“成品”: 其实,我们真正想要的,只是那个编译后生成的、小小的、仅有几MB的二进制可执行文件myapp而已。

结果就是,为了带上那瓶几MB的“矿泉水”(myapp),我们却背上了一个装满了“水净化设备、地质勘探工具、以及一堆包装盒”的、重达1GB的巨型登山包。这,显然是不可接受的。

第一阶段瘦身:学习“打包的基本功”——Dockerfile最佳实践

在学习“空间魔法”之前,我们先来优化一下打包的基本功。

  • 技巧一:学会“断舍离”——使用.dockerignore文件 在打包之前,先告诉Docker,哪些东西根本就不要装进来。在你的项目根目录下,创建一个.dockerignore文件,就像.gitignore一样,写入那些你不想打包进镜像的文件名。

.git
.vscode
README.md

这就像你在打包行李前,先把那些“肯定用不上”的东西,从行李箱旁边就拿走了。

技巧二:选择一个更轻便的“背包”——使用alpine镜像 golang:1.19这个基础镜像太大了。我们可以换成golang:1.19-alpine。Alpine是一个极简的Linux发行版,体积只有几MB。

Dockerfile

# 版本二:换了个轻便的背包
FROM golang:1.19-alpine
# ... 其他不变

仅仅是这一个改变,你的镜像体积可能就会从800MB,骤降到300MB左右。

技巧三:合并你的“打包动作”——减少镜像层 Dockerfile中的每一条RUN, COPY, ADD指令,都会在镜像里,新建一个“层”。层数越多,镜像可能就越大。我们可以用&&操作符,把多个RUN命令合并成一条。

Dockerfile

# 不好的写法
RUN apt-get update
RUN apt-get install -y vim# 好的写法 (只产生一层)
RUN apt-get update && apt-get install -y vim

  • 这就像你把要装的东西,一次性都准备好,再打开箱子放进去,而不是放一件,关上,再打开,再放一件。

经过这一系列“基本功”的优化,我们的镜像可能已经“瘦”到了300MB左右。但这,还远远不够。接下来,才是见证奇迹的时刻。

终极奥义:“空间魔法”——多阶段构建 (Multi-stage builds)

现在,我们要引入一个全新的思维:把“生产车间”和“零售包装”彻底分开!

  • 核心理念: 我们用一个临时的、包含了所有“重型生产设备”(编译环境)的镜像,作为我们的“生产车间”。在这个车间里,我们完成所有的编译、构建工作,生产出我们最终想要的那个、小巧玲珑的“最终成品”(比如那个几MB的二进制文件)。 然后,我们再准备一个全新的、极其干净、几乎空无一物的“零售包装盒”(比如一个alpinescratch镜像)。 最后,我们施展魔法,只把那个“最终成品”,从“生产车间”里拿出来,放到这个干净的“零售包装盒”里。至于那个堆满了各种笨重工具的“生产车间”,我们直接把它整个扔掉

听起来是不是很酷?让我们来看看“魔法”是如何实现的。

版本三:一个极致瘦身的“魔法收纳包”

Dockerfile

# --- 第一阶段:命名为“builder”的“生产车间” ---
FROM golang:1.19-alpine AS builder# 设置工作目录
WORKDIR /app# 复制所有“原材料”
COPY . .# 在车间里,用“重型设备”进行生产
# CGO_ENABLED=0 GOOS=linux 是为了编译一个静态的、可以在任何Linux上运行的二进制文件
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp .# --- 第二阶段:一个全新的、干净的“零售包装盒” ---
FROM alpine:latest# 设置工作目录
WORKDIR /root/# 见证魔法的时刻!
# 从我们刚才那个叫“builder”的生产车间里,只把最终成品“myapp”复制出来
COPY --from=builder /app/myapp .# 规定这个包装盒的默认启动命令
CMD ["./myapp"]

我们来解读一下这个“魔法咒语”:

  • FROM golang:1.19-alpine AS builder AS builder就是给这个阶段,起了一个名字,叫builder。它就是我们的“生产车间”。
  • FROM alpine:latest 这是魔法的关键!当Dockerfile里出现第二个FROM指令时,就意味着开启了一个全新的、和前面完全隔离的构建阶段。我们选择了一个仅有5MB大小的alpine作为我们干净的“包装盒”。
  • COPY --from=builder /app/myapp . 这就是“跨位面物质传送”!--from=builder这个参数,精准地告诉Docker:“我要从那个名叫builder的阶段(生产车间)里,把/app/myapp这个文件,复制到我当前这个全新的环境里。”

现在,我们来构建这个最终版本的镜像,并再次检查它的“体重”:

Bash

docker build -t myapp:v3 .
docker images myapp:v3

这一次,你会看到一个让你目瞪口呆的数字。myapp:v3这个镜像的体积,可能只有10MB左右!

我们成功地,把一个800MB的“巨型行李箱”,变成了一个10MB的“随身手拿包”!瘦身率超过了98%!

“瘦身”之后,我们赢得了什么?

一个更小的镜像,带给你的好处,是指数级的。

  1. 更快的部署速度: 你的CI/CD流水线,在拉取和推送镜像时,时间从几分钟,缩短到了几秒钟。
  2. 更低的存储成本: 你的镜像仓库,占用的空间大大减小。
  3. 更高的安全性: 你的最终运行环境里,只包含一个你的应用本身,没有任何多余的工具(比如wget, curl甚至bash)。黑客即使侥幸进入了你的容器,也会发现自己“赤手空拳”,几乎无计可施。这极大地减小了“攻击面”。

你,已经是“收纳大师”

现在,再回头看看你的Dockerfile。

它不再是一份简单的“打包清单”。它是一份经过深思熟虑的、充满了工程智慧的“精密制造工艺图”。你掌握的,也不仅仅是几个命令,而是一种“关注本质、剔除冗余”的软件工程哲学。

去吧,去为你所有的应用,都量身定制一个更小、更快、更安全的“行囊”。在这条通往专业DevOps的路上,你已经迈出了最坚实、也最漂亮的一步。

http://www.dtcms.com/a/337018.html

相关文章:

  • 【音频信号发生器】基本应用
  • LAMP 架构部署:Linux+Apache+MariaDB+PHP
  • C# 使用注册表开机自启
  • [C#] WPF - 自定义控件(行列间距UniformGrid)
  • docker compose再阿里云上无法使用的问题
  • 矿物分类系统开发笔记(一):数据预处理
  • 楼宇自控系统深化设计需关注哪些核心要点?技术与应用解析
  • Casadi库C++运行速度比python版本慢解决方法
  • 从第一性原理理解Embedding:独立模型vs大模型内嵌层的本质区别
  • linux应用软件编程:线程
  • 使用Idea安装JDK
  • 04.IO 重定向和管道
  • 【深度学习】pytorch深度学习框架的环境配置
  • 如何在服务器 clone github 项目
  • axure chrome 浏览器插件的使用
  • goland怎么取消自动删除未使用的包
  • 学习链接。
  • 【秋招笔试】2025.08.17字节跳动秋招机考真题
  • 论文阅读 2025-8-9 [DiC, DropKey]
  • C++中内存池(Memory Pool)详解和完整示例
  • python实现pdfs合并
  • Leetcode 深度优先搜索 (3)
  • WPF中BindingList<T>和List<T>
  • vue3 + antd实现简单的图片点开可以缩小放大查看
  • 浅谈 Python 正则表达式中的 groups()
  • Linux文件相关命令
  • Disbursement on Quarantine Policy(概率、逆元计算期望)
  • 寻北仪如何靠“小”征服地下世界?
  • AI需要防火墙,云计算需要重新构想
  • 20. 云计算-多租户