精彩评论
al2359(2年前 (2023-02-06))
求科学离线插件,谢谢![email protected]
评:改版梅林固件安装SS【shadowsocks】科学上网插件教程al2359(2年前 (2023-02-06))
求科学离线插件,谢谢!!!
评:改版梅林固件安装SS【shadowsocks】科学上网插件教程
開發者周即將結束,但我們直到現在還沒有談論容器。你們中的一些人可能知道,我們已經在幕後研究容器平台一段時間了。
我們計劃在 6 月底發布 Containers 公開測試版,今天我們將帶您先睹為快它的獨特之處。
工人是向世界各地運送軟體的最簡單且開銷最小的方式。但有時你需要做得更多。您可能想要:
以任何語言運行用戶生成的程式碼
執行需要完整 Linux 環境的 CLI 工具
使用幾 GB 的記憶體或多個 CPU 核心
無需進行重大重寫即可從 AWS、GCP 或 Azure 移植現有應用程式
Cloudflare Containers 讓您能夠以簡單、可擴展和全球化的方式完成所有這些工作。
透過與 Workers 的深度整合以及基於 Durable Objects 建構的架構,Workers 可以成為您的:
API 閘道:讓您在要求到達容器之前控制路由、驗證、快取和速率限制
服務網格:使用可程式路由層在容器之間建立私人連接
Orchestrator:讓您為容器編寫自訂排程、擴充功能和健康檢查邏輯
您無需部署新服務、編寫自訂 Kubernetes 操作員或費力地進行控制平面配置來擴展平台,只需編寫程式碼即可。
讓我們看看它是什麼樣子的。
首先,我們來看一個有狀態的例子。
想像一下,您正在建立一個平台,最終用戶可以在其中運行由 LLM 產生的程式碼。此代碼不受信任,因此每個使用者都需要自己的安全沙盒。此外,您希望使用者能夠按順序執行多個請求,可能寫入本機檔案或儲存記憶體狀態。
為此,您需要為每個使用者會話按需建立一個容器,然後將後續請求路由到該容器。您可以按照以下步驟完成此操作:
首先,編寫一些基本的 Wrangler 配置,然後透過 Worker 將請求路由到容器:
import { Container } from "cloudflare:workers"; export default { async fetch(request, env) { const url = new URL(request.url); if (url.pathname.startsWith("/execute-code")) { const { sessionId, messages } = await request.json(); // pass in prompt to get the code from Llama 4 const codeToExecute = await env.AI.run("@cf/meta/llama-4-scout-17b-16e-instruct", { messages }); // get a different container for each user session const id = env.CODE_EXECUTOR.idFromName(sessionId); const sandbox = env.CODE_EXECUTOR.get(id); // execute a request on the container return sandbox.fetch("/execute-code", { method: "POST", body: codeToExecute }); } // ... rest of Worker ... }, }; // define your container using the Container class from cloudflare:workers export class CodeExecutor extends Container { defaultPort = 8080; sleepAfter = "1m"; }
然後,使用單一命令部署您的程式碼:wrangler deploy。這將建立您的容器鏡像,將其推送到 Cloudflare 的註冊表,準備容器在全球範圍內快速啟動,並部署您的 Worker。
$ wrangler deploy
就是這樣。
它是如何運作的?
您的 Worker 會按需建立並啟動容器。每次env.CODE_EXECUTOR.get(id)使用唯一 ID 進行呼叫時,它都會將請求傳送到唯一的容器實例。容器將在第一次自動啟動fetch,然後在可設定的逾時時間(在本例中為 1 分鐘)後進入睡眠狀態。您只需為容器運行的時間付費。
當您要求新容器時,我們會在傳入請求附近的 Cloudflare 位置啟動一個容器。這意味著無論在哪個地區,低延遲工作負載都能得到良好的服務。 Cloudflare 負責所有預熱和緩存,因此您不必考慮它。
這允許每個用戶在自己的安全環境中運行程式碼。
無狀態和自動擴展應用程式在 Cloudflare Containers 上同樣運作良好。
假設您想要運行一個容器,該容器獲取視訊檔案並使用FFmpeg將其轉換為動畫 GIF 。與前面的範例不同,任何容器都可以滿足任何請求,但您仍然不想不必要地跨越海洋發送位元組並返回。因此,理想情況下,該應用程式可以在任何地方部署。
為此,您需要在 Wrangler 配置中聲明一個容器並打開autoscaling。此特定配置可確保一個執行個體始終在執行,並且如果 CPU 使用率超過容量的 75%,則會新增其他執行個體:
"containers": [ { "class_name": "GifMaker", "image": "./Dockerfile", // container source code can be alongside Worker code "instance_type": "basic", "autoscaling": { "minimum_instances": 1, "cpu_target": 75, } } ], // ...rest of wrangler.jsonc...
要路由請求,您只需撥打電話env.GIF_MAKER.fetch,請求就會自動發送到最近的容器:
import { Container } from "cloudflare:workers"; export class GifMaker extends Container { defaultPort: 1337, } export default { async fetch(request, env) { const url = new URL(request.url); if (url.pathname === "/make-gif") { return env.GIF_MAKER.fetch(request) } // ... rest of Worker ... }, };
從上面的例子可以看出,在 Workers 上執行一個基本的容器服務只需要幾行配置和一點 Workers 程式碼。無需擔心容量、工件註冊、區域或擴展。
為了更進階的用途,我們設計了 Cloudflare Containers 在 Durable Objects 上運作並與 Workers 協同工作。讓我們來看看底層架構並了解它支援的一些高級用例。
使用底層的Durable Objects可以實現到容器的路由。在上面的例子中,Container類別cloudflare:workers僅僅包裝了一個支援容器的持久對象,並為常見模式提供了輔助方法。在本文的其餘部分,我們將直接查看使用 Durable Objects 的範例,因為這應該可以闡明平台的底層設計。
每個 Durable Object 都充當可編程的 sidecar,可以代理對容器的請求並管理其生命週期。這使得您能夠以在其他平台上難以實現的方式控制和擴展您的容器。
您可以透過呼叫 Durable 物件上的 RPC 方法手動啟動、停止和執行特定容器上的命令,該容器現在在 處有一個新物件this.ctx.container:
class MyContainer extends DurableObject { // these RPC methods are callable from a Worker async customBoot(entrypoint, envVars) { this.ctx.container.start({ entrypoint, env: envVars }); } async stopContainer() { const SIGTERM = 15; this.ctx.container.signal(SIGTERM); } async startBackupScript() { await this.ctx.container.exec(["./backup"]); } }
您還可以監視您的容器並運行鉤子來響應容器狀態的變化。
例如,假設您有一個在容器中執行建置的 CI 作業。您想要根據退出狀態向佇列發送訊息。您可以輕鬆地編寫此行為:
class BuilderContainer extends DurableObject { constructor(ctx, env) { super(ctx, env) async function onContainerExit() { await this.env.QUEUE.send({ status: "success", message: "Build Complete" }); } async function onContainerError(err) { await this.env.QUEUE.send({ status: "error", message: err}); } this.ctx.container.start(); this.ctx.container.monitor().then(onContainerExit).catch(onContainerError); } async isRunning() { return this.ctx.container.running; } }
最後,如果您需要在每次運行時將狀態載入到容器中,則可以使用狀態掛鉤在容器休眠之前保留容器中的狀態,並在容器啟動後將狀態重新載入到容器中:
import { startAndWaitForPort } from "./helpers" class MyContainer extends DurableObject { constructor(ctx, env) { super(ctx, env) this.ctx.blockConcurrencyWhile(async () => { this.ctx.storage.sql.exec('CREATE TABLE IF NOT EXISTS state (value TEXT)'); this.ctx.storage.sql.exec('INSERT INTO state (value) SELECT '' WHERE NOT EXISTS (SELECT * FROM state)'); await startAndWaitForPort(this.ctx.container, 8080); await this.setupContainer(); this.ctx.container.monitor().then(this.onContainerExit); }); } async setupContainer() { const initialState = this.ctx.storage.sql.exec('SELECT * FROM state LIMIT 1').one().value; return this.ctx.container .getTcpPort(8080) .fetch("http://container/state", { body: initialState, method: 'POST' }); } async onContainerExit() { const response = await this.ctx.container .getTcpPort(8080) .fetch('http://container/state'); const newState = await response.text(); this.ctx.storage.sql.exec('UPDATE state SET value = ?', newState); } }
Durable Objects 不僅可讓您對容器生命週期進行細粒度的控制,整個 Workers 平台還可讓您根據需要擴展路由和調度行為。
工作人員在全球300 多個地點提供可程式入口邏輯。從這個意義上講,它們提供了與 API 網關類似的功能。
例如,假設您想要根據標頭中的資訊將請求路由到容器的不同版本。這可以透過幾行程式碼實現:
export default { async fetch(request, env) { const isExperimental = request.headers.get("x-version") === "experimental"; if (isExperimental) { return env.MY_SERVICE_EXPERIMENTAL.fetch(request); } else { return env.MY_SERVICE_STANDARD.fetch(request); } }, };
或者您想要限制並驗證對容器的請求:
async fetch(request, env) { const url = new URL(request.url); if (url.pathname.startsWith('/api/')) { const token = request.headers.get("token"); const isAuthenticated = await authenticateRequest(token); if (!isAuthenticated) { return new Response("Not authenticated", { status: 401 }); } const { withinRateLimit } = await env.MY_RATE_LIMITER.limit({ key: token }); if (!withinRateLimit) { return new Response("Rate limit exceeded for token", { status: 429 }); } return env.MY_APP.fetch(request); } // ... }
預設情況下,容器是私有的,只能透過 Workers 訪問,Workers 可以連接到眾多容器連接埠之一。在容器內部,您可以公開一個簡單的 HTTP 端口,但是從最終用戶到我們將資料發送到主機中容器的 TCP 端口的那一刻,請求仍然會被加密。由於通訊透過 Cloudflare 網路中繼,容器不需要設定 TLS 憑證即可在其開放連接埠中建立安全連線。
您也可以從客戶端透過 WebSocket 連線到容器。請參閱此儲存庫以取得使用 Websockets 的完整範例。
正如持久性物件可以充當容器的代理一樣,它也可以充當容器的代理。設定容器時,您可以關閉 Internet 存取權並確保傳出的請求通過 Workers。
// ... when starting the container... this.ctx.container.start({ workersAddress: '10.0.0.2:8080', enableInternet: false, // 'enableInternet' is false by default }); // ... container requests to '10.0.0.2:8080' securely route to a different service... override async onContainerRequest(request: Request) { const containerId = this.env.SUB_SERVICE.idFromName(request.headers['X-Account-Id']); return this.env.SUB_SERVICE.get(containerId).fetch(request); }
您可以確保進出容器的所有流量都是安全的,並且端對端加密,而無需自行處理網路問題。
這使您可以保護和連接 Cloudflare 網路內的容器…甚至在連接到外部專用網路時也可以保護和連接容器。
您可能需要超出 Cloudflare 開箱即用功能範圍的自訂調度和擴充邏輯。
我們不希望您必須管理複雜的 API 呼叫鍊或編寫運算子來取得所需的邏輯。只需編寫一些 Worker 程式碼。
例如,假設您的容器有一個很長的啟動期,涉及從外部來源載入資料。您需要手動預熱容器,並且需要控制要預熱的特定區域。此外,您還需要設定可透過 Workers 存取的手動健康檢查。您可以使用 Workers 和 Durable Objects 相當簡單地實現這一點。
import { Container, DurableObject } from "cloudflare:workers"; // A singleton Durable Object to manage and scale containers class ContainerManager extends DurableObject { scale(region, instanceCount) { for (let i = 0; i < instanceCount; i++) { const containerId = env.CONTAINER.idFromName(`instance-${region}-${i}`); // spawns a new container with a location hint await env.CONTAINER.get(containerId, { locationHint: region }).start(); } } async setHealthy(containerId, isHealthy) { await this.ctx.storage.put(containerId, isHealthy); } } // A Container class for the underlying compute class MyContainer extends Container { defaultPort = 8080; async onContainerStart() { // run healthcheck every 500ms await this.scheduleEvery(0.5, 'healthcheck'); } async healthcheck() { const manager = this.env.MANAGER.get( this.env.MANAGER.idFromName("manager") ); const id = this.ctx.id.toString(); await this.container.fetch("/_health") .then(() => manager.setHealthy(id, true)) .catch(() => manager.setHealthy(id, false)); } }
Durable ContainerManager Object 公開了scaleRPC 調用,您可以根據需要使用該調用region,並使用位置提示instanceCount擴大給定區域中活動容器實例的數量。程式碼在容器上執行手動定義的方法,並在管理器中追蹤其狀態,以供系統中的其他邏輯使用。this.schedulehealthcheck
這些建構塊讓使用者自己處理複雜的調度邏輯。有關使用標準 Durable Objects 的更詳細範例,請參閱此儲存庫。
我們很高興看到您在編排使用容器建立的複雜應用程式時提出的模式,並相信在 Workers 和 Durable Objects 之間,您將擁有所需的工具。
由於現在是2025 年開發者周,如果我們不談論剛剛正式發布的工作流程和變得更加完善的代理,那就太失禮了。
最後,讓我們快速看一下如何將容器與這兩個工具整合。
您需要從R2下載一個大文件,壓縮它,然後上傳。您要確保此操作成功,但不想自己編寫重試邏輯和錯誤處理。此外,您不需要處理旋轉的 R2 API 令牌或擔心網路連接——它預設應該是安全的。
這對於使用容器的工作流程來說是一個絕佳的機會。容器可以完成壓縮檔案的繁重工作,Workers 可以將資料傳輸到 R2 和從 R2 傳輸數據,而 Workflow 可以確保持久執行。
export class EncoderWorkflow extends WorkflowEntrypoint<Env, Params> { async run(event: WorkflowEvent<Params>, step: WorkflowStep) { const id = this.env.ENCODER.idFromName(event.instanceId); const container = this.env.ENCODER.get(id); await step.do('init container', async () => { await container.init(); }); await step.do('compress the object with zstd', async () => { await container.ensureHealthy(); const object = await this.env.ARTIFACTS.get(event.payload.r2Path); const result = await container.fetch('http://encoder/zstd', { method: 'POST', body: object.body }); await this.env.ARTIFACTS.put(`results${event.payload.r2Path}`, result.body); }); await step.do('cleanup container', async () => { await container.destroy(); }); } }
最後,假設您有一個需要啟動雲端基礎架構的 AI 代理程式(您喜歡冒險)。為此,您需要使用Terraform,但由於它是從命令列運行的,因此您無法在 Workers 上執行它。
透過定義工具,您可以使代理程式從容器執行 shell 命令:
// Make tools that call to a container from an agent const createExternalResources = tool({ description: "runs Terraform in a container to create resources", parameters: z.object({ sessionId: z.number(), config: z.string() }), execute: async ({ sessionId, config }) => { return this.env.TERRAFORM_RUNNER.get(sessionId).applyConfig(config); }, }); // Expose RPC Methods that call to the container class TerraformRunner extends DurableObject { async applyConfig(config) { await this.ctx.container.getTcpPort(8080).fetch(APPLY_URL, { method: 'POST', body: JSON.stringify({ config }), }); } // ...rest of DO... }
當與其他工具結合使用時,容器的功能會更加強大。工人們可以以安全、簡單的方式輕鬆完成這項任務。
Workers 和 Containers 之間的深度整合也使得根據成本選擇合適的工具變得容易。
使用 Cloudflare Containers,您只需按實際使用量付費。當請求發送到容器或手動啟動容器時,就會開始收費。容器進入睡眠狀態後充電停止,這可以在可設定的逾時後自動發生。這使得擴展到零變得容易,並且即使在流量變化很大的情況下也能獲得高利用率。
容器每運行 10 毫秒按以下費率計費:
記憶體:每 GB 秒 0.0000025 美元
CPU:每 vCPU 秒 0.000020 美元
磁碟每 GB 秒 0.00000007 美元
每月 1 TB 的免費資料傳輸結束後,容器的傳出資料將按區域定價。我們將從現在到測試版期間制定細節,並將在各個方面推出清晰、透明的定價,以便您了解自己的立場。
工人的重量比容器輕,並且在等待 I/O 時無需充電,為您節省成本。這意味著,如果可以的話,在 Worker 上運行可以幫助您節省成本。幸運的是,在 Cloudflare 上可以輕鬆地將請求路由到正確的工具。
在紙面上比較容器和功能服務永遠是蘋果和橘子之間的比較,結果會根據用例而有很大差異。但要分享一個我們自己的真實例子,一年前當 Cloudflare 收購 Baselime 時,Baselime 是 AWS Lambda 的重度使用者。透過遷移到 Cloudflare,他們將雲端運算費用降低了 80%。
下面我們想分享一個有代表性的例子,比較同時使用容器和無伺服器功能的應用程式的成本。我們很容易想出一個不理想的範例,該範例在另一個平台上針對錯誤類型的工作負載以次優的方式使用容器。我們不會在這裡這樣做。我們知道,控制雲端成本可能具有挑戰性,而成本是決定應用程式的哪些部分使用哪種類型的運算的關鍵部分。
在下面的範例中,我們將 Cloudflare Containers + Workers 與 Google Cloud Run 進行比較,後者是一個給我們留下深刻印象的備受好評的容器平台。
假設您執行一個每月處理 5000 萬個請求的應用程序,並且每個請求平均消耗 500 毫秒的掛鐘時間。但對此應用程式的請求並不完全相同——一半的請求需要容器,而另一半只需使用無伺服器功能即可滿足。
每月請求數 |
掛鐘時間(持續時間) |
所需計算 |
Cloudflare |
Google雲 |
2500萬 |
500毫秒 |
容器+無伺服器功能 |
容器 + 工人 |
Google Cloud Run + Google Cloud Run 函數 |
2500萬 |
500毫秒 |
無伺服器函數 |
勞工 |
Google Cloud Run 函數 |
在 Cloud Run 和 Cloudflare Containers 上,一個容器可以處理多個請求。在某些平台(例如 AWS Lambda)上,每個容器執行個體僅限於單一請求,隨著請求數量的增加,成本會顯著增加。在這個場景下,一個擁有 4GB 記憶體和一半 vCPU 的容器上可以同時執行 50 個請求。這意味著,為了處理 2500 萬個請求(每個請求耗時 500 毫秒),我們需要 625,000 秒的計算時間
在這個例子中,流量是突發的,我們希望避免支付空閒時間的費用,所以我們將使用 Cloud Run 的基於請求的定價。
|
每 vCPU 秒價格 |
每 GB 秒記憶體價格 |
每百萬請求的價格 |
計算 + 請求的月費 |
Cloudflare 容器 |
0.000020 美元 |
0.0000025 美元 |
0.30 美元 |
20.00 美元 |
Google Cloud Run |
0.000024 美元 |
0.0000025 美元 |
0.40 美元 |
23.75 美元 |
* 比較不包括任何提供者的免費套餐,並使用單一 Tier 1 GCP 區域
兩個平台的計算價格相當。但正如我們在本文前面所展示的,Cloudflare 上的容器可以在任何地方按需運行,而無需配置和管理區域。每個容器都有一個可編程的邊車,它有自己的資料庫,由持久性物件支援。正是與平台其他部分的深度整合使得 Cloudflare 上的容器具有獨特的可程式性。
其他請求可以使用較少的計算以及用JavaScript、TypeScript、Python或Rust編寫的程式碼來滿足,因此我們將使用 Workers 和 Cloud Run Functions。
這 2500 萬個請求每個運行 500 毫秒,每個請求花費 480 毫秒等待 I/O。這意味著 Workers 只會收取 20 毫秒的「CPU 時間」費用,也就是 Worker 實際使用計算的時間。在建立發出推理請求的 AI 應用程式時,甚至在建立 REST API 和其他業務邏輯時,這種低 CPU 時間與高掛鐘時間的比率極為常見。大部分時間都花在等待 I/O 上。根據我們的數據,我們通常看到 Workers 每個請求使用的 CPU 時間不到 5 毫秒,而掛鐘時間(等待 API 或 I/O)則為數秒。
Cloud Run Function 將使用具有 0.083 vCPU 和 128 MB 記憶體的實例,並在整個 500 毫秒的掛鐘時間內對 CPU 和 GiB 進行充電。
|
「牆上時間」總價 |
「CPU 時間」總價 |
計算 + 請求的總價格 |
Cloudflare Workers |
不適用 |
0.83 美元 |
8.33 美元 |
Google Cloud Run 函數 |
1.44 美元 |
不適用 |
11.44 美元 |
* 比較不包括免費套餐,並使用單一 Tier 1 GCP 區域。
此比較假設您已將 Google Cloud Run Functions 配置為每個執行個體最多 20 個並發請求。在 Google Cloud Run Functions 上,執行個體可以處理的最大並發請求數取決於函數的效率以及您對流量高峰可能引入的尾部延遲的容忍度。
工作器會自動水平擴展,不需要您配置並發設定(並希望正確配置),並且可以在300 多個位置運行。
最重要的成本指標是開發和運行應用程式的總成本。而獲得最佳結果的唯一方法就是使用適合該工作的正確計算。所以問題可以歸結為摩擦和整合。您能多輕鬆地將理想的構建塊整合在一起?
隨著越來越多的軟體使用生成式人工智慧並向 LLM 發出推理請求,現代應用程式必須與無數服務進行通訊和整合。大多數系統越來越即時、越來越活躍,通常保持開放的長期連接,並行執行任務。 10 年前,在虛擬機器或容器中執行應用程式實例並完成一天的工作可能已經可行,但當我們與 2025 年的開發人員交談時,他們通常會為特定用例帶來多種形式的計算。
這表明選擇一個可以無縫地將流量從一個運算來源轉移到另一個運算來源的平台的重要性。如果您想要限制速率、提供服務端渲染的頁面、API 回應和靜態資產、處理身份驗證和授權、向 AI 模型發出推理請求、透過工作流程運行核心業務邏輯或提取流數據,只需在 Workers 中處理請求即可。僅將較重的計算保存在實際上唯一的選擇上。使用 Cloudflare Workers 和 Containers,這就像 Worker 中的 if-else 語句一樣簡單。這使得選擇適合工作的工具變得容易。
我們正在收集回饋並對我們的 API 進行最後的修改,並將於 2025 年 6 月下旬向公眾發佈公開測試版。
從建立 Cloudflare Workers 的第一天起,我們的目標就是建立一個整合平台,讓 Cloudflare 產品作為一個系統協同工作,而不僅僅是作為獨立產品的集合。我們對容器採取了同樣的方法,旨在使 Cloudflare 不僅成為全球部署容器的最佳場所,而且成為部署開發人員正在構建的完整應用程式類型的最佳場所,這些應用程式將容器與無伺服器功能、工作流程、代理等結合使用。
我們很高興很快就能將它交到您手中。今年夏天請保持警覺。
存取1.1.1.1即可開始使用我們的免費應用程序,讓您的網路更快、更安全。
要詳細了解我們幫助建立更好的互聯網的使命,請從這裡開始。如果您正在尋找新的職業方向,請查看我們的職缺。
「梦想一旦被付诸行动,就会变得神圣,如果觉得我的文章对您有用,请帮助本站成长」
上一篇:川普的阳谋,细思极恐,一张孤立中国的大网(深度分解)
下一篇:在VmShell香港CMI服务器上安装Hysteria 2协议,翻墙
求科学离线插件,谢谢![email protected]
评:改版梅林固件安装SS【shadowsocks】科学上网插件教程求科学离线插件,谢谢!!!
评:改版梅林固件安装SS【shadowsocks】科学上网插件教程