From 940cbf2fcb74a2cb4e72c8295bcc3df4bcfa1ee2 Mon Sep 17 00:00:00 2001 From: dietrich ayala Date: Thu, 24 Sep 2026 17:50:13 +0200 Subject: [PATCH] Model variety for generation runs: qwen3.8-27b-fast-2 and deepseek-v4.1-flash Two more model sources behind the gateway, because candidates from a different model fail differently where more replicas of the same model do not: - qwen3.8-27b-fast-2: qwen3.8-27b on a new four-card GPU VM (bench/gpu-vm/), 16 in flight; absolute-seam batch after the prompt fixes 8/32 compiled, best 82/108. - deepseek-v4.1-flash: DeepSeek-V4.1-Flash EXL3 on a two-node ARM pair (bench/sparks/dsv41/), its own proxy frontend; first batch 4/8 compiled, best 82/108. qwen3.8-flash-next drops to two replicas and 16 in flight. - bench/rig-snapshot.sh snapshots and restores each rig role's serving configs; bench/sparks/hf-to-nas.sh fetches weights to the NAS. - docs/infrastructure.md gains the rig change log; measurements in docs/model-first-cost.md and docs/rig-capacity.md. Hosts are named by role only. --- bench/gpu-vm/create-vm.sh | 88 +++++ bench/gpu-vm/folknet-qwen38-vllm-worker | 54 +++ .../folknet-qwen38-vllm-worker@.service | 23 ++ bench/gpu-vm/provision.sh | 65 ++++ bench/rig-snapshot.sh | 312 ++++++++++++++++++ bench/sparks/dsv41/env.template | 80 +++++ .../dsv41/folknet-dsv41-exl3-pair.service | 32 ++ bench/sparks/dsv41/ssh-config-worker | 9 + bench/sparks/hf-to-nas.sh | 112 +++++++ docs/infrastructure.md | 217 ++++++++++-- docs/model-first-cost.md | 58 +++- docs/rig-capacity.md | 24 ++ 12 files changed, 1051 insertions(+), 23 deletions(-) create mode 100644 bench/gpu-vm/create-vm.sh create mode 100644 bench/gpu-vm/folknet-qwen38-vllm-worker create mode 100644 bench/gpu-vm/folknet-qwen38-vllm-worker@.service create mode 100644 bench/gpu-vm/provision.sh create mode 100755 bench/rig-snapshot.sh create mode 100644 bench/sparks/dsv41/env.template create mode 100644 bench/sparks/dsv41/folknet-dsv41-exl3-pair.service create mode 100644 bench/sparks/dsv41/ssh-config-worker create mode 100644 bench/sparks/hf-to-nas.sh diff --git a/bench/gpu-vm/create-vm.sh b/bench/gpu-vm/create-vm.sh new file mode 100644 index 00000000..b94e06c1 --- /dev/null +++ b/bench/gpu-vm/create-vm.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# Creates the four-card serving VM on a Proxmox node. Runs ON the node as root: +# +# ssh root@ "VM_SSH_PUBKEY='$(cat ~/.ssh/.pub)' bash -s -- " \ +# < bench/gpu-vm/create-vm.sh +# +# The GPUs are taken from an existing, stopped VM () that already has the +# cards passed through: its hostpci entries and its QEMU args (the 64-bit PCI hole that +# four 24 GB cards need) are copied, nothing else of it is touched. The two VMs share +# those cards, so only one of them can run at a time; the source VM must keep +# onboot 0, and the new VM's description says which cards it borrows. +# +# Ubuntu 24.04 cloud image, q35 + OVMF, DHCP on the node's bridge (the address it got +# is printed at the end). The OS disk is kept small: checkpoints live on the NAS, so +# the node's thin pool (shared with other tenants' disks) is not asked for model space. +set -euo pipefail +main() { + +VMID="${1:?usage: create-vm.sh }" +NAME="${2:?usage: create-vm.sh }" +SRC="${3:?usage: create-vm.sh }" +: "${VM_SSH_PUBKEY:?set VM_SSH_PUBKEY to the public key line that may log in}" + +CORES="${VM_CORES:-32}" +MEMORY_MB="${VM_MEMORY_MB:-163840}" +DISK_GB="${VM_DISK_GB:-120}" +STORAGE="${VM_STORAGE:-local-lvm}" +BRIDGE="${VM_BRIDGE:-vmbr0}" +IMAGE="${VM_IMAGE:-/var/lib/vz/template/iso/noble-server-cloudimg-amd64.img}" +USER_NAME="${VM_USER:-ubuntu}" + +if qm status "$VMID" >/dev/null 2>&1; then + echo "vmid $VMID already exists on $(hostname); pick another" >&2 + exit 1 +fi +if [ "$(qm status "$SRC" | awk '{print $2}')" != stopped ]; then + echo "source vm $SRC must be stopped: it holds the same cards" >&2 + exit 1 +fi +test -f "$IMAGE" || { echo "no cloud image at $IMAGE" >&2; exit 1; } + +src_conf="$(qm config "$SRC")" +hostpci="$(printf '%s\n' "$src_conf" | grep '^hostpci[0-9]*:')" +args="$(printf '%s\n' "$src_conf" | sed -n 's/^args: //p')" +[ -n "$hostpci" ] || { echo "source vm $SRC has no hostpci entries" >&2; exit 1; } +cards="$(printf '%s\n' "$hostpci" | awk '{print $2}' | cut -d, -f1 | paste -sd' ')" + +keyfile="$(mktemp)" +trap 'rm -f "$keyfile"' EXIT +printf '%s\n' "$VM_SSH_PUBKEY" > "$keyfile" + +qm create "$VMID" --name "$NAME" \ + --machine q35 --bios ovmf --cpu host --sockets 1 --cores "$CORES" --numa 1 \ + --memory "$MEMORY_MB" --balloon 0 \ + --efidisk0 "$STORAGE:1,efitype=4m,pre-enrolled-keys=0" \ + --scsihw virtio-scsi-single \ + --net0 "virtio,bridge=$BRIDGE" \ + --serial0 socket --vga none --agent enabled=1 --ostype l26 \ + --ide2 "$STORAGE:cloudinit" \ + --ciuser "$USER_NAME" --sshkeys "$keyfile" --ipconfig0 ip=dhcp \ + --onboot 1 \ + --description "Four-card model-serving replica. Borrows the GPUs of VM $SRC ($cards); only one of the two may run at a time, and VM $SRC stays onboot 0." +qm importdisk "$VMID" "$IMAGE" "$STORAGE" --format raw +qm set "$VMID" --scsi0 "$STORAGE:vm-$VMID-disk-1,discard=on,iothread=1,ssd=1" --boot order=scsi0 +qm resize "$VMID" scsi0 "${DISK_GB}G" +[ -z "$args" ] || qm set "$VMID" --args "$args" +while read -r key val; do + qm set "$VMID" "--${key%:}" "$val" +done <<< "$hostpci" + +qm start "$VMID" +# The stock cloud image has no guest agent, so the DHCP address is found by the VM's MAC +# in the node's neighbour table after a ping sweep of the bridge's /24. +mac="$(qm config "$VMID" | sed -n 's/^net0: virtio=\([^,]*\),.*/\1/p' | tr 'A-F' 'a-f')" +net="$(ip -4 -o addr show "$BRIDGE" | awk '{print $4}' | head -1)" +prefix="${net%.*}" +echo "vm $VMID mac $mac; waiting for it to take an address on ${prefix}.0/24" +for _ in $(seq 1 60); do + for i in $(seq 1 254); do ping -c1 -W1 "$prefix.$i" >/dev/null 2>&1 & done + wait + addr="$(ip neigh show dev "$BRIDGE" | awk -v m="$mac" 'tolower($3)==m {print $1; exit}')" + [ -n "$addr" ] && { echo "$addr"; return 0; } + sleep 5 +done +echo "no address seen for $mac yet; check the serial console (qm terminal $VMID)" >&2 + +} +main "$@" diff --git a/bench/gpu-vm/folknet-qwen38-vllm-worker b/bench/gpu-vm/folknet-qwen38-vllm-worker new file mode 100644 index 00000000..f6820665 --- /dev/null +++ b/bench/gpu-vm/folknet-qwen38-vllm-worker @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# Starts one qwen3.8-27b worker (vLLM, W4A16 AutoRound + DFlash2 speculative decoding) +# on one RTX 3090 of the four-card VM. Installed as /usr/local/libexec/folknet-qwen38-vllm-worker +# and run by folknet-qwen38-vllm-worker@.service. +# +# Copied from the recipe the rig's eight-card sampling host runs for its "fast" tier: +# same image (folknet/qwen38-27b-3090:e5554bb, moved over with docker save | docker load), +# same prepared checkpoint directory, same environment. The only differences are the +# paths and that each worker publishes on all interfaces, because the load balancer in +# front of these workers runs on another host. +set -euo pipefail + +profile="${1:?worker profile is required}" + +image="folknet/qwen38-27b-3090:e5554bb" +models="/srv/models/qwen38-27b" +cache_root="/srv/cache/qwen38-27b" + +case "${profile}" in + fast-0) gpu_ids="0"; port="8101" ;; + fast-1) gpu_ids="1"; port="8102" ;; + fast-2) gpu_ids="2"; port="8103" ;; + fast-3) gpu_ids="3"; port="8104" ;; + *) echo "unsupported worker profile: ${profile}" >&2; exit 64 ;; +esac +context="long"; max_len="131072"; max_seqs="4" + +test -d "${models}/Qwen3.8-27B-W4A16-AutoRound-fast" +test -f "${models}/Qwen3.8-27B-DFlash2-W4A16/model.safetensors" +mkdir -p "${cache_root}/${profile}" + +container="folknet-qwen38-${profile}" +/usr/bin/docker rm -f "${container}" >/dev/null 2>&1 || true + +exec /usr/bin/docker run --rm \ + --name "${container}" \ + --gpus "device=${gpu_ids}" \ + --ipc host \ + --publish "0.0.0.0:${port}:${port}" \ + --volume "${models}:/app/models:ro" \ + --volume "${cache_root}/${profile}:/cache" \ + --env "PORT=${port}" \ + --env SPEC=dflash2 \ + --env DFLASH_TOKENS=7 \ + --env PREFIX_CACHE=1 \ + --env "CTX=${context}" \ + --env "DFLASH_MAX_LEN=${max_len}" \ + --env "MAX_SEQS=${max_seqs}" \ + --env GPU_UTIL=0.93 \ + --env VLLM_OFFLOAD_KEEP_SHM=1 \ + --env HF_HUB_OFFLINE=1 \ + --env TRANSFORMERS_OFFLINE=1 \ + --env "EXTRA_ARGS=" \ + "${image}" single diff --git a/bench/gpu-vm/folknet-qwen38-vllm-worker@.service b/bench/gpu-vm/folknet-qwen38-vllm-worker@.service new file mode 100644 index 00000000..b18138ff --- /dev/null +++ b/bench/gpu-vm/folknet-qwen38-vllm-worker@.service @@ -0,0 +1,23 @@ +[Unit] +Description=FolkNet Qwen3.8-27B W4A16 DFlash2 worker %i (four-card VM) +After=docker.service network-online.target +Wants=network-online.target +Requires=docker.service + +[Service] +Type=simple +ExecStart=/usr/local/libexec/folknet-qwen38-vllm-worker %i +ExecStop=-/usr/bin/docker stop --time 30 folknet-qwen38-%i +Restart=on-failure +RestartSec=10s +TimeoutStartSec=20min +TimeoutStopSec=2min +KillSignal=SIGTERM +LimitNOFILE=1048576 +NoNewPrivileges=true +ProtectHome=true +ProtectSystem=strict +ReadWritePaths=/srv/cache/qwen38-27b + +[Install] +WantedBy=multi-user.target diff --git a/bench/gpu-vm/provision.sh b/bench/gpu-vm/provision.sh new file mode 100644 index 00000000..0acf316b --- /dev/null +++ b/bench/gpu-vm/provision.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# Provisions the four-card serving VM made by bench/gpu-vm/create-vm.sh. Runs INSIDE the +# guest as root (re-runnable; every step checks before it changes anything): +# +# ssh @ "sudo APT_PROXY=http://:3142 NAS_EXPORT=:/ bash -s" \ +# < bench/gpu-vm/provision.sh +# +# Installs the NVIDIA server driver (headless, open kernel modules), docker with the +# NVIDIA container toolkit, the guest agent, and mounts the NAS export that holds the +# checkpoints read-only at /mnt/models-nas. A reboot is needed after the first run +# so the driver loads; the script says so. +set -euo pipefail +main() { + +# 595 is the branch the rig's bare-metal eight-card host runs (CUDA 13.2), new enough +# for the cu130 torch in the serving image. +DRIVER="${NVIDIA_DRIVER:-595}" +NAS_EXPORT="${NAS_EXPORT:-}" +NAS_MOUNT="${NAS_MOUNT:-/mnt/models-nas}" + +if [ -n "${APT_PROXY:-}" ]; then + # The rig's apt cache refuses HTTPS CONNECT, so HTTPS repositories go direct. + printf 'Acquire::http::Proxy "%s";\nAcquire::https::Proxy "DIRECT";\nAcquire::Retries "5";\n' \ + "$APT_PROXY" > /etc/apt/apt.conf.d/01proxy +fi +export DEBIAN_FRONTEND=noninteractive +# A fresh cloud image is still running cloud-init and unattended-upgrades; wait for the +# dpkg lock rather than failing on it. +cloud-init status --wait >/dev/null 2>&1 || true +apt() { apt-get -o DPkg::Lock::Timeout=900 "$@"; } +apt update +apt install -y qemu-guest-agent nfs-common ca-certificates curl gnupg jq rsync \ + "nvidia-headless-${DRIVER}-server-open" "nvidia-utils-${DRIVER}-server" +systemctl enable --now qemu-guest-agent || true + +# Docker from the Ubuntu archive (docker.io), because it comes through the apt cache; +# only the NVIDIA toolkit repository needs a direct HTTPS fetch. +rm -f /etc/apt/sources.list.d/docker.list +if ! command -v nvidia-ctk >/dev/null; then + curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \ + | gpg --dearmor --yes -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg + curl -fsSL https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \ + | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \ + > /etc/apt/sources.list.d/nvidia-container-toolkit.list +fi +apt update +apt install -y docker.io docker-buildx nvidia-container-toolkit +nvidia-ctk runtime configure --runtime=docker +systemctl restart docker + +if [ -n "$NAS_EXPORT" ]; then + mkdir -p "$NAS_MOUNT" + line="$NAS_EXPORT $NAS_MOUNT nfs4 ro,vers=4.2,nconnect=8,hard,_netdev,nofail 0 0" + grep -qF " $NAS_MOUNT " /etc/fstab || echo "$line" >> /etc/fstab + mountpoint -q "$NAS_MOUNT" || mount "$NAS_MOUNT" +fi + +if nvidia-smi -L >/dev/null 2>&1; then + nvidia-smi -L +else + echo "driver installed but not loaded: reboot the guest, then check nvidia-smi" +fi + +} +main "$@" diff --git a/bench/rig-snapshot.sh b/bench/rig-snapshot.sh new file mode 100755 index 00000000..3142bcca --- /dev/null +++ b/bench/rig-snapshot.sh @@ -0,0 +1,312 @@ +#!/usr/bin/env bash +# Snapshots and restores the serving configuration of every rig role, from the dev box. +# +# bench/rig-snapshot.sh snapshot