原文链接:https://blog.cloudflare.com/cloudflare-containers-coming-2025/

简单、可扩展、全球化:Cloudflare Workers 容器平台将于 2025 年 6 月上线

2025-04-11

  • Mike Nomitch
  • Gabi Villalonga Simón

12 分钟阅读

开发者周已接近尾声,但我们还没讨论过容器:直到现在。正如你们中的一些人可能知道的那样,我们已经在幕后开发容器平台有一段时间了。

我们计划在六月下旬发布容器平台的开放测试版(open beta),今天我们将抢先介绍其独特之处。

Workers 是以极少开销在全球范围内部署软件的最简单方式。但有时你需要做得更多。你可能想要:

  • 运行任何语言的用户生成代码
  • 执行需要完整 Linux 环境的命令行工具(CLI tool)
  • 使用数 GB 的内存或多个 CPU 核心
  • 无需大规模重写即可从 AWS、GCP 或 Azure 迁移现有应用程序

Cloudflare Containers 让您能够做到以上所有事情,同时保持简单、可扩展和全球化。

通过与 Workers 的深度集成以及基于 Durable Objects 构建的架构,Workers 可以成为您的:

  • API 网关​:让您在请求到达容器之前控制路由、身份验证、缓存和速率限制
  • 服务网格​:通过可编程的路由层在容器之间创建私有连接
  • 编排器​:允许您为容器编写自定义的调度、扩展和健康检查逻辑

您无需部署新服务、编写自定义 Kubernetes Operator 或费力地配置控制平面来扩展平台,只需编写代码即可。

让我们看看它是什么样子。

部署不同类型的应用程序

有状态工作负载:执行 AI 生成的代码

首先,让我们看一个有状态的例子。

假设您正在构建一个平台,最终用户可以在该平台上运行由 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();
      // 传入提示以从 Llama 4 获取代码
      const codeToExecute = await env.AI.run("@cf/meta/llama-4-scout-17b-16e-instruct", { messages });

      // 为每个用户会话获取不同的容器
      const id = env.CODE_EXECUTOR.idFromName(sessionId);
      const sandbox = env.CODE_EXECUTOR.get(id);

      // 在容器上执行请求
      return sandbox.fetch("/execute-code", { method: "POST", body: codeToExecute });
    }

    // ... Worker 的其余部分 ...
  },
};

// 使用 cloudflare:workers 中的 Container 类定义您的容器
export class CodeExecutor extends Container {
  defaultPort = 8080;
  sleepAfter = "1m"; // 1分钟后休眠
}

然后,使用单个命令部署您的代码:wrangler deploy。这将构建您的容器镜像,将其推送到 Cloudflare 的注册表,准备好容器在全球范围内快速启动,并部署您的 Worker。

$ wrangler deploy

就这样。

它是如何工作的?

您的 Worker 按需创建和启动容器。每次您使用唯一的 ID 调用 env.CODE_EXECUTOR.get(id) 时,它会将请求发送到唯一的容器实例。容器将在第一次 fetch 时自动启动,然后在可配置的超时(本例中为 1 分钟)后自动进入休眠状态。您只需为容器活动运行的时间付费。

当您请求新容器时,我们会在靠近传入请求的 Cloudflare 位置启动一个。这意味着无论哪个区域,低延迟工作负载都能得到很好的服务。Cloudflare 会处理所有的预热和缓存,因此您无需考虑这些。

这使得每个用户都可以在自己的安全环境中运行代码。

无状态和全球化:无处不在的 FFmpeg

无状态和自动扩展的应用程序在 Cloudflare Containers 上同样运行良好。

假设您想运行一个容器,该容器接收一个视频文件并使用 FFmpeg 将其转换为动画 GIF。与前面的示例不同,任何容器都可以处理任何请求,但您仍然不希望不必要地跨洋来回发送字节。因此,理想情况下,应用程序可以部署在任何地方。

为此,您在 Wrangler 配置中声明一个容器并启用 autoscaling。此特定配置确保始终运行一个实例,并且如果 CPU 使用率超过容量的 75%,则会添加额外的实例:

"containers": [
  {
    "class_name": "GifMaker",
    "image": "./Dockerfile", // 容器源代码可以与 Worker 代码放在一起
    "instance_type": "basic", // 实例类型
    "autoscaling": {
      "minimum_instances": 1, // 最小实例数
      "cpu_target": 75, // CPU 目标利用率
    }
  }
],
// ... 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)
    }

    // ... Worker 的其余部分 ...
  },
};

超越基础

从上面的示例中,您可以看到在 Workers 上运行基本的容器服务只需要几行配置和少量 Workers 代码。无需担心容量、镜像仓库、区域或扩展问题。

对于更高级的用途,我们将 Cloudflare Containers 设计为在 Durable Objects 之上运行并与 Workers 协同工作。让我们看一下底层架构,并了解它启用的一些高级用例。

作为可编程 Sidecar 的 Durable Objects

路由到容器的功能是使用底层的 Durable Objects 实现的。在上面的示例中,来自 cloudflare:workersContainer 类只是包装了一个启用了容器的 Durable Object,并为常见模式提供了辅助方法。在本文的其余部分,我们将直接使用 Durable Objects 的示例,因为这应该能阐明该平台的底层设计。

每个 Durable Object 都充当一个可编程的 Sidecar,可以代理到容器的请求并管理其生命周期。这使您能够以在其他平台上难以实现的方式控制和扩展容器。

您可以通过调用其 Durable Object 上的 RPC 方法来手动启动、停止特定容器并对其执行命令,该 Durable Object 现在在 this.ctx.container 处有一个新对象:

class MyContainer extends DurableObject {
  // 这些 RPC 方法可以从 Worker 调用
  async customBoot(entrypoint, envVars) {
    this.ctx.container.start({ entrypoint, env: envVars });
  }

  async stopContainer() {
    const SIGTERM = 15;
    this.ctx.container.signal(SIGTERM);
  }

  async onContainerRequest(request) {
    // 此方法针对每个到容器的请求调用
    // 您可以在请求到达容器之前修改它
    return this.ctx.container.fetch(request);
  }

  // ... DO 的其余部分 ...
}

使用 Workers 作为服务网格

默认情况下,容器是私有的,只能通过 Workers 访问,Workers 可以连接到容器的多个端口之一。在容器内部,您可以公开一个普通的 HTTP 端口,但从最终用户到我们将数据发送到主机上容器 TCP 端口的那一刻,请求仍将被加密。由于通信通过 Cloudflare 网络中继,容器无需设置 TLS 证书即可在其开放端口上建立安全连接。

您也可以通过 WebSocket 从客户端连接到容器。有关使用 Websockets 的完整示例,请参阅此存储库。

正如 Durable Object 可以充当到容器的代理一样,它也可以充当来自容器的代理。设置容器时,您可以关闭 Internet 访问,并确保传出请求通过 Workers。

// ... 启动容器时 ...
this.ctx.container.start({
  workersAddress: '10.0.0.2:8080', // Workers 地址
  enableInternet: false, // 'enableInternet' 默认为 false
});

// ... 容器对 '10.0.0.2:8080' 的请求安全地路由到不同的服务 ...
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 网络内保护和连接容器……甚至在连接到外部私有网络时也是如此。

使用 Workers 作为编排器

您可能需要超出 Cloudflare 开箱即用功能的自定义调度和扩展逻辑。

我们不希望您为了获得所需逻辑而管理复杂的 API 调用链或编写 Operator。只需编写一些 Worker 代码即可。

例如,假设您的容器有一个很长的启动周期,涉及从外部源加载数据。您需要手动预热容器,并需要控制预热的具体区域。此外,您需要设置可通过 Workers 访问的手动健康检查。使用 Workers 和 Durable Objects,您可以相当简单地实现这一点。

import { Container, DurableObject } from "cloudflare:workers";

// 用于管理和扩展容器的单例 Durable Object
class ContainerManager extends DurableObject {
  scale(region, instanceCount) {
    for (let i = 0; i < instanceCount; i++) {
      const containerId = env.CONTAINER.idFromName(`instance-${region}-${i}`);
      // 使用位置提示生成一个新容器
      await env.CONTAINER.get(containerId, { LocationHint: region }).start();
    }
  }

  async setHealthy(containerId, isHealthy) {
    await this.ctx.storage.put(containerId, isHealthy);
  }
}

// 用于底层计算的 Container 类
class MyContainer extends Container {
  defaultPort = 8080;

  async onContainerStart() {
    // 每 500 毫秒运行一次健康检查
    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));
  }
}

ContainerManager Durable Object 公开了 scale RPC 调用,您可以根据需要使用区域和实例数调用它,这会使用位置提示增加给定区域中活动 Container 实例的数量。this.schedule 代码在 Container 上执行手动定义的 healthcheck 方法,并在 Manager 中跟踪其状态,以供系统中的其他逻辑使用。

这些构建块让用户可以自己处理复杂的调度逻辑。有关使用标准 Durable Objects 的更详细示例,请参阅此存储库。

我们很高兴看到您在使用容器构建复杂应用程序进行编排时想出的模式,并相信在 Workers 和 Durable Objects 之间,您将拥有所需的工具。

与更多 Cloudflare 开发者平台集成

既然是 2025 年开发者周,我们不能不提刚刚正式发布(GA)的 Workflows 和刚刚变得更好的 Agents。

让我们最后快速看一下如何将 Containers 与这两个工具集成。

使用 Workflows 和 R2 运行短期作业

您需要从 R2 下载一个大文件,对其进行压缩,然后上传。您希望确保此操作成功,但又不想自己编写重试逻辑和错误处理。此外,您不想处理轮换 R2 API 令牌或担心网络连接——它应该默认是安全的。

这是使用 Containers 的 Workflow 的绝佳机会。容器可以完成压缩文件的繁重工作,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('初始化容器', async () => {
      await container.init();
    });

    await step.do('使用 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('清理容器', async () => {
      await container.destroy();
    });
  }
}

从 Agent 调用容器

最后,假设您有一个需要启动云基础设施的 AI Agent(您喜欢冒险)。为此,您想使用 Terraform,但由于它是从命令行运行的,因此无法在 Workers 上运行。

通过定义一个工具,您可以使您的 Agent 从容器运行 shell 命令:

// 创建从 Agent 调用容器的工具
const createExternalResources = tool({
  description: "在容器中运行 Terraform 以创建资源",
  parameters: z.object({ sessionId: z.number(), config: z.string() }),
  execute: async ({ sessionId, config }) => {
    // 调用 TerraformRunner DO 的 applyConfig 方法
    return this.env.TERRAFORM_RUNNER.get(sessionId).applyConfig(config);
  },
});

// 公开调用容器的 RPC 方法
class TerraformRunner extends DurableObject {
  async applyConfig(config) {
    // 通过 TCP 端口向容器内的服务发送请求
    await this.ctx.container.getTcpPort(8080).fetch(APPLY_URL, {
      method: 'POST',
      body: JSON.stringify({ config }),
    });
  }

  // ... DO 的其余部分 ...
}

当与其他工具结合使用时,容器的功能会强大得多。Workers 使以安全简单的方式实现这一点变得容易。

按使用付费,并使用正确的工具

Workers 和 Containers 之间的深度集成也使得在成本方面轻松选择合适的工具。

使用 Cloudflare Containers,您只需为使用的资源付费。当请求发送到容器或手动启动容器时开始计费。容器进入休眠状态后停止计费,这可以在可配置的超时后自动发生。这使得轻松扩展到零成为可能,并允许您即使在流量变化很大的情况下也能获得高利用率。

容器按其活动运行的每 10 毫秒计费,费率如下:

  • 内存:每 GB 秒 \$0.0000025
  • CPU:每 vCPU 秒 \$0.000020
  • 磁盘:每 GB 秒 \$0.0000007

每月 1 TB 的免费数据传输后,从容器传出的流量将按区域定价。我们将在现在到测试版发布期间确定细节,并将推出涵盖所有维度的清晰透明的定价,以便您了解自己的状况。

Workers 比容器更轻量级,并且通过在等待 I/O 时不收费来为您节省资金。这意味着,如果可以的话,在 Worker 上运行有助于您节省成本。幸运的是,在 Cloudflare 上,将请求路由到正确的工具很容易。

成本比较

在纸面上比较容器和函数服务总是像拿苹果和橙子比较,结果会因用例而有很大差异。但为了分享我们自己的一个真实示例,一年前 Cloudflare 收购 Basetime 时,Basetime 是 AWS Lambda 的重度用户。通过迁移到 Cloudflare,他们将云计费降低了 80%。

下面我们想分享一个有代表性的示例,比较一个同时使用容器和无服务器函数的应用程序的成本。我们很容易想出一个在其他平台上以次优方式、针对错误类型的工作负载使用容器的人为示例。我们不会在这里这样做。我们知道,理解云成本可能具有挑战性,并且成本是决定为应用程序的哪些部分使用哪种计算类型的关键部分。

在下面的示例中,我们将比较 Cloudflare Containers + Workers 与 Google Cloud Run,后者是一个备受推崇的容器平台,给我们留下了深刻印象。

示例应用程序

假设您运行一个应用程序,每月处理 5000 万个请求,每个请求平均消耗 500 毫秒的实际时间(wall-time)。但是,对此应用程序的请求并不完全相同——一半的请求需要容器,另一半可以仅使用无服务器函数来处理。

每月请求数 实际时间(持续时间) 所需计算 Cloudflare Google Cloud
2500 万 500 毫秒 容器 + 无服务器函数 Containers + Workers Google Cloud Run + Google Cloud Run Functions
2500 万 500 毫秒 无服务器函数 Workers Google Cloud Run Functions

容器定价

在 Cloud Run 和 Cloudflare Containers 上,一个容器可以处理多个请求。在某些平台(如 AWS Lambda)上,每个容器实例仅限于单个请求,随着请求数量的增长,成本会显着增加。在这种情况下,50 个请求可以同时在具有 4 GB 内存和半个 vCPU 的容器上运行。这意味着要处理 2500 万个 500 毫秒的请求,我们需要 625,000 秒的计算量。

在此示例中,流量是突发性的,我们希望避免为空闲时间付费,因此我们将使用 Cloud Run 的基于请求的定价。

每 vCPU 秒价格 每 GB 秒内存价格 每百万请求价格 每月计算 + 请求价格
Cloudflare Containers \$0.000020 \$0.0000025 \$0.30 \$20.00
Google Cloud Run \$0.000024 \$0.0000025 \$0.40 \$23.75

比较不包括任一提供商的免费套餐,并使用单个 Tier 1 GCP 区域

两个平台的计算定价具有可比性。但正如我们之前展示的,Cloudflare 上的容器可以在任何地方按需运行,无需配置和管理区域。每个容器都有一个可编程的 sidecar,拥有自己的数据库,由 Durable Objects 支持。正是与平台其余部分的深度集成使得 Cloudflare 上的容器具有独特的可编程性。

函数定价

其他请求可以用更少的计算量来处理,并且代码是用 JavaScript、TypeScript、Python 或 Rust 编写的,因此我们将使用 Workers 和 Cloud Run Functions。

这 2500 万个请求也运行 500 毫秒,每个请求花费 480 毫秒等待 I/O。这意味着 Workers 只会收取 20 毫秒的“CPU 时间”费用,即 Worker 实际使用计算的时间。这种低 CPU 时间与高实际时间的比率在构建发出推理请求的 AI 应用程序时,甚至在仅仅构建 REST API 和其他业务逻辑时都极为常见。大多数时间都花在等待 I/O 上。根据我们的数据,我们通常看到 Workers 每个请求使用不到 5 毫秒的 CPU 时间,而实际时间则长达数秒(等待 API 或 I/O)。

Cloud Run Function 将使用具有 0.083 vCPU 和 128 MB 内存的实例,并按 CPU 秒和 GiB 秒为整个 500 毫秒的实际时间计费。

“实际时间”总价 “CPU 时间”总价 计算 + 请求总价
Cloudflare Workers 不适用 \$0.83 \$8.33
Google Cloud Run Functions \$1.44 不适用 \$11.44

比较不包括免费套餐,并使用单个 Tier 1 GCP 区域。

此比较假定您已将 Google Cloud Run Functions 配置为每个实例最多处理 20 个并发请求。在 Google Cloud Run Functions 上,一个实例可以处理的最大并发请求数因函数的效率以及您自己对流量峰值可能引入的尾部延迟的容忍度而异。

Workers 自动水平扩展,不需要您配置并发设置(并希望设置正确),并且可以在 300 多个位置运行。

成本的整体视图

最重要的成本指标是开发和运行应用程序的总成本。获得最佳结果的唯一方法是为工作选择正确的计算工具。因此,问题归结为摩擦和集成。您可以多么轻松地将理想的构建块集成在一起?

随着越来越多的软件利用生成式 AI 并向 LLM 发出推理请求,现代应用程序必须与众多服务进行通信和集成。大多数系统越来越实时和“健谈”,通常保持长连接打开,并行执行任务。将应用程序实例运行在 VM 或容器中并称之为完成任务可能在 10 年前有效,但是当我们与 2025 年的开发人员交谈时,他们最常为特定用例引入多种形式的计算。

这显示了选择一个可以无缝地将流量从一种计算源转移到另一种计算源的平台的重要性。如果您想进行速率限制、提供服务器端渲染页面、API 响应和静态资产、处理身份验证和授权、向 AI 模型发出推理请求、通过 Workflow 运行核心业务逻辑或摄取流数据,只需在 Workers 中处理请求。仅在确实是唯一选择的情况下才使用更重的计算。使用 Cloudflare Workers 和 Containers,这就像在 Worker 中使用 if-else 语句一样简单。这使得为工作选择正确的工具变得容易。

2025 年 6 月上线

我们现在正在收集反馈并对我们的 API 进行最后的润色,并将在 2025 年 6 月下旬向公众发布开放测试版。

从构建 Cloudflare Workers 的第一天起,我们的目标就是构建一个集成平台,让 Cloudflare 产品作为一个系统协同工作,而不仅仅是一系列独立产品的集合。我们对 Containers 采取了同样的方法,目标是不仅使 Cloudflare 成为在全球范围内部署容器的最佳场所,而且是部署开发人员正在构建的、将容器与无服务器函数、Workflows、Agents 等结合使用的完整应用程序类型的最佳场所。

我们很高兴很快能将其交到您的手中。敬请期待今年夏天。

Cloudflare 的连接云保护整个企业网络,帮助客户高效构建互联网规模的应用程序,加速任何网站或互联网应用程序,抵御 DDoS 攻击,阻止黑客入侵,并能帮助您踏上零信任之旅。

从任何设备访问 1.1.1.1 开始使用我们的免费应用程序,让您的互联网更快、更安全。

要了解更多关于我们帮助构建更好互联网的使命,请从这里开始。如果您正在寻找新的职业方向,请查看我们的空缺职位。


总结付费规则:

  1. 按使用付费 (Pay-for-what-you-use)​:核心原则是你只为你实际使用的资源付费。
  2. 计费触发与停止​:
    • 开始计费​:当有请求发送到容器,或者你手动启动容器时开始计费。
    • 停止计费​:当容器进入**休眠 (hibernation)**状态后停止计费。休眠可以在一个可配置的超时时间后自动发生(例如文章示例中的 sleepAfter = "1m")。
  3. 计费粒度​:按容器活动运行 (active runtime)每 10 毫秒计费。这意味着即使是很短时间的运行也会被精确计量。
  4. 计费维度与费率​:
    • 内存 (Memory)​:每 GB 秒 \$0.0000025
    • CPU (CPU)​:每 vCPU 秒 \$0.000020
    • 磁盘 (Disk)​:每 GB 秒 \$0.0000007
  5. 数据传输 (Egress Traffic)​:
    • 免费额度​:每月有 1 TB 的免费传出流量。
    • 收费​:超出免费额度后,从容器传出的流量将按区域定价 (regionally priced)。具体的价格细节将在测试版发布前确定。
  6. 可扩展至零 (Scale-to-zero)​:由于只有在活动时才计费,并且可以自动休眠,容器可以轻松地扩展到零个实例,这意味着在没有流量或不活动时,你不需要为空闲资源付费。
  7. 与 Workers 的成本对比​:
    • 文章强调 Workers 更轻量级,并且采用 CPU 时间 (CPU Time) 计费,即只计算 Worker 实际执行代码消耗 CPU 的时间,不计算等待 I/O(如网络请求)的时间。
    • 容器(像 Cloud Run)通常按实际时间 (Wall Time) 或资源分配时间计费,即使在等待 I/O 时也可能计费。
    • 因此,如果任务可以用 Worker 完成(特别是 I/O 密集型任务),使用 Worker 会更具成本效益。Cloudflare 平台的设计使得在 Worker 和 Container 之间路由请求变得简单,方便用户为工作的不同部分选择最合适的、成本最优的工具。

总结来说​:Cloudflare Containers 的计费是基于容器实际活动运行期间所消耗的内存、CPU 和磁盘资源,按非常小的时间粒度(10ms)计量。不活动时会自动休眠停止计费,实现了成本效益高的“按需使用”和“扩展至零”。传出流量在超过免费额度后会额外收费。平台鼓励结合使用 Workers 来进一步优化成本。


来源:
https://www.nodeseek.com/post-311983-1