@cloudflare/sandbox 0.0.0-4aceb32 → 0.0.0-5213e94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/Dockerfile +10 -6
- package/README.md +1 -1
- package/container_src/jupyter-service.ts +11 -1
- package/container_src/jupyter_config.py +48 -0
- package/container_src/startup.sh +33 -32
- package/package.json +1 -1
- package/src/sandbox.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @cloudflare/sandbox
|
|
2
2
|
|
|
3
|
+
## 0.2.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#53](https://github.com/cloudflare/sandbox-sdk/pull/53) [`c87db11`](https://github.com/cloudflare/sandbox-sdk/commit/c87db117693a86cfb667bf09fb7720d6a6e0524d) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Improve jupyterlab config to speed up startup
|
|
8
|
+
|
|
9
|
+
## 0.2.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#51](https://github.com/cloudflare/sandbox-sdk/pull/51) [`4aceb32`](https://github.com/cloudflare/sandbox-sdk/commit/4aceb3215c836f59afcb88b2b325016b3f623f46) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Handle intermittent interpreter failures and decouple jupyter startup
|
|
14
|
+
|
|
3
15
|
## 0.2.1
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/Dockerfile
CHANGED
|
@@ -37,6 +37,7 @@ RUN apt-get update && apt-get install -y \
|
|
|
37
37
|
ca-certificates \
|
|
38
38
|
gnupg \
|
|
39
39
|
lsb-release \
|
|
40
|
+
strace \
|
|
40
41
|
&& rm -rf /var/lib/apt/lists/*
|
|
41
42
|
|
|
42
43
|
# Set Python 3.11 as default python3
|
|
@@ -56,17 +57,20 @@ RUN apt-get update && apt-get install -y ca-certificates curl gnupg \
|
|
|
56
57
|
COPY --from=bun-source /usr/local/bin/bun /usr/local/bin/bun
|
|
57
58
|
COPY --from=bun-source /usr/local/bin/bunx /usr/local/bin/bunx
|
|
58
59
|
|
|
59
|
-
# Install Jupyter
|
|
60
|
+
# Install minimal Jupyter components
|
|
60
61
|
RUN pip3 install --no-cache-dir \
|
|
61
|
-
jupyter \
|
|
62
|
-
|
|
62
|
+
jupyter-server \
|
|
63
|
+
jupyter-client \
|
|
63
64
|
ipykernel \
|
|
64
|
-
|
|
65
|
+
orjson \
|
|
66
|
+
&& python3 -m ipykernel install --user --name python3
|
|
67
|
+
|
|
68
|
+
# Install scientific packages
|
|
69
|
+
RUN pip3 install --no-cache-dir \
|
|
65
70
|
matplotlib \
|
|
66
71
|
numpy \
|
|
67
72
|
pandas \
|
|
68
|
-
seaborn
|
|
69
|
-
&& python3 -m ipykernel install --user --name python3
|
|
73
|
+
seaborn
|
|
70
74
|
|
|
71
75
|
# Install JavaScript kernel (ijavascript) - using E2B's fork
|
|
72
76
|
RUN npm install -g --unsafe-perm git+https://github.com/e2b-dev/ijavascript.git \
|
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ npm install @cloudflare/sandbox
|
|
|
72
72
|
1. **Create a Dockerfile** (temporary requirement, will be removed in future releases):
|
|
73
73
|
|
|
74
74
|
```dockerfile
|
|
75
|
-
FROM docker.io/cloudflare/sandbox:0.2.
|
|
75
|
+
FROM docker.io/cloudflare/sandbox:0.2.3
|
|
76
76
|
|
|
77
77
|
# Expose the ports you want to expose
|
|
78
78
|
EXPOSE 3000
|
|
@@ -133,16 +133,26 @@ export class JupyterService {
|
|
|
133
133
|
* Pre-warm context pools for better performance
|
|
134
134
|
*/
|
|
135
135
|
private async warmContextPools() {
|
|
136
|
+
// Delay pre-warming to avoid startup rush that triggers Bun WebSocket bug
|
|
137
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
138
|
+
|
|
136
139
|
try {
|
|
137
140
|
console.log(
|
|
138
141
|
"[JupyterService] Pre-warming context pools for better performance"
|
|
139
142
|
);
|
|
140
143
|
|
|
141
|
-
//
|
|
144
|
+
// Warm one at a time with delay between them
|
|
142
145
|
await this.jupyterServer.enablePoolWarming("python", 1);
|
|
146
|
+
|
|
147
|
+
// Small delay between different language kernels
|
|
148
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
149
|
+
|
|
143
150
|
await this.jupyterServer.enablePoolWarming("javascript", 1);
|
|
144
151
|
} catch (error) {
|
|
145
152
|
console.error("[JupyterService] Error pre-warming context pools:", error);
|
|
153
|
+
console.error(
|
|
154
|
+
"[JupyterService] Pre-warming failed but service continues"
|
|
155
|
+
);
|
|
146
156
|
}
|
|
147
157
|
}
|
|
148
158
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Minimal Jupyter configuration focused on kernel-only usage
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
c = get_config() # noqa
|
|
6
|
+
|
|
7
|
+
# Disable all authentication - we handle security at container level
|
|
8
|
+
c.ServerApp.token = ''
|
|
9
|
+
c.ServerApp.password = ''
|
|
10
|
+
c.IdentityProvider.token = ''
|
|
11
|
+
c.ServerApp.allow_origin = '*'
|
|
12
|
+
c.ServerApp.allow_remote_access = True
|
|
13
|
+
c.ServerApp.disable_check_xsrf = True
|
|
14
|
+
c.ServerApp.allow_root = True
|
|
15
|
+
c.ServerApp.allow_credentials = True
|
|
16
|
+
|
|
17
|
+
# Also set NotebookApp settings for compatibility
|
|
18
|
+
c.NotebookApp.token = ''
|
|
19
|
+
c.NotebookApp.password = ''
|
|
20
|
+
c.NotebookApp.allow_origin = '*'
|
|
21
|
+
c.NotebookApp.allow_remote_access = True
|
|
22
|
+
c.NotebookApp.disable_check_xsrf = True
|
|
23
|
+
c.NotebookApp.allow_credentials = True
|
|
24
|
+
|
|
25
|
+
# Performance settings
|
|
26
|
+
c.ServerApp.iopub_data_rate_limit = 1000000000 # E2B uses 1GB/s
|
|
27
|
+
|
|
28
|
+
# Minimal logging
|
|
29
|
+
c.Application.log_level = 'ERROR'
|
|
30
|
+
|
|
31
|
+
# Disable browser
|
|
32
|
+
c.ServerApp.open_browser = False
|
|
33
|
+
|
|
34
|
+
# Optimize for container environment
|
|
35
|
+
c.ServerApp.ip = '0.0.0.0'
|
|
36
|
+
c.ServerApp.port = 8888
|
|
37
|
+
|
|
38
|
+
# Kernel optimizations
|
|
39
|
+
c.KernelManager.shutdown_wait_time = 0.0
|
|
40
|
+
c.MappingKernelManager.cull_idle_timeout = 0
|
|
41
|
+
c.MappingKernelManager.cull_interval = 0
|
|
42
|
+
|
|
43
|
+
# Disable terminals
|
|
44
|
+
c.ServerApp.terminals_enabled = False
|
|
45
|
+
|
|
46
|
+
# Disable all extensions to speed up startup
|
|
47
|
+
c.ServerApp.jpserver_extensions = {}
|
|
48
|
+
c.ServerApp.nbserver_extensions = {}
|
package/container_src/startup.sh
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
# Function to check if Jupyter is ready
|
|
4
4
|
check_jupyter_ready() {
|
|
5
|
-
|
|
5
|
+
# Check if API is responsive and kernelspecs are available
|
|
6
|
+
curl -s http://localhost:8888/api/kernelspecs > /dev/null 2>&1
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
# Function to notify Bun server that Jupyter is ready
|
|
@@ -12,19 +13,10 @@ notify_jupyter_ready() {
|
|
|
12
13
|
echo "[Startup] Jupyter is ready, notified Bun server"
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
# Start Jupyter
|
|
16
|
+
# Start Jupyter server in background
|
|
16
17
|
echo "[Startup] Starting Jupyter server..."
|
|
17
|
-
jupyter
|
|
18
|
-
--
|
|
19
|
-
--port=8888 \
|
|
20
|
-
--no-browser \
|
|
21
|
-
--allow-root \
|
|
22
|
-
--NotebookApp.token='' \
|
|
23
|
-
--NotebookApp.password='' \
|
|
24
|
-
--NotebookApp.allow_origin='*' \
|
|
25
|
-
--NotebookApp.disable_check_xsrf=True \
|
|
26
|
-
--NotebookApp.allow_remote_access=True \
|
|
27
|
-
--NotebookApp.allow_credentials=True \
|
|
18
|
+
jupyter server \
|
|
19
|
+
--config=/container-server/jupyter_config.py \
|
|
28
20
|
> /tmp/jupyter.log 2>&1 &
|
|
29
21
|
|
|
30
22
|
JUPYTER_PID=$!
|
|
@@ -37,18 +29,21 @@ BUN_PID=$!
|
|
|
37
29
|
# Monitor Jupyter readiness in background
|
|
38
30
|
(
|
|
39
31
|
echo "[Startup] Monitoring Jupyter readiness in background..."
|
|
40
|
-
MAX_ATTEMPTS=
|
|
32
|
+
MAX_ATTEMPTS=60
|
|
41
33
|
ATTEMPT=0
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
|
|
35
|
+
# Track start time for reporting
|
|
36
|
+
START_TIME=$(date +%s.%N)
|
|
37
|
+
|
|
45
38
|
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
|
|
46
39
|
if check_jupyter_ready; then
|
|
47
40
|
notify_jupyter_ready
|
|
48
|
-
|
|
41
|
+
END_TIME=$(date +%s.%N)
|
|
42
|
+
ELAPSED=$(awk "BEGIN {printf \"%.2f\", $END_TIME - $START_TIME}")
|
|
43
|
+
echo "[Startup] Jupyter server is ready after $ELAPSED seconds ($ATTEMPT attempts)"
|
|
49
44
|
break
|
|
50
45
|
fi
|
|
51
|
-
|
|
46
|
+
|
|
52
47
|
# Check if Jupyter process is still running
|
|
53
48
|
if ! kill -0 $JUPYTER_PID 2>/dev/null; then
|
|
54
49
|
echo "[Startup] WARNING: Jupyter process died. Check /tmp/jupyter.log for details"
|
|
@@ -56,21 +51,27 @@ BUN_PID=$!
|
|
|
56
51
|
# Don't exit - let Bun server continue running in degraded mode
|
|
57
52
|
break
|
|
58
53
|
fi
|
|
59
|
-
|
|
54
|
+
|
|
60
55
|
ATTEMPT=$((ATTEMPT + 1))
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
56
|
+
|
|
57
|
+
# Start with faster checks
|
|
58
|
+
if [ $ATTEMPT -eq 1 ]; then
|
|
59
|
+
DELAY=0.5 # Start at 0.5s
|
|
60
|
+
else
|
|
61
|
+
# Exponential backoff with 1.3x multiplier (less aggressive than 1.5x)
|
|
62
|
+
DELAY=$(awk "BEGIN {printf \"%.2f\", $DELAY * 1.3}")
|
|
63
|
+
# Cap at 2s max (instead of 5s)
|
|
64
|
+
if [ $(awk "BEGIN {print ($DELAY > 2)}") -eq 1 ]; then
|
|
65
|
+
DELAY=2
|
|
66
|
+
fi
|
|
71
67
|
fi
|
|
68
|
+
|
|
69
|
+
# Log with current delay for transparency
|
|
70
|
+
echo "[Startup] Jupyter not ready yet (attempt $ATTEMPT/$MAX_ATTEMPTS, next check in ${DELAY}s)"
|
|
71
|
+
|
|
72
|
+
sleep $DELAY
|
|
72
73
|
done
|
|
73
|
-
|
|
74
|
+
|
|
74
75
|
if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
|
|
75
76
|
echo "[Startup] WARNING: Jupyter failed to become ready within attempts"
|
|
76
77
|
echo "[Startup] Jupyter logs:"
|
|
@@ -80,4 +81,4 @@ BUN_PID=$!
|
|
|
80
81
|
) &
|
|
81
82
|
|
|
82
83
|
# Wait for Bun server (main process)
|
|
83
|
-
wait $BUN_PID
|
|
84
|
+
wait $BUN_PID
|
package/package.json
CHANGED
package/src/sandbox.ts
CHANGED
|
@@ -37,7 +37,7 @@ export function getSandbox(ns: DurableObjectNamespace<Sandbox>, id: string) {
|
|
|
37
37
|
|
|
38
38
|
export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
|
|
39
39
|
defaultPort = 3000; // Default port for the container's Bun server
|
|
40
|
-
sleepAfter = "
|
|
40
|
+
sleepAfter = "20m"; // Keep container warm for 20 minutes to avoid cold starts
|
|
41
41
|
client: JupyterClient;
|
|
42
42
|
private sandboxName: string | null = null;
|
|
43
43
|
private codeInterpreter: CodeInterpreter;
|