INTEGRITY Dokumentace

Rozhraní Container

Container třída ↗ z @cloudflare/containers ↗ je nejběžnější způsob, jak z Workeru pracovat s instancemi kontejnerů.

Container rozšiřuje DurableObject. Durable Object řídí směrování, perzistentní stav a lifecycle hooky, zatímco proces kontejneru spouští váš obraz uvnitř Linux VM. Protože vaše podtřída je Durable Object, máte přístup k celému Durable Object API, včetně this.ctx.storage pro trvalé úložiště založené na SQLite a this.ctx.id pro jedinečný identifikátor instance. Pro trvalé uložení stavu, který má přežít restarty kontejneru, jako je konfigurace, uživatelská data nebo výsledky úloh, použijte úložiště Durable Object.

npm i @cloudflare/containers

Poté definujte třídu, která rozšiřuje Container a nastavte sdílené vlastnosti na třídě:

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

export class SandboxContainer extends Container {
	defaultPort = 8080;
	requiredPorts = [8080, 9222];
	sleepAfter = "5m";
	envVars = {
		NODE_ENV: "production",
		LOG_LEVEL: "info",
	};
	entrypoint = ["npm", "run", "start"];
	enableInternet = false;
	pingEndpoint = "localhost/ready";
}

export default {
	async fetch(request, env) {
		return getContainer(env.SANDBOX_CONTAINER, "workspace-123").fetch(request);
	},
};
import { Container, getContainer } from "@cloudflare/containers";

export class SandboxContainer extends Container {
	defaultPort = 8080;
	requiredPorts = [8080, 9222];
	sleepAfter = "5m";
	envVars = {
		NODE_ENV: "production",
		LOG_LEVEL: "info",
	};
	entrypoint = ["npm", "run", "start"];
	enableInternet = false;
	pingEndpoint = "localhost/ready";
}

export default {
	async fetch(request: Request, env) {
		return getContainer(env.SANDBOX_CONTAINER, "workspace-123").fetch(request);
	},
};

Container třída rozšiřuje DurableObject, takže všechny Durable Object funkcionalita je k dispozici, včetně Úložiště SQLite, alarmy, a Metody RPC. Disk kontejneru je ve výchozím nastavení dočasný, ale úložiště Durable Object přetrvává i po restartu kontejneru.

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

export class MyContainer extends Container {
	defaultPort = 8080;

	async runAndPersist() {
		const res = await this.containerFetch("/run-task");
		const body = await res.text();
		this.ctx.storage.sql.exec(
			"INSERT OR REPLACE INTO results (value) VALUES (?)",
			body,
		);
		return body;
	}
}
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	defaultPort = 8080;

	async runAndPersist() {
		const res = await this.containerFetch("/run-task");
		const body = await res.text();
		this.ctx.storage.sql.exec(
			"INSERT OR REPLACE INTO results (value) VALUES (?)",
			body,
		);
		return body;
	}
}

Spouštění příkazů

Použijte this.ctx.container.exec() ke spuštění dalšího procesu uvnitř běžícího Containeru. Přečtěte si Spouštění příkazů pro příklady spouštění, streamování, výstupu a řízení procesů.

Vlastnosti

Nastavte je jako pole třídy ve své podtřídě. Platí pro každou instanci kontejneru.

Hooky životního cyklu

Tyto metody přepište, pokud chcete při změně stavu kontejneru spustit kód Workeru. Podrobnosti najdete v příklad status hooks pro úplný příklad.

onStart

Spustí kód Workeru po spuštění kontejneru.

onStart(): void | Promise<void>

Vrací: void | Promise<void>. Vyřeší se po dokončení veškeré startovací logiky.

Použijte to k logování startu, naplnění počátečních dat nebo naplánování opakujících se úloh pomocí schedule().

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

export class MyContainer extends Container {
	defaultPort = 8080;

	async onStart() {
		await this.containerFetch("http://localhost/bootstrap", {
			method: "POST",
		});
	}
}
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	defaultPort = 8080;

    override async onStart() {
    	await this.containerFetch("http://localhost/bootstrap", {
    		method: "POST",
    	});
    }

}

onStop

Spustí kód Workeru po ukončení procesu kontejneru.

onStop(params: StopParams): void | Promise<void>

Parametry:

Vrací: void | Promise<void>. Vyřeší se po dokončení vaší logiky pro ukončení.

Použijte to k logování, upozorňování nebo restartování kontejneru.

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

export class MyContainer extends Container {
	onStop({ exitCode, reason }) {
		console.log("Container stopped", { exitCode, reason });
	}
}
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	override onStop({ exitCode, reason }) {
		console.log("Container stopped", { exitCode, reason });
	}
}

onError

Zpracování chyb při spuštění a kontrole portu.

onError(error: unknown): any

Parametry:

Vrací: any. Výchozí implementace chybu zaznamená do logu a znovu ji vyvolá.

Tuto metodu přepište, pokud chcete potlačit chyby, oznámit je externí službě nebo se pokusit o restart.

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

export class MyContainer extends Container {
	onError(error) {
		console.error("Container failed to start", error);
		throw error;
	}
}
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	override onError(error: unknown) {
		console.error("Container failed to start", error);
		throw error;
	}
}

onActivityExpired

Spustí kód Workeru, když vyprší časovač sleepAfter časovač vyprší.

onActivityExpired(): Promise<void>

Vrací: Promise<void>. Vyřeší se po dokončení vaší logiky pro dobu nečinnosti.

Volá se, když sleepAfter časový limit vyprší bez příchozích požadavků. Výchozí implementace volá stop().

Pokud tuto metodu přepíšete, aniž byste kontejner zastavili, časovač se obnoví a hook se znovu spustí při dalším vypršení.

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

export class MyContainer extends Container {
	sleepAfter = "2m";

	async onActivityExpired() {
		const state = await this.getState();
		console.log("Container is idle, stopping it now", state.status);

		await this.stop();
	}
}
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	sleepAfter = "2m";

    override async onActivityExpired() {
    	const state = await this.getState();
    	console.log("Container is idle, stopping it now", state.status);

    	await this.stop();
    }

}

Metody požadavků

fetch

Zpracování příchozích požadavků HTTP nebo WebSocket.

fetch(request: Request): Promise<Response>

Parametry:

Vrací: Promise<Response> z kontejneru nebo z vlastní směrovací logiky.

Standardně fetch přeposílá požadavek do procesu kontejneru na defaultPort. Kontejner se automaticky spustí, pokud ještě neběží.

Přepsat fetch pokud potřebujete před předáním kontejneru směrovací logiku, ověřování nebo jiný middleware. Uvnitř přepsané metody zavolejte this.containerFetch() místo this.fetch() abyste se vyhnuli nekonečné rekurzi:

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

export class MyContainer extends Container {
	defaultPort = 8080;

	async fetch(request) {
		const url = new URL(request.url);

		if (url.pathname === "/health") {
			return new Response("ok");
		}

		return this.containerFetch(request);
	}
}
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	defaultPort = 8080;

    override async fetch(request: Request): Promise<Response> {
    	const url = new URL(request.url);

    	if (url.pathname === "/health") {
    		return new Response("ok");
    	}

    	return this.containerFetch(request);
    }

}

fetch je jediná metoda, která podporuje proxování WebSocket. Viz Příklad WebSocketu pro úplný příklad.

containerFetch

Odešle HTTP požadavek přímo procesu kontejneru. Uživatelé by obecně měli raději používat fetch pokud nebyla přepsána.

containerFetch(request: Request, port?: number): Promise<Response>
containerFetch(url: string | URL, init?: RequestInit, port?: number): Promise<Response>

Parametry:

Vrací: Promise<Response> z kontejneru.

Toto dělá výchozí fetch() implementace volá interně a je to metoda, kterou byste měli zavolat i vy uvnitř přepsané fetch() metodu, abyste se vyhnuli nekonečné rekurzi. Metoda také přijímá standardní signaturu ve stylu fetch s řetězcem URL a RequestInit, což se hodí, pokud vytváříte nový požadavek namísto předávání toho stávajícího.

Nepodporuje WebSockets. Použijte fetch() hodnotou switchPort() pro ně.

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

export class MyContainer extends Container {
	defaultPort = 8080;

	async fetch(request) {
		const url = new URL(request.url);

		if (url.pathname === "/metrics") {
			return this.containerFetch(
				"http://localhost/internal/metrics",
				{
					headers: {
						authorization: request.headers.get("authorization") ?? "",
					},
				},
				9090,
			);
		}

		return this.containerFetch(request);
	}
}
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	defaultPort = 8080;

    override async fetch(request: Request): Promise<Response> {
    	const url = new URL(request.url);

    	if (url.pathname === "/metrics") {
    		return this.containerFetch(
    			"http://localhost/internal/metrics",
    			{
    				headers: {
    					authorization: request.headers.get("authorization") ?? "",
    				},
    			},
    			9090,
    		);
    	}

    	return this.containerFetch(request);
    }

}

Spuštění a zastavení

Ve většině případů nemusíte tyto metody volat přímo. fetch() a containerFetch() spustí kontejner automaticky. Volejte je explicitně, pokud potřebujete kontejner předehřát, spustit úlohu podle plánu nebo řídit životní cyklus z hooku životního cyklu.

startAndWaitForPorts

Spustí kontejner a počká, až cílové porty začnou přijímat spojení.

startAndWaitForPorts(args?: StartAndWaitForPortsOptions): Promise<void>
startAndWaitForPorts(
  ports?: number | number[],
  cancellationOptions?: CancellationOptions,
  startOptions?: ContainerStartConfigOptions,
): Promise<void>

Parametry:

Vrací: Promise<void>. Vyřeší se poté, co jsou cílové porty připravené a onStart() proběhl.

Toto je nejbezpečnější způsob, jak kontejner explicitně spustit, pokud potřebujete mít jistotu, že je připravený ještě před odesláním provozu.

Tato metoda podporuje také poziční ports, cancellationOptions, a startOptions argumenty, objektová podoba se ale čte snáze.

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

export default {
	async scheduled(_event, env) {
		const container = getContainer(env.API_CONTAINER, "tenant-42");

		await container.startAndWaitForPorts({
			ports: [8080, 9222],
			startOptions: {
				envVars: {
					API_KEY: env.API_KEY,
					TENANT_ID: "tenant-42",
				},
			},
			cancellationOptions: {
				portReadyTimeoutMS: 30_000,
			},
		});
	},
};
import { getContainer } from "@cloudflare/containers";

export default {
	async scheduled(_event, env) {
		const container = getContainer(env.API_CONTAINER, "tenant-42");

    	await container.startAndWaitForPorts({
    		ports: [8080, 9222],
    		startOptions: {
    			envVars: {
    				API_KEY: env.API_KEY,
    				TENANT_ID: "tenant-42",
    			},
    		},
    		cancellationOptions: {
    			portReadyTimeoutMS: 30_000,
    		},
    	});
    },

};

Viz příklad proměnných prostředí a secrets pro úplný příklad.

start

Spustí kontejner, aniž by čekal, až budou všechny porty připravené.

start(startOptions?: ContainerStartConfigOptions, waitOptions?: WaitOptions): Promise<void>

Parametry:

Vrací: Promise<void>. Vyřeší se poté, co se pokus o spuštění zdaří a onStart() proběhl.

Použijte to v případě, že kontejner nevystavuje žádné porty, například u dávkové úlohy nebo cron úlohy, nebo pokud chcete připravenost řídit sami pomocí waitForPort(). Pokud potřebujete počkat, až budou připraveny všechny porty, použijte startAndWaitForPorts() místo toho.

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

export default {
	async scheduled(_event, env) {
		const container = getContainer(env.JOB_CONTAINER, "nightly-report");

		await container.start({
			entrypoint: ["node", "scripts/nightly-report.js"],
			envVars: {
				REPORT_DATE: new Date().toISOString(),
			},
			enableInternet: false,
		});
	},
};
import { getContainer } from "@cloudflare/containers";

export default {
	async scheduled(_event, env) {
		const container = getContainer(env.JOB_CONTAINER, "nightly-report");

    	await container.start({
    		entrypoint: ["node", "scripts/nightly-report.js"],
    		envVars: {
    			REPORT_DATE: new Date().toISOString(),
    		},
    		enableInternet: false,
    	});
    },

};

Viz příklad cron pro úplný příklad.

waitForPort

Opakovaně kontrolujte jeden port, dokud nezačne přijímat připojení.

waitForPort(waitOptions: WaitOptions): Promise<number>

Parametry:

Vrací: Promise<number>. Číselná návratová hodnota je užitečná zejména tehdy, když koordinujete vlastní logiku připravenosti napříč více čekáními.

Vyvolá výjimku, pokud se port nezpřístupní během limitu počtu pokusů. Použijte to po start() pokud potřebujete kontrolovat více portů nezávisle na sobě nebo ve stanoveném pořadí.

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

export class MyContainer extends Container {
	async warmInspector() {
		await this.start();

		const retryCount = await this.waitForPort({
			portToCheck: 9222,
			retries: 20,
			waitInterval: 500,
		});

		console.log("Inspector port became ready:", retryCount);
	}
}
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	async warmInspector() {
		await this.start();

    	const retryCount = await this.waitForPort({
    		portToCheck: 9222,
    		retries: 20,
    		waitInterval: 500,
    	});

    	console.log("Inspector port became ready:", retryCount);
    }

}

stop

Odešle signál procesu kontejneru.

stop(signal?: 'SIGTERM' | 'SIGINT' | 'SIGKILL' | number): Promise<void>

Parametry:

Vrací: Promise<void>. Vyřeší se poté, co je signál odeslán a dokončí se čekající zpracování zastavení.

Výchozí hodnota je SIGTERM, což dává procesu možnost řízeně se ukončit. Aktivuje onStop().

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

export class MyContainer extends Container {
	defaultPort = 8080;

	async fetch(request) {
		if (new URL(request.url).pathname === "/admin/stop") {
			await this.stop();
			return new Response("Container is stopping");
		}

		return this.containerFetch(request);
	}
}
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	defaultPort = 8080;

    override async fetch(request: Request): Promise<Response> {
    	if (new URL(request.url).pathname === "/admin/stop") {
    		await this.stop();
    		return new Response("Container is stopping");
    	}

    	return this.containerFetch(request);
    }

}

destroy

Okamžitě ukončí proces kontejneru.

destroy(): Promise<void>

Vrací: Promise<void>. Vyřeší se poté, co runtime kontejner zničí.

Tím se odešle SIGKILL. Použijte jej, pokud potřebujete kontejner odstranit okamžitě a nemůžete čekat na řízené ukončení. Aktivuje onStop().

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

export class MyContainer extends Container {
	defaultPort = 8080;

	async fetch(request) {
		if (new URL(request.url).pathname === "/admin/destroy") {
			await this.destroy();
			return new Response("Container destroyed");
		}

		return this.containerFetch(request);
	}
}
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	defaultPort = 8080;

    override async fetch(request: Request): Promise<Response> {
    	if (new URL(request.url).pathname === "/admin/destroy") {
    		await this.destroy();
    		return new Response("Container destroyed");
    	}

    	return this.containerFetch(request);
    }

}

Stav a monitorování

getState

Přečtěte aktuální stav kontejneru.

getState(): Promise<State>

Vrací: Promise<State> s:

running znamená, že se kontejner spouští a ještě neprošel kontrolou stavu. healthy znamená, že běží a přijímá požadavky.

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

export class MyContainer extends Container {
	async logState() {
		const state = await this.getState();

		if (state.status === "stopped_with_code") {
			console.error("Container exited with code", state.exitCode);
			return;
		}

		console.log("Container status:", state.status);
	}
}
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	async logState() {
		const state = await this.getState();

    	if (state.status === "stopped_with_code") {
    		console.error("Container exited with code", state.exitCode);
    		return;
    	}

    	console.log("Container status:", state.status);
    }

}

renewActivityTimeout

Resetujte sleepAfter časovač.

renewActivityTimeout(): void

Vrací: void.

Příchozí požadavky automaticky resetují časovač. Tuto metodu volejte ručně z úloh na pozadí, například z naplánované úlohy nebo dlouho běžící operace, které se mají počítat jako aktivita a zabránit uspání kontejneru.

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

export class MyContainer extends Container {
	defaultPort = 8080;

	async processJobs(jobIds) {
		for (const jobId of jobIds) {
			this.renewActivityTimeout();

			await this.containerFetch(`http://localhost/jobs/${jobId}`, {
				method: "POST",
			});
		}
	}
}
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	defaultPort = 8080;

    async processJobs(jobIds: string[]) {
    	for (const jobId of jobIds) {
    		this.renewActivityTimeout();

    		await this.containerFetch(`http://localhost/jobs/${jobId}`, {
    			method: "POST",
    		});
    	}
    }

}

Plánování

schedule

Naplánuje metodu třídy ke spuštění později.

schedule<T>(when: Date | number, callback: string, payload?: T): Promise<Schedule<T>>

Parametry:

Vrací: Promise<Schedule<T>> s:

Nepřepisujte alarm() ↗ přímo. Container třída používá alarm handler ke správě životního cyklu kontejneru, proto použijte schedule() místo toho.

Následující příklad naplánuje opakovanou zprávu o stavu už od spuštění kontejneru:

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

export class MyContainer extends Container {
	defaultPort = 8080;

	async onStart() {
		await this.schedule(60, "healthReport");
	}

	async healthReport() {
		const state = await this.getState();
		console.log("Container status:", state.status);
		await this.schedule(60, "healthReport");
	}
}
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
	defaultPort = 8080;

    override async onStart() {
    	await this.schedule(60, "healthReport");
    }

    async healthReport() {
    	const state = await this.getState();
    	console.log("Container status:", state.status);
    	await this.schedule(60, "healthReport");
    }

}

Zachytávání odchozího provozu

Zachytávání odchozího provozu umožňuje zachytávat, simulovat nebo blokovat HTTP požadavky, které kontejner odesílá na externí hostitele. To se hodí pro sandboxing, testování nebo směrování odchozího provozu přes kód Workeru jako proxy.

import {
	Container,
	ContainerProxy,
	getContainer,
} from "@cloudflare/containers";

export class MyContainer extends Container {
	defaultPort = 8080;
	enableInternet = true;

	static outboundByHost = {
		"blocked.example.com": () => {
			return new Response("Blocked", { status: 403 });
		},
	};

	static outbound = async (request, _env, ctx) => {
		console.log(`[${ctx.containerId}] outbound:`, request.url);
		return fetch(request);
	};
}

export { ContainerProxy };

export default {
	async fetch(request, env) {
		return getContainer(env.MY_CONTAINER).fetch(request);
	},
};
import {
	Container,
	ContainerProxy,
	getContainer,
} from "@cloudflare/containers";

export class MyContainer extends Container {
	defaultPort = 8080;
	enableInternet = true;

    static outboundByHost = {
    	"blocked.example.com": () => {
    		return new Response("Blocked", { status: 403 });
    	},
    };

    static outbound = async (request, _env, ctx) => {
    	console.log(`[${ctx.containerId}] outbound:`, request.url);
    	return fetch(request);
    };

}

export { ContainerProxy };

export default {
	async fetch(request: Request, env) {
		return getContainer(env.MY_CONTAINER).fetch(request);
	},
};

Další informace najdete v tématu Zpracování odchozího provozu.

Pomocné funkce

Tyto funkce jsou exportovány společně s Container třída z @cloudflare/containers.

getContainer

Získejte stub pro pojmenovanou instanci kontejneru.

getContainer<T>(binding: DurableObjectNamespace<T>, name?: string): DurableObjectStub<T>

Parametry:

Vrací: DurableObjectStub<T> pro pojmenovanou instanci kontejneru.

Použijte to, pokud chcete mít jeden kontejner na logickou entitu, například uživatelskou relaci, dokument nebo herní místnost, identifikovanou stabilním názvem.

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

export default {
	async fetch(request, env) {
		const { sessionId } = await request.json();
		return getContainer(env.MY_CONTAINER, sessionId).fetch(request);
	},
};
import { getContainer } from "@cloudflare/containers";

export default {
	async fetch(request: Request, env) {
		const { sessionId } = await request.json();
		return getContainer(env.MY_CONTAINER, sessionId).fetch(request);
	},
};

getRandom

Získejte stub pro náhodně vybranou instanci kontejneru.

getRandom<T>(binding: DurableObjectNamespace<T>, instances?: number): Promise<DurableObjectStub<T>>

Parametry:

Vrací: Promise<DurableObjectStub<T>> pro náhodně vybranou instanci.

Použijte to pro bezstavové úlohy, kde může každý kontejner zpracovat jakýkoli požadavek a vy chcete rozložit zátěž mezi více instancí.

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

export default {
	async fetch(request, env) {
		const container = await getRandom(env.WORKER_POOL, 5);
		return container.fetch(request);
	},
};
import { getRandom } from "@cloudflare/containers";

export default {
	async fetch(request: Request, env) {
		const container = await getRandom(env.WORKER_POOL, 5);
		return container.fetch(request);
	},
};

Viz příklad bezstavových instancí pro úplný příklad.

switchPort

Cílení na jiný port kontejneru při zachování používání fetch().

switchPort(request: Request, port: number): Request

Parametry:

Vrací: Request kopii s nastaveným cílovým portem.

Použijte to, pokud potřebujete cílit na konkrétní port a zároveň potřebujete podporu WebSocketu. Pokud WebSockety nepotřebujete, předejte port přímo containerFetch() místo toho.

import { getContainer, switchPort } from "@cloudflare/containers";

export default {
	async fetch(request, env) {
		const container = getContainer(env.MY_CONTAINER);
		return container.fetch(switchPort(request, 9090));
	},
};
import { getContainer, switchPort } from "@cloudflare/containers";

export default {
	async fetch(request: Request, env) {
		const container = getContainer(env.MY_CONTAINER);
		return container.fetch(switchPort(request, 9090));
	},
};