在 Modal 无服务器 GPU 上按需部署 Tabby:完整实操指南
【免费下载链接】tabbySelf-hosted AI coding assistant项目地址: https://gitcode.com/GitHub_Trending/tab/tabby
Modal 是一个 serverless GPU 平台,通过它运行 Tabby 可以实现"按需启动、空闲休眠"的弹性部署:当一段时间没有请求到达时,Modal 会自动让容器休眠以节省 GPU 成本。本文以 modal/index.md 为核心教程骨架,结合仓库中 app.py 的完整代码与 Tabby 服务端源码,从镜像构建、模型预热、ASGI 代理到modal serve发布,带你一步步把 Tabby 跑在云端,并给出每个配置项的源码级解释。
为什么选择 Modal 运行 Tabby
Tabby 是一个自托管的 AI 编码助手(self-hosted AI coding assistant),通常以tabby serve方式常驻运行,持续占用一块 GPU。而 Modal 的价值在于:
- 按需运行(on demand):Tabby 实例只在有请求时才启动;
- 自动休眠省成本:当 Tabby 服务器一段时间内没有请求时,Modal 会调度容器休眠(sleep),从而停止 GPU 计费;
- 弹性并发:容器内可配置同时处理多个请求,空闲容器可保留一段时间后再回收。
这种模式非常适合个人开发者或小团队:既拥有自托管 Tabby 的全部能力,又不必为 24 小时空转的 GPU 付费。
环境准备与核心变量
首先在 Python 脚本中导入 Modal 的必要组件:
from modal import Image, App, asgi_app, gpuImage:定义运行容器镜像;App:声明一个 Modal 应用;asgi_app:把 Tabby 的 HTTP 服务挂载为 Modal 的 web endpoint;gpu:声明 GPU 配置。
然后设置基础变量:
IMAGE_NAME = "tabbyml/tabby" MODEL_ID = "TabbyML/StarCoder-1B" CHAT_MODEL_ID = "TabbyML/Qwen2-1.5B-Instruct" EMBEDDING_MODEL_ID = "TabbyML/Nomic-Embed-Text" GPU_CONFIG = gpu.T4() TABBY_BIN = "/opt/tabby/bin/tabby"各变量含义如下:
| 变量 | 作用 |
|---|---|
IMAGE_NAME | 基础 Docker 镜像,使用 Tabby 官方镜像tabbyml/tabby |
MODEL_ID | 代码补全模型,/completions端点使用(仓库中 serve.rs 注释明确其为/completionsAPI 的模型) |
CHAT_MODEL_ID | 对话模型,供/chat/completions端点使用(见 serve.rs) |
EMBEDDING_MODEL_ID | 嵌入模型,用于代码搜索 / 文档检索等 embedding 服务 |
GPU_CONFIG | Modal 的 GPU 配置,这里选用gpu.T4() |
TABBY_BIN | Tabby 二进制在镜像内的路径/opt/tabby/bin/tabby,与官方 Dockerfile 的安装位置一致(见 Dockerfile.cuda) |
值得注意的是:Tabby 官方镜像默认的ENTRYPOINT就是/opt/tabby/bin/tabby(见 Dockerfile.cuda),后续构建镜像时需要清空它以交给 Modal 接管进程。
选择 GPU:Modal 支持的配置
教程中GPU_CONFIG = gpu.T4()选择了低成本 T4,同时给出 Modal 目前支持的 GPU 清单(数据来自官方教程原文):
| GPU | 显存 | 定位与说明 |
|---|---|---|
T4 | 16 GiB | 低成本选项,适合小模型 |
L4 | 24 GiB | 中端选项 |
A100 | 40 / 80 GiB | 云端最强 GPU 之一 |
H100 | 80 GiB | Hopper 架构旗舰数据中心 GPU,强化 FP8 精度支持,内置 Transformer Engine,对 GPT-3 (175B) 级模型训练较上一代最高快 4 倍 |
A10G | — | 相比 T4,ML 训练性能最高提升 3.3 倍、推理性能最高提升 3 倍、图形性能最高提升 3 倍 |
Any | — | 按可用性自动选择任意 GPU 类别 |
选择 GPU 的关键标准是:让所选模型能装进显存(fit the model into VRAM)。1B 级补全模型 + 1.5B 级对话模型 + 嵌入模型,在 T4 的 16 GiB 显存内可以合理共存,这也是教程选择gpu.T4()的原因。
定义容器镜像:预热模型权重
为了让容器冷启动更快,教程把模型权重预置进镜像,这样容器启动时无需重新下载模型,而是利用 Modal 内部文件系统实现更快的冷启动。
下载权重的函数
def download_model(model_id: str): import subprocess subprocess.run( [ TABBY_BIN, "download", "--model", model_id, ] )该函数调用tabby download --model <model_id>。在服务端源码中,download子命令对应 download.rs:它接收--model(模型 ID)和--prefer-local-file(跳过远程文件检查,默认 false)两个参数,底层通过 tabby-download 从模型注册表解析模型信息(含 sha256 校验)并下载权重。也就是镜像构建期间就把权重落盘,运行时serve阶段无需再联网拉取。
组装镜像
image = ( Image.from_registry( IMAGE_NAME, add_python="3.11", ) .dockerfile_commands("ENTRYPOINT []") .run_function(download_model, kwargs={"model_id": EMBEDDING_MODEL_ID}) .run_function(download_model, kwargs={"model_id": CHAT_MODEL_ID}) .run_function(download_model, kwargs={"model_id": MODEL_ID}) .pip_install("asgi-proxy-lib") )构建过程分四步:
Image.from_registry(IMAGE_NAME, add_python="3.11"):基于tabbyml/tabby镜像,并补充 Python 3.11 运行时;.dockerfile_commands("ENTRYPOINT []"):清空官方镜像默认的 ENTRYPOINT(即tabby命令入口),让 Modal 以自己的入口运行,实现 seamless serverless 部署;- 三个
.run_function(download_model, ...):依次预下载嵌入、对话、补全三个模型的权重,写入镜像层; .pip_install("asgi-proxy-lib"):安装asgi-proxy-lib,用于在 localhost 上对接 Modal 的 ASGI web server。
应用函数:启动 Tabby 并桥接 ASGI
核心端点函数用 Modal 的@app.function表示,完成四件事:启动 Tabby 进程并等待就绪、创建 ASGI 代理把 Modal web endpoint 的请求隧道转发到本地 Tabby 服务、设置单容器最大并发请求数、设置空闲容器回收时间。
app = App("tabby-server", image=image) @app.function( gpu=GPU_CONFIG, allow_concurrent_inputs=10, container_idle_timeout=120, timeout=360, ) @asgi_app() def app_serve(): import socket import subprocess import time from asgi_proxy import asgi_proxy launcher = subprocess.Popen( [ TABBY_BIN, "serve", "--model", MODEL_ID, "--chat-model", CHAT_MODEL_ID, "--port", "8000", "--device", "cuda", "--parallelism", "1", ] ) # Poll until webserver at 127.0.0.1:8000 accepts connections before running inputs. def tabby_ready(): try: socket.create_connection(("127.0.0.1", 8000), timeout=1).close() return True except (socket.timeout, ConnectionRefusedError): # Check if launcher webserving process has exited. # If so, a connection can never be made. retcode = launcher.poll() if retcode is not None: raise RuntimeError(f"launcher exited unexpectedly with code {retcode}") return False while not tabby_ready(): time.sleep(1.0) print("Tabby server ready!") return asgi_proxy("http://localhost:8000")Modal 函数参数逐项解读
| 参数 | 值 | 含义 |
|---|---|---|
gpu | GPU_CONFIG(T4) | 指定容器 GPU |
allow_concurrent_inputs | 10 | 单个容器允许同时处理最多 10 个请求 |
container_idle_timeout | 120 | 空闲容器保留 120 秒(2 分钟)后回收 |
timeout | 360 | 函数执行超时上限 360 秒 |
Tabby serve 参数与源码对应
启动命令对应tabby serve,其参数在 serve.rs 中定义:
--model <MODEL_ID>:指定/completions补全模型;--chat-model <CHAT_MODEL_ID>:指定/chat/completions对话模型(设置它后,serve.rs 才会注册/v1/chat/completions与/v1beta/chat/completions路由,否则这两个路由返回501 NOT_IMPLEMENTED);--port 8000:监听端口 8000(默认是 8080,见 serve.rs);--device cuda:使用 GPU 推理(默认是 CPU,见 serve.rs);--parallelism 1:模型推理并行度。源码注释明确提示:增加该值会显著提升内存(尤其是显存)占用(serve.rs),在 T4 上设为 1 是保守且合理的选择。
merge_args(serve.rs)会把 CLI 参数合并进配置:--model覆盖 completion 配置,--chat-model覆盖 chat 配置(若config.toml中已有同名模型会打印覆盖警告)。因此本教程通过命令行参数注入三个模型,完全等价于在config.toml中配置对应模型字段。
就绪探活(health check)
tabby_ready()每 1 秒尝试连接127.0.0.1:8000:
- 连接成功 → 返回 True,Tabby 已就绪;
- 超时 / 拒绝连接 → 检查
launcher进程是否已退出,若已退出则抛出RuntimeError(避免无限空等),否则继续轮询。
这一步非常关键:Modal 只有等app_serve返回 ASGI 代理后才开始路由流量,所以必须先确保 Tabby 内部 HTTP 服务真正可接受连接。
ASGI 代理桥接
asgi_proxy("http://localhost:8000")把 Modal web endpoint 收到的请求,在容器内隧道转发到 Tabby 的 8000 端口。对客户端(编辑器扩展)而言,看到的就是一个标准的 Tabby server URL。
发布服务:modal serve
在包含app.py的目录下执行:
modal serve app.py部署成功后,Modal 会输出 web endpoint 地址,格式为:
https://<USERNAME>--tabby-server-app-serve-dev.modal.runtabby-server对应App("tabby-server", ...)的应用名,app-serve对应@app.function装饰的函数名,dev表示modal serve的开发(dev)环境。
强制重建镜像
如果遇到问题,尤其是缓存相关的问题(比如镜像里拉到了旧的模型权重或旧的镜像 tag),可以用强制重建来忽略缓存层、拉取最新镜像 tag:
MODAL_FORCE_BUILD=1 modal serve app.py这也与app.py头部注释中的用法说明一致(见 app.py)。
部署完成后,把输出的 URL 填入 Tabby 编辑器扩展的 server url 配置,即可开始使用云端代码补全与对话能力。完整可运行的代码可直接参考教程同目录的 app.py。
小结
本文完整走通了"Modal 无服务器 GPU + Tabby"的部署链路:用Image预置三个模型的权重以加速冷启动,用@app.function(gpu=..., allow_concurrent_inputs=..., container_idle_timeout=...)声明按需运行与休眠策略,用asgi_proxy将本地 8000 端口的 Tabby HTTP 服务桥接为 Modal web endpoint,最后通过modal serve app.py一键发布。核心收益是:只有请求到来时才启动 GPU 容器,空闲 2 分钟自动休眠,GPU 成本随流量按需产生——这正是自托管 AI 编码助手在云端低成本运行的典型姿势。
如果你对 Tabby 的 serve 参数或模型下载机制感兴趣,可以继续阅读 serve.rs 与 tabby-download 的源码实现。
【免费下载链接】tabbySelf-hosted AI coding assistant项目地址: https://gitcode.com/GitHub_Trending/tab/tabby
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考