Skip to content

Commit e3a824e

Browse files
mikenomitchkodster28nikitassharmairvinebroque
authored andcommitted
Some minor tweaks (cloudflare#23150)
* Adds Containers docs * A variety of tweaks to containers docs * Fixes bad links * replace icon * remove extra file * Apply suggestions from code review Various small fixes Co-authored-by: Nikita Sharma <[email protected]> * Removes unnecessary pre-req and wrong TCP statement * Apply suggestions from code review Co-authored-by: Brendan Irvine-Broque <[email protected]> * Apply suggestions from code review * Update pricing docs, make sure to clarify what is part of Workers Paid plan * Various tweaks --------- Co-authored-by: kodster28 <[email protected]> Co-authored-by: Nikita Sharma <[email protected]> Co-authored-by: Brendan Irvine-Broque <[email protected]>
1 parent f87948c commit e3a824e

28 files changed

+2140
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
pcx_content_type: reference
3+
title: Architecture
4+
sidebar:
5+
order: 9
6+
---
7+
8+
This page describes the architecture of Cloudflare Containers.
9+
10+
## How and where containers run
11+
12+
After you deploy a Worker that uses a Container, your image is uploaded to
13+
[Cloudflare's Registry](/containers/image-management) and distributed globally to Cloudflare's Network.
14+
Cloudflare will pre-schedule instances and pre-fetch images across the globe to ensure quick start
15+
times when scaling up the number of concurrent container instances. This allows you to call
16+
`env.YOUR_CONTAINER.get(id)` and get a new instance quickly without worrying
17+
about the underlying scaling.
18+
19+
When a request is made to start a new container instance, the nearest location
20+
with a pre-fetched image is selected. Subsequent requests to the same instance,
21+
regardless of where they originate, will be routed to this location as long as
22+
the instance stays alive.
23+
24+
Starting additional container instances will use other locations with pre-fetched images,
25+
and Cloudflare will automatically begin prepping additional machines behind the scenes
26+
for additional scaling and quick cold starts. Because there are a finite number pre-warmed
27+
locations, some container instances may be started in locations that are farther away from
28+
the end-user. This is done to ensure that the container instance starts quickly. You are
29+
only charged for actively running instances and not for any unused pre-warmed images.
30+
31+
Each container instance runs inside its own VM, which provides strong
32+
isolation from other workloads running on Cloudflare's network. Containers
33+
should be built for the `linux/amd64` architecture, and should stay within
34+
[size limits](/containers/platform-details/#limits). Logging, metrics collection, and
35+
networking are automatically set up on each container.
36+
37+
## Life of a Container Request
38+
39+
When a request is made to any Worker, including one with an associated Container, it is generally handled
40+
by a datacenter in a location with the best latency between itself and the requesting user.
41+
A different datacenter may be selected to optimize overall latency, if [Smart Placement](/workers/configuration/smart-placement/)
42+
is on, or if the nearest location is under heavy load.
43+
44+
When a request is made to a Container instance, it is sent through a Durable Object, which
45+
can be defined by either using a `DurableObject` or the [`Container` class](/containers/container-package), which
46+
extends Durable Objects with Container-specific APIs and helpers. We recommend using `Container`, see
47+
the [`Container` class documentation](/containers/container-package) for more details.
48+
49+
Each Durable Object is a globally routable isolate that can execute code and store state. This allows
50+
developers to easily address and route to specific container instances (no matter where they are placed),
51+
define and run hooks on container status changes, execute recurring checks on the instance, and store persistent
52+
state associated with each instance.
53+
54+
As mentioned above, when a container instance starts, it is launched in the nearest pre-warmed location. This means that
55+
code in a container is usually executed in a different location than the one handling the Workers request.
56+
57+
:::note
58+
Currently, Durable Objects may be co-located with their associated Container instance, but often are not.
59+
60+
Cloudflare is currently working on expanding the number of locations in which a Durable Object can run,
61+
which will allow container instances to always run in the same location as their Durable Object.
62+
:::
63+
64+
Because all Container requests are passed through a Worker, end-users cannot make TCP or
65+
UDP requests to a Container instance. If you have a use case that requires inbound TCP
66+
or UDP from an end-user, please [let us know](https://forms.gle/AGSq54VvUje6kmKu8).
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
---
2+
pcx_content_type: reference
3+
title: Beta Info & Roadmap
4+
sidebar:
5+
order: 2
6+
---
7+
8+
Currently, Containers are in beta. There are several changes we plan to make prior to GA:
9+
10+
## Upcoming Changes and Known Gaps
11+
12+
### Limits
13+
14+
Container limits will be raised in the future. We plan to increase
15+
both maximum instance size and maximum number of instances in an account.
16+
17+
See the [Limits documentation](/containers/platform-details/#limits) for more information.
18+
19+
### Autoscaling and load balancing
20+
21+
Currently, Containers are not autoscaled or load balanced. Containers can be scaled manually
22+
by calling `get()` on their binding with a unique ID.
23+
24+
We plan to add official support for utilization-based autoscaling and latency-aware load balancing
25+
in the future.
26+
27+
See the [Autoscaling documentation](/containers/scaling-and-routing) for more information.
28+
29+
### Reduction of log noise
30+
31+
Currently, the `Container` class uses Durable Object alarms to help manage Container shutdown. This
32+
results in unnecessary log noise in the Worker logs. You can filter these logs out in the dashboard
33+
by adding a Query, but this is not ideal.
34+
35+
We plan to automatically reduce log noise in the future.
36+
37+
### Dashboard Updates
38+
39+
The dashboard will be updated to show:
40+
41+
- the status of Container rollouts
42+
- links from Workers to their associated Containers
43+
44+
### Co-locating Durable Objects and Containers
45+
46+
Currently, Durable Objects are not co-located with their associated Container. When requesting a container,
47+
the Durable Object will find one close to it, but not on the same machine.
48+
49+
We plan to co-locate Durable Objects with their Container in the future.
50+
51+
### More advanced Container placement
52+
53+
We currently prewarm servers across our global network with container images to ensure quick start times.
54+
There are times in which you may request a new container and it will be started in a location that
55+
farther from the end user than is desired. We are optimizing this process to ensure that this happens
56+
as little as possible, but it may still occur.
57+
58+
### Atomic code updates across Workers and Containers
59+
60+
When deploying a Container with `wrangler deploy`, the Worker code will be immediately
61+
updated while the Container code will slowly be updated using a rolling deploy.
62+
63+
This means that you must ensure Worker code is backwards compatible with the old Container code.
64+
65+
In the future, Worker code in the Durable Object will only update when associated Container code updates.
66+
67+
## Feedback wanted
68+
69+
There are several areas where we wish to gather feedback from users:
70+
71+
- Do you want to integrate Containers with any other Cloudflare services? If so, which ones and how?
72+
- Do you want more ways to interact with a Container via Workers? If so, how?
73+
- Do you need different mechanisms for routing requests to containers?
74+
- Do you need different mechanisms for scaling containers? (see [scaling documentation](/containers/scaling-and-routing) for information on autoscaling plans)
75+
76+
At any point during the Beta, feel free to [give feedback using this form](https://forms.gle/CscdaEGuw5Hb6H2s7).
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
pcx_content_type: navigation
3+
title: Container Package
4+
sidebar:
5+
order: 8
6+
---
7+
8+
When writing code that interacts with a container instance, you can either use a
9+
Durable Object directly or use the [`Container` module](https://github.com/cloudflare/containers)
10+
importable from [`@cloudflare/containers`](https://www.npmjs.com/package/@cloudflare/containers).
11+
12+
```javascript
13+
import { Container } from "@cloudflare/containers";
14+
15+
class MyContainer extends Container {
16+
defaultPort = 8080;
17+
sleepAfter = "5m";
18+
}
19+
```
20+
21+
We recommend using the `Container` class for most use cases.
22+
23+
Install it with `npm install @cloudflare/containers`.
24+
25+
The `Container` class extends `DurableObject` so all Durable Object functionality is available.
26+
It also provides additional functionality and a nice interface for common container behaviors,
27+
such as:
28+
29+
- sleeping instances after an inactivity timeout
30+
- making requests to specific ports
31+
- running status hooks on startup, stop, or error
32+
- awaiting specific ports before making requests
33+
- setting environment variables and secrets
34+
35+
See the [Containers GitHub repo](https://github.com/cloudflare/containers) for more details
36+
and the complete API.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
pcx_content_type: navigation
3+
title: Durable Object Interface
4+
external_link: /durable-objects/api/container/
5+
sidebar:
6+
order: 81
7+
---
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
---
2+
type: example
3+
summary: A simple frontend app with a containerized backend
4+
pcx_content_type: example
5+
title: Static Frontend, Container Backend
6+
sidebar:
7+
order: 3
8+
description: A simple frontend app with a containerized backend
9+
---
10+
11+
import { WranglerConfig, Details } from "~/components";
12+
13+
A common pattern is to serve a static frontend application (e.g., React, Vue, Svelte) using Static Assets,
14+
then pass backend requests to a containerized backend application.
15+
16+
In this example, we'll show an example using a simple `index.html` file served as a static asset,
17+
but you can select from one of many frontend frameworks. See our [Workers framework examples](/workers/framework-guides/web-apps/) for more information.
18+
19+
For a full example, see the [Static Frontend + Container Backend Template](https://github.com/mikenomitch/static-frontend-container-backend).
20+
21+
## Configure Static Assets and a Container
22+
23+
<WranglerConfig>
24+
```json
25+
{
26+
"name": "cron-container",
27+
"main": "src/index.ts",
28+
"assets": {
29+
"directory": "./dist",
30+
"binding": "ASSETS"
31+
},
32+
"containers": [
33+
{
34+
"class_name": "Backend",
35+
"image": "./Dockerfile",
36+
}
37+
],
38+
"durable_objects": {
39+
"bindings": [
40+
{
41+
"class_name": "Backend",
42+
"name": "BACKEND"
43+
}
44+
]
45+
},
46+
"migrations": [
47+
{
48+
"new_sqlite_classes": [
49+
"Backend"
50+
],
51+
"tag": "v1"
52+
}
53+
]
54+
}
55+
```
56+
</WranglerConfig>
57+
58+
## Add a simple index.html file to serve
59+
60+
Create a simple `index.html` file in the `./dist` directory.
61+
62+
<Details header="index.html">
63+
```html
64+
<!DOCTYPE html>
65+
<html lang="en">
66+
67+
<head>
68+
<meta charset="UTF-8">
69+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
70+
<title>Widgets</title>
71+
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.13.3/cdn.min.js"></script>
72+
</head>
73+
74+
<body>
75+
<div x-data="widgets()" x-init="fetchWidgets()">
76+
<h1>Widgets</h1>
77+
<div x-show="loading">Loading...</div>
78+
<div x-show="error" x-text="error" style="color: red;"></div>
79+
<ul x-show="!loading && !error">
80+
<template x-for="widget in widgets" :key="widget.id">
81+
<li>
82+
<span x-text="widget.name"></span> - (ID: <span x-text="widget.id"></span>)
83+
</li>
84+
</template>
85+
</ul>
86+
87+
<div x-show="!loading && !error && widgets.length === 0">
88+
No widgets found.
89+
</div>
90+
91+
</div>
92+
93+
<script>
94+
function widgets() {
95+
return {
96+
widgets: [],
97+
loading: false,
98+
error: null,
99+
100+
async fetchWidgets() {
101+
this.loading = true;
102+
this.error = null;
103+
104+
try {
105+
const response = await fetch('/api/widgets');
106+
if (!response.ok) {
107+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
108+
}
109+
this.widgets = await response.json();
110+
} catch (err) {
111+
this.error = err.message;
112+
} finally {
113+
this.loading = false;
114+
}
115+
}
116+
}
117+
}
118+
</script>
119+
120+
</body>
121+
122+
</html>
123+
```
124+
</Details>
125+
126+
In this example, we are using [Alpine.js](https://alpinejs.dev/) to fetch a list of widgets from `/api/widgets`.
127+
128+
This is meant to be a very simple example, but you can get significantly more complex.
129+
See [examples of Workers integrating with frontend frameworks](/workers/framework-guides/web-apps/) for more information.
130+
131+
## Define a Worker
132+
133+
Your Worker needs to be able to both serve static assets and route requests to the containerized backend.
134+
135+
In this case, we will pass requests to one of three container instances if the route starts with `/api`,
136+
and all other requests will be served as static assets.
137+
138+
```javascript
139+
import { Container, getRandom } from "@cloudflare/containers";
140+
141+
const INSTANCE_COUNT = 3;
142+
143+
class Backend extends Container {
144+
defaultPort = 8080; // pass requests to port 8080 in the container
145+
sleepAfter = "2h"; // only sleep a container if it hasn't gotten requests in 2 hours
146+
}
147+
148+
export default {
149+
async fetch(request, env) {
150+
const url = new URL(request.url);
151+
if (url.pathname.startsWith("/api")) {
152+
// note: "getRandom" to be replaced with latency-aware routing in the near future
153+
const containerInstance = getRandom(env.BACKEND, INSTANCE_COUNT);
154+
return containerInstance.fetch(request);
155+
}
156+
157+
return env.ASSETS.fetch(request);
158+
},
159+
};
160+
```
161+
162+
:::note
163+
This example uses the `getRandom` function, which is a temporary helper that will randomly
164+
select of of N instances of a Container to route requests to.
165+
166+
In the future, we will provide improved latency-aware load balancing and autoscaling.
167+
168+
This will make scaling stateless instances simple and routing more efficient. See the
169+
[autoscaling documentation](/containers/scaling-and-routing) for more details.
170+
:::
171+
172+
## Define a backend container
173+
174+
Your container should be able to handle requests to `/api/widgets`.
175+
176+
In this case, we'll use a simple Golang backend that returns a hard-coded list of widgets.
177+
178+
<Details header="server.go">
179+
```go
180+
package main
181+
182+
import (
183+
"encoding/json"
184+
"log"
185+
"net/http"
186+
)
187+
188+
func handler(w http.ResponseWriter, r \*http.Request) {
189+
widgets := []map[string]interface{}{
190+
{"id": 1, "name": "Widget A"},
191+
{"id": 2, "name": "Sprocket B"},
192+
{"id": 3, "name": "Gear C"},
193+
}
194+
195+
w.Header().Set("Content-Type", "application/json")
196+
w.Header().Set("Access-Control-Allow-Origin", "*")
197+
json.NewEncoder(w).Encode(widgets)
198+
199+
}
200+
201+
func main() {
202+
http.HandleFunc("/api/widgets", handler)
203+
log.Fatal(http.ListenAndServe(":8080", nil))
204+
}
205+
206+
```
207+
</Details>

0 commit comments

Comments
 (0)