Ray Serve 部署 Stable Diffusion 图像生成服务:从模型加载到弹性伸缩的完整实战
【免费下载链接】rayRay is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.项目地址: https://gitcode.com/gh_mirrors/ra/ray
导读
本文基于 Ray 官方仓库中的 Stable Diffusion 教程(doc/source/serve/tutorials/stable-diffusion.md),完整讲解如何用 Ray Serve 将一个 Stable Diffusion XL 图像生成模型封装为 HTTP 服务。你将学会:用 FastAPI 定义/imagine接口、用 DeploymentHandle 串联"入口 + 模型"两个 Deployment、通过serve run一条命令启动服务,以及利用min_replicas: 0的自动伸缩配置在 GPU 上实现"无请求不占资源、有请求自动拉起"的按需推理。文中代码与配置均取自仓库中的 stable_diffusion.py,并辅以 Serve 源码佐证其底层机制。
1. 示例概览与架构
这个示例在 Ray Serve 上运行一个 Stable Diffusion 图像生成应用。它由两个 Deployment 组成:
- APIIngress:基于 FastAPI 的 HTTP 入口,接收用户的 prompt 请求,并把推理任务转发给下游模型 Deployment;
- StableDiffusionXL:真正持有 SDXL 模型的 GPU 推理 Deployment,负责执行文生图(text-to-image)扩散采样。
两者通过DeploymentHandle以远程调用的方式协作,入口与模型解耦,模型层可以独立水平扩展。整个应用以APIIngress.bind(StableDiffusionXL.bind())组合成一个 Serve Application,启动后对外暴露http://127.0.0.1:8000/imagine接口。
本示例使用的模型为stabilityai/stable-diffusion-xl-base-1.0,Web 框架为 FastAPI。
2. 环境安装
运行本示例需要先安装 Ray Serve 以及推理相关的依赖:
pip install "ray[serve]" requests torch diffusers==0.35.2 transformers各依赖的职责如下:
| 依赖 | 用途 |
|---|---|
ray[serve] | Ray 分布式运行时与 Serve 组件 |
requests | 客户端发送 HTTP 请求、下载结果图片 |
torch | 深度学习框架,驱动扩散模型推理 |
diffusers==0.35.2 | Hugging Face 扩散模型管线(DiffusionPipeline) |
transformers | 加载 SDXL 所需的文本编码器等模型组件 |
注意:示例代码在 2023 年编写,当时依赖为
diffusers==0.33.1、transformers==4.51.3(见 stable_diffusion.py 中__main__的 runtime_env 写法)。教程正文指定了diffusers==0.35.2,实际运行时请以你所用 Ray 版本兼容的依赖组合为准。推理需要一张可用的 CUDA GPU。
将下面第 3 节中的代码保存为stable_diffusion.py,即可按第 4 节方式启动。
3. 代码逐段解析
完整示例代码位于 doc/source/serve/doc_code/stable_diffusion.py,这里按职责拆解。
3.1 导入与 FastAPI 入口(APIIngress)
from io import BytesIO from fastapi import FastAPI from fastapi.responses import Response import torch from ray import serve from ray.serve.handle import DeploymentHandle app = FastAPI() @serve.deployment(num_replicas=1) @serve.ingress(app) class APIIngress: def __init__(self, diffusion_model_handle: DeploymentHandle) -> None: self.handle = diffusion_model_handle @app.get( "/imagine", responses={200: {"content": {"image/png": {}}}}, response_class=Response, ) async def generate(self, prompt: str, img_size: int = 512): assert len(prompt), "prompt parameter cannot be empty" image = await self.handle.generate.remote(prompt, img_size=img_size) file_stream = BytesIO() image.save(file_stream, "PNG") return Response(content=file_stream.getvalue(), media_type="image/png")几个关键点:
@serve.deployment(num_replicas=1):声明该 Deployment 固定 1 个副本,作为轻量入口无需伸缩;@serve.ingress(app):把 FastAPI 应用挂到 Deployment 上。源码中该装饰器的定义见 python/ray/serve/api.py,其作用是把 ASGI 应用包装进 Deployment,用于解析 HTTP 请求;- 构造函数接收一个
DeploymentHandle类型的参数——这是 Ray Serve 注入子 Deployment 句柄的约定写法。DeploymentHandle类定义见 python/ray/serve/handle.py; await self.handle.generate.remote(prompt, img_size=img_size):通过 handle 异步调用下游StableDiffusionXL.generate方法,remote()返回的是一个可等待对象,await后得到 PIL Image;- 接口响应头声明
image/png媒体类型,最终把 PNG 二进制作为 HTTP 响应体返回。
3.2 GPU 推理模型(StableDiffusionXL)
@serve.deployment( ray_actor_options={"num_gpus": 1}, autoscaling_config={"min_replicas": 0, "max_replicas": 2}, ) class StableDiffusionXL: def __init__(self): from diffusers import DiffusionPipeline model_id = "stabilityai/stable-diffusion-xl-base-1.0" self.pipe = DiffusionPipeline.from_pretrained( model_id, torch_dtype=torch.float16, variant="fp16", use_safetensors=True ) self.pipe = self.pipe.to("cuda") def generate(self, prompt: str, img_size: int = 512): assert len(prompt), "prompt parameter cannot be empty" with torch.autocast("cuda"): image = self.pipe(prompt, height=img_size, width=img_size).images[0] return image关键点:
ray_actor_options={"num_gpus": 1}:要求每个副本占用 1 张 GPU。Serve 会将该字段传递给 Ray Actor 的调度选项,源码见 python/ray/serve/deployment.py 的ray_actor_options属性;autoscaling_config={"min_replicas": 0, "max_replicas": 2}:启用自动伸缩,副本数在 0~2 之间按流量动态调整(详见第 5 节);- 模型加载使用 FP16 精度(
torch_dtype=torch.float16),并指定variant="fp16"下载官方 fp16 权重、use_safetensors=True使用 safetensors 格式,兼顾显存占用与加载安全; generate在torch.autocast("cuda")上下文里执行一次扩散采样,返回self.pipe(...).images[0]这张 PIL 图像。
3.3 组装应用
entrypoint = APIIngress.bind(StableDiffusionXL.bind())bind()把 Deployment 类实例化为一个带参数的 Application 蓝图,嵌套的bind表达依赖关系:APIIngress 在启动时自动拿到 StableDiffusionXL 的 DeploymentHandle。
4. 启动服务:serve run
在stable_diffusion.py所在目录执行:
serve run stable_diffusion:entrypointserve run是 Ray Serve 的命令行入口,其实现位于 python/ray/serve/scripts.py。该命令会:
- 解析
module:attribute形式的导入路径,导入stable_diffusion模块并取到entrypoint; - 若 Ray 尚未初始化,自动执行
ray.init(...)(源码见 scripts.py); - 启动 Serve 控制器与 HTTP 代理(默认监听
127.0.0.1:8000); - 部署应用并阻塞等待,方便观察日志。
serve run还支持--runtime-env、--working-dir、--route-prefix、--name等选项(见 scripts.py),例如可指定--name my-sdxl为应用命名;也可以传入一个 Serve config YAML 文件来声明多应用与 HTTP 选项。
启动成功后,终端会输出类似下面的日志:
(ServeController pid=362, ip=10.0.44.233) INFO 2023-03-08 16:44:57,579 controller 362 http_state.py:129 - Starting HTTP proxy with name 'SERVE_CONTROLLER_ACTOR:SERVE_PROXY_ACTOR-7396d5a9efdb59ee01b7befba448433f6c6fc734cfa5421d415da1b3' on node '7396d5a9efdb59ee01b7befba448433f6c6fc734cfa5421d415da1b3' listening on '127.0.0.1:8000' (ServeController pid=362, ip=10.0.44.233) INFO 2023-03-08 16:44:57,588 controller 362 http_state.py:133 - Starting HTTP proxy with name 'SERVE_CONTROLLER_ACTOR:SERVE_PROXY_ACTOR-a30ea53938547e0bf88ce8672e578f0067be26a7e26d23465c46300b' on node 'a30ea53938547e0bf88ce8672e578f0067be26a7e26d23465c46300b' listening on '127.0.0.1:8000' (ProxyActor pid=439, ip=10.0.44.233) INFO: Started server process [439] (ProxyActor pid=5779) INFO: Started server process [5779] (ServeController pid=362, ip=10.0.44.233) INFO 2023-03-08 16:44:59,362 controller 362 deployment_state.py:1333 - Adding 1 replica to deployment 'APIIngress'. 2023-03-08 16:45:01,316 SUCC <string>:93 -- Deployed Serve app successfully.日志要点:
Starting HTTP proxy ... listening on '127.0.0.1:8000':HTTP 代理已在 8000 端口就绪;Adding 1 replica to deployment 'APIIngress':控制器开始为入口 Deployment 创建副本;Deployed Serve app successfully.:应用部署成功,可以开始接收请求。
5. 从 0 到 1 的 GPU 自动伸缩(核心机制)
教程特别强调了一个设计:autoscaling_config将min_replicas设为 0,意味着StableDiffusionXL初始没有任何副本,只有当请求真正到达时才会拉起副本;当一段时间没有请求时,Serve 会把它缩回 0 个副本以释放 GPU 资源。
这套机制在源码中的落点是AutoscalingConfig类(python/ray/serve/config.py),相关字段说明:
| 字段 | 默认值 | 作用 |
|---|---|---|
min_replicas | 1 | 最小副本数,设为 0 表示允许缩容到零(scale-to-zero) |
max_replicas | 1 | 最大副本数,必须不小于min_replicas |
target_ongoing_requests | 2 | 每个副本的目标并发请求数(含排队),缩放器以此为依据决定扩缩容 |
look_back_period_s | 30.0 | 指标聚合的回看时间窗口 |
upscale_delay_s | 30.0 | 检测到需要扩容后等待的秒数,防止抖动 |
downscale_delay_s | 600.0 | 缩容(到 >0 的值)前的等待秒数 |
downscale_to_zero_delay_s | None | 从 1 缩到 0 前的等待秒数,不设则沿用downscale_delay_s |
upscaling_factor/downscaling_factor | None | 每次扩容/缩容决策的乘性"增益"系数,抑制震荡 |
当min_replicas=0时,Autoscaler 周期性地聚合回看窗口内的请求指标,与target_ongoing_requests比较后把副本数调整到 0~2 之间(逻辑见 python/ray/serve/_private/application_state.py 中AutoscalingConfig(**new_config)的装配)。值得注意的是:缩容到 0 虽然省 GPU,但恢复服务时会引入冷启动延迟——重新拉起副本需要重新加载 SDXL 模型(数 GB 权重),首次请求会明显变慢。因此该模式适合低频、突发型的调用场景。
6. 发送请求并保存图片
服务启动后,用下面的 Python 代码发起推理请求:
import requests prompt = "a cute cat is dancing on the grass." input = "%20".join(prompt.split(" ")) resp = requests.get(f"http://127.0.0.1:8000/imagine?prompt={input}") with open("output.png", 'wb') as f: f.write(resp.content)说明:
- 因为接口通过 URL query 接收 prompt,所以这里先把句子中的空格替换为
%20编码,避免中文/空格破坏 URL; - 请求返回的响应体就是 PNG 图片的二进制内容,直接写入
output.png即可; - 也可以携带可选参数
img_size(默认 512)控制生成分辨率,例如?prompt=...&img_size=768,该参数会原样传给generate()中的height/width。
仓库中的示例文件在__main__分支里给出了完整自测流程(stable_diffusion.py):调用serve.run(entrypoint)后先通过 handle 远程触发一次推理预热,再走 HTTP 拉取图片并断言output.png存在。这也可以作为不依赖 CLI、在脚本内直接部署 Serve 的参考写法——对应serve.run的 API 定义见 python/ray/serve/api.py。
7. 小结与扩展建议
通过本文,你已经掌握了一条完整的"SDXL 模型服务化"链路:
- 两段式部署:FastAPI 入口与 GPU 模型推理分离,通过
DeploymentHandle解耦; - 一条命令上线:
serve run stable_diffusion:entrypoint即完成控制器启动、应用部署; - 按需占用 GPU:
min_replicas: 0实现 scale-to-zero,兼顾成本与弹性。
基于此模板可继续扩展的方向:
- 多模型/多尺寸:为不同模型(如 SD 1.5、SDXL-Turbo)各建一个 Deployment,入口按请求参数路由;
- 批处理与队列:给
generate增加 batch 逻辑,利用max_ongoing_requests控制并发; - 生产配置:改用 Serve config YAML(
serve run config.yaml)管理多应用、HTTP 选项与日志配置; - 客户端调用方式:除 HTTP 外,也可通过 DeploymentHandle 在 Python 内直接调用,降低序列化开销。
【免费下载链接】rayRay is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.项目地址: https://gitcode.com/gh_mirrors/ra/ray
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考