在本指南中,您将部署一个 Worker,该 Worker 能够响应最终用户请求以向一个或多个容器(Containers)发出请求。 在此示例中,每个容器都会运行一个用 Go 编写的小型 Web 服务器。
这个示例 Worker 应该能让您对简单的容器使用有所了解,并为更复杂的用例提供一个起点。
在本指南中,我们将与您的 Worker 代码一起构建并推送容器镜像。默认情况下,此过程使用 Docker ↗ 来完成。
当您运行 wrangler deploy 时,必须在本地运行 Docker。对于大多数人来说,安装 Docker 的最佳方式是按照安装 Docker Desktop 的文档 ↗进行操作。其他工具(如 Colima ↗)也可能有效。
您可以通过在终端中运行 docker info 命令来检查 Docker 是否正常运行。如果 Docker 正在运行,该命令将成功执行。如果 Docker 未运行,docker info 命令将挂起或返回包含 "Cannot connect to the Docker daemon" 消息的错误。
运行以下命令以从入门模板创建并部署带有容器的新 Worker:
npm create cloudflare@latest -- --template=cloudflare/templates/containers-templateyarn create cloudflare --template=cloudflare/templates/containers-templatepnpm create cloudflare@latest --template=cloudflare/templates/containers-template如果要将代码更改部署到 Worker 或容器代码中,可以使用 Wrangler CLI 运行以下命令:
npx wrangler deployyarn wrangler deploypnpm wrangler deploy运行 wrangler deploy 时,会发生以下情况:
- Wrangler 使用 Docker 构建您的容器镜像。
- Wrangler 将您的镜像推送到自动与您的 Cloudflare 账户集成的容器镜像注册表。
- Wrangler 部署您的 Worker,并配置 Cloudflare 的网络以准备好生成容器的实例。
第一次部署时,构建和推送通常需要最长的时间。随后的部署会更快,因为它们重用缓存的镜像层 ↗。
部署后,运行以下命令以显示 Cloudflare 账户中的容器列表及其部署状态:
npx wrangler containers listyarn wrangler containers listpnpm wrangler containers list并使用以下命令查看部署到 Cloudflare 注册表的镜像:
npx wrangler containers images listyarn wrangler containers images listpnpm wrangler containers images list现在,打开您的 Worker 的 URL。它应该看起来像 https://hello-containers.<YOUR_WORKERS_SUBDOMAIN>.workers.dev。
如果您向路径 /container/1 或 /container/2 发出请求,您的 Worker 会将请求路由到特定的容器。"/container/" 之后的每个不同路径都将路由到一个唯一的容器。
如果您向 /lb 发出请求,您将对随机选择的 3 个容器之一的请求进行负载均衡。
您可以通过读取每个请求的输出来确认此行为。
既然您已经部署了第一个容器,让我们解释一下 Worker 的代码、配置文件、容器的代码中发生了什么,以及请求是如何路由的。
您的 Wrangler 配置文件 定义了 Worker 和容器的配置:
{
"containers": [
{
"max_instances": 10,
"class_name": "MyContainer",
"image": "./Dockerfile",
},
],
"durable_objects": {
"bindings": [
{
"name": "MY_CONTAINER",
"class_name": "MyContainer",
},
],
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["MyContainer"],
},
],
}[[containers]]
max_instances = 10
class_name = "MyContainer"
image = "./Dockerfile"
[[durable_objects.bindings]]
name = "MY_CONTAINER"
class_name = "MyContainer"
[[migrations]]
tag = "v1"
new_sqlite_classes = [ "MyContainer" ]关于此配置的要点:
image指向一个 Dockerfile、包含 Dockerfile 的目录,或指向一个完全限定的镜像引用,例如registry.cloudflare.com/<YOUR_ACCOUNT_ID>/<IMAGE>:<TAG>。class_name必须是 Durable Object 类名称。max_instances声明将运行的并发运行容器实例的最大数量。- Durable Object 必须使用
new_sqlite_classes而不是new_classes。
您的容器镜像必须能够在 linux/amd64 架构上运行,但除此之外,几乎没有其他限制。
在您刚刚部署的示例中,它是一个简单的 Golang 服务器,它使用将在 Worker 中设置的 MESSAGE 环境变量和自动生成的环境变量 CLOUDFLARE_DEPLOYMENT_ID,在端口 8080 上响应请求。
func handler(w http.ResponseWriter, r *http.Request) {
message := os.Getenv("MESSAGE")
instanceId := os.Getenv("CLOUDFLARE_DEPLOYMENT_ID")
fmt.Fprintf(w, "Hi, I'm a container and this is my message: %s, and my instance ID is: %s", message, instanceId)
}首先要注意扩展了 Container ↗ 类的 MyContainer:
export class MyContainer extends Container {
defaultPort = 8080;
sleepAfter = '10s';
envVars = {
MESSAGE: 'I was passed in via the container class!',
};
override onStart() {
console.log('Container successfully started');
}
override onStop() {
console.log('Container successfully shut down');
}
override onError(error: unknown) {
console.log('Container error:', error);
}
}这定义了容器的基本配置:
defaultPort设置了fetch和containerFetch方法将用来与容器通信的端口。它还会阻止请求,直到容器侦听该端口为止。sleepAfter设置容器在空闲一定时间后休眠的超时。envVars设置在容器启动时将传递给容器的环境变量。onStart、onStop和onError分别是容器启动、停止或出错时运行的钩子。
Container 类本身扩展了 DurableObject,因此您的子类可以访问完整的 Durable Object API。Durable Object 处理路由、生命周期和持久状态,而容器进程在 Linux VM 中运行您的镜像。这意味着您可以使用 this.ctx.storage 来持久化容器重启后仍然存在的数据,并驻留在容器本身附近。
有关更多详细信息,请参阅 容器类参考 和 低级 Durable Object 容器 API。
当请求进入 Cloudflare 时,您的 Worker 的 fetch 处理程序 将被调用。这是处理传入请求的代码。示例代码中的 fetch 处理程序在不同的路由上以两种方式启动容器:
-
向
/container/发出请求会将每个路径的请求传递给新容器。这是通过启动新的容器实例来完成的。您可能会注意到,对新路径的第一个请求比随后的请求耗时更长,这是因为新容器正在启动。if (pathname.startsWith("/container")) { const container = env.MY_CONTAINER.getByName(pathname); return await container.fetch(request); } -
向
/lb发出请求将跨多个容器对请求进行负载均衡。这里使用了一个简单的getRandom辅助方法,该方法从设定的数量(在此示例中为 3 个)中随机选取一个 ID,然后路由到该容器实例。您可以将其替换为您选择实现的任何路由或负载均衡逻辑:if (pathname.startsWith("/lb")) { const container = await getRandom(env.MY_CONTAINER, 3); return await container.fetch(request); }
这允许多种使用容器的方式:
- 如果您只是想将请求发送到许多无状态且可互换的容器,则应使用负载均衡。
- 如果您有状态服务或需要单独寻址的容器,则应请求特定的容器实例。
- 如果您正在运行短期作业,希望对容器生命周期进行细粒度控制,希望参数化容器入口点或环境变量,或者希望将多个容器调用链接在一起,则应请求特定的容器实例。
容器仪表板 ↗向您显示有关您的容器的有用信息,包括:
- 状态和运行状况
- 指标
- 日志
启动 Worker 后,通过在仪表板侧边栏中选择 Workers & Pages > Containers(容器) 前往容器仪表板。
要做更多: