Initial commit: Complete deployment scripts, power governor, systemd units, and architecture documentation for Turing multi-GPU LLM rig

This commit is contained in:
wmantly
2026-09-01 01:55:19 +00:00
commit aeb72cecfa
13 changed files with 1925 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
# 🤖 API, OpenWebUI & Thinking / Reasoning Control Guide
The API proxy (`ollama-proxy.py`) translates incoming **Ollama**, **OpenAI**, and **Anthropic Messages** API calls into optimized requests for the `llama-server` backend.
---
## 1. Controlling Reasoning & Thinking
By default, Qwen3.8 and DeepSeek reasoning models have their Jinja template default set to `xhigh` effort, which can cause excessive thinking on simple queries. The proxy provides full granular control over thinking modes.
### Option A: Model Tag Aliases (Recommended for OpenWebUI Dropdown)
Select any of the registered alias tags directly in your client:
| Model Tag | Thinking Mode | Behavior |
| :--- | :--- | :--- |
| **`qwen:fast`** / **`qwen:nothink`** | **Disabled** (0 reasoning tokens) | Answers immediately with zero thinking delay! |
| **`qwen`** / **`qwen:latest`** | **Low Effort** (Default) | Concise, focused 13 sentence reasoning trace before answering. |
| **`qwen:think`** / **`qwen:deep`** | **High Effort (`xhigh`)** | Full deep multi-step reasoning for complex math/coding. |
---
### Option B: OpenWebUI UI Controls & Parameters
* **Thinking Toggle**: Toggle "Thinking" ON/OFF in the chat interface.
* **Reasoning Effort Setting**:
* `none` / `off` $\rightarrow$ Thinking disabled.
* `low` $\rightarrow$ Brief, focused reasoning.
* `medium` $\rightarrow$ Balanced reasoning.
* `high` / `xhigh` $\rightarrow$ Deep reasoning.
---
### Option C: API Payload Parameters
#### 1. Disabling Thinking (Ollama Format)
```json
{
"model": "qwen",
"messages": [{"role": "user", "content": "What is 2+2?"}],
"options": {
"enable_thinking": false
}
}
```
#### 2. Specifying Thinking Token Budget (Anthropic / OpenAI Format)
```json
{
"model": "qwen",
"messages": [{"role": "user", "content": "Solve this equation: 3x + 12 = 45"}],
"thinking": {
"type": "enabled",
"budget_tokens": 512
}
}
```
---
## 2. Multimodal Vision Support
Send images directly via standard base64 strings in the `images` array (Ollama format) or `image_url` data URLs (OpenAI/Anthropic format).
The proxy features automatic **magic-byte MIME detection** supporting `image/png`, `image/jpeg`, `image/webp`, and `image/gif`.
---
## 3. Supported API Endpoints
* **Ollama Endpoints**:
* `POST /api/chat` (Streaming & non-streaming)
* `POST /api/generate` (Streaming & non-streaming)
* `GET /api/tags`
* `POST /api/show`
* `GET /api/ps`
* `POST /api/embed` & `POST /api/embeddings`
* **Anthropic Messages Endpoint**:
* `POST /v1/messages` (Claude Code, Continue.dev, Anthropic SDK)
* `POST /v1/messages/count_tokens`
* **OpenAI Backend**:
* `POST /v1/chat/completions` (Forwarded directly to `llama-server`)
+70
View File
@@ -0,0 +1,70 @@
# 🔬 Hardware Architecture & Key Technical Learnings
This document details the practical hardware behaviors, quirks, and engineering solutions discovered while building and optimizing this 3-GPU Turing inference rig on an **HPE ProLiant DL380p Gen8** server.
---
## 1. NVIDIA CMP 50HX Mining GPUs: VBIOS & Power States
### Why CMP 50HX Mining Cards Idle at ~75W85W Stock
* **No Display / Headless Architecture**: CMP 50HX mining cards lack display outputs and display engines.
* **Missing Deep P8 VBIOS Tables**: Standard GeForce VBIOSes drop to P8 state (405 MHz GDDR6 / ~10W) when no display is active. Mining VBIOSes lock the cards in P0 state (1,890 MHz core / 7,000 MHz GDDR6) by default.
### OEM Reference VBIOS vs. MSI VBIOS
Comparing the hardware profiles of two CMP 50HX cards on the exact same rig:
* **GPU 2 (MSI Board `0x1462`, VBIOS `90.02.60.00.17`)**:
* Idles at **`~31.8W`** when core is locked to 300600 MHz.
* Minimum fan speed: **`25%`**.
* Aggressive P3 voltage gating tables.
* **GPU 1 (NVIDIA Reference OEM `0x10DE`, VBIOS `90.02.60.00.01`)**:
* Idles at **`~61.2W`** when core is locked to 300600 MHz.
* Minimum fan speed: **`40%`** (locked in VBIOS).
* Higher static VRM voltage rail leakage.
* **Actionable Solution**: Cross-flash GPU 2's MSI VBIOS (`90.02.60.00.17`) onto GPU 1 using `nvflash -6` to cut GPU 1's idle draw by ~30W and match fan curves.
---
## 2. Power Cap vs Inference Throughput
* **100W Power Cap (`-pl 100`)**: Capping CMP 50HX cards to 100W throttles GPU 1's core clock down to **1,305 MHz** during tensor-parallel splits, dropping generation speed from **18.0 tok/s $\rightarrow$ ~14.0 tok/s**.
* **150W Power Cap (`-pl 150`)**: Provides sufficient headroom for full **1,8001,900 MHz boost clocks**, sustaining **18.03 tok/s** while protecting against unnecessary 225W power spikes and heat.
---
## 3. Automated Dynamic Power Governor ([`power-governor.py`](../scripts/power-governor.py))
* **The Problem with GPU Utilization Polling**: On Turing GPUs during batch=1 token generation, `utilization.gpu` fluctuates between 0% and 5% between token steps. Relying on GPU utilization causes false idle triggers.
* **The Solution**: Direct slot polling against `llama-server` (`http://127.0.0.1:8080/slots` `is_processing: True`).
* **Governor Behavior**:
* When `is_processing: True`: Instantly sets unconstrained clocks (`nvidia-smi -rgc`) $\rightarrow$ **18.03 tok/s**.
* When idle for > 15 seconds: Sets low-power 300600 MHz core clocks $\rightarrow$ **drops power to ~31W/card**.
---
## 4. HPE ProLiant DL380p Gen8 Platform Learnings
### Sandy Bridge-EP (v1) vs. Ivy Bridge-EP (v2) PCIe 3.0 Jitter
* **The Sandy Bridge-EP v1 Bug**: Intel's first-generation PCIe 3.0 controller on `Xeon E5-2600 v1` (2012) suffered from transmitter signal margin attenuation (*Intel Errata BD78/BD105*). Over riser cables, the 8.0 GHz eye diagram degrades, forcing links down to Gen 1 (2.5 GT/s).
* **The Ivy Bridge-EP v2 Fix**: `Xeon E5-2600 v2` (22nm Tri-Gate) completely redesigned the PCIe 3.0 PHY with CTLE equalization, reliably locking Gen 3 speeds across risers.
### NUMA Socket Pinning
* Dual-socket Xeon servers have two distinct NUMA nodes.
* If all GPUs are plugged into **Primary Riser 1**, they are physically connected to **CPU Socket 1 (NUMA Node 1)**.
* Binding `llama-server` to NUMA Node 1 (`CPUAffinity` & `NUMAPolicy=bind`) eliminates all host-to-device memory traffic over the cross-socket Intel QPI bus.
### External PSU & Common Grounding
* When GPUs receive PCIe data signals from the server motherboard but 12V power from an external PSU:
* Ensure the **server chassis** and **external PSU casing** share a solid common ground to eliminate high-frequency ground loop noise on PCIe differential clock lines (`REFCLK`).
---
## 5. 20GB VRAM Modding Feasibility (CMP 50HX TU102)
* **Architecture**: CMP 50HX uses the **TU102 PCB layout (320-bit bus, 10 memory pads)**.
* **Memory Swap**:
* Desolder 10x 1GB (8Gbit) GDDR6 BGA-180 chips (e.g., Samsung `K4Z80325BC-HC14`).
* Solder 10x 2GB (16Gbit) GDDR6 BGA-180 chips (e.g., Samsung `K4ZAF325BM-HC14` or Micron `D9ZCL`).
* Modify memory strapping resistor dividers to signal 16Gbit density to the TU102 memory controller.
* **Payoff**:
* 2x Modded Cards = **40 GB Total VRAM** (capable of running full 70B/72B models like `Llama-3.3-70B` or `Qwen2.5-72B` on just 2 dedicated x16 slots).
* 4x Modded Cards = **80 GB Total VRAM** (enterprise A100-tier capacity for under $1,000).
+64
View File
@@ -0,0 +1,64 @@
# 📦 Proxmox VE LXC Container GPU Passthrough & Hardware Permissions
Running multi-GPU AI inference and hardware power management inside a Proxmox LXC container requires specific device mappings, cgroup permissions, and capability flags.
---
## 1. Proxmox Host Configuration (`/etc/pve/lxc/<CTID>.conf`)
Add the following lines to your container configuration file on the Proxmox host:
```ini
# /etc/pve/lxc/<CTID>.conf
# 1. Unconfined AppArmor profile (Required for NVML clock/power limit modification)
lxc.apparmor.profile: unconfined
# 2. Grant SYS_ADMIN capability for hardware clock management
lxc.cap.keep: sys_admin sys_rawio
# 3. Allow all NVIDIA device cgroups
lxc.cgroup2.devices.allow: c 195:* rwm
lxc.cgroup2.devices.allow: c 235:* rwm
lxc.cgroup2.devices.allow: c 510:* rwm
lxc.cgroup2.devices.allow: c 511:* rwm
# 4. Pass-through NVIDIA character device nodes
lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file
lxc.mount.entry: /dev/nvidia1 dev/nvidia1 none bind,optional,create=file
lxc.mount.entry: /dev/nvidia2 dev/nvidia2 none bind,optional,create=file
lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file
lxc.mount.entry: /dev/nvidia-modeset dev/nvidia-modeset none bind,optional,create=file
```
---
## 2. Verifying Permissions Inside the Container
Restart the container, then run:
```bash
# Verify all GPUs are visible
nvidia-smi
# Test NVML Persistence Mode (Requires CAP_SYS_ADMIN)
nvidia-smi -pm 1
# Test Power Capping
nvidia-smi -i 1,2 -pl 150
# Test Dynamic Clock Locking
nvidia-smi -i 1,2 -lgc 600,600
nvidia-smi -i 1,2 -rgc
```
If all four commands return with code `0` and "All done", your container has full hardware rights.
---
## 3. Important Systemd Service Security Flags
When running services that execute `nvidia-smi` hardware commands inside systemd:
* Do **NOT** set `NoNewPrivileges=true` in `gpu-power-governor.service`. `NoNewPrivileges=true` blocks processes from acquiring permissions to execute privileged NVML clock-locking calls.
+101
View File
@@ -0,0 +1,101 @@
# 🛠️ Complete Multi-GPU LLM Server Setup Guide
This guide walks through deploying the complete multi-GPU inference stack on any fresh Ubuntu/Debian server or Proxmox container with NVIDIA Turing GPUs.
---
## 1. System Prerequisites
### Install Base Dependencies & NVIDIA Drivers
```bash
sudo apt-get update && sudo apt-get install -y \
build-essential cmake ninja-build git curl wget \
python3 python3-pip python3-venv \
libcurl4-openssl-dev libssl-dev pkg-config numactl
# Ensure NVIDIA driver and CUDA Toolkit (12.x+) are installed
nvidia-smi
nvcc --version
```
### Install NVIDIA NCCL (for Multi-GPU Tensor Parallelism)
```bash
# In Python venv or system:
pip3 install nvidia-nccl-cu12
```
---
## 2. Compile `llama.cpp` with NCCL & Turing cuBLAS Optimization
Turing architecture (`sm_75`) requires specific CMake flags to avoid throttled DP4A integer paths and enable fast cuBLAS GEMM tensor parallel synchronization:
```bash
git clone https://github.com/ggml-org/llama.cpp /opt/llama.cpp
cd /opt/llama.cpp
mkdir -p build-nccl
cd build-nccl
cmake .. \
-GNinja \
-DGGML_CUDA=ON \
-DGGML_CUDA_GRAPHS=ON \
-DGGML_CUDA_FORCE_CUBLAS=ON \
-DGGML_CUDA_PEER_MAX_BATCH_SIZE=128 \
-DGGML_CUDA_ARCHITECTURES="75" \
-DCMAKE_BUILD_TYPE=Release
ninja llama-server
```
---
## 3. Preparing Model Weights & Multimodal Vision Projector
### Download GGUF Model Weights
```bash
mkdir -p /opt/models/gguf
cd /opt/models/gguf
# Download Qwen3.8-27B-Uncensored (or any Qwen2.5 / 27B / 32B model)
wget -c "https://huggingface.co/.../Qwen3.8-27B-Uncensored-HauhauCS-Aggressive-Q4_K_P.gguf"
```
### Extracting Vision Projector (`mmproj`)
If converting from Hugging Face safetensors, extract the multimodal ViT projector to GGUF using `convert_image_encoder_to_gguf.py`:
```bash
python3 /opt/llama.cpp/examples/llava/convert_image_encoder_to_gguf.py \
-m /opt/models/orcarouter_Qwen3.8-27B-Uncensored \
--output-dir /opt/models/gguf \
--llava-projector
```
Result: `/opt/models/gguf/mmproj-Qwen3.8-27B-Uncensored-f16.gguf` (931 MB).
---
## 4. Key Server Parameters Explained
In `scripts/start-server.sh`:
* `--split-mode tensor`: Splits every attention head and FFN layer across all 3 GPUs simultaneously using NCCL AllReduce.
* `-c 262144`: Enables the full 256K token context window.
* `--parallel 1`: Allocates a single dedicated KV cache slot to prevent multi-slot VRAM duplication.
* `--cache-type-k q4_0 --cache-type-v q4_0`: Quantizes the KV cache to 4-bit, shrinking 256K context memory footprint by 75% (down to ~8.8 GB).
* `--image-max-tokens 2048`: Prevents out-of-memory spikes when decoding high-resolution 4K images.
* `--jinja`: Uses native Jinja chat templates with reasoning control.
---
## 5. Systemd Production Deployment
Copy service files and enable on boot:
```bash
sudo cp systemd/*.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now llama-server.service ollama-proxy.service gpu-power-governor.service
```
Verify services:
```bash
systemctl status llama-server ollama-proxy gpu-power-governor
```