前言
由於 Docker 讓我們開發者不論是在開發還是上線環境有一個統一的環境可以讓我們執行,導致容器化的部屬方式變得非常熱門。
然而,每次當我們在 Docker 打包 golang 的執行檔時,常常發現鏡像(Docker Image)
佔用空間非常大,甚至可能有幾GB的大小這麼多。於是,優化其鏡像大小成為了一個重要的議題。
本篇文章將會將實際專案,從原本1.51GB
大小的鏡像縮減至38MB
,如果對於本文有更好的建議也歡迎留言提出來。
初始Dockerfile
Dockerfile.prod
1 | FROM golang:1.19-alpine AS builder |
當我一開始執行 docker build
的時候,發現打包後竟然高達1.51GB
,這樣線上環境如果要pull的話肯定很花時間和流量。
1 | $ docker build -f ./Dockerfile.prod -t poabob/pano-go:prod . |
優化流程
使用 .dockerignore
避免非必要的文件打包進入鏡像
我們可以在原始檔案發現在打包的時候會將本地專案目錄的資料完全複製到容器之中
,可是如果專案目錄有些檔案本來就沒有必要被打包
進去的話,勢必就要來避免這些檔案的移動。
1 | COPY . /app/go |
我們可以使用 .dockerignore
來聲明哪些檔案在打包的時候要避免掉(用法跟.gitignore
一樣)。
.dockerignore
1 | **/.git |
接著我們可以再來重新打包試試看大小有無變化。
1 | $ docker build -f ./Dockerfile.prod -t poabob/pano-go:prod . |
- 結果將原本的
1.51GB
縮減至946MB
,原因是因為dist
目錄中有python 的 service
本來就不該被打包進來。
減少使用會增加鏡像layer的指令
鏡像中的 layer 與我們 Git 的 commit 一樣,用來區分版本與版本之間的差異,藉此來我們在重複打包的時候,可以藉由原本 Layer 中儲存的 Cache 來節省我們打包的時間。
但是鏡像的 layer 是會佔空間的,所以每當我們打包環境的層數越多,就會讓該鏡像越肥大。目前有三種指令會增加 layer 的數量。
RUN
ADD
COPY
Dockerfile.prod
1 | FROM golang:1.19-alpine AS builder |
1 | $ docker build -f ./Dockerfile.prod -t poabob/pano-go:prod . |
- 結果還是原本的
946MB
QQ,理論上應該縮小的。
使用更小的鏡像執行環境
當我們在 golang 官方鏡像的 DockerHub 可以看到,每個鏡像都有不同的標籤來當作後綴
,而我一開始就使用 alpine
作為打包環境其實已經算是最小了,其他標籤解釋如下:
buster
Debian LTS 版本 10.7
,其代號就是Buster
。所以原始環境是以 Debian 作為底層作業系統,並且擁有完整的相關依賴函式庫
,缺點就是肥大
。
alpine
- 基於
Alpine Linux
所產生的鏡像,其佔用空間算是最小的
,大多數人都會使用它來作為縮小鏡像大小的手段,但是他就是因為什麼都沒有,很容易遇到相關依賴函式庫不支援的狀況
。
- 基於
slim
- 相對來說是
上述兩者的折衷選擇
,提供較少的資源,達到減少空間的效果,但是專案能不能正常運行還是要實際測試才知道。
- 相對來說是
使用多階段構建(multistage builds),不打包執行環境到鏡像中
上述操作流程我們很明顯的可以看到有把 golang
的執行環境給整個打包進來,可是我們的應用程式都打包成為二進位制檔案
了,根本不需要這個開發環境
。
於是 Dockerfile
可以讓我們在同一個檔案內,使用不同鏡像進行多階段構建(multistage builds)
,我們只需要打包好執行檔,並將執行檔打包進去另外一個乾淨的執行環境
即可。
Dockerfile.prod
1 | # Build Stage |
1 | $ docker build -f ./Dockerfile.prod -t poabob/pano-go:prod . |
- 結果將
946MB
大幅度的縮減至38MB
,並且測試過後基本上沒有任何功能上的缺失。
結論
本篇文章透過以下方式來減少我們在打包鏡像所遇到的鏡像肥大問題。
- 使用
.dockerignore
- 減少使用會增加鏡像layer的指令
- 使用更小的鏡像執行環境
- 使用
多階段構建(multistage builds)
其中本篇是基於 golang
作為使用的範例,根據不同的程式語言像是 python
這種直譯式語言可能就不符合使用多階段構建(multistage builds)
的方式,所以根據條件以及需求還煩請各位大大審慎評估後使用,畢竟很常聽說 alpine
鏡像的坑大大小小都有,最後還是得讓應用可以正常運行才是主要的任務。
參考資料
- https://juejin.cn/post/7126754041442336775
- http://blog.itpub.net/70002215/viewspace-2781629/
- https://www.timiguo.com/archives/223/
- https://hub.docker.com/_/golang