GPU Sniper 是一套 GPU 服务器管理、GPU 状态监控、空闲通知和 GPU 占用任务发布平台。系统通过 SSH 连接被管理的 GPU 服务器,定时采集 nvidia-smi 数据,在 Web 控制台展示 GPU、显存、进程和事件,并支持创建可审计、可取消、可限时的 GPU 占用任务。
当前服务器部署目录为 /opt/gpu-sniper,生产服务通过 Docker Compose 运行,公网入口示例为 http://115.190.116.57/GPU-Sniper/。
- 登录认证:单管理员账号登录,JWT 访问令牌。
- 服务器管理:新增、查看、更新、删除 GPU 服务器;支持密码和 SSH 私钥两种认证方式。
- 连接检测:测试 SSH 连通性、主机名、
nvidia-smi可用性,并记录在线/离线状态。 - GPU 监控:采集 GPU 型号、UUID、显存、利用率、温度、功耗和状态。
- 进程监控:采集 GPU 进程 PID、用户、命令、参数、显存占用和运行时长。
- 事件记录:记录 GPU 空闲/忙碌变化、通知事件、任务事件和操作审计。
- 实时刷新:前端通过轮询和 SSE 获取 GPU 状态变化。
- 邮件通知:配置 SMTP 邮件渠道,支持 STARTTLS、SSL/TLS 或不加密模式。
- 通知规则:支持 GPU 空闲、服务器全空闲、占卡成功、占卡失败、释放前提醒。
- 通知静默:通知邮件内可带当日静默链接,避免重复打扰。
- 手动占卡:按 GPU 数量或指定 GPU 序号发布占用任务。
- 占卡参数:设置每卡显存、目标利用率、最长占用分钟数和进程显示名称。
- 自动占卡规则:满足空闲显存和 GPU 数量条件后自动创建占用任务。
- 任务生命周期:等待、启动中、轮询中、运行、释放、失败、超时、取消。
- 任务日志:读取远端
/tmp/gpu_sniper_<task_id>.log日志。 - 自动释放:到达最长占用时间后自动终止远端占用进程。
- 后端:FastAPI、SQLAlchemy、Alembic、APScheduler、AsyncSSH、PostgreSQL/SQLite、Redis。
- 前端:React 19、TypeScript、Vite、TanStack Query、lucide-react。
- 部署:Docker Compose、Nginx 反向代理、PostgreSQL 16、Redis 7。
- 远端 Worker:
scripts/gpu_sniper_reserve.py,优先使用 PyTorch,占用失败时回退到 CUDA Driver API。
backend/ FastAPI 后端、数据库模型、迁移、服务和测试
frontend/ React 控制台
scripts/gpu_sniper_reserve.py 远端 GPU 占用 worker
deploy/ Nginx、生产环境变量示例和运维手册
docker-compose.yml 本地开发 Compose
docker-compose.prod.yml 生产 Compose
.env.example 开发环境变量示例
docker compose up --build访问:
- 前端:
http://localhost:8080 - API:
http://localhost:8000 - 健康检查:
http://localhost:8000/health
默认开发账号:
- 用户名:
admin - 密码:
admin123456
cd backend
python -m venv ../.venv
../.venv/bin/python -m pip install -r requirements.txt
../.venv/bin/python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000Windows PowerShell:
cd backend
python -m venv ..\.venv
..\.venv\Scripts\python -m pip install -r requirements.txt
..\.venv\Scripts\python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000cd frontend
npm install
npm run dev默认 API 地址为 http://127.0.0.1:8000。如需修改:
VITE_API_BASE_URL=http://localhost:8000 npm run dev服务器需要安装:
- Docker 和 Docker Compose plugin
- Nginx
- Git
- 可访问目标 GPU 服务器的网络
推荐部署目录:
sudo mkdir -p /opt/gpu-sniper
sudo chown -R "$USER":"$USER" /opt/gpu-sniper
cd /opt/gpu-snipercp deploy/production.env.example .env编辑 .env,至少替换以下值:
POSTGRES_PASSWORDDATABASE_URL中的 PostgreSQL 密码APP_SECRET_KEYCREDENTIAL_ENCRYPTION_KEYJWT_SECRETADMIN_USERNAMEADMIN_PASSWORDPUBLIC_BASE_URLCORS_ORIGINS
可用以下命令生成随机值:
openssl rand -hex 32生产环境会拒绝使用默认弱密钥和默认管理员密码。
cd /opt/gpu-sniper
docker compose -f docker-compose.prod.yml --env-file .env up -d --build
docker compose -f docker-compose.prod.yml --env-file .env ps生产 Compose 默认端口:
- API:
127.0.0.1:18000 - Web:
127.0.0.1:18080 - PostgreSQL:Compose 内部访问
- Redis:Compose 内部访问
deploy/nginx.gpu-sniper.conf 用于把 GPU Sniper 挂载到 /GPU-Sniper/:
include /opt/gpu-sniper/deploy/nginx.gpu-sniper.conf;该配置会将:
/GPU-Sniper/代理到前端容器/GPU-Sniper/api/代理到 API 容器/GPU-Sniper/api/stream代理到 SSE 流,且关闭 buffering
检查并重载:
sudo nginx -t
sudo systemctl reload nginxcurl -fsS http://127.0.0.1:18000/health
curl -fsS http://127.0.0.1:18080/
curl -fsS http://115.190.116.57/GPU-Sniper/| 变量 | 说明 |
|---|---|
APP_ENV |
development、test 或 production |
APP_SECRET_KEY |
应用密钥,生产必须替换 |
CREDENTIAL_ENCRYPTION_KEY |
SSH 密码/私钥加密密钥,生产必须替换 |
JWT_SECRET |
JWT 签名密钥,生产必须替换 |
ADMIN_USERNAME |
初始化管理员用户名 |
ADMIN_PASSWORD |
初始化管理员密码,生产必须替换 |
DATABASE_URL |
SQLAlchemy 数据库连接 |
REDIS_URL |
Redis 连接 |
CORS_ORIGINS |
允许访问 API 的前端来源,逗号分隔 |
PUBLIC_BASE_URL |
用户访问系统的公网地址,用于通知静默链接 |
LOG_LEVEL |
日志级别 |
COLLECTOR_ENABLED |
是否启用 GPU 采集任务 |
COLLECTOR_INTERVAL_SECONDS |
GPU 状态采集间隔 |
RESERVATION_WATCH_INTERVAL_SECONDS |
占卡任务检查间隔 |
NOTIFICATION_DAY_START_HOUR |
当日通知静默边界小时 |
NOTIFICATION_TIMEZONE |
通知时间区,例如 Asia/Shanghai |
GPU_SNIPER_API_PORT |
生产 API 本机监听端口 |
GPU_SNIPER_WEB_PORT |
生产 Web 本机监听端口 |
- 打开前端地址。
- 输入管理员用户名和密码。
- 登录后进入控制台。访问令牌失效时前端会自动回到登录状态。
- 点击“添加服务器”。
- 填写服务器名称、IP/域名、SSH 端口、用户名。
- 选择认证方式:
- 密码:填写 SSH 密码。
- SSH Key:填写私钥内容。
- 提交后系统会保存服务器并立即执行连接测试。
- 连接测试会检查 SSH、主机名和
nvidia-smi。
被管理服务器需要满足:
- SSH 可连接。
- 安装 NVIDIA 驱动和
nvidia-smi。 - 运行占卡任务时需要
python3。 - 如需 PyTorch 后端,远端环境需要安装
torch;否则 worker 会尝试使用 NVIDIA CUDA Driver API 回退方案。
进入某台服务器后可查看:
- GPU 序号、型号、UUID。
- 总显存、已用显存、空闲显存。
- GPU 利用率、显存利用率、温度、功耗。
- 状态:
idle、busy、reserved、unknown。 - GPU 进程:PID、用户、命令、参数、显存占用、运行时长。
- GPU 事件:空闲/忙碌变化记录。
状态判断逻辑:
- 远端进程属于 GPU Sniper 占用任务,或 PID 匹配运行中的任务,则标记为
reserved。 - 存在其他 GPU 进程时标记为
busy。 - 利用率低且显存基本空闲时标记为
idle。 - 其他情况标记为
busy或unknown。
- 点击“发布任务”。
- 选择服务器。
- 填写每卡显存 GB、占卡数量或指定 GPU 序号。
- 填写目标利用率百分比。
- 填写最长占用分钟数;
0表示不自动超时释放。 - 可填写进程显示名称,例如
python train.py --config sft.yaml。 - 点击发布。
任务创建后,后端会:
- 刷新目标服务器 GPU 状态。
- 根据显存和状态选择 GPU。
- 上传
scripts/gpu_sniper_reserve.py到远端/tmp/gpu_sniper_reserve.py。 - 使用
nohup启动远端 worker。 - 保存远端 PID、日志路径和任务状态。
任务日志路径格式:
/tmp/gpu_sniper_<task_id>.log
运行中的任务可以在控制台取消。取消时后端会:
- 对远端 PID 发送
TERM。 - 使用任务 ID 兜底清理匹配的 worker 进程。
- 刷新 GPU 状态。
- 将任务状态更新为
cancelled。
设置了最长占用时间的任务到期后会自动释放,状态更新为 expired。
进入“设置”后配置邮件服务:
- SMTP Host
- 端口
- 账号
- 密码或授权码
- 发件人
- 收件人
- 加密方式:STARTTLS、SSL/TLS 或 none
保存后可发送测试邮件。密码字段在更新时可留空,表示沿用已保存密码。
系统支持以下通知:
- GPU 空闲通知:满足空闲 GPU 数量和单卡剩余显存阈值时发送。
- 服务器全空闲通知:整台服务器所有 GPU 都空闲时发送。
- 占卡成功:手动任务成功启动后发送。
- 占卡失败:任务启动失败或远端进程异常退出时发送。
- 释放前提醒:设置了任务时长的占用任务,在释放前若干分钟提醒。
每类通知支持启用/停用和冷却时间,避免重复发送。
“通知”页面展示系统内通知事件,包括:
- 成功消息。
- 失败消息。
- 测试邮件结果。
- 占卡和 GPU 空闲相关事件。
通知可在前端删除;对应事件也会从后端记录中删除。
后端 API 支持自动占卡规则:
- 服务器 ID。
- 按数量或指定 GPU 序号。
- 每卡显存。
- 目标利用率。
- 最长占用时间。
- 最低空闲显存阈值。
- 启用状态。
调度器会周期性检查规则。当满足条件且同一规则没有等待中或运行中的任务时,会自动创建占卡任务。
查看容器:
cd /opt/gpu-sniper
docker compose -f docker-compose.prod.yml --env-file .env ps查看日志:
docker compose -f docker-compose.prod.yml --env-file .env logs -f api
docker compose -f docker-compose.prod.yml --env-file .env logs -f web
sudo tail -f /var/log/nginx/access.log /var/log/nginx/error.log重启 API:
docker compose -f docker-compose.prod.yml --env-file .env up -d --build api停止服务:
docker compose -f docker-compose.prod.yml --env-file .env down备份数据库:
docker compose -f docker-compose.prod.yml --env-file .env exec -T postgres \
pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" > "backup-$(date +%Y%m%d-%H%M%S).sql"恢复数据库:
docker compose -f docker-compose.prod.yml --env-file .env exec -T postgres \
psql -U "$POSTGRES_USER" "$POSTGRES_DB" < backup.sql升级:
cd /opt/gpu-sniper
git pull --ff-only
docker compose -f docker-compose.prod.yml --env-file .env up -d --build
curl -fsS http://127.0.0.1:18000/health认证:
POST /api/auth/loginGET /api/auth/me
服务器:
GET /api/serversPOST /api/serversGET /api/servers/{server_id}PATCH /api/servers/{server_id}DELETE /api/servers/{server_id}POST /api/servers/{server_id}/test
GPU:
GET /api/servers/{server_id}/gpusGET /api/servers/{server_id}/processesGET /api/servers/{server_id}/eventsGET /api/stream/gpu-status?token=<jwt>
通知:
GET /api/notification-channelsPOST /api/notification-channelsPATCH /api/notification-channels/{channel_id}DELETE /api/notification-channels/{channel_id}POST /api/notification-channels/{channel_id}/testGET /api/notification-rulesPOST /api/notification-rulesPATCH /api/notification-rules/{rule_id}DELETE /api/notification-rules/{rule_id}GET /api/notification-eventsDELETE /api/notification-events/{event_id}GET /api/notifications/mute-day?token=<token>
占卡:
GET /api/reservationsPOST /api/reservationsGET /api/reservations/{reservation_id}POST /api/reservations/{reservation_id}/cancelGET /api/reservations/{reservation_id}/logsGET /api/reservation-rulesPOST /api/reservation-rulesPATCH /api/reservation-rules/{rule_id}DELETE /api/reservation-rules/{rule_id}
健康检查:
GET /health
- 不要提交
.env、.env.bak-*、私钥、密码或数据库备份。 - SSH 凭据会在数据库中加密保存,生产环境必须设置强
CREDENTIAL_ENCRYPTION_KEY。 - 生产环境必须替换默认管理员密码和所有密钥。
- 建议使用 HTTPS 域名访问,避免凭据和令牌在明文 HTTP 中传输。
- 占卡 worker 只负责占用指定 GPU 资源;任务归属、PID、日志和释放动作都由后端记录和控制。
后端测试:
cd backend
pytest前端构建:
cd frontend
npm install
npm run buildGPU 占用任务必须可审计、可停止、可限时。进程显示名称仅用于让远端进程在 ps、nvidia-smi 等工具中显示为用户指定的名称;它不会改变 SSH 用户、PID、权限、任务所有权、日志路径或释放控制。