Remotion LabRemotion Lab
伺服器端渲染Cloudflare Containers

Cloudflare Containers

使用 Cloudflare Workers 和 Containers 部署 Remotion 伺服器端渲染的完整指南,含 wrangler.toml 設定與 R2 儲存整合。

Cloudflare Containers

Cloudflare Containers 是一個 Beta 階段的平台,可從 Cloudflare Worker 函式呼叫 Docker 容器,讓你在 Cloudflare 的全球邊緣網路上執行 Remotion 渲染工作。需要付費的 Workers 方案。

架構概覽

Cloudflare Containers 的渲染流程如下:

  1. 客戶端向 Cloudflare Worker 發送渲染請求
  2. Worker 函式驗證請求並啟動 Container 實例
  3. Container 執行 Remotion 渲染(包含 Chrome 和 FFmpeg)
  4. 渲染完成的影片儲存至 Cloudflare R2 Bucket
  5. Worker 回傳影片的存取 URL 給客戶端

前置需求

  • 付費的 Cloudflare Workers 方案(Free 方案不支援 Containers)
  • 已安裝 Wrangler CLI
  • 已安裝 Docker
  • Cloudflare R2 Bucket(用於儲存渲染輸出)

安裝 Wrangler CLI

npm install -g wrangler
wrangler login

快速開始

複製官方示範存放庫:

git clone https://github.com/remotion-dev/cloudflare-containers-demo
cd cloudflare-containers-demo
npm install

wrangler.toml 設定

以下是完整的 wrangler.toml 設定範例,涵蓋 Worker 和 Container 的配置:

wrangler.toml
name = "remotion-renderer"
main = "src/worker.ts"
compatibility_date = "2024-03-01"
compatibility_flags = ["nodejs_compat"]
 
# Container 設定
[containers]
image = "your-dockerhub-username/remotion-renderer:latest"
max_instances = 10
 
# R2 Bucket 綁定(用於儲存渲染輸出)
[[r2_buckets]]
binding = "OUTPUT_BUCKET"
bucket_name = "remotion-renders"
 
# 環境變數
[vars]
MAX_RENDER_DURATION_SECONDS = "300"
OUTPUT_FORMAT = "mp4"
 
# 生產環境設定
[env.production]
name = "remotion-renderer-prod"
 
[env.production.containers]
image = "your-dockerhub-username/remotion-renderer:latest"
max_instances = 50
 
[[env.production.r2_buckets]]
binding = "OUTPUT_BUCKET"
bucket_name = "remotion-renders-prod"

CPU 與記憶體設定

wrangler.toml(資源限制)
[containers]
image = "your-dockerhub-username/remotion-renderer:latest"
max_instances = 10
 
# 指定 Container 資源(視帳號配額而定)
[containers.resources]
vcpu = 2
memory_mb = 4096

Worker 函式實作

src/worker.ts
interface Env {
  CONTAINER: Container;
  OUTPUT_BUCKET: R2Bucket;
}
 
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== "POST") {
      return new Response("Method Not Allowed", { status: 405 });
    }
 
    const { compositionId, inputProps } = await request.json<{
      compositionId: string;
      inputProps: Record<string, unknown>;
    }>();
 
    // 啟動 Container 並執行渲染
    const container = await env.CONTAINER.newUniqueId();
    const stub = env.CONTAINER.get(container);
 
    const renderResponse = await stub.fetch("http://container/render", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ compositionId, inputProps }),
    });
 
    if (!renderResponse.ok) {
      const error = await renderResponse.text();
      return Response.json({ error }, { status: 500 });
    }
 
    const { outputPath } = await renderResponse.json<{ outputPath: string }>();
 
    // 從 Container 取得渲染結果並上傳至 R2
    const videoResponse = await stub.fetch(`http://container/output/${outputPath}`);
    const videoBuffer = await videoResponse.arrayBuffer();
 
    const objectKey = `renders/${crypto.randomUUID()}.mp4`;
    await env.OUTPUT_BUCKET.put(objectKey, videoBuffer, {
      httpMetadata: { contentType: "video/mp4" },
    });
 
    const publicUrl = `https://pub-your-account-id.r2.dev/${objectKey}`;
    return Response.json({ url: publicUrl });
  },
};

Dockerfile 設定

Dockerfile
FROM node:18-slim
 
# 安裝 Chromium 依賴
RUN apt-get update && apt-get install -y \
  chromium \
  ffmpeg \
  fonts-noto-cjk \
  --no-install-recommends \
  && rm -rf /var/lib/apt/lists/*
 
WORKDIR /app
 
COPY package*.json ./
RUN npm ci --only=production
 
COPY . .
RUN npm run build
 
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV CHROME_PATH=/usr/bin/chromium
 
EXPOSE 8080
CMD ["node", "dist/server.js"]

部署

# 建置並推送 Docker 映像
docker build -t your-dockerhub-username/remotion-renderer:latest .
docker push your-dockerhub-username/remotion-renderer:latest
 
# 部署至 Cloudflare
wrangler deploy
 
# 部署至生產環境
wrangler deploy --env production

限制與注意事項

Cloudflare Containers 目前仍在 Beta 階段,官方示範是概念驗證專案,尚未具備生產就緒的狀態。目前未考慮以下面向:

  • 無認證機制:示範不包含 API 金鑰或 JWT 驗證
  • 無渲染佇列:並行請求可能互相競爭資源
  • 無速率限制:容易受到濫用攻擊
  • 無進度回報:客戶端無法得知渲染進度
  • 錯誤不會傳播:渲染失敗時客戶端收不到詳細錯誤訊息
  • 影片以隨機名稱儲存:缺乏系統化的命名規則

生產環境建議實作

在正式上線前,應補充以下功能:

src/worker.ts(含認證)
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // API 金鑰驗證
    const authHeader = request.headers.get("Authorization");
    if (authHeader !== `Bearer ${env.API_SECRET}`) {
      return Response.json({ error: "未授權" }, { status: 401 });
    }
 
    // 速率限制(搭配 Cloudflare Rate Limiting 規則設定)
    // 佇列管理(搭配 Cloudflare Queues)
    // 進度回報(搭配 Durable Objects 或 WebSocket)
 
    // ... 渲染邏輯
  },
};

費用考量

使用 Cloudflare Containers 的費用由以下部分組成:

  • Workers 執行時間費用(每百萬次請求計費)
  • Container 運算費用(依 vCPU 和記憶體用量計費)
  • R2 儲存費用(每 GB/月)
  • R2 資料傳輸費用(Class A/B 操作)

請查閱 Cloudflare Containers 定價R2 定價 了解最新費率。

相關資源