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.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;使得用戶可…