Dockerfile 優(yōu)化實踐筆記

多階段構(gòu)建(Multi-stage Build)

通過 AS builder 等分離構(gòu)建階段與運行階段,只把構(gòu)建產(chǎn)物和必要配置拷貝到最終鏡像,最小化生產(chǎn)鏡像、減少構(gòu)建體積。

最終鏡像中不會包含構(gòu)建階段的源碼、開發(fā)依賴、構(gòu)建工具等,僅包含運行階段需要的產(chǎn)物與配置。

# ========== 階段一:構(gòu)建 ==========
FROM node:22-slim AS builder
WORKDIR /app

COPY . /app

RUN corepack enable pnpm && pnpm install && pnpm build

# ========== 階段二:生產(chǎn)運行 ==========
FROM nginx:latest AS production

# 從 builder 階段僅復制構(gòu)建產(chǎn)物與 Nginx 配置
COPY --from=builder /app/web-ele/dist /etc/nginx/site_avaliable/vben-admin

# 后續(xù)部署內(nèi)容

說明

  • COPY --from=builder:從指定階段復制文件到當前階段,不引入 builder 的其它層。
  • 可通過 docker build --target <AS 階段名稱> 可以只構(gòu)建到該階段

啟用 BuildKit 與緩存

BuildKit 是 Docker 官方下一代構(gòu)建引擎,需 Docker 18.09 及以上,并在環(huán)境中顯式啟用。

主要能力

  • 多階段任務并行:根據(jù)依賴關(guān)系將無依賴任務并行執(zhí)行,縮短總構(gòu)建時間。
  • Dockerfile 變更下的持久化緩存:通過 --mount=type=cache 將目錄掛載到宿主機緩存卷,在依賴文件未變時復用緩存。
  • 導出中間產(chǎn)物:通過 --output=type=local,dest=<路徑> 將某階段產(chǎn)物導出到本地,便于測試或復用。
  • 默認非 root:默認不以 root 運行,需時可用 --user=root。
  • 新指令:常用如 --mount,以及 --network、--platform 等。
    • --mount=type=cache,target=<緩存目錄>:持久化緩存,多次構(gòu)建共享。
    • --mount=type=secret,id=<id>,target=<路徑>:注入構(gòu)建期密鑰,構(gòu)建結(jié)束后不留在鏡像中。
    • --mount=type=bind,source=<宿主機路徑>,target=<容器路徑>:掛載本地文件,多用于本地調(diào)試,不建議在生產(chǎn)構(gòu)建中依賴。

1. 在宿主機或 CI 中啟用 BuildKit

在宿主機或 CI 配置中設置環(huán)境變量即可啟用;使用 docker-compose 構(gòu)建時建議同時開啟 COMPOSE_DOCKER_CLI_BUILD。

# 例如 .gitlab-ci.yml
variables:
  DOCKER_BUILDKIT: 1          # 啟用 BuildKit 引擎
  COMPOSE_DOCKER_CLI_BUILD: 1 # 使用 docker-compose build 時生效(可選)

2. 將依賴目錄掛載為緩存卷

把包管理器緩存目錄掛載為 BuildKit 緩存卷,可在鎖文件未變時復用依賴,加速后續(xù)構(gòu)建。

# 語法:RUN --mount=type=cache,target=<緩存目錄> <命令>
# pnpm:緩存指定 store 目錄
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile

# npm:緩存 .npm 目錄,build 時用 --cache 指定緩存路徑
# RUN --mount=type=cache,target=/root/.npm \
#     npm build --cache /root/.npm

參數(shù)說明

  • type=cache:使用 BuildKit 緩存卷。
  • id:緩存卷標識,不同 id 互不共享(可選,用于區(qū)分 pnpm/npm 等)。
  • target:容器內(nèi)緩存目錄路徑,與包管理器實際使用的路徑一致。

為什么能優(yōu)化

  1. Docker 按層緩存:每條指令對應一層,前面任一層內(nèi)容變化,后續(xù)層緩存都會失效。
  2. 常見寫法是先 COPY . .RUN pnpm install。項目文件一旦改動,COPY 層變化,安裝層緩存即失效。
  3. 使用 BuildKit + --mount=type=cache 將 pnpm 的 store(或 npm 的 cache)掛到宿主機緩存卷后,即使項目文件變化,只要鎖文件未變,仍可復用該卷中的依賴,從而加速安裝。

注意:在 GitLab CI 等共享 Runner 上,同一 Runner 上的不同流水線會共享該緩存卷。

緩存失效條件

  • 使用的 target 路徑或緩存卷標識(如 id)發(fā)生變化。
  • 當前 RUN 所依賴的上下文發(fā)生變化(例如安裝所依賴的 package.json / lockfile 變化)。

Dockerfile 指令優(yōu)化

  1. 把常變指令放在后面。前面盡量只放不常變的步驟(如先只復制依賴描述文件 → 執(zhí)行安裝 → 再復制源碼),提高層緩存命中率。
  2. COPY 時避免 COPY . .。盡量只復制當前步驟需要的文件或目錄,減少無關(guān)變更導致緩存失效。
  3. 合并 RUN。用 && 將多條命令寫在一個 RUN 中,減少層數(shù),便于維護與緩存。
  4. 安裝后清理。安裝系統(tǒng)依賴后刪除 apt 緩存、臨時文件等,減小鏡像體積。

.dockerignore

通過 .dockerignore 排除不需要參與構(gòu)建的文件(如 node_modules、日志、編輯器配置等),可加快構(gòu)建并避免把敏感或無關(guān)文件 COPY 進鏡像。

執(zhí)行時機:在 docker build 時,Docker CLI 會先讀取構(gòu)建上下文目錄下的 .dockerignore,過濾掉匹配的文件,再把剩余文件列表發(fā)給 Docker Daemon 參與構(gòu)建;過濾發(fā)生在發(fā)送上下文之前,而不是在容器內(nèi)執(zhí)行時再過濾。

Docker CLI:接收并解析用戶輸入命令的客戶端。

Docker Daemon:在宿主機上常駐的守護進程,負責實際構(gòu)建與運行。

輕量鏡像選擇

優(yōu)先選用輕量基礎鏡像,例如 node:-slim、nginx:alpine* 等,只保留運行所需的最小依賴,可減小最終鏡像體積并降低攻擊面。

例如 Google 的 distroless 系列鏡像僅包含應用運行時依賴,不含 shell、包管理器等,依賴越少,潛在漏洞面越小。

參考內(nèi)容

BuildKit 介紹 - 知乎

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容