主題: Cloudflare

Cloudflare Containers 上線了,但別把每個 Worker 都塞進 Docker

Cloudflare Containers 已正式上線。這篇從冷啟動、計費、暫時磁碟與手動擴縮,整理它和 Workers 的實際分工。

動態迷因(展開/收合)
Workers 還是 Containers?先別看 logo,先看工作負載。 · 來源:GIPHY

把 Docker image 丟上雲端,最好不用先養 Kubernetes——這個願望不新鮮,所以 Cloudflare Containers 正式上線後,很容易得到一個過度興奮的結論:既然終於能跑完整 Linux,那就把原本的 Worker 全部裝進 container。

先不要。

Containers 解決的是 Workers 刻意不處理的 runtime 缺口:原生執行檔、完整 filesystem、較多 CPU 與記憶體,以及既有 container image。它不是 Workers 的新版包裝,也不是「Cloudflare 終於變成一般主機」。比較準確的架構是:Worker 當前門與控制平面,Container 只處理真的需要 Linux 的工作。

這個差別,會直接決定延遲、費用與你要維護多少生命週期邏輯。

先問需求,不要先問「能不能跑 Docker」

Cloudflare 的入門文件就是由 Worker 接收請求,再路由到一個或多個 Container。Container class 本身建立在 Durable Object 上,負責 routing、lifecycle 與持久狀態;真正的 image 則在 Linux VM 裡執行。

這不是文件範例剛好這樣寫,而是產品模型。選擇時可以先用一條簡單界線:

工作 優先選擇
驗證、轉址、cache、輕量 API、邊緣 routing Worker
FFmpeg、headless browser、LibreOffice、原生 CLI Container,由 Worker 呼叫
需要完整 Linux runtime 或既有 Docker image Container,由 Worker 呼叫
可靠檔案或業務資料 R2、database 或 Durable Object storage
低流量但必須長駐的傳統 web app 先和 VPS/其他 container 平台實測成本

如果一段程式在 Worker runtime 已經跑得好好的,換成 Container 通常只會多出 image、冷啟動、instance sizing 與磁碟生命週期。Docker 的熟悉感不是遷移理由。

反過來說,若你正在為了塞進 Worker 而改寫 native dependency、拆掉 browser automation,或把 CPU-heavy 工作扭成很多小請求,Container 就不是退步,而是把工作放回合適的執行環境。

最實用的形狀:Worker 在前,Container 在後

最小概念大致長這樣:

import { Container } from "@cloudflare/containers";

export class Backend extends Container {
  defaultPort = 8080;
  sleepAfter = "10m";
}

export default {
  async fetch(request, env) {
    return env.BACKEND.getByName("shared").fetch(request);
  },
};

Worker 可以先做 authentication、rate limit、cache key 與 routing,再依 tenant、session 或 job ID 找到特定 Container。重工作完成後,結果寫回 R2 或 database,而不是留在 Container 的 local disk。

這樣拆有一個很實際的好處:大部分請求不必喚醒 Linux。健康檢查、快取命中、拒絕的請求與已完成工作的查詢,都能在 Worker 層結束。只有真正需要原生 runtime 的路徑才支付 Container 成本。

如果任務要花幾秒甚至幾分鐘,我也不會讓瀏覽器同步等它跑完。更穩的介面是:Worker 建立 job、立即回傳 ID,Container 非同步處理,前端再查狀態。這同時避開冷啟動被使用者直接感受到,也讓 retry 與 timeout 比較容易定義。

Scale to zero 不是一句咒語

官方計價以 10 毫秒為單位。CPU 按實際 active usage 計算,但記憶體與磁碟是依選定 instance type 的配置量、在 instance 運行期間計費。Container sleep 後停止計費,不過請求或手動啟動就會開始算;Workers 與每個 Container 對應的 Durable Object 也另外計費。

所以成本不是只有「CPU 跑了多久」,而比較接近:

Container runtime
+ provisioned memory and disk while awake
+ active CPU
+ network egress
+ Worker and Durable Object usage

sleepAfter 因此是成本設定,不只是語法糖。設太長,低流量服務會為閒置的記憶體與磁碟付費;設太短,下一位使用者就更常碰到冷啟動。官方 FAQ 指出,Container 冷啟動常見約 1–3 秒,實際仍受 image 大小與 entrypoint 工作量影響。

我的預設做法是從小 instance、短而合理的 sleepAfter 開始,再觀察三個數字:

  1. 每小時 cold starts 次數
  2. p95 job 啟動延遲
  3. instance 醒著但沒有工作的時間比例

若延遲影響轉換率或工作完成率,再把休眠時間拉長;不要一開始就用「保持溫熱比較快」替整月的 idle resource 買單。

動態迷因(展開/收合)
Scale to zero 不是唸一句咒語;條件不對,魔法就不會發生。 · 來源:GIPHY

磁碟會消失,狀態要自己放對地方

Containers FAQ寫得很直接:local disk 是 ephemeral。Instance 進入 sleep 後,下次啟動會得到由 image 定義的全新磁碟。

因此 local disk 適合:

  • 解壓縮中的暫存檔
  • 影片轉檔的中間產物
  • 可重新下載或重新計算的 cache

它不適合訂單、使用者上傳的唯一副本、job 的最終狀態,或任何重啟後不能消失的資料。Durable Object storage 可以保存和 instance 靠近的狀態;大檔通常放 R2,關聯資料則交給 database。FUSE 可以掛 object storage,但官方也提醒不要期待原生 SSD 的效能。

把「Container 還醒著」當成 durability 保證也不行。平台不保證 instance 能連續運行固定時間,host 維護時仍可能搬移或重啟。真正可恢復的 job 必須能從外部狀態重新開始。

GA 不等於自動擴縮已經幫你想好

Cloudflare 在 2026 年 4 月宣布 Containers 與 Sandboxes 正式上線,但目前的 scaling model 仍需要你理解 instance ID。

Scaling and Routing 文件說明,Container 現在是透過 unique ID 手動取得與啟動;取得 reference 本身不會自動 start。無狀態服務可以用 getRandom 在固定數量的 instances 中隨機分流,但 Cloudflare 也明確表示,內建 autoscaling 與 routing 還在後續規劃。

這表示現在很適合的工作包括:

  • 每個 tenant 或 session 有明確 identity
  • 數量可控的 browser/media worker pool
  • 短時間 batch job
  • 需要隔離 runtime 的 AI code execution

但如果需求是「一個完全無狀態的 web service,流量來了就自動從 3 台擴到 300 台,而且不用寫 routing」,不要把產品名稱裡的 Cloudflare 自動腦補成這項能力已完成。先把 instance mapping、併發上限、過載回應與 retry 寫進設計。

我的判斷規則

我會依序問:

  1. Worker 能直接完成嗎? 能就停在 Worker。
  2. 是否真的需要 Linux、native binary 或更大的資源? 是才進 Container。
  3. 請求能承受 1–3 秒級冷啟動嗎? 不能就改成 job,或為 warm time 付費。
  4. instance 消失後能恢復嗎? 不能就先把狀態移出 local disk。
  5. 誰負責 routing 與擴縮? 目前答案仍包含你的 Worker 程式碼。
  6. 有把完整帳單算進去嗎? Container、egress、Worker、Durable Object 都要算。

這套規則刻意把「用 Container」放在後面。不是因為 Containers 不成熟,而是平台已經替簡單 request 做好 Worker;重新租一個完整 Linux runtime,應該拿來解決 Worker 確實不適合的問題。

本站實作觀察:這個出版站維持 static 就夠了

這個技術部落格本身就是一個不該用 Container 的例子。Wrangler 設定把 assets.directory 指向 ./dist,發佈指令是 pnpm build && wrangler deploy。目前 request path 沒有 Container binding、image、native binary 或常駐 process;runtime 工作只是送出已建好的 HTML、CSS、JavaScript 與媒體檔。

因此我的判斷刻意很小:

  1. Build 與內容解析在發佈前完成。
  2. 讀者的 request 只需要一份 static document,不需要 Linux runtime。
  3. 加入 Container 只會多一個 instance lifecycle,卻沒有消掉眼前的限制。

未來若 build 真的需要 native tool,我會先讓它在 CI 執行,將輸出發成 static asset。只有使用者觸發的 runtime 工作非 Linux 不可——例如轉檔 job 或隔離的 code execution——才需要考慮 Container。

這是對本站設定的 source inspection,不是 load test,也不是 Container 成本 benchmark。它能證明目前的發佈形狀,不能宣稱 production latency、流量或未來工作負載。

結論:需要 Linux 時才租 Linux

Cloudflare Containers 最有價值的地方,不是讓所有專案都 Docker 化,而是補上 Workers 與傳統 container hosting 之間的缺口。你可以保留 Worker 的快速入口、全球 routing 與 bindings,同時把 FFmpeg、browser、原生套件或既有 image 放進更自由的環境。

我會保留一個很無聊的預設:Worker 負責每個請求都會經過的事;Container 負責少數非 Linux 不可的事。

當 Container 只是因為「Docker 比 Worker 熟」而存在,它多半是額外的維運層。當它消除一場 runtime 改寫、承接重運算,或隔離不可信工作時,才真的值回那一秒冷啟動與那份帳單。


外部參考連結