cover

Serverless:不是沒有伺服器,是你不管伺服器

Serverless 的核心概念是「只管程式碼,不管伺服器」。你上傳一個 function,平台負責部署、擴展、維運。沒有流量時不消耗資源、不產生費用;有流量時自動擴展處理。聽起來很理想,但 Serverless 有其適用範圍和限制。這篇文章從自架 Infra 團隊的角度出發,分析什麼時候用 Serverless、什麼時候不該用、以及如何混合部署。

架構概覽

flowchart TD
  Event[事件觸發\nHTTP / Webhook / Cron] -->|呼叫| Runtime[Function Runtime\n載入程式碼]
  Runtime -->|Cold Start| Init[初始化環境\n載入依賴]
  Init --> Exec[執行函式\n處理請求]
  Exec --> Resp[回傳結果]
  Resp --> Scale{有後續請求?}
  Scale -->|有| Exec
  Scale -->|一段時間無| Zero[Scale to Zero\n釋放資源]

架構概覽

flowchart TD
  Client[Client] --> CDN[CDN / Edge\nCloudflare / Vercel]

  CDN -->|static| Static[Static Files\nNext.js SSG / SPA]
  CDN -->|api routes| Serverless[Serverless Functions\nVercel / Cloudflare Workers]
  CDN -->|heavy api| Gateway[API Gateway\n自架 Nginx]

  Serverless -->|read/write| DB[Database\nPlanetScale / Supabase]
  Serverless -->|webhook| Webhook[Third-party Webhook\nLine / Slack / Discord]

  Gateway -->|proxy| SelfHosted[Self-hosted Services\nDocker + Portainer]
  SelfHosted --> PG[(PostgreSQL)]
  SelfHosted --> Redis[(Redis)]

  subgraph "Serverless 適合"
    Static
    Serverless
    Webhook
  end

  subgraph "自架 適合"
    SelfHosted
    PG
    Redis
  end

混合架構:前端和輕量 API 用 Serverless(Vercel/Cloudflare Workers),重度後端邏輯和資料庫用自架。兩者透過 API 互相溝通。

核心概念

  1. Serverless 適合什麼:(1)流量不穩定的服務:例如 Bot Webhook,平時沒有流量、偶爾突然大量湧入,Serverless 自動擴展完美應對。(2)前端部署:Next.js 的 SSR/SSG 部署到 Vercel,零維運、自動 CDN 分發。(3)輕量 API:表單提交、email 寄送、第三方 API proxy,邏輯簡單、執行時間短的操作。(4)排程任務:每天跑一次的報表生成、資料同步,不需要 24 小時跑一台伺服器。

  2. Serverless 不適合什麼:(1)長時間運行的任務:Serverless function 通常有執行時間限制(Vercel 10-60 秒、Lambda 15 分鐘),超時就會被 kill。(2)需要持久連線的服務:WebSocket、database connection pool 不適合 Serverless,因為每次 invocation 可能是不同的 instance。(3)對延遲敏感的服務:Cold start(第一次呼叫時要啟動容器)會增加 100ms-3s 的延遲,頻繁呼叫但流量不穩定的 API 會有明顯的 p99 延遲。(4)成本不可控的場景:流量非常大且穩定時,Serverless 的按次計費可能比一台固定伺服器貴很多。

  3. Cold Start 問題:Serverless function 在一段時間沒有被呼叫後,平台會回收其執行環境。下一次呼叫時需要重新建立環境(載入程式碼、初始化依賴),這個過程就是 Cold Start。不同平台和語言的 Cold Start 時間差異很大:Cloudflare Workers(V8 isolate)約 0-5ms、Vercel Edge Functions 約 10-50ms、AWS Lambda(Node.js)約 100-500ms、AWS Lambda(Java)約 1-3 秒。降低 Cold Start 的方法:減小 bundle size、避免重型框架、用 Provisioned Concurrency(Lambda)或 Edge Runtime。

  4. 成本模型:Serverless 的計費通常是「呼叫次數 + 執行時間 + 記憶體」。例如 AWS Lambda:每 100 萬次呼叫 0.0000166。如果一個 function 每天被呼叫 1000 次、每次執行 200ms、用 256MB 記憶體,月費不到 20/月的 VPS 反而便宜。計算公式:月呼叫量 × 平均執行秒數 × 記憶體 GB × 單價,和等效 VPS 比較。

使用情境

  • Bot Webhook:Discord/Line/Telegram Bot 用 Webhook 接收訊息。平時沒有訊息、偶爾突然有大量訊息湧入(群組活動)。部署到 Cloudflare Workers,完全不需要維護伺服器。Worker 收到 Webhook、驗證簽名、處理指令、回覆訊息,整個流程在 50ms 內完成。

  • 前端 + 輕量 API:行銷頁面或文件網站用 Next.js 開發,部署到 Vercel。前端 SSG 產生靜態頁面,API Routes 處理表單提交和 email 寄送。整個網站的維運成本接近零(Vercel free tier 通常足夠)。

  • 混合架構:電商平台的核心服務(訂單、庫存、付款)部署在自架 Docker 上,因為需要穩定的資料庫連線和長時間的 transaction。但 Line 訊息通知、每日報表信寄送、圖片裁切等輕量任務部署到 Serverless。兩者透過 HTTP API 或 訊息佇列 溝通。

實作範例 / 設定範例

Cloudflare Workers(Bot Webhook)

// worker.ts - Discord Bot Webhook handler
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== 'POST') {
      return new Response('Method not allowed', { status: 405 });
    }
 
    // 驗證 Discord 簽名
    const isValid = await verifyDiscordSignature(request, env.DISCORD_PUBLIC_KEY);
    if (!isValid) {
      return new Response('Invalid signature', { status: 401 });
    }
 
    const body = await request.json();
 
    // Ping(Discord 驗證 Webhook URL 用)
    if (body.type === 1) {
      return Response.json({ type: 1 });
    }
 
    // Slash command
    if (body.type === 2) {
      const command = body.data.name;
 
      if (command === 'status') {
        // 查詢自架 API 的狀態
        const status = await fetch(`${env.SELF_HOSTED_API}/health`);
        const data = await status.json();
 
        return Response.json({
          type: 4,
          data: {
            content: `Server status: ${data.status}\nUptime: ${data.uptime}`,
          },
        });
      }
    }
 
    return Response.json({ type: 1 });
  },
};

Vercel Serverless Function(API Route)

// app/api/contact/route.ts - Next.js API Route(Vercel 部署)
import { NextResponse } from 'next/server';
 
export async function POST(request: Request) {
  const body = await request.json();
  const { name, email, message } = body;
 
  // 驗證
  if (!name || !email || !message) {
    return NextResponse.json(
      { error: 'Missing required fields' },
      { status: 400 }
    );
  }
 
  // 寄送 email(透過第三方服務)
  await fetch('https://api.resend.com/emails', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.RESEND_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      from: 'noreply@example.com',
      to: 'team@example.com',
      subject: `Contact form: ${name}`,
      text: `From: ${name} (${email})\n\n${message}`,
    }),
  });
 
  return NextResponse.json({ success: true });
}
 
// Edge Runtime(更快的 Cold Start)
export const runtime = 'edge';

成本估算比較表

| 方案 | 月呼叫量 | 執行時間 | 月費用估算 |
|------|---------|---------|-----------|
| Cloudflare Workers (Free) | 10 萬次 | <10ms | $0 |
| Cloudflare Workers (Paid) | 1000 萬次 | <10ms | $5 |
| Vercel (Free) | 10 萬次 | <10s | $0 |
| Vercel (Pro) | 100 萬次 | <10s | $20 |
| AWS Lambda | 100 萬次 | 200ms/256MB | ~$0.50 |
| 自架 VPS (4GB) | 無限 | 無限 | $20-40 |

何時從 Serverless 遷回自架

決策依據:
1. 月費用 > 等效 VPS 的 2 倍 → 考慮遷回
2. 執行時間經常超過限制 → 必須遷回
3. 需要持久連線(WebSocket / DB pool)→ 必須遷回
4. Cold Start 造成不可接受的 p99 延遲 → 考慮遷回
5. 需要存取內網服務且 VPN 設定太複雜 → 考慮遷回

常見問題與風險

  • Cold Start 延遲:使用者在低流量時段(例如凌晨)呼叫 API,因為 Cold Start 等了 2 秒才收到回應。避免方式:用 Edge Runtime / Cloudflare Workers(Cold Start 極短)。對延遲敏感的服務考慮 Provisioned Concurrency 或直接用自架。

  • Vendor Lock-in:用了太多平台特有的功能(Vercel 的 middleware、Cloudflare 的 KV/D1),日後要遷移到其他平台或自架就很痛苦。避免方式:核心商業邏輯用標準的 Web API(Fetch API、Web Crypto),不依賴平台特有 API。把平台特有的部分隔離到薄薄的 adapter 層。

  • Debug 困難:Serverless function 在遠端執行,沒辦法直接 attach debugger。日誌分散在平台的 dashboard 上,和自架的日誌系統不整合。避免方式:用結構化日誌(JSON format),有需要時整合到自架的 EFK。在本地用 wrangler dev(Cloudflare)或 vercel dev(Vercel)模擬 Serverless 環境。

  • 成本失控:某個 function 進入無限迴圈、或被惡意呼叫大量次數,帳單暴增。避免方式:在平台上設定 spending limit。設定 rate limiting(Cloudflare 可以用 WAF 規則)。監控每個 function 的呼叫量和費用趨勢。

優點

  • 零維運:不需要管理伺服器、OS 更新、安全 patch
  • 自動擴展:從 0 到數千個並發,平台自動處理
  • 按使用量計費:低流量服務的成本極低

缺點 / 限制

  • 執行時間限制(通常 10 秒 - 15 分鐘)
  • Cold Start 延遲對即時性要求高的場景不利
  • Vendor Lock-in 風險,遷移成本高
  • 高穩定流量下,成本可能高於自架

延伸閱讀