news 2026/9/20 4:01:06

Ray Serve 部署 Stable Diffusion 图像生成服务:从模型加载到弹性伸缩的完整实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ray Serve 部署 Stable Diffusion 图像生成服务:从模型加载到弹性伸缩的完整实战

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.2Hugging Face 扩散模型管线(DiffusionPipeline)
transformers加载 SDXL 所需的文本编码器等模型组件

注意:示例代码在 2023 年编写,当时依赖为diffusers==0.33.1transformers==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 格式,兼顾显存占用与加载安全;
  • generatetorch.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:entrypoint

serve run是 Ray Serve 的命令行入口,其实现位于 python/ray/serve/scripts.py。该命令会:

  1. 解析module:attribute形式的导入路径,导入stable_diffusion模块并取到entrypoint
  2. 若 Ray 尚未初始化,自动执行ray.init(...)(源码见 scripts.py);
  3. 启动 Serve 控制器与 HTTP 代理(默认监听127.0.0.1:8000);
  4. 部署应用并阻塞等待,方便观察日志。

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_configmin_replicas设为 0,意味着StableDiffusionXL初始没有任何副本,只有当请求真正到达时才会拉起副本;当一段时间没有请求时,Serve 会把它缩回 0 个副本以释放 GPU 资源。

这套机制在源码中的落点是AutoscalingConfig类(python/ray/serve/config.py),相关字段说明:

字段默认值作用
min_replicas1最小副本数,设为 0 表示允许缩容到零(scale-to-zero)
max_replicas1最大副本数,必须不小于min_replicas
target_ongoing_requests2每个副本的目标并发请求数(含排队),缩放器以此为依据决定扩缩容
look_back_period_s30.0指标聚合的回看时间窗口
upscale_delay_s30.0检测到需要扩容后等待的秒数,防止抖动
downscale_delay_s600.0缩容(到 >0 的值)前的等待秒数
downscale_to_zero_delay_sNone从 1 缩到 0 前的等待秒数,不设则沿用downscale_delay_s
upscaling_factor/downscaling_factorNone每次扩容/缩容决策的乘性"增益"系数,抑制震荡

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 模型服务化"链路:

  1. 两段式部署:FastAPI 入口与 GPU 模型推理分离,通过DeploymentHandle解耦;
  2. 一条命令上线serve run stable_diffusion:entrypoint即完成控制器启动、应用部署;
  3. 按需占用 GPUmin_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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 4:00:37

视频号小店打印组件1.8.3安装配置与故障排查全指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 4:00:33

LibreChat自托管AI聊天平台:部署与多模型聚合实战

最早有自托管AI聊天工具这个念头&#xff0c;是因为我在日常工作里要同时用好几个平台的模型——写代码用GPT系列&#xff0c;长文本分析用Claude&#xff0c;偶尔还要接本地部署的小模型跑测试。每个平台一个网页&#xff0c;账号密码来回切&#xff0c;上下文还不同步&#x…

作者头像 李华
网站建设 2026/9/20 4:00:08

Python程序如何彻底停止?交互模式、死循环、后台进程全攻略

对着黑乎乎的终端窗口发愣&#xff0c;应该是每个Python初学者都会经历的一幕&#xff1a;明明只是想写个循环练手&#xff0c;结果程序跑起来就再也停不下来&#xff1b;或者刚打开python命令进入>>>提示符&#xff0c;想退出却试遍了quit、exit、close都不得其法&am…

作者头像 李华
网站建设 2026/9/20 3:57:32

OpenHands 实战:TaoToken 跑通 Kimi K2.7 Code 修复一个 Python 仓库 issue

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 3:57:17

Kimi Code VS Code使用教程:AI编程助手安装配置与实战技巧

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华