概述



在構建 Docker 映像時,往往會不經意地增加容量。大部分原因在於 不必要生成的層。這篇文章使用 docker history 指令來檢查映像的'家譜',並分享我透過這個方法進行實際的映像輕量化經驗。

利用這個命令可以準確找到讓映像變重的原因,並製作出更高效的映像。


docker history: 檢查映像的家譜

docker history 是一個顯示特定映像如何生成及其組成歷史的指令。您可以查看 Dockerfile 中每個指令是堆疊為什麼 層(layer),以及每個層生成的時間和容量。

基本用法非常簡單。

docker history [選項] <映像名稱:標籤>

例如,檢查本地的 my-app:latest 映像歷史的命令如下:

docker history my-app:latest

從輸出結果可以看到從映像的最上層(最新)到基礎映像(最舊)按順序展示。

IMAGE          CREATED         CREATED BY                                      SIZE
a6215f271958   5 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      7 weeks ago     /bin/sh -c #(nop) ADD file:f28242cf608f6...   7.81MB
  • CREATED BY: 創建此層的 Dockerfile 指令

  • SIZE: 該層佔用的容量

有用的提示: 使用 --no-trunc 查看完整命令

docker history 的基本輸出會在 CREATED BY 欄位的命令過長時中途截斷。此時,若使用--no-trunc 選項,則可以不被截斷地查看所有命令,這對於精確分析非常重要。

docker history --no-trunc my-app:latest

為什麼層分析很重要: 實際優化經驗



Docker 映像基本原則是每個 Dockerfile 的 一行指令產生一層。每層則是堆疊在上一層上的'變更'。

問題在於,若不必要地拆分層,會驟增映像容量。

案例: COPYRUN chown 的陷阱

在撰寫 Dockerfile 時,常常需要將專案檔案拷貝到容器中(COPY),接著將這些檔案的所有權改為特定用戶(例如: appuser)。

低效率的方法: 2 個層

最初,我是這樣撰寫 Dockerfile 的:

# 1. 將檔案複製過來(以 root 擁有權複製)
COPY . .

# 2. 改變所有權(以不同命令執行)
RUN chown -R appuser:appgroup /app

查詢 docker history 生成的映像歷史後,發現創建了兩個層。

IMAGE          CREATED BY                                        SIZE
<layer_id_2>   /bin/sh -c chown -R appuser:appgroup /app        150MB  <-- (問題!)
<layer_id_1>   /bin/sh -c #(nop) COPY dir:abc in /app           150MB

這裡出現了嚴重的問題。

  1. COPY . . 命令將 150MB 的檔案添加到層 1

  2. RUN chown 命令則是將層 1 的 150MB 檔案直接複製,然後僅變更所有權資訊並新存入層 2

檔案內容相同,但一個屬於 root,另一個屬於 appuser,各自存儲在不同的層中。結果,這兩個層僅使映像總容量達到 300MB(150MB + 150MB)

有效的方法: 1 個層

這個問題可以通過將 COPY 命令的 --chown 標誌使用來簡單解決。

# 1. 同時指定所有權
COPY --chown=appuser:appgroup . .

修改後的 Dockerfile 後,重新查詢 docker history

IMAGE          CREATED BY                                             SIZE
<layer_id_1>   /bin/sh -c #(nop) COPY --chown=appuser... dir:abc     150MB

僅生成了一個層,映像的總容量減少至150MB。只是將兩行命令合併成一行,卻使映像容量減少近一半。


結論

docker history 不僅僅是查看映像的生成歷史,它是映像優化的核心分析工具

修改 Dockerfile 之後,習慣性使用 docker history --no-trunc 來檢查是否按照預期生成了層,是否存在佔用不必要容量的層,這種小習慣可以創造出輕量且高效的映像。