更多

如果你已经跟随我们之前的教程,亲手将自己的应用装进了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.pswp.cn/bicheng/93799.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/93799.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/93799.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

英文PDF翻译成中文怎么做?试试PDF翻译工具

在全球化快速发展的时代,跨语言交流变得愈发频繁,无论是学术研究、商务合作还是日常学习,都离不开一个高效、准确的翻译工具。尤其是对于PDF文件的翻译需求,更是日益增长。今天,就让我们一起深入了解几款在PDF翻译领域…

macOS使用brew切换Python版本【超详细图解】

目录 一、更新Homebrew仓库 二、安装pyenv 三、将pyenv添加到bash_profile文件中 四、使.bash_profile文件的更改生效 五、安装需要的Python版本 六、设置全局使用的Python版本 七、检查Python版本是否切换成功 pyenv常用命令 一、更新Homebrew仓库 brew update 这个…

[矩阵置零]

初始思路分析 这段代码实现了将矩阵中元素为0的行和列全部置零的功能。主要思路是使用标记数组记录需要置零的行和列。以下是详细分析&#xff1a; 1. 初始化阶段 int m matrix.size(); int n matrix[0].size(); vector<bool> row(m), col(n);获取矩阵的行数m和列数n创…

redis-集成prometheus监控(k8s)

一. 简介&#xff1a; 关于redis的简介和部署&#xff0c;可以参考单独的文章redis-sentinel基础概念及部署-CSDN博客&#xff0c;这里就不细说了。这里只讲讲如何在k8s中部署export并基于prometheus做redis的指标采集。 二. 实现方式&#xff1a; 首先我们需要先部署exporter…

OVS:ovn为什么默认选择Geneve作为二层隧道网络协议?

首先确认 Geneve 是一种封装协议,可能提供比 VLAN 或 VXLAN 更灵活的扩展能力,这对 OVN 的多租户场景很重要。可能需要支持更多元数据字段,比如携带网络策略信息,这符合 SDN 集中控制的需求。 性能方面需要考虑封装效率和硬件支持情况,虽然 Geneve 头部稍大,但现代网卡的…

grep命令要点、详解和示例

grep技术要点 1) 工作模型&#xff08;3 件事&#xff09; 输入&#xff1a;从文件或标准输入&#xff08;-&#xff09;读入&#xff0c;一次按“行”处理&#xff08;除非用 -z 改成以 NUL 作为“行”分隔&#xff09;。匹配&#xff1a;把每一行拿去和模式&#xff08;patte…

nVidia Tesla P40使用anaconda本地重编译pytorch3d成功加载ComfyUI-3D-Pack

背景 自己用的是nVidia Tesla P40&#xff0c;垃圾佬专属卡 使用下面的由YanWenKun提供的ComfyUI-3D-Pack预安装环境&#xff0c;但在本地编译pytorch3d这一步出错&#xff0c;后面有出错信息&#xff0c;如果有和我一样的卡一样的问题&#xff0c;参看此文的解决方法 老版本…

网络基础——协议认识

文章目录网络基础网络的发展——引出一些概念协议认识初识协议协议分层协议分层的模型再谈协议为什么要有TCP/IP协议TCP/IP协议的宏观认识宏观理解TCP/IP协议和操作系统的关系协议的真正本质网络基础 本篇文章&#xff0c;我们将正式进入网络部分的学习。这是网络部分的第一篇…

云原生俱乐部-RH134知识点总结(2)

这一章的内容也会比较多&#xff0c;因为预期三篇文章更完RH134系列&#xff0c;所以每章安排的内容都比较多&#xff0c;并且RH134上面的都是重点&#xff0c;一点也不好写。昨天一天将RH124系列写完了&#xff0c;今天争取将RH134系列写完。至于我为什么要着急将这些写完&…

深度学习-计算机视觉-微调 Fine-tune

1. 迁移学习迁移学习&#xff08;transfer learning&#xff09;是一种机器学习方法&#xff0c;通过将源数据集&#xff08;如ImageNet&#xff09;上训练得到的模型知识迁移到目标数据集&#xff08;如特定场景的椅子识别任务&#xff09;。这种方法的核心在于利用预训练模型…

STL库——string(类函数学习)

ʕ • ᴥ • ʔ づ♡ど &#x1f389; 欢迎点赞支持&#x1f389; 个人主页&#xff1a;励志不掉头发的内向程序员&#xff1b; 专栏主页&#xff1a;C语言&#xff1b; 文章目录 前言 一、STL简介 二、string类的优点 三、标准库中的string类 四、string的成员函数 4.1、构造…

登上Nature!清华大学光学神经网络研究突破

2025深度学习发论文&模型涨点之——光学神经网络光学神经网络的基本原理是利用光的传播、干涉、衍射等特性来实现神经网络中的信息处理和计算。在传统神经网络中&#xff0c;信息以电信号的形式在电子元件之间传输和处理&#xff0c;而在光学神经网络中&#xff0c;信息则以…

【java】对word文件设置只读权限

文件流输出时 template.getXWPFDocument().enforceCommentsProtection(); 文件输出时 //打开己创建的word文档 XWPFDocument document new XWPFDocument(new FileInputStream("output.docx")); //设置文档为只读 document.enforceReadonlyProtection(); //保存文…

Zookeeper 在 Kafka 中扮演了什么角色?

在 Apache Kafka 的早期架构中&#xff0c;ZooKeeper 扮演了分布式协调服务角色&#xff0c;负责管理和协调整个 Kafka 集群。 尽管新版本的 Kafka 正在逐步移除对 ZooKeeper 的依赖&#xff0c;但在许多现有和较早的系统中&#xff0c;了解 ZooKeeper 的作用仍然非常重要。 Zo…

什么叫做 “可迭代的产品矩阵”?如何落地?​

“可迭代的产品矩阵” 不是静态的产品组合&#xff0c;而是围绕用户需求与商业目标构建的动态生态。它以核心产品为根基&#xff0c;通过多维度延伸形成产品网络&#xff0c;同时具备根据市场反馈持续优化的弹性&#xff0c;让产品体系既能覆盖用户全生命周期需求&#xff0c;又…

Nginx代理配置详解:正向代理与反向代理完全指南

系列文章索引&#xff1a; 第一篇&#xff1a;《Nginx入门与安装详解&#xff1a;从零开始搭建高性能Web服务器》第二篇&#xff1a;《Nginx基础配置详解&#xff1a;nginx.conf核心配置与虚拟主机实战》第三篇&#xff1a;《Nginx代理配置详解&#xff1a;正向代理与反向代理…

Vue3 Element-plus 封装Select下拉复选框选择器

废话不多说&#xff0c;样式如下&#xff0c;代码如下&#xff0c;需要自取<template><el-selectv-model"selectValue"class"checkbox-select"multiple:placeholder"placeholder":style"{ width: width }"change"change…

jenkins 自动部署

一、win10 环境安装&#xff1a; 1、jdk 下载安装&#xff1a;Index of openjdk-local 2、配置环境变量&#xff1a; 3、jenkins 下载&#xff1a;Download and deploy 下载后的结果&#xff1a;jenkins.war 4、jenkins 启动&#xff1a; 5、创建管理员用户 admin 登录系统…

2020 GPT3 原文 Language Models are Few-Shot Learners 精选注解

本文为个人阅读GPT3&#xff0c;部分内容注解&#xff0c;由于GPT3原文篇幅较长&#xff0c;且GPT3无有效开源信息 这里就不再一一粘贴&#xff0c;仅对原文部分内容做注解&#xff0c;仅供参考 详情参考原文链接 原文链接&#xff1a;https://arxiv.org/pdf/2005.14165 语言模…

设计模式笔记_行为型_迭代器模式

1. 迭代器模式介绍迭代器模式&#xff08;Iterator Pattern&#xff09;是一种行为设计模式&#xff0c;旨在提供一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而又不需要暴露该对象的内部表示。这个模式的主要目的是将集合的遍历与集合本身分离&#xff0c;使得用户可…