Node Docker Image 瘦身:從 1.2GB 到 78MB 的六層優化順序
Docker · Node.js · Container Optimization
Node Docker image 從 1.2GB 瘦到 78MB:先砍浪費,再談極限
這則 Threads 摘要了一篇 Practical Developer 的案例:一個 Node.js + TypeScript service,透過六個步驟把 image 從 1.21GB 降到 78MB。真正可複用的重點不是「一律用 Alpine / distroless」,而是按風險由低到高處理:base image、.dockerignore、多階段建構、layer cache,最後才評估 Alpine 與 distroless。
1. 換 slim base image
node:22 → node:22-slim,約 1.21GB 降到 412MB。這是低風險高收益,先移除大多數 runtime 不需要的工具鏈。
2. 加 .dockerignore
避免 node_modules、.git、.env、coverage、build artifacts 被 COPY 進 image layer。這同時是 size、cache 與安全問題。
3. 多階段建構
builder stage 保留 TypeScript / dev dependencies;runtime stage 只複製 dist、production node_modules 與 package.json,搭配 npm prune --omit=dev。
4. 重排 cache layer
先 COPY package*.json 再 npm ci,最後才 COPY source code。大小不變,但改 code 時不會讓 dependency install cache 失效。
| 步驟 | 大小變化 | 主要收益 | 風險 |
|---|---|---|---|
| node:22 → node:22-slim | 1.21GB → 412MB | 移除不必要 OS/toolchain | 低 |
| .dockerignore | 412MB → 388MB | 避免 secrets / local artifacts 進 image | 低 |
| multi-stage + prune | 388MB → 198MB | 編譯依賴不進 runtime | 低到中 |
| cache ordering | 大小不變,重建 94s → 18s | CI/CD rebuild 快很多 | 低 |
| Alpine runtime | 198MB → 96MB | 更小 runtime | 中:musl libc 相容性 |
| Distroless | 96MB → 78MB | 最小攻擊面,無 shell / package manager | 中:除錯與相依檔案要更嚴謹 |
Threads 高價值留言:有人提醒 Alpine 使用 musl libc,Node.js 後端 API 在高流量、複雜網路或 native module 場景可能遇到相容性/效能問題。這點很重要:Alpine 不應該是盲目預設,尤其有 sharp、bcrypt、node-gyp 或複雜 native dependency 時,要先壓測與驗證。
建議採用順序
- 先做 node:slim、.dockerignore、多階段建構與 npm ci/cache ordering;這幾步幾乎不改架構,收益最大。
- 確認 image 內沒有 .env、private key、測試資料與多餘 node_modules。
- 有 native modules 時,不要直接把 runtime 換 Alpine;先確認 libc 相容性與 production latency。
- 要用 distroless 前,先補好 healthcheck、structured logging、debug sidecar / ephemeral container 流程,因為 container 內沒有 shell 可救火。
- 以 digest pinning、SBOM、漏洞掃描與 non-root user 補上供應鏈安全,不要只看 MB 數。
Sources
Threads: @oneday0013
Original article: Your Docker Image Is 1.2GB. Here Is How To Get It Under 80MB.
Threads: @oneday0013
Original article: Your Docker Image Is 1.2GB. Here Is How To Get It Under 80MB.