1. 程式人生 > 程式設計 >聽說你的 Docker 映象比較胖?

聽說你的 Docker 映象比較胖?

減肥,是一個自己心知肚明卻無能為力的事情,我雖然減不了肥,但我能把我 Docker 映象的肥減一下。

如果有看之前的一篇文章 使用 Docker 搭建 Python 開發環境 的話,你會發現一個很明顯的問題,那就是構建出來的映象太大了,如下圖右下角的位置:

myproject image
足足有 937MB 之大,可以說很肥了,如果你磁碟空間本來就緊張的話,你可能就會破口大罵了,如果你覺得這不是問題的話,再想想如果你需要把這個映象釋出到 Docker Hub 又或是任何一個映象倉庫呢?我相信在你 docker push 的時候會罵街。

為什麼會這麼肥?

Emm...就是平時吃的比較多又不喜歡做運動,躺在床上躺在沙發上賊舒服...

扯遠了扯遠了,回到正題,在我們聊如何減肥之前,先好好了解一下為什麼會肥吧,事出必有因嘛。 上文提到的那個肥大的映象叫 myproject 映象,我們先回顧下這個映象的 Dockerfile 吧:

FROM python:3.7
RUN pip install requests
CMD  ["python3"]
複製程式碼

以我敏銳的直覺,已經發現了問題絕對出在第一行,不要問為什麼,讓我裝 x 一次。

我們先執行下 docker images python:3.7,會看到:

python:3.7

原因是找到了,作為 myproject 的基礎映象 python:3.7 映象本來就很大,所以我們在這基礎上構建的映象只會更大(不考慮刪除基礎映象內部的檔案),那 python:3.7 為什麼這麼大?我們執行 docker history python:3.7

來檢視這個基礎映象的建立歷史:

python:3.7 history
我們能看到,這個映象建立的歷史的每一層,SIZE 比較大的層,都是往映象塞內容的記錄,如果想剛具體每一層都做了什麼,可以執行 docker history --no-trunc python:3.7,輸出雖然不太友好,但能清楚看到的的確確容量大的層都是在往映象裡面塞東西的層,大致分為系統底層、各種用於構建的庫和工具、Python 原始碼包等,再回顧我們的 myproject,我們也在往裡面塞我們需要的內容,基礎映象的內容 + 我們寫的 Dockerfile 的內容,組合成我們最終的映象。

我們在 history 中看到,一個映象的建立歷史是一層一層的,有沒有感覺像 Git 提交一樣,也是一層一層的疊上來?這每一層,都對應了 Dockerfile 的每一行,每一層都是獨立的,每一層都有自己的一席之地(容量)。

有沒有立竿見影的減肥方法?

Emm...對我自己而言,可能沒有,但對於 Docker 映象來說,的確是有的。

我們已經知道 python:3.7 這個基礎映象本身就很肥大,那我們能不能不使用它?答案是當然的,我們完全可以從零自定義自己的映象,感覺就像是在一臺全新的伺服器自己去編譯安裝自己需要的軟體、環境,但我們現在是要立竿見影,從零做一個自己的映象好像有點花時間,所以我們使用 python:3.7-alpine 映象 去替換 python:3.7 映象,Dockerfile 的內容更新為:

FROM python:3.7-alpine
RUN pip install requests
CMD  ["python3"]
複製程式碼

然後我們執行 docker build -t myproject:alpine . 進行構建,接著來看看這個映象有多大吧,執行 docker images myproject 檢視:

myproject:alpine
沒看錯,myproject:alpine 的容量只有 107MB,是原來的近九分之一,這個效果可謂是立竿見影了。

經過測試,myproject 的程式碼也能正常執行,一切都很完美,對不對?

其他的減肥方法呢?

如果只是針對 myproject 的話,在經過上面的瘦身之後,我是覺得滿意了,但如果有其他比 myproject 更復雜的映象,我們可以:

儘可能使用小的基礎映象且最好是官方映象

如上文,我們已經是這麼做了。

儘量減少映象的層數

myproject 的 Dockerfile 演示不了這個方法,現在假如我們有一個名為 demo 的映象,Dockerfile,內容如下:

FROM python:3.7-alpine
RUN apk update
RUN apk add build-base
RUN pip install requests
RUN pip install flask
RUN pip install gunicorn
CMD ["python3"]
複製程式碼

我們來看看這個映象的建立歷史,執行 docker history demo

demo history
發現有 demo 的 Dockerfile 產生的層數共有 6 層,其中 5 層是 RUN 指令,1 層是 CMD 指令,我們這裡就可以對 RUN 指令的 5 層進行優化了,Dockerfile 更新為:

FROM python:3.7-alpine
RUN apk update \
    && apk add build-base \
    && pip install requests flask gunicorn
CMD ["python3"]
複製程式碼

在重新構建之後檢視映象的建立歷史:

demo history2
建立歷史的層數從 6 層減少到了 2 層,但映象的總容量依舊是 277MB,是的,減少層並沒有減少容量,但如果你嘗試過前後的執行時區別,你會發現後者會更快的構建完(拋開網路因素),跑的更快,難道不也是減肥後帶來的效果嗎?

打包儘可能少的內容

如:我們在為某個專案容器化時,往往不是這個專案的所有檔案都需要打包到映象內,我們可以在打包時把不必要的檔案忽略,可以使用 .dockerignore,把不需要打包到映象的檔案、目錄填寫在裡面即可,.gitignore 既視感。

DockerSlim

dockersl.im/

上一張官網的截圖,見識一下它的威力

DockerSlim
上圖,經過 docker-slim 處理過的映象,30 倍的差距,簡直可怕。

寫在最後

本文介紹了好些個減肥方法,有的簡單易懂方便實現,有的可能就需要花點心思去專研,有的或許有副作用但效果驚人,是的,這跟人類減肥差不多,我的初衷也是希望借用減肥來讓大家(對 Docker 不熟的)更容易消化和理解。在這裡祝大家減肥成功。