在昨天的 [Day 23] 中,我們成功將 OmniVibe AI 拓展為跨平台 Agent,完成了 LINE Bot + Gemini Multi-modal Webhook 的整合,讓使用者在通訊軟體內就能極速提煉語音與影片。
現在,OmniVibe AI 已經具備了 Web Dashboard、Chrome Extension 與 LINE Bot 三端服務。然而,當開發團隊需要頻繁更新 Prompt、修復 Bug 或發布新功能時,手動 build 鏡像並部署至伺服器不僅費時,更面臨「部署期間服務中斷(Downtime)」的風險。
今天,我們將正式進入雲端部署篇,為 OmniVibe AI 搭建基於 Google Cloud 生態系的生產級 CI/CD 自動化流水線:利用 Docker 多階段構建 (Multi-stage Build) 極小化鏡像,結合 Google Cloud Build + Artifact Registry + Cloud Run,實現代碼一推即自動構建、零停機(Zero-downtime)無縫滾動更新!
我們建立一套完全受管(Fully Managed)且具備高彈性的 Google Cloud 原生 CI/CD 架構:
graph TD
subgraph Local & Git Repository
Dev[開發者 Push Code] --> GitHub[GitHub / GitLab Repo]
end
subgraph Google Cloud CI/CD Pipeline
GitHub -- Webhook Trigger --> CloudBuild[Google Cloud Build]
CloudBuild --> DockerBuild[Docker Multi-Stage Build]
DockerBuild --> TestCheck[自動執行 Zod / Unit Tests]
TestCheck --> ArtifactReg[Google Artifact Registry (鏡像庫)]
end
subgraph Serverless Production Hosting
ArtifactReg -- Deploy Image --> CloudRun[Google Cloud Run]
CloudRun --> RevNew[建立 New Revision B (0% Traffic)]
RevNew -- Health Check Pass --> TrafficShift[Traffic Shift (100% Traffic)]
TrafficShift --> RevOld[Old Revision A (Scales to 0)]
end
Dockerfile)為了讓 Cloud Run 的冷啟動時間(Cold Start)降到最低,我們採用 Next.js 的 output: 'standalone' 模式,並透過 Docker 多階段構建(deps, builder, runner)將最終鏡像體積從 1.2GB 大幅縮減至 < 180MB:
# ==========================================
# 階段 1: 安裝依賴套件 (deps)
# ==========================================
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# ==========================================
# 階段 2: 構建應用程式 (builder)
# ==========================================
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# 禁用 Next.js 遙測
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
# 構建 Next.js 產物 (確保 next.config.mjs 有設定 output: 'standalone')
RUN npm run build
# ==========================================
# 階段 3: 生產運行環境 (runner)
# ==========================================
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=8080
ENV HOSTNAME="0.0.0.0"
# 建立非 Root 安全使用者
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 8080
CMD ["node", "server.js"]
💡 小技巧:在
next.config.mjs中加入output: 'standalone',Next.js 會自動追蹤並僅打包執行 Node.js 伺服器所需的最小node_modules依賴。
cloudbuild.yaml)我們建立 cloudbuild.yaml 配置文件,定義自動編譯、推送到 Artifact Registry 以及部署至 Cloud Run 的自動化步驟:
steps:
# 1. 取得歷史快取鏡像以加速 Docker Build
- name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args:
- '-c'
- |
docker pull ${_LOCATION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/omnivibe-app:latest || exit 0
# 2. 執行 Docker 構建 (利用 Docker Cache)
- name: 'gcr.io/cloud-builders/docker'
args:
- 'build'
- '-t'
- '${_LOCATION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/omnivibe-app:$SHORT_SHA'
- '-t'
- '${_LOCATION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/omnivibe-app:latest'
- '--cache-from'
- '${_LOCATION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/omnivibe-app:latest'
- '.'
# 3. 將構建好的鏡像推送至 Google Artifact Registry
- name: 'gcr.io/cloud-builders/docker'
args:
- 'push'
- '${_LOCATION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/omnivibe-app:$SHORT_SHA'
# 4. 部署至 Cloud Run 並帶入環境變數與自動流量切換
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: gcloud
args:
- 'run'
- 'deploy'
- 'omnivibe-service'
- '--image=${_LOCATION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/omnivibe-app:$SHORT_SHA'
- '--region=${_LOCATION}'
- '--platform=managed'
- '--allow-unauthenticated'
- '--min-instances=1' # 保留 1 個預熱實例降低冷啟動延遲
- '--max-instances=10' # 自動擴展上限
- '--cpu=2'
- '--memory=2Gi'
- '--set-env-vars=NODE_ENV=production'
- '--set-secrets=GEMINI_API_KEY=GEMINI_API_KEY:latest,QDRANT_URL=QDRANT_URL:latest'
images:
- '${_LOCATION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/omnivibe-app:$SHORT_SHA'
- '${_LOCATION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/omnivibe-app:latest'
substitutions:
_LOCATION: 'asia-east1' # 台灣彰化 GCP 機房
_REPOSITORY: 'omnivibe-repo'
Google Cloud Run 原生支援 Revision(版本)管理 與 Traffic Splitting(流量切分)。當新版代碼觸發 CI/CD 部署時:
Revision(例如 omnivibe-service-00022)。# 透過 gcloud CLI 手動驗證流量切分(或於 GCP Console 界面設定)
gcloud run services update-traffic omnivibe-service \
--region=asia-east1 \
--to-latest
| 評比維度 | 傳統 VPS 手動部署 | Google Cloud Run + Cloud Build 流水線 |
|---|---|---|
| 部署流程 | 手動 SSH -> git pull -> build -> restart | git push 自動觸發,全自動完成 |
| 服務中斷 | 重啟期間服務暫停 10-30 秒 | 零停機 (Zero-downtime) 滾動更新 |
| 鏡像體積 | 1.2 GB (完整 node_modules) | < 180 MB (Standalone 最佳化) |
| 機房延遲 | 隨意地區 | asia-east1 (台灣在地機房,延遲 < 10ms) |
| 密鑰安全 | 硬編碼或地端 .env 檔案 | 整合 GCP Secret Manager 嚴密防護 |
今天我們為 OmniVibe AI 完成了雲端生產環境自動化部署:
然而,當使用者上傳數小時的超長影片時,若直接在 HTTP 請求中同步等待,容易造成 API Timeout。
👉 明天(Day 25),我們將進入【微服務與非同步任務篇】:實戰 Cloud Tasks + Cloud Pub/Sub 處理長影音背景非同步提煉與 Webhook 通知!
我們明天見!🔥