- Published on
在 Nx-NextJS 專案中透過 output:standalone 降低 docker image size
- Authors
- Name
- Wen Chen
前言
在寫這篇文章時,光標題就想了很久,主要是想做個紀錄,可能有機會剛好幫到使用相同技術組合的人。
這陣子專案開始在做打包上線,工作就漸漸從開發轉變至成品的優化。
先提供一下目前專案使用的技術組合:
NextJs
: 12.3.0@nrwl/next
: 14.5.4nx
: 14.5.4
因為這個專案最終上線是由我們完成 dockerFile 的撰寫與打包,最終再將 Image 交給架構去 deploy,但當我們參考一些範例完成初版本的 image 後, 便發現檔案大小似乎意外肥大,在本機打包出來動輒 1GB,因為 monorepo 的關係,過程中也不斷去確認是否有複製到其他專案的資料,反覆修正後似乎就是這麼大。
經過一番搜尋,我將目光放到了:Output File Tracing
關於 Output File Tracing,除了官方文件以外,網上也有不少中文資源可以翻閱,這裡就不加贅述。
總之,看起來可以大幅減少 image 的體積,那就來試試吧!
補充:
啟用 output file tracing 後 build 完會在匯出的資料夾內找到一個 .next/standalone
資料夾,透過運行裡面的 server.js
即可取代 next start
,
在進行 image 打包時,只需要打包 standalone
資料夾即可,不需要和過去一樣打包所有 build 完的檔案與 node_module
public
和.next/static
資料夾預設不會被包進 standalone 內,如果沒有額外透過 CDN 處理的話,可以手動複製進去資料夾內。
實作
首先先照著官方範例在專案的 next.config.js
啟用該功能:
// next.config.js
module.exports = {
output: 'standalone'
}
在正常有支援 output 的版本下,加入上面那行應該就會運作了,不過在 monorepo 架構下,根據官方提供的文件, 我們需要再加入一行設定:
// next.config.js
module.exports = {
experimental: {
// this includes files from the monorepo base two directories up
outputFileTracingRoot: path.join(__dirname, '../../'),
}
}
如此一來才有辦法將專案資料夾上層目錄納入處理。加入上述設定後可以再測試 build
與 start
是否可以成功運行,如果可以,請直接跳到最後結語。
給執行到上一步還無法成功建置的人:
沒錯,我就是照著官方作法做完還沒辦法成功建置的人,經過一番搜尋,找到了一則 Github issue,照做之後居然成功執行了。
- 完成前兩項操作後,到專案 pages 底下的任何一隻檔案( 我加在
index.tsx
)
// pages/index.tsx
import path from 'path';
path.resolve('./next.config.js');
從他本身的評論看起來,作者好像也不知道為何可以運作...翻閱其他官方文件似乎也沒有找到相關的說明,不過底下其他評論看起來,似乎有許多人靠上面的操作使 standalone
成功運作起來。
因為當初確實是專案跑不起來才去找討論區的解法來做嘗試,但在寫這篇文章的當下我有刻意回去把上面那兩行扣刪掉,但卻無法在復現該錯誤,這部分再麻煩有其他知道原理的大神替我解惑了QQ
該則 Issue 底下最新的留言有提到
nx : 16.3.0
已對該錯誤做修正,如果是使用較新版本 NX 的朋友可能就不會遇到此問題了。
結語
最終設定完打包出來的 Docker Image 從原先的 1GB 減少到 161 MB 左右。
如果近期也有使用 NextJS 且需要打包成 docker image 的人不妨可以試試看,瘦身效果非常明顯!
底下附上更新過後的 dockerFile 內容:
FROM node:16.20.0-alpine AS builder
# alpine 輕量化 Image 不包含必要的 python 資源,需另外加載才可正確執行 npm 安裝指令
RUN apk add --no-cache libc6-compat python3 make g++
# build step 就是執行 docker build,此處略過,網路上很多範例,沒有什麼特別的操作。
FROM node:16.20.0-alpine AS serve-step
# 變數待執行時才帶入
ARG project_name
ARG port
WORKDIR /app
# 指定 group 和使用者
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# 將檔案綁定至 group 與使用者
COPY /build/dist/apps/${project_name}/.next/standalone ./
# 複製靜態資料
COPY /build/dist/apps/${project_name}/.next/static ./dist/apps/${project_name}/.next/static
COPY /build/dist/apps/${project_name}/public ./apps/${project_name}/public
USER nextjs
# server.js 運行路徑
ENV APP_PATH=apps/${project_name}/server.js
EXPOSE ${port}
ENV PORT ${port}
CMD node $APP_PATH