Tối ưu hóa Docker Image cho Next.js: Nghệ thuật sử dụng Multi-stage Build với Node 23

,

・Published on:

Áp dụng kỹ thuật Multi-stage Build để tối ưu hóa ứng dụng Next.js. Cách tiếp cận này giúp giảm dung lượng image cuối cùng đáng kể và tăng tính bảo mật.


# Layer install node_modules
FROM node:23-alpine3.20 AS base

WORKDIR /app

COPY package.json yarn.lock .yarnrc.yml ./

RUN corepack enable

RUN yarn install --immutable

# Layer get production node_modules
FROM node:23-alpine3.20 AS bundle_node_modules

WORKDIR /app

COPY package.json yarn.lock .

COPY --from=base /app/node_modules node_modules

RUN npm prune --production

# Layer build source
FROM node:23-alpine3.20 AS builder

WORKDIR /app

COPY --from=base /app/node_modules ./node_modules
COPY package.json yarn.lock tsconfig.json next.config.ts postcss.config.mjs next-env.d.ts .
COPY public public
COPY src src

RUN npm run build

# Final image
FROM node:23-alpine3.20 AS runner

WORKDIR /app

COPY --from=bundle_node_modules /app/node_modules ./node_modules
COPY --from=builder /app/.next .next 

EXPOSE 3000

CMD ["npm", "run", "start"]

Chiến lược chia để trị (Divide and Conquer)

Thay vì gộp tất cả vào một bước, chúng ta sẽ chia quy trình thành 4 giai đoạn (stages) riêng biệt:

  1. Base: Cài đặt dependencies.
  2. Bundle: Tách lọc dependencies dùng cho production.
  3. Builder: Build source code Next.js.
  4. Runner: Chạy ứng dụng với những gì tối giản nhất.

Stage 1: Base – Cài đặt Dependencies

FROM node:23-alpine3.20 AS base
WORKDIR /app
COPY package.json yarn.lock .yarnrc.yml ./
RUN corepack enable
RUN yarn install --immutable

Tại sao làm vậy?

  • Chúng ta sử dụng node:23-alpine3.20: Phiên bản Alpine siêu nhẹ giúp giảm kích thước nền tảng ban đầu.
  • Lệnh COPY chỉ sao chép các file định nghĩa package trước. Điều này tận dụng cơ chế Docker Layer Caching. Nếu bạn chỉ sửa code mà không sửa thư viện, Docker sẽ bỏ qua bước cài đặt này ở lần build sau -> Tốc độ build cực nhanh.
  • yarn install --immutable: Đảm bảo yarn.lock không bị thay đổi ngầm, giữ sự nhất quán tuyệt đối.

Stage 2: Bundle Node Modules – Cuộc “thanh lọc”

Đây là bước thông minh nhất của Dockerfile này:

FROM node:23-alpine3.20 AS bundle_node_modules
WORKDIR /app
COPY package.json yarn.lock .
COPY --from=base /app/node_modules node_modules
RUN npm prune --production

Điều gì xảy ra ở đây?
Chúng ta copy toàn bộ node_modules đã cài ở bước 1, nhưng sau đó chạy npm prune --production. Lệnh này sẽ xóa bỏ toàn bộ các devDependencies (như eslint, typescript, các thư viện testing…).

Kết quả: Folder node_modules của bạn giảm cân đáng kể, chỉ giữ lại những thư viện thực sự cần thiết để ứng dụng chạy.

Stage 3: Builder – Xây dựng ứng dụng

FROM node:23-alpine3.20 AS builder
WORKDIR /app
COPY --from=base /app/node_modules ./node_modules
# ... (copy source files)
RUN npm run build

Ở bước này, chúng ta cần đầy đủ node_modules (bao gồm cả devDependencies) để biên dịch TypeScript và build Next.js. Sau khi chạy npm run build, chúng ta sẽ thu được thư mục .next chứa ứng dụng đã được tối ưu.

Stage 4: Runner – Đích đến cuối cùng

FROM node:23-alpine3.20 AS runner
WORKDIR /app

COPY --from=bundle_node_modules /app/node_modules ./node_modules
COPY --from=builder /app/.next .next 

EXPOSE 3000
CMD ["npm", "run", "start"]

Đây là image cuối cùng sẽ được deploy. Hãy chú ý những gì chúng ta copy vào:

  1. node_modules từ stage bundle_node_modules (Chỉ chứa prod dependencies – Rất nhẹ).
  2. .next từ stage builder (Kết quả build).

Chúng ta KHÔNG copy mã nguồn gốc (thư mục src), không copy các file config thừa thãi.

Tổng kết: Tại sao bạn nên dùng Dockerfile này?

  1. Siêu nhẹ: Nhờ dùng Alpine và npm prune, kích thước image giảm tối đa.
  2. Bảo mật: Mã nguồn gốc (source code) và các công cụ phát triển (dev tools) không tồn tại trong image production. Hacker sẽ khó khai thác hơn.
  3. Tốc độ build: Tận dụng tối đa caching của Docker nhờ việc tách lớp cài đặt thư viện.
  4. Hiện đại: Sử dụng Node 23 mới nhất và Yarn Berry (thông qua Corepack).

Hy vọng bài viết giúp bạn có cái nhìn rõ hơn về cách tối ưu hóa Dockerfile. Hãy thử áp dụng ngay vào dự án của bạn nhé!