应用级第一部分:GPU 物理切分(运维层)1.1 MIG 硬切脚本 (mig_setup.sh)适用于 A100/H100,将一张卡切分为 7个1g.5gb 实例。#!/bin/bash # 文件名: mig_setup.sh # 功能: 在指定 GPU 上启用 MIG 并创建实例 # 运行: sudo ./mig_setup.sh 2 (表示对 GPU 2 进行切分) GPU_INDEX=$1 if [ -z "$GPU_INDEX" ]; then echo "请指定 GPU 索引 (例如: sudo ./mig_setup.sh 2)" exit 1 fi echo "[$(date)] 开始配置 GPU $GPU_INDEX 的 MIG 模式..." # 1. 开启 MIG 模式 echo "步骤1: 开启 MIG 模式..." sudo nvidia-smi -i $GPU_INDEX -mig 1 if [ $? -ne 0 ]; then echo "错误: 开启 MIG 模式失败,请检查 GPU 型号是否支持 (A100/H100)。" exit 1 fi # 2. 等待初始化 sleep 5 # 3. 创建 MIG 实例 # 配置: 7个 1g.5gb 实例 (适合 7 个轻量推理服务) echo "步骤2: 创建 7 个 1g.5gb MIG 实例..." sudo nvidia-smi mig -cgi 1g.5gb -C -i $GPU_INDEX if [ $? -ne 0 ]; then echo "错误: 创建 MIG 实例失败,可能显存不足或配置不支持。" exit 1 fi echo "[$(date)] GPU $GPU_INDEX MIG 配置完成!" echo "请运行 'nvidia-smi' 查看切分结果。"1.2 MPS 软切配置 (mps_setup.sh)适用于共享一张卡,通过时间片分发。#!/bin/bash # 文件名: mps_setup.sh # 功能: 启动 MPS 控制 daemon,限制最大线程百分比 # 注意: 这是一个全局设置,影响整张卡 GPU_INDEX=3 # 固定使用 GPU 3 做 MPS echo "启动 MPS 服务,限制最大算力占比..." # 设置环境变量 export CUDA_VISIBLE_DEVICES=$GPU_INDEX export CUDA_MPS_PIPE_DIRECTORY=/tmp/nvidia-mps-pipe-gpu$GPU_INDEX export CUDA_MPS_LOG_DIRECTORY=/tmp/nvidia-mps-log-gpu$GPU_INDEX # 创建目录 mkdir -p $CUDA_MPS_PIPE_DIRECTORY mkdir -p $CUDA_MPS_LOG_DIRECTORY # 启动 MPS 控制 daemon # -d 表示 daemon 模式 nvidia-cuda-mps-control -d if [ $? -eq 0 ]; then echo "MPS 服务已启动,目录: $CUDA_MPS_PIPE_DIRECTORY" # 设置默认活跃线程百分比 (可选) echo "set_active_thread_percentage 50" | nvidia-cuda-mps-control else echo "MPS 启动失败,请检查驱动版本。" fi第二部分:Python 代码 (应用层)2.1 核心代码:gpu_manager.py""" 文件名: gpu_manager.py 功能: 完善后的 Ray + GPU 资源管理核心代码 时间: 2026-05-24 作者: Assistant 架构说明: 1. GPUTool: 封装 nvidia-smi 命令 (只读操作,无需 sudo) 2. GPUSlicer: 执行逻辑切分 (依赖底层已配置好的 MIG/MPS) 3. Ray Actor: 模型服务载体 4. Scheduler: 资源调度器 """ import ray import subprocess import os import time import logging import threading from typing import Dict, List, Optional from dataclasses import dataclass, field from enum import Enum # 设置日志 logging.basicConfig(level=logging.INFO) logger = logging.getLogger("GPU_Manager") # ========================================== # 1. 枚举与数据结构定义 # ========================================== class SliceType(Enum): """切片类型枚举""" HARD = "hard" # 整卡 MIG = "mig" # 硬切 (多实例 GPU) MPS = "mps" # 软切 (多进程服务) @dataclass class GPUSlice: """ 表示一个 GPU 切片实例 注意: 这是一个逻辑对象,代表一个可用的计算资源单元 """ slice_id: str gpu_index: int slice_type: SliceType profile: str # 规格描述, 如 "1g.5gb" 或 "full" memory_mb: float compute_fraction: float # 算力占比 0.0-1.0 ray_resource_key: str # Ray 调度用的资源 Key ray_resource_value: float # Ray 资源量 (通常为 1.0) cuda_visible: str # 传递给子进程的 CUDA_VISIBLE_DEVICES mps_pipe_dir: Optional[str] = None # 仅 MPS 需要 # ========================================== # 2. 底层 GPU 工具类 (GPUTool) # ========================================== class GPUTool: """ 底层 GPU 工具类 注意: 这里只包含查询和非破坏性操作。 生产环境中,MIG 开启/关闭通常由 K8s Operator 或运维脚本处理,而非应用代码。 """ @staticmethod def get_all_gpus() - List[Dict]: """获取所有 GPU 的基本信息 (索引, 名称, 显存)""" cmd = [ "nvidia-smi", "--query-gpu=index,name,memory.total,memory.free", "--format=csv,noheader,nounits" ] try: result = subprocess.run(cmd, capture_output=True, text=True, check=True) gpus = [] for line in result.stdout.strip().split("\n"): if not line.strip(): continue parts = [p.strip() for p in line.split(",")] gpus.append({ "index": int(parts[0]), "name": parts[1], "memory_total_mb": float(parts[2]), "memory_free_mb": float(parts[3]), }) return gpus except subprocess.CalledProcessError as e: logger.error(f"查询 GPU 信息失败: