昨天我們已經成功讓 Todo API 跟 PostgreSQL 容器整合,
今天的目標是讓這整個系統更「像在雲端運作」——
我們要模擬生產部署(production environment),
並練習 docker-compose.prod.yml
的設計與啟動流程。
在開發階段,我們通常會:
npm run dev
自動重啟但在「上線部署」時,這些都不需要。
我們希望:
npm start
(production 模式)所以今天要做的事情是:
讓整個專案有「開發用 compose」和「生產用 compose」。
在專案根目錄,改成這樣的 Dockerfile
:
# ========== 開發階段 (builder) ==========
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build # 若是 TypeScript 專案,這會輸出到 dist/
# ========== 生產階段 (runner) ==========
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# 僅複製必要檔案(避免帶入 dev 依賴)
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/prisma ./prisma
RUN npm ci --only=production
EXPOSE 3000
CMD ["node", "dist/index.js"]
這樣會:
docker-compose.prod.yml
新增這個檔案:
version: "3.9"
services:
api:
build:
context: .
dockerfile: Dockerfile
image: ts-todo-api:prod
container_name: ts-todo-api-prod
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:password@db:5432/todo
depends_on:
- db
restart: always
db:
image: postgres:16-alpine
container_name: todo-db-prod
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: todo
volumes:
- db_data:/var/lib/postgresql/data
restart: always
volumes:
db_data:
這份 Compose 就是「生產版」:
restart: always
)npm run dev
docker compose -f docker-compose.prod.yml build
這會使用剛剛的 multi-stage Dockerfile,
生成一個更小的 ts-todo-api:prod
image。
你可以檢查大小:
docker images ts-todo-api:prod
docker compose -f docker-compose.prod.yml up -d
檢查容器狀態:
docker compose -f docker-compose.prod.yml ps
輸出:
NAME SERVICE STATUS PORTS
ts-todo-api-prod api running 0.0.0.0:3000->3000/tcp
todo-db-prod db running 0.0.0.0:5432->5432/tcp
docker compose -f docker-compose.prod.yml logs -f
會看到:
🚀 Server running on port 3000
Database connected successfully!
一切順利 🎉
模擬一次「服務重啟」的情境:
docker compose -f docker-compose.prod.yml restart api
模擬「整個系統重新上線」:
docker compose -f docker-compose.prod.yml down
docker compose -f docker-compose.prod.yml up -d
因為有 restart: always
就算伺服器重啟或 Docker daemon 重新啟動,
容器也會自動復原。
在生產環境中,你可以加入 healthcheck:
api:
...
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/todos"]
interval: 30s
timeout: 5s
retries: 3
這樣 Docker 會定期檢查 API 是否正常,
若失敗就標記為 unhealthy。
ts-todo-api/
├─ src/
│ └─ index.ts
├─ prisma/
│ └─ schema.prisma
├─ Dockerfile
├─ docker-compose.yml # 開發用
├─ docker-compose.prod.yml # 生產用
├─ package.json
└─ .env
開發用:
docker compose up
生產用:
docker compose -f docker-compose.prod.yml up -d
項目 | 開發版 | 生產版 |
---|---|---|
啟動指令 | npm run dev |
npm start |
是否掛 volume | ✅(方便即時修改) | ❌(固定程式) |
是否使用 cache | ✅ | ✅(build cache) |
是否含 devDependencies | ✅ | ❌ |
映像大小 | 約 350MB | 約 180MB |
重啟策略 | 無 | restart: always |
這樣在真正部署到雲端(例如 EC2、VPS、GCP Run)時,
就能直接使用生產版的 Compose。
今天的練習就像是正式走上「部署舞台」。
我學會了:
docker-compose.prod.yml
的設計邏輯-f
切換不同環境設定restart
、healthcheck
、logs
的部署操作整個感覺就像從「學生作品」變成「正式產品」:
容器小、啟動快、重啟穩。