Monitor container lifecycle
import { TypeScriptExample, WranglerConfig, PackageManagers, Render, } from "~/components";
Log when a Container becomes ready, exits successfully, or fails. The Durable Object starts a named image and uses monitor() to observe its exit.
Configure the Worker
name = "container-lifecycle"
main = "src/index.ts"
upload_source_maps = true
compatibility_date = "$today"
[observability]
enabled = true
[[containers]]
class_name = "MyContainer"
scheduling_policy = "durable_object"
[containers.images.base]
dockerfile = "./Dockerfile"
[[durable_objects.bindings]]
name = "MY_CONTAINER"
class_name = "MyContainer"
[exports.MyContainer]
type = "durable-object"
storage = "sqlite"
Observe readiness and exit
start() initiates startup. The Worker waits for a successful /health response before forwarding traffic. Concurrent requests share that readiness check, and a failed check can be retried by the next request.
import { DurableObject } from "cloudflare:workers";
interface Env {
MY_CONTAINER: DurableObjectNamespace<MyContainer>;
}
const INACTIVITY_TIMEOUT_MS = 5 * 60 * 1000;
export class MyContainer extends DurableObject<Env> {
private starting: Promise<void> | undefined;
private monitoring: Promise<void> | undefined;
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
const container = ctx.container!;
if (container.running) {
void ctx.blockConcurrencyWhile(() =>
container.setInactivityTimeout(INACTIVITY_TIMEOUT_MS),
);
this.observeExit();
}
}
async fetch(request: Request): Promise<Response> {
// Concurrent requests share startup and readiness checks.
this.starting ??= this.startAndWaitForPort().finally(() => {
this.starting = undefined;
});
await this.starting;
const url = new URL(request.url);
url.protocol = "http:";
url.host = "container";
const forwarded = new Request(url, request);
forwarded.headers.delete("host");
return this.ctx.container!.getTcpPort(8080).fetch(forwarded);
}
private async startAndWaitForPort(): Promise<void> {
const container = this.ctx.container!;
if (!container.running) {
container.start({
image: container.images.base,
instance: "lite",
enableInternet: false,
});
}
this.observeExit();
await container.setInactivityTimeout(INACTIVITY_TIMEOUT_MS);
const port = container.getTcpPort(8080);
let lastError: unknown;
for (let attempt = 0; attempt < 100; attempt++) {
try {
const response = await port.fetch("http://container/health", {
signal: AbortSignal.timeout(1000),
});
await response.body?.cancel();
if (!response.ok) {
throw new Error(`Health check returned ${response.status}`);
}
console.log("Container is ready");
return;
} catch (error) {
lastError = error;
await scheduler.wait(200);
}
}
throw new Error("Container did not become ready on port 8080", {
cause: lastError,
});
}
private observeExit(): void {
if (this.monitoring) return;
this.monitoring = this.ctx
.container!.monitor()
.then(() => console.log("Container exited successfully"))
.catch((error: unknown) => console.error("Container failed:", error))
.finally(() => {
this.monitoring = undefined;
});
this.ctx.waitUntil(this.monitoring);
}
}
export default {
fetch(request: Request, env: Env): Promise<Response> {
return env.MY_CONTAINER.getByName("demo").fetch(request);
},
} satisfies ExportedHandler<Env>;
monitor() resolves without a value after a successful exit. It rejects for failures, including nonzero process exits. The constructor restores monitoring and the inactivity timeout when the Durable Object restarts with a running Container.
A pending monitor can keep the Durable Object active for up to 15 minutes. The five-minute inactivity timeout starts after the Durable Object becomes inactive, not five minutes after the last HTTP request. Monitoring is not a durable event subscription across restarts. Refer to monitor().
Run the example
Start a Docker-compatible engine. Install Wrangler in your project, using version 4.136.0 or later for local development.
Visit http://localhost:8787/ and inspect the Worker logs for readiness. Container completion or failure is logged when observed by monitor().
谢谢你的反馈!