Skip to content

Repository files navigation

GPU Sniper

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 开发

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 8000

Windows 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 8000

前端本地运行

cd frontend
npm install
npm run dev

默认 API 地址为 http://127.0.0.1:8000。如需修改:

VITE_API_BASE_URL=http://localhost:8000 npm run dev

生产部署

1. 准备服务器

服务器需要安装:

  • Docker 和 Docker Compose plugin
  • Nginx
  • Git
  • 可访问目标 GPU 服务器的网络

推荐部署目录:

sudo mkdir -p /opt/gpu-sniper
sudo chown -R "$USER":"$USER" /opt/gpu-sniper
cd /opt/gpu-sniper

2. 配置环境变量

cp deploy/production.env.example .env

编辑 .env,至少替换以下值:

  • POSTGRES_PASSWORD
  • DATABASE_URL 中的 PostgreSQL 密码
  • APP_SECRET_KEY
  • CREDENTIAL_ENCRYPTION_KEY
  • JWT_SECRET
  • ADMIN_USERNAME
  • ADMIN_PASSWORD
  • PUBLIC_BASE_URL
  • CORS_ORIGINS

可用以下命令生成随机值:

openssl rand -hex 32

生产环境会拒绝使用默认弱密钥和默认管理员密码。

3. 启动服务

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 内部访问

4. 配置 Nginx

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 nginx

5. 健康检查

curl -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 developmenttestproduction
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 本机监听端口

使用手册

登录

  1. 打开前端地址。
  2. 输入管理员用户名和密码。
  3. 登录后进入控制台。访问令牌失效时前端会自动回到登录状态。

添加 GPU 服务器

  1. 点击“添加服务器”。
  2. 填写服务器名称、IP/域名、SSH 端口、用户名。
  3. 选择认证方式:
    • 密码:填写 SSH 密码。
    • SSH Key:填写私钥内容。
  4. 提交后系统会保存服务器并立即执行连接测试。
  5. 连接测试会检查 SSH、主机名和 nvidia-smi

被管理服务器需要满足:

  • SSH 可连接。
  • 安装 NVIDIA 驱动和 nvidia-smi
  • 运行占卡任务时需要 python3
  • 如需 PyTorch 后端,远端环境需要安装 torch;否则 worker 会尝试使用 NVIDIA CUDA Driver API 回退方案。

查看 GPU 状态

进入某台服务器后可查看:

  • GPU 序号、型号、UUID。
  • 总显存、已用显存、空闲显存。
  • GPU 利用率、显存利用率、温度、功耗。
  • 状态:idlebusyreservedunknown
  • GPU 进程:PID、用户、命令、参数、显存占用、运行时长。
  • GPU 事件:空闲/忙碌变化记录。

状态判断逻辑:

  • 远端进程属于 GPU Sniper 占用任务,或 PID 匹配运行中的任务,则标记为 reserved
  • 存在其他 GPU 进程时标记为 busy
  • 利用率低且显存基本空闲时标记为 idle
  • 其他情况标记为 busyunknown

发布手动占卡任务

  1. 点击“发布任务”。
  2. 选择服务器。
  3. 填写每卡显存 GB、占卡数量或指定 GPU 序号。
  4. 填写目标利用率百分比。
  5. 填写最长占用分钟数;0 表示不自动超时释放。
  6. 可填写进程显示名称,例如 python train.py --config sft.yaml
  7. 点击发布。

任务创建后,后端会:

  1. 刷新目标服务器 GPU 状态。
  2. 根据显存和状态选择 GPU。
  3. 上传 scripts/gpu_sniper_reserve.py 到远端 /tmp/gpu_sniper_reserve.py
  4. 使用 nohup 启动远端 worker。
  5. 保存远端 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

API 摘要

认证:

  • POST /api/auth/login
  • GET /api/auth/me

服务器:

  • GET /api/servers
  • POST /api/servers
  • GET /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}/gpus
  • GET /api/servers/{server_id}/processes
  • GET /api/servers/{server_id}/events
  • GET /api/stream/gpu-status?token=<jwt>

通知:

  • GET /api/notification-channels
  • POST /api/notification-channels
  • PATCH /api/notification-channels/{channel_id}
  • DELETE /api/notification-channels/{channel_id}
  • POST /api/notification-channels/{channel_id}/test
  • GET /api/notification-rules
  • POST /api/notification-rules
  • PATCH /api/notification-rules/{rule_id}
  • DELETE /api/notification-rules/{rule_id}
  • GET /api/notification-events
  • DELETE /api/notification-events/{event_id}
  • GET /api/notifications/mute-day?token=<token>

占卡:

  • GET /api/reservations
  • POST /api/reservations
  • GET /api/reservations/{reservation_id}
  • POST /api/reservations/{reservation_id}/cancel
  • GET /api/reservations/{reservation_id}/logs
  • GET /api/reservation-rules
  • POST /api/reservation-rules
  • PATCH /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 build

运行边界

GPU 占用任务必须可审计、可停止、可限时。进程显示名称仅用于让远端进程在 psnvidia-smi 等工具中显示为用户指定的名称;它不会改变 SSH 用户、PID、权限、任务所有权、日志路径或释放控制。

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages