docker: optional Cloudflare tunnel for JupyterLab (UNSLOTH_JUPYTER_CLOUDFLARE)
Mirror the public-link convenience Studio already has for its own UI, for JupyterLab. Off by default; opt in two ways: docker run -e UNSLOTH_JUPYTER_CLOUDFLARE=1 ... unsloth/unsloth docker exec <container> unsloth-jupyter-tunnel --force unsloth-jupyter-tunnel waits for JupyterLab, reuses a cached cloudflared (or fetches the static binary for the arch, no account needed), and starts a quick tunnel to the Jupyter port; the https://<name>.trycloudflare.com URL is printed to docker logs. supervisord runs it as the jupyter-cloudflare program, autostarted only when UNSLOTH_JUPYTER_CLOUDFLARE=1 (studio_launch.sh exports a 0 default so the autostart gate expands, matching the sshd pattern). JupyterLab still enforces its password, so the tunnel is not an open door. Verified: the helper fetches cloudflared and mints a working trycloudflare URL that reaches JupyterLab (HTTP 200) inside a running container.
This commit is contained in:
parent
7606081ef6
commit
08f9b67f60
5 changed files with 89 additions and 1 deletions
|
|
@ -7,6 +7,7 @@
|
|||
!studio_launch.sh
|
||||
!unsloth_studio_update.sh
|
||||
!unsloth_llama_update.sh
|
||||
!unsloth_jupyter_tunnel.sh
|
||||
!unsloth_nb_compat.py
|
||||
!unsloth_pip_shim.py
|
||||
!unsloth_ipython_startup.py
|
||||
|
|
|
|||
|
|
@ -167,9 +167,13 @@ COPY unsloth_llama_update.sh /usr/local/bin/unsloth-llama-update
|
|||
# API, so it is not rate-limited; deterministic portable bundle that runs on CPU
|
||||
# and every supported GPU) rather than the host-probing installer.
|
||||
COPY fetch_llama_prebuilt.py /usr/local/lib/unsloth/fetch_llama_prebuilt.py
|
||||
# Optional public Cloudflare tunnel for JupyterLab (UNSLOTH_JUPYTER_CLOUDFLARE=1,
|
||||
# or `unsloth-jupyter-tunnel --force`); supervisord runs it as jupyter-cloudflare.
|
||||
COPY unsloth_jupyter_tunnel.sh /usr/local/bin/unsloth-jupyter-tunnel
|
||||
RUN chmod +x /usr/local/bin/unsloth-studio-launch \
|
||||
/usr/local/bin/unsloth-studio-update \
|
||||
/usr/local/bin/unsloth-llama-update
|
||||
/usr/local/bin/unsloth-llama-update \
|
||||
/usr/local/bin/unsloth-jupyter-tunnel
|
||||
|
||||
# Studio web UI, JupyterLab, sshd. All bind 0.0.0.0 inside the container's
|
||||
# network namespace; the operator publishes them explicitly with -p.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ set -euo pipefail
|
|||
|
||||
export JUPYTER_PORT="${JUPYTER_PORT:-8888}"
|
||||
export UNSLOTH_STUDIO_HOME="${UNSLOTH_STUDIO_HOME:-/opt/unsloth-studio}"
|
||||
# Default off so supervisord's %(ENV_UNSLOTH_JUPYTER_CLOUDFLARE)s autostart gate
|
||||
# resolves; set to 1 (docker run -e) to expose JupyterLab on a trycloudflare URL.
|
||||
export UNSLOTH_JUPYTER_CLOUDFLARE="${UNSLOTH_JUPYTER_CLOUDFLARE:-0}"
|
||||
|
||||
# Make the runtime env visible to SSH sessions, which get a fresh login shell
|
||||
# without the `docker run -e` vars. Secrets are excluded on purpose: tokens,
|
||||
|
|
@ -79,6 +82,11 @@ fi
|
|||
mkdir -p /workspace
|
||||
echo "Unsloth Studio -> http://localhost:8000 (first-boot password below)"
|
||||
echo "JupyterLab -> http://localhost:${JUPYTER_PORT} (${JUPYTER_NOTE})"
|
||||
if [[ "${UNSLOTH_JUPYTER_CLOUDFLARE}" == "1" ]]; then
|
||||
echo "JupyterLab tunnel-> enabled; public trycloudflare URL appears below once it is up"
|
||||
else
|
||||
echo "JupyterLab tunnel-> off (set UNSLOTH_JUPYTER_CLOUDFLARE=1 for a public link)"
|
||||
fi
|
||||
if [[ "${UNSLOTH_ENABLE_SSHD}" == "true" ]]; then
|
||||
echo "sshd -> port 22 (key-only)"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -53,6 +53,21 @@ stdout_logfile_maxbytes=0
|
|||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
; Optional public Cloudflare quick-tunnel for JupyterLab. Started only when
|
||||
; UNSLOTH_JUPYTER_CLOUDFLARE=1 (studio_launch.sh exports a 0 default so this
|
||||
; expands). The trycloudflare URL is printed to docker logs by cloudflared.
|
||||
[program:jupyter-cloudflare]
|
||||
command=/usr/local/bin/unsloth-jupyter-tunnel
|
||||
directory=/workspace
|
||||
autostart=%(ENV_UNSLOTH_JUPYTER_CLOUDFLARE)s
|
||||
autorestart=true
|
||||
startsecs=5
|
||||
environment=HOME="/root",USER="root"
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[program:sshd]
|
||||
command=/usr/sbin/sshd -D -e
|
||||
autostart=%(ENV_UNSLOTH_ENABLE_SSHD)s
|
||||
|
|
|
|||
60
docker/unsloth_jupyter_tunnel.sh
Executable file
60
docker/unsloth_jupyter_tunnel.sh
Executable file
|
|
@ -0,0 +1,60 @@
|
|||
#!/usr/bin/env bash
|
||||
# Optional public Cloudflare quick-tunnel for JupyterLab, mirroring the tunnel
|
||||
# Studio creates for its own UI. Off by default. Two ways to use it:
|
||||
#
|
||||
# * at run time: docker run -e UNSLOTH_JUPYTER_CLOUDFLARE=1 ... unsloth/unsloth
|
||||
# -> the https://<name>.trycloudflare.com URL is printed in
|
||||
# `docker logs` once JupyterLab is up.
|
||||
# * on demand: docker exec <container> unsloth-jupyter-tunnel --force
|
||||
#
|
||||
# The tunnel gives a public https URL that works from anywhere with no account
|
||||
# or open inbound port. JupyterLab still requires its password, so the notebook
|
||||
# is not open to the world; treat the URL as sensitive all the same.
|
||||
set -u
|
||||
|
||||
FORCE=0
|
||||
[ "${1:-}" = "--force" ] && FORCE=1
|
||||
if [ "$FORCE" != "1" ] && [ "${UNSLOTH_JUPYTER_CLOUDFLARE:-0}" != "1" ]; then
|
||||
echo "[jupyter-tunnel] disabled (set UNSLOTH_JUPYTER_CLOUDFLARE=1, or run with --force)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PORT="${JUPYTER_PORT:-8888}"
|
||||
|
||||
echo "[jupyter-tunnel] waiting for JupyterLab on port ${PORT} ..."
|
||||
for _ in $(seq 1 90); do
|
||||
if curl -fsS -o /dev/null "http://localhost:${PORT}/login" 2>/dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Reuse a cloudflared already on the host (Studio caches one for its own
|
||||
# tunnel); otherwise fetch the static binary for this arch. No account needed.
|
||||
CFD=""
|
||||
for cand in \
|
||||
"${UNSLOTH_STUDIO_HOME:-/opt/unsloth-studio}/bin/cloudflared" \
|
||||
/usr/local/bin/cloudflared \
|
||||
cloudflared; do
|
||||
if command -v "$cand" >/dev/null 2>&1; then CFD="$(command -v "$cand")"; break; fi
|
||||
[ -x "$cand" ] && { CFD="$cand"; break; }
|
||||
done
|
||||
if [ -z "$CFD" ]; then
|
||||
case "$(uname -m)" in
|
||||
x86_64|amd64) A=amd64;;
|
||||
aarch64|arm64) A=arm64;;
|
||||
*) A=amd64;;
|
||||
esac
|
||||
CFD=/usr/local/bin/cloudflared
|
||||
echo "[jupyter-tunnel] downloading cloudflared (${A}) ..."
|
||||
if ! curl -fsSL -o "$CFD" \
|
||||
"https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${A}"; then
|
||||
echo "[jupyter-tunnel] could not download cloudflared" >&2
|
||||
exit 1
|
||||
fi
|
||||
chmod +x "$CFD"
|
||||
fi
|
||||
|
||||
echo "[jupyter-tunnel] starting Cloudflare quick-tunnel to JupyterLab (port ${PORT})."
|
||||
echo "[jupyter-tunnel] the https://<name>.trycloudflare.com URL appears below; log in with your Jupyter password."
|
||||
exec "$CFD" tunnel --no-autoupdate --url "http://localhost:${PORT}"
|
||||
Loading…
Add table
Add a link
Reference in a new issue