經過前面四周的開發、AI 功能整合與資安補強,我們的 AI 個人財務追蹤器已經是一套功能齊全且具備防禦力的現代 Web 應用。今天我們正式邁入階段五:CI/CD、部署與運運落地!
在上雲部署前,最大的痛點往往是「在我電腦上明明可以跑(It works on my machine)」。為了解決跨環境的依賴問題,我們需要將 Next.js 前端、Node.js 後端與 PostgreSQL 資料庫完全容器化(Containerization)。
今天我們的目標是運用 AI 幫我們撰寫具備多階段構建(Multi-stage Build)、極小化鏡像體積的 Dockerfile,並結合 docker-compose.yml 打造一鍵啟動的全棧本地與預發佈環境!
傳統撰寫 Dockerfile 需要對 Linux 映像檔、Alpine 底層包、Node.js 快取機制與獨立輸出(Standalone Output)有深厚瞭解。讓 AI 來做這件事雖然快,但如果給的 prompt 不夠精確,AI 很可能會打包出高達 1.5 GB 的巨無霸 Docker 鏡像,甚至把敏感環境變數直接硬編碼(Hard-code)進包裡!
我們希望 AI 幫我們為 Next.js + Prisma 專案建立高效能、安全的生產級 Dockerfile,並搭配 PostgreSQL 與 Redis 的 docker-compose 檔案。
你是一位資深 DevOps 工程師。請幫我的 Next.js ( App Router + Prisma ORM ) 專案撰寫 Docker 容器化配置。
需求如下:
1. 撰寫 Production-ready 的 `Dockerfile`:
- 使用 Multi-stage Build(deps, builder, runner)以最小化最終 Image 體積。
- 包含 Next.js 的 standalone 模式優化。
- 確保 Prisma Client 可以正確生成並被 runner 階段讀取。
- 基於安全考慮,使用非 root 用戶(nextjs:nodejs)運行應用。
2. 撰寫 `docker-compose.yml`:
- 包含 app 本體、PostgreSQL 16 與 Redis (作為快取與 Rate Limit)。
- 設定 healthcheck 與正確的依賴順序 (depends_on)。
AI 給出了結構看起來相當專業的多階段 Dockerfile 與 docker-compose.yml:
# AI 生成的 Dockerfile
FROM node:20-alpine AS base
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npx prisma generate
RUN npm run build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]
這份 Dockerfile 表面上使用了 Multi-stage Build,但在實際執行 docker build 與 docker-compose up 時,卻踩到了兩個極為棘手的容器化巨坑!
當容器運行到 runner 階段啟動 server.js 時,突然跳出經典報爆錯:Error: PrismaClientInitializationError: Prisma Client could not locate the Query Engine syslib.
原因:Alpine Linux 使用的是 musl libc,而 Prisma 在 builder 階段編譯時生成了對應的二進位檔案,但在 runner 階段 AI 沒有將 .prisma/client 或 prisma 相關的引擎檔跟 .next/standalone 一起複製過去!此外,Alpine 缺少 OpenSSL 相關依賴庫。
在 docker-compose.yml 中,AI 寫了 depends_on: [postgres],但 PostgreSQL 容器啟動(Container Created)不等於資料庫已經準備好接受連線(Database Ready)。結果 app 容器一開起來就因為連不上 DB 斷開並拋出 ECONNREFUSED 崩潰!
我們引導 AI 修正 Alpine 依賴包、補齊 Prisma 檔案複製,並在 docker-compose 加入嚴格的 healthcheck 檢查:
libc6-compat 與 openssl。.prisma 生成物被完整轉移至 runner 階段。pg_isready 狀態後,App 才能開始執行啟動命令。修正後的配置:
修正後的 Dockerfile:
FROM node:20-alpine AS base
# 補齊 Alpine 缺少的 C 函式庫與 OpenSSL 支援 Prisma
RUN apk add --no-cache libc6-compat openssl
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
COPY prisma ./prisma/
RUN npm ci
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# 產生 Prisma 客戶端二進位檔
RUN npx prisma generate
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# 關鍵:複製 standalone 目錄與 static 檔案
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# 複製生成的 Prisma 引擎與 Client 防止執行期找不到引擎
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/.prisma ./node_modules/.prisma
USER nextjs
EXPOSE 3000
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
CMD ["node", "server.js"]
修正後的 docker-compose.yml:
version: '3.8'
services:
postgres:
image: postgres:16-alpine
restart: always
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: finance_db
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myuser -d finance_db"]
interval: 5s
timeout: 5s
retries: 5
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
DATABASE_URL: "postgresql://myuser:mypassword@postgres:5432/finance_db?schema=public"
NODE_ENV: "production"
depends_on:
postgres:
condition: service_healthy
volumes:
postgres_data:
next.config.js 開啟 output: 'standalone' 後,Next.js 會自動追蹤 import 並打包最精簡的 node_modules。最終鏡像體積從原本直衝 1.2 GB 暴減至 180 MB,構建速度與部署效率翻倍!這樣我們就獲得隨處可執行的 Docker 映像檔~
明天 Day 25,我們將指示 AI 撰寫 GitHub Actions Workflow,實現每次 Git Push 自動跑測試與打包 Docker 的 CI/CD Pipeline!