Add one-click AutoDL deploy script and GPU lane config

scripts/deploy_autodl.sh: idempotent setup for an AutoDL box -- installs app
deps (keeping the image's CUDA torch), picks the cupy wheel from torch's CUDA
version for GPU foreground, fetches weights, writes the auth token, renders a
supervisord instance (crash auto-restart) + a ~/.bashrc login autostart, then
starts the service and waits for /healthz. Args: --token, --port (default
6006), --config (default configs/gpu.yaml).

configs/gpu.yaml: the GPU lane (CUDA + bf16, full pipeline with cross-check,
despill off) promoted from a box-local file into the repo so the deploy is
reproducible.

The public port mapping and the "no auto-start without an interactive login"
gap are AutoDL platform limits, documented at the top of the script.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 03:42:05 +00:00
parent dbd061e119
commit 28d3fe8842
2 changed files with 233 additions and 0 deletions
+61
View File
@@ -0,0 +1,61 @@
# GPU lane: CUDA + bf16 across all three models. Full pipeline (BiRefNet seg +
# ViTMatte + HR-matting cross-check). Foreground estimation auto-uses the CuPy GPU
# backend when cupy is installed (see foreground.use_gpu, default on). despill is
# off (it false-positives on subjects sharing the background hue).
screen_color: null
model:
model_name: hustvl/vitmatte-base-composition-1k
device: cuda
matting_method: vitmatte
fallback_to_chroma_alpha: false
precision: bf16
attn_query_chunk: 2048
segmentation:
enabled: true
backend: birefnet
model_name: ZhengPeng7/BiRefNet
device: cuda
input_size: 1024
precision: bf16
trimap:
sure_bg_threshold: 0.92
sure_fg_threshold: 0.12
unknown_radius_ratio: 0.012
fg_safe_radius_ratio: 0.006
seg_core_threshold: 0.60
seg_loose_threshold: 0.08
mode: directional
seg_low: 0.15
bg_hue_proj_min: 4.0
seg_force_fg: 0.98
alpha_post:
chroma_suppress: true
chroma_suppress_bg_low: 0.35
chroma_suppress_bg_high: 0.80
chroma_suppress_strength: 1.0
suppress_raw_lo: 0.85
suppress_raw_hi: 0.98
cross_check:
enabled: true
model_name: ZhengPeng7/BiRefNet_HR-matting
input_size: 2048
reuse_as_seg: false
precision: bf16
proj_min: 3.0
l_min: 45.0
feather_sigma: 2.0
gate_lo: 0.70
gate_hi: 0.95
foreground:
enabled: true
method: ml
ml_regularization: 0.00001
despill:
enabled: false
+172
View File
@@ -0,0 +1,172 @@
#!/bin/bash
# One-click AutoDL deploy for the BGfilter HTTP service.
#
# Sets up (idempotently): app deps + model weights + optional CuPy GPU foreground
# + a supervisord instance (crash auto-restart) + an interactive-login autostart,
# then starts the service and verifies /healthz.
#
# Usage:
# scripts/deploy_autodl.sh [--token <TOKEN>] [--port <PORT>] [--config <cfg>]
# --token auth token; sets the X-Genarrative-Image-Token requirement.
# Omit to run OPEN (no auth) -- a warning is printed.
# --port public-mapped container port (default 6006).
# --config pipeline config (default configs/gpu.yaml).
#
# Assumes: the repo is already checked out (this script does NOT git pull), root
# user, torch/CUDA provided by the AutoDL image, python at /root/miniconda3.
#
# Out of scope (AutoDL limitations, not fixable here):
# * The public URL mapping (e.g. 6006 -> https://...:8443) is set in the AutoDL
# web console, not by this script.
# * After a container restart the service only comes back when you open an
# interactive shell (SSH / JupyterLab terminal) -- AutoDL has no systemd/boot
# hook, so the autostart lives in ~/.bashrc.
set -euo pipefail
# ---- args ----
TOKEN=""
PORT=6006
CONFIG="configs/gpu.yaml"
while [ $# -gt 0 ]; do
case "$1" in
--token) TOKEN="${2:-}"; shift 2;;
--port) PORT="${2:-}"; shift 2;;
--config) CONFIG="${2:-}"; shift 2;;
-h|--help) sed -n '2,30p' "$0" | sed 's/^# \?//'; exit 0;;
*) echo "unknown arg: $1" >&2; exit 1;;
esac
done
# ---- paths ----
APP_DIR="$(cd "$(dirname "$0")/.." && pwd)"
PY=/root/miniconda3/bin/python
[ -x "$PY" ] || PY="$(command -v python)"
BINDIR="$(dirname "$PY")"
SUPERVISORD="$BINDIR/supervisord"
SUPERVISORCTL="$BINDIR/supervisorctl"
TOKEN_FILE="$HOME/.bgfilter_token"
RUN_WRAPPER="$HOME/bgfilter_run.sh"
SUP_CONF="$HOME/supervisord.conf"
SUP_SOCK="$HOME/bgfilter-supervisor.sock"
LOG=/tmp/bgfilter.log
echo "[deploy] app_dir=$APP_DIR python=$PY port=$PORT config=$CONFIG"
# ---- 1. detect CUDA -> pick the matching cupy wheel (GPU foreground) ----
CUDA_VER="$("$PY" -c 'import torch;print(torch.version.cuda or "")' 2>/dev/null || echo "")"
echo "[deploy] torch CUDA = ${CUDA_VER:-none}"
CUPY_PKG=""
case "$CUDA_VER" in
13.*) CUPY_PKG="cupy-cuda13x";;
12.*) CUPY_PKG="cupy-cuda12x";;
11.*) CUPY_PKG="cupy-cuda11x";;
esac
# ---- 2. deps (keep the image's CUDA torch: never let pip replace torch/torchvision) ----
echo "[deploy] installing requirements (excluding torch/torchvision)..."
grep -vE '^(torch|torchvision)$' "$APP_DIR/requirements.txt" > /tmp/bgfilter-reqs.txt
"$PY" -m pip install -q -r /tmp/bgfilter-reqs.txt
if [ -n "$CUPY_PKG" ]; then
echo "[deploy] installing $CUPY_PKG (GPU foreground)..."
"$PY" -m pip install -q "$CUPY_PKG" || echo "[deploy] WARN: $CUPY_PKG failed; foreground falls back to CPU"
else
echo "[deploy] no CUDA -> skip cupy (foreground stays on CPU)"
fi
# ---- 3. weights (hf-mirror; the script skips ones already on disk) ----
echo "[deploy] fetching weights..."
( cd "$APP_DIR" && "$PY" scripts/fetch_weights.py )
# ---- 4. auth token ----
if [ -n "$TOKEN" ]; then
printf '%s' "$TOKEN" > "$TOKEN_FILE"; chmod 600 "$TOKEN_FILE"
echo "[deploy] auth ENABLED (token -> $TOKEN_FILE, chmod 600)"
else
rm -f "$TOKEN_FILE"
echo "[deploy] WARN: no --token -> service is OPEN (no auth)"
fi
# ---- 5. launch wrapper (exec so supervisord tracks the uvicorn PID) ----
cat > "$RUN_WRAPPER" <<WRAP
#!/bin/bash
cd "$APP_DIR" || exit 1
export BGFILTER_CONFIG="$CONFIG"
[ -f "$TOKEN_FILE" ] && export BGFILTER_AUTH_TOKEN="\$(cat "$TOKEN_FILE")"
exec "$PY" -m uvicorn app:app --host 0.0.0.0 --port $PORT --workers 1
WRAP
chmod +x "$RUN_WRAPPER"
# ---- 6. supervisord config ----
cat > "$SUP_CONF" <<SUP
[unix_http_server]
file=$SUP_SOCK
chmod=0700
[supervisord]
logfile=$HOME/supervisord.log
pidfile=$HOME/supervisord.pid
childlogdir=$HOME
nodaemon=false
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix://$SUP_SOCK
[program:bgfilter]
command=/bin/bash $RUN_WRAPPER
directory=$APP_DIR
autostart=true
autorestart=true
startsecs=20
startretries=5
stopwaitsecs=25
stopasgroup=true
killasgroup=true
redirect_stderr=true
stdout_logfile=$LOG
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=2
SUP
# ---- 7. interactive-login autostart in ~/.bashrc (idempotent) ----
if ! grep -q "supervisord -c $SUP_CONF" "$HOME/.bashrc" 2>/dev/null; then
cat >> "$HOME/.bashrc" <<BRC
# --- BGfilter supervisord auto-start (idempotent; added by deploy_autodl.sh) ---
if ! pgrep -f "supervisord -c $SUP_CONF" >/dev/null 2>&1; then
rm -f "$SUP_SOCK" "$HOME/supervisord.pid" 2>/dev/null
"$SUPERVISORD" -c "$SUP_CONF" >/dev/null 2>&1
fi
BRC
echo "[deploy] added autostart to ~/.bashrc"
else
echo "[deploy] autostart already in ~/.bashrc"
fi
# ---- 8. start (or reload) supervisord ----
if pgrep -f "supervisord -c $SUP_CONF" >/dev/null 2>&1; then
echo "[deploy] supervisord already running -> reload + restart bgfilter"
"$SUPERVISORCTL" -c "$SUP_CONF" reread || true
"$SUPERVISORCTL" -c "$SUP_CONF" update || true
"$SUPERVISORCTL" -c "$SUP_CONF" restart bgfilter || true
else
rm -f "$SUP_SOCK" "$HOME/supervisord.pid" 2>/dev/null
"$SUPERVISORD" -c "$SUP_CONF"
echo "[deploy] supervisord started"
fi
# ---- 9. wait for readiness + report ----
echo "[deploy] waiting for /healthz on port $PORT (models load ~10-15s)..."
for _ in $(seq 1 40); do
if [ "$(curl -s -o /dev/null -w '%{http_code}' -m 5 "http://127.0.0.1:$PORT/healthz" || true)" = "200" ]; then
echo "[deploy] READY: healthz 200"
break
fi
sleep 2
done
"$SUPERVISORCTL" -c "$SUP_CONF" status || true
echo
echo "[deploy] done. Manage the service with:"
echo " $SUPERVISORCTL -c $SUP_CONF status|restart|stop|tail bgfilter"