1. 其他教程
  2. 使用 Modal 部署 Gradio

使用 Modal 部署 Gradio 应用

简介

Gradio 是一种使用简单直观的 Python API 测试和演示机器学习应用的好方法。结合 Modal 以开发者为先的云基础设施,您可以利用强大的 GPU 更快地运行大型模型。而且您不需要云服务提供商的账户,也不需要任何配置文件。

在本教程中,我们将引导您设置一个 Modal 账户,在 Modal 上部署一个简单的 Gradio 应用,并讨论 Gradio 粘性会话要求和处理并发性的一些细节。

在 Modal 上部署简单的 Gradio 应用

让我们部署一个 Gradio 风格的“Hello, world”应用,它允许用户输入他们的名字,然后回复一个简短的问候语。我们不会在应用中原样使用此代码,但了解最初的 Gradio 版本是什么样子很有用。

import gradio as gr

# A simple Gradio interface for a greeting function
def greet(name):
    return f"Hello {name}!"

demo = gr.Interface(fn=greet, inputs="text", outputs="text")
demo.launch()

要在 Modal 上部署此应用,您需要

  • 定义您的容器镜像,
  • 将 Gradio 应用封装在 Modal Function 中,
  • 并使用 Modal 的 CLI 部署它!

先决条件:安装和设置 Modal

在开始之前,如果您还没有 Modal 账户,则需要创建一个。然后您可以使用这些账户凭据进行身份验证来设置您的环境。

  • modal.com 注册。
  • 在本地开发环境中安装 Modal 客户端。
pip install modal
  • 验证您的账户。
modal setup

太棒了,现在我们可以开始构建我们的应用了!

第 1 步:定义我们的 modal.Image

首先,让我们创建一个名为 gradio_app.py 的新文件,导入 modal,并定义我们的镜像。Modal Images 是通过对我们的 Image 实例顺序调用方法来定义的。

对于这个简单的应用,我们将

  • debian_slim 镜像开始,
  • 选择 Python 版本 (3.12),
  • 并安装依赖项——只需要 fastapigradio
import modal

app = modal.App("gradio-app")
web_image = modal.Image.debian_slim(python_version="3.12").uv_pip_install(
    "fastapi[standard]",
    "gradio",
)

请注意,您不需要在本地环境中安装 gradiofastapi——本地只需要 modal

第 2 步:将 Gradio 应用封装在 Modal 部署的 FastAPI 应用中

像许多 Gradio 应用一样,上面的示例通过在脚本末尾对我们的演示调用 launch() 来运行。然而,Modal 运行的是函数,而不是脚本——准确地说是无服务器函数。

为了让 Modal 来提供我们的 demo 服务,我们可以利用 Gradio 和 Modal 对 fastapi 应用的支持。我们使用 @modal.asgi_app() 函数装饰器来部署该函数返回的 Web 应用。我们使用 mount_gradio_app 函数将 Gradio demo 作为路由添加到 Web 应用中。

with web_image.imports():
	import gradio as gr
    from gradio.routes import mount_gradio_app
    from fastapi import FastAPI
     
@app.function(
    image=web_image,
    max_containers = 1, # we'll come to this later 
)
@modal.concurrent(max_inputs=100) # allow multiple users at one time
@modal.asgi_app()
def ui():
    """A simple Gradio interface for a greeting function."""
    def greet(name):
	    return f"Hello {name}!"
	
	demo = gr.Interface(fn=greet, inputs="text", outputs="text")

    return mount_gradio_app(app=FastAPI(), blocks=demo, path="/")

让我们快速回顾一下这里发生了什么

  • 我们使用 Image.imports 上下文管理器来定义我们的导入。当您的函数在云中运行时,这些导入将可用。
  • 我们将代码移到 Python 函数 ui 中,并用 @app.function 装饰它,将其封装为 Modal 无服务器函数。我们将镜像和其他参数(我们稍后会讨论)作为输入提供给装饰器。
  • 我们添加了 @modal.concurrent 装饰器,它允许每个容器同时处理多个请求。
  • 我们添加了 @modal.asgi_app 装饰器,它告诉 Modal 这个特定的函数正在提供一个 ASGI 应用(这里是一个 fastapi 应用)。要使用此装饰器,您的 ASGI 应用需要作为函数的返回值。

第 3 步:在 Modal 上部署

要部署应用,只需运行以下命令

modal deploy <path-to-file>

第一次运行应用时,Modal 将构建并缓存镜像,这大约需要 30 秒。只要您不更改镜像,后续部署只需几秒钟。

镜像构建完成后,Modal 将打印您的 Web 应用 URL 和 Modal 仪表板 URL。Web 应用 URL 应类似于 https://{workspace}-{environment}--gradio-app-ui.modal.run。将其粘贴到 Web 浏览器中并试用您的应用!

重要注意事项

粘性会话

Modal Function 是无服务器的,这意味着每个客户端请求都是独立的。虽然这有助于自动扩展,但如果您的应用需要某种服务器端状态,则需要格外小心。

Gradio 依赖于 REST API,它本身是无状态的。但它确实需要粘性会话,这意味着来自特定客户端的每个请求都必须路由到同一个容器。然而,Modal 在这方面不做任何保证。

满足此约束的一种简单方法是在 @app.function 装饰器中设置 max_containers = 1,并将 @modal.concurrentmax_inputs 参数设置为一个相当大的数字——就像我们上面所做的那样。这意味着 Modal 不会启动超过一个容器来为您的应用提供请求服务,这有效地满足了粘性会话要求。

并发性和队列

Gradio 和 Modal 都有并发性和队列的概念,要充分利用您的计算资源,需要了解它们如何相互作用。

Modal 将客户端请求排队到每个已部署的 Function,并同时执行请求,直到达到该 Function 的并发限制。如果请求传入且并发限制已满足,Modal 将启动一个新的容器——直到达到为该 Function 设置的最大值。在我们的案例中,我们的 Gradio 应用由一个 Modal Function 表示,因此所有请求共享一个队列和并发限制。因此,Modal 限制了运行时请求的数,无论它们在做什么。

另一方面,Gradio 允许开发者使用多个队列,每个队列都有自己的并发限制。然后可以将一个或多个事件监听器分配给一个队列,这对于管理计算开销大的请求的 GPU 资源非常有用。

仔细思考这些队列和限制如何相互作用,可以帮助您优化应用的性能和资源利用率,同时避免共享或丢失状态等不良结果。

创建 GPU Function

管理 GPU 利用率的另一种选择是将您的 GPU 计算部署在它们自己的 Modal Function 中,并从 Gradio 应用内部调用此远程 Function。这使您可以充分利用 Modal 的无服务器自动扩展功能,同时将所有客户端 HTTP 请求路由到单个 Gradio CPU 容器。

gradio