名称: modal-serverless-gpu 描述: 用于运行机器学习工作负载的无服务器GPU云平台。当您需要按需GPU访问而无需基础设施管理、部署机器学习模型为API或运行具有自动扩展的批处理作业时使用。 版本: 1.0.0 作者: Orchestra Research 许可证: MIT 标签: [基础设施, Serverless, GPU, 云, 部署, Modal] 依赖: [modal>=0.64.0]
Modal Serverless GPU
运行Modal无服务器GPU云平台上机器学习工作负载的全面指南。
何时使用Modal
使用Modal当:
- 运行GPU密集型机器学习工作负载而无需管理基础设施
- 部署机器学习模型为自动扩展的API
- 运行批处理作业(训练、推理、数据处理)
- 需要按秒计费的GPU定价,无闲置成本
- 快速原型机器学习应用
- 运行定时作业(类似cron的工作负载)
关键特性:
- Serverless GPUs: T4、L4、A10G、L40S、A100、H100、H200、B200按需可用
- Python原生: 在Python代码中定义基础设施,无需YAML
- 自动扩展: 从零扩展到100+ GPU即时
- 亚秒级冷启动: 基于Rust的基础设施,容器启动快速
- 容器缓存: 镜像层缓存以加速迭代
- Web端点: 将函数部署为REST API,零停机更新
使用替代方案当:
- RunPod: 用于长时间运行的状态持久化pod
- Lambda Labs: 用于保留GPU实例
- SkyPilot: 用于多云编排和成本优化
- Kubernetes: 用于复杂多服务架构
快速开始
安装
pip install modal
modal setup # 打开浏览器进行身份验证
Hello World with GPU
import modal
app = modal.App("hello-gpu")
@app.function(gpu="T4")
def gpu_info():
import subprocess
return subprocess.run(["nvidia-smi"], capture_output=True, text=True).stdout
@app.local_entrypoint()
def main():
print(gpu_info.remote())
运行: modal run hello_gpu.py
基本推理端点
import modal
app = modal.App("text-generation")
image = modal.Image.debian_slim().pip_install("transformers", "torch", "accelerate")
@app.cls(gpu="A10G", image=image)
class TextGenerator:
@modal.enter()
def load_model(self):
from transformers import pipeline
self.pipe = pipeline("text-generation", model="gpt2", device=0)
@modal.method()
def generate(self, prompt: str) -> str:
return self.pipe(prompt, max_length=100)[0]["generated_text"]
@app.local_entrypoint()
def main():
print(TextGenerator().generate.remote("Hello, world"))
核心概念
关键组件
| 组件 | 用途 |
|---|---|
App |
函数和资源的容器 |
Function |
带有计算规格的无服务器函数 |
Cls |
具有生命周期钩子的基于类的函数 |
Image |
容器镜像定义 |
Volume |
用于模型/数据的持久存储 |
Secret |
安全凭据存储 |
执行模式
| 命令 | 描述 |
|---|---|
modal run script.py |
执行并退出 |
modal serve script.py |
开发模式,实时重新加载 |
modal deploy script.py |
持久化云部署 |
GPU配置
可用GPU
| GPU | VRAM | 最佳用途 |
|---|---|---|
T4 |
16GB | 经济型推理,小型模型 |
L4 |
24GB | 推理,Ada Lovelace架构 |
A10G |
24GB | 训练/推理,比T4快3.3倍 |
L40S |
48GB | 推荐用于推理(最佳成本/性能) |
A100-40GB |
40GB | 大型模型训练 |
A100-80GB |
80GB | 超大型模型 |
H100 |
80GB | 最快,FP8 + Transformer Engine |
H200 |
141GB | 从H100自动升级,4.8TB/s带宽 |
B200 |
最新 | Blackwell架构 |
GPU规格模式
# 单GPU
@app.function(gpu="A100")
# 特定内存版本
@app.function(gpu="A100-80GB")
# 多GPU(最多8个)
@app.function(gpu="H100:4")
# GPU带备选方案
@app.function(gpu=["H100", "A100", "L40S"])
# 任何可用GPU
@app.function(gpu="any")
容器镜像
# 带pip的基本镜像
image = modal.Image.debian_slim(python_version="3.11").pip_install(
"torch==2.1.0", "transformers==4.36.0", "accelerate"
)
# 从CUDA基础镜像
image = modal.Image.from_registry(
"nvidia/cuda:12.1.0-cudnn8-devel-ubuntu22.04",
add_python="3.11"
).pip_install("torch", "transformers")
# 带系统包
image = modal.Image.debian_slim().apt_install("git", "ffmpeg").pip_install("whisper")
持久存储
volume = modal.Volume.from_name("model-cache", create_if_missing=True)
@app.function(gpu="A10G", volumes={"/models": volume})
def load_model():
import os
model_path = "/models/llama-7b"
if not os.path.exists(model_path):
model = download_model()
model.save_pretrained(model_path)
volume.commit() # 持久化更改
return load_from_path(model_path)
Web端点
FastAPI端点装饰器
@app.function()
@modal.fastapi_endpoint(method="POST")
def predict(text: str) -> dict:
return {"result": model.predict(text)}
完整ASGI应用
from fastapi import FastAPI
web_app = FastAPI()
@web_app.post("/predict")
async def predict(text: str):
return {"result": await model.predict.remote.aio(text)}
@app.function()
@modal.asgi_app()
def fastapi_app():
return web_app
Web端点类型
| 装饰器 | 用例 |
|---|---|
@modal.fastapi_endpoint() |
简单函数 → API |
@modal.asgi_app() |
完整FastAPI/Starlette应用 |
@modal.wsgi_app() |
Django/Flask应用 |
@modal.web_server(port) |
任意HTTP服务器 |
动态批处理
@app.function()
@modal.batched(max_batch_size=32, wait_ms=100)
async def batch_predict(inputs: list[str]) -> list[dict]:
# 输入自动批处理
return model.batch_predict(inputs)
秘密管理
# 创建秘密
modal secret create huggingface HF_TOKEN=hf_xxx
@app.function(secrets=[modal.Secret.from_name("huggingface")])
def download_model():
import os
token = os.environ["HF_TOKEN"]
调度
@app.function(schedule=modal.Cron("0 0 * * *")) # 每日午夜
def daily_job():
pass
@app.function(schedule=modal.Period(hours=1))
def hourly_job():
pass
性能优化
冷启动缓解
@app.function(
container_idle_timeout=300, # 保持温暖5分钟
allow_concurrent_inputs=10, # 处理并发请求
)
def inference():
pass
模型加载最佳实践
@app.cls(gpu="A100")
class Model:
@modal.enter() # 在容器启动时运行一次
def load(self):
self.model = load_model() # 在预热期间加载
@modal.method()
def predict(self, x):
return self.model(x)
并行处理
@app.function()
def process_item(item):
return expensive_computation(item)
@app.function()
def run_parallel():
items = list(range(1000))
# 分发到并行容器
results = list(process_item.map(items))
return results
常见配置
@app.function(
gpu="A100",
memory=32768, # 32GB RAM
cpu=4, # 4个CPU核心
timeout=3600, # 最大1小时
container_idle_timeout=120,# 保持温暖2分钟
retries=3, # 失败重试
concurrency_limit=10, # 最大并发容器数
)
def my_function():
pass
调试
# 本地测试
if __name__ == "__main__":
result = my_function.local()
# 查看日志
# modal app logs my-app
常见问题
| 问题 | 解决方案 |
|---|---|
| 冷启动延迟 | 增加container_idle_timeout,使用@modal.enter() |
| GPU OOM | 使用更大的GPU(A100-80GB),启用梯度检查点 |
| 镜像构建失败 | 固定依赖版本,检查CUDA兼容性 |
| 超时错误 | 增加timeout,添加检查点 |