使用 BentoCloud 无服务器 GPU 部署 Tabby:从 Bento 服务到模型缓存全流程指南
【免费下载链接】tabbySelf-hosted AI coding assistant项目地址: https://gitcode.com/GitHub_Trending/tab/tabby
本篇技术指南围绕仓库 bentoml 部署指南 展开,完整讲解如何把自托管 AI 编程助手 Tabby 部署到 BentoCloud 的 serverless GPU 基础设施上。读者将掌握service.py、bentofile.yaml、setup-docker.sh与bentodeploy.yaml四个关键文件的编写方法,理解模型预缓存加速冷启动、ASGI 反向代理、以及基于 rclone + Cloudflare R2 的持久化数据同步机制,最终通过一条bentoml deploy命令完成云端上线。
方案概览:为什么选择 BentoCloud
BentoCloud 提供面向 GPU 负载的 serverless 基础设施,支持模型的云端部署、管理与自动扩缩容。对于 Tabby 这类由补全模型(Completion)、聊天模型(Chat)与嵌入模型(Embedding)共同驱动的 AI 编程助手而言,将其封装为 Bento 服务后,可以享受 BentoCloud 的以下收益:
- 按需扩容:无需自建 GPU 集群,serverless 模型按请求量弹性伸缩;
- 模型预缓存:利用 BentoCloud 的内部文件系统把模型权重烘焙进容器镜像,避免冷启动时重新下载模型,显著缩短服务就绪时间;
- 对象存储持久化:通过部署钩子(hook)将 Tabby 运行数据(索引、事件、配置)同步到 Cloudflare R2,保证 serverless 实例销毁后数据不丢失。
整个部署链路由仓库中四个文件构成,全部位于 bentoml 部署目录:
| 文件 | 作用 |
|---|---|
| service.py | 定义 Bento 服务、启动 Tabby 子进程、声明 GPU 资源 |
| bentofile.yaml | 构建容器镜像的清单,指定 CUDA 版本、系统包与安装脚本 |
| setup-docker.sh | 镜像构建时执行:安装 Tabby 二进制、rclone、katana 并预下载模型 |
| bentodeploy.yaml | 声明部署名称、环境变量与 R2 对象存储配置 |
第一步:定义 Bento 服务与 GPU 资源
首先编写service.py来声明 Bento 服务。这份脚本通过@bentoml.service装饰器声明服务运行所需的 GPU 资源:
@bentoml.service( resources={"gpu": 1, "gpu_type": "nvidia-l4"}, traffic={"timeout": 10}, )参数含义如下:
resources.gpu:每个 worker 分配的 GPU 数量,这里为 1;resources.gpu_type:GPU 型号,这里指定为 Nvidia L4;traffic.timeout:请求超时时间(秒),这里为 10 秒,适用于代码补全这类低延迟推理场景。
BentoCloud 支持的 GPU 选型
BentoCloud 当前支持以下 GPU 规格,可按成本与推理负载权衡选择:
- T4:高性价比入门选择,配备 16GiB 显存;
- L4:中端 GPU,提供 24GiB 显存,是本教程默认选型;
- A100:云端最强算力,提供 40GiB 与 80GiB 两种显存配置,适合大模型推理。
说明:GPU 的具体定价与配额以 BentoCloud 官方定价页面为准,本仓库不包含相关价格数据,部署前请以云平台实际页面信息为准。
第二步:定义容器镜像并预缓存模型
为了让镜像"开箱即用",需要编写bentofile.yaml来构建一个预置了 Tabby 模型缓存的容器镜像。该清单指定 CUDA 版本为 11.7.1,并枚举了镜像所需的系统包与依赖:
service: 'service:Tabby' include: - '*.py' python: packages: - asgi-proxy-lib docker: cuda_version: "11.7.1" system_packages: - unzip - git - curl - software-properties-common setup_script: "./setup-docker.sh"各字段的作用:
service:指向service.py中定义的Tabby类;include:把当前目录下所有.py文件打入镜像;python.packages:声明 Python 依赖,其中asgi-proxy-lib用于建立 ASGI 代理,使云端 Web 端点与本地 Tabby 服务通过 localhost 通信;docker.cuda_version:指定基础镜像 CUDA 版本为 11.7.1,与下方 Tabby 发行版的cuda117构建一一对应;docker.system_packages:安装unzip(解压 Tabby 发行包)、git、curl与software-properties-common;docker.setup_script:指向镜像构建阶段执行的setup-docker.sh。
setup-docker.sh:安装 Tabby 并预取模型权重
setup-docker.sh在镜像构建阶段执行,核心任务是安装 Tabby 二进制并下载三类模型权重。仓库中的实际脚本比文档示例更完整,还额外安装了 katana(爬虫工具)与 rclone(对象存储同步工具),并配置了 git 的 safe.directory:
#!/bin/sh set -ex # Install tabby DISTRO=tabby_x86_64-manylinux2014-cuda117 curl -L https://github.com/TabbyML/tabby/releases/download/v0.14.0/$DISTRO.zip \ -o $DISTRO.zip unzip $DISTRO.zip chmod a+x dist/$DISTRO/* mv dist/$DISTRO/* /usr/local/bin/ rm $DISTRO.zip rm -rf dist # Install katana curl -L https://github.com/projectdiscovery/katana/releases/download/v1.1.2/katana_1.1.2_linux_amd64.zip -o katana.zip unzip katana.zip katana mv katana /usr/bin/ rm katana.zip # Install rclone curl https://rclone.org/install.sh | bash # Config git git config --system --add safe.directory "*" # Download models su bentoml -c "TABBY_MODEL_CACHE_ROOT=/home/bentoml/tabby-models tabby download --model StarCoder-1B" su bentoml -c "TABBY_MODEL_CACHE_ROOT=/home/bentoml/tabby-models tabby download --model Qwen2-1.5B-Instruct" su bentoml -c "TABBY_MODEL_CACHE_ROOT=/home/bentoml/tabby-models tabby download --model Nomic-Embed-Text"关键点解读:
- 发行版与 CUDA 匹配:下载的是
tabby_x86_64-manylinux2014-cuda117发行包,其中的cuda117与bentofile.yaml声明的 CUDA 11.7.1 匹配,确保推理在 GPU 上可用; - 以
bentoml用户运行下载:BentoCloud 服务运行在bentoml用户下,因此模型下载必须通过su bentoml -c以该用户身份执行,否则模型缓存目录权限不匹配会导致服务启动时读不到模型; - 模型缓存根目录重定向:通过环境变量
TABBY_MODEL_CACHE_ROOT=/home/bentoml/tabby-models把模型下载到 BentoCloud 内部文件系统,这正是"利用内部文件系统免去重新下载、加速冷启动"的关键。
从源码看,TABBY_MODEL_CACHE_ROOT是 Tabby 官方支持的环境变量。tabby-common 的 path.rs 中通过env::var("TABBY_MODEL_CACHE_ROOT")读取该变量,并据此确定模型目录:
static ref TABBY_MODEL_CACHE_ROOT: Option<PathBuf> = env::var("TABBY_MODEL_CACHE_ROOT").ok().map(PathBuf::from);models_dir()函数(path.rs)在设置了该变量时优先返回缓存根目录,否则回退到默认的~/.tabby/models。而tabby download命令的 CLI 参数(模型 ID 与--prefer-local-file开关)在 download.rs 中定义,底层调用tabby_download::download_model完成权重拉取。
第三步:Service 定义详解
service.py的核心是把 Tabby 封装为一个 BentoML 服务,整体逻辑分四层:
- 启动 Tabby 进程并等待就绪;
- 建立 ASGI 代理,把 BentoCloud Web 端点的请求转发到本地 Tabby 服务器(
127.0.0.1:8000); - 为每个 worker 分配 1 块 Nvidia L4 GPU,并设置 10 秒请求超时;
- 通过
on_deployment与on_shutdown钩子,在部署与销毁时把持久化数据在本地与对象存储之间双向同步。
仓库中完整实现如下(service.py):
from __future__ import annotations from asgi_proxy import asgi_proxy import os import time import bentoml import socket import subprocess class TabbyServer: def __init__(self, model_id: str, chat_model_id: str) -> None: self.launcher = subprocess.Popen( [ "tabby", "serve", "--model", model_id, "--chat-model", chat_model_id, "--device", "cuda", "--port", "8000", ] ) def ready(self) -> bool: 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 = self.launcher.poll() if retcode is not None: raise RuntimeError(f"launcher exited unexpectedly with code {retcode}") return False def wait_until_ready(self) -> None: while not self.ready(): time.sleep(1.0) app = asgi_proxy("http://127.0.0.1:8000") @bentoml.service( resources={"gpu": 1, "gpu_type": "nvidia-l4"}, traffic={"timeout": 10}, ) @bentoml.mount_asgi_app(app, path="/") class Tabby: @bentoml.on_deployment def prepare(): download_tabby_dir("tabby-local") @bentoml.on_shutdown def shutdown(self): upload_tabby_dir("tabby-local") def __init__(self) -> None: model_id = "StarCoder-1B" chat_model_id = "Qwen2-1.5B-Instruct" # Start the server subprocess. self.server = TabbyServer(model_id, chat_model_id) # Wait for the server to be ready. self.server.wait_until_ready() def download_tabby_dir(username: str) -> None: """Download the tabby directory for the given user.""" # Ensure the bucket `tabby-cloud-managed` and the path `users/tabby-local` exist in your R2 storage if os.system(f"rclone sync r2:/tabby-cloud-managed/users/{username} ~/.tabby") == 0: print("Tabby directory downloaded successfully.") else: raise RuntimeError("Failed to download tabby directory") def upload_tabby_dir(username: str) -> None: """Upload the tabby directory for the given user.""" if os.system(f"rclone sync --links ~/.tabby r2:/tabby-cloud-managed/users/{username}") == 0: print("Tabby directory uploaded successfully.") else: raise RuntimeError("Failed to upload tabby directory")几个值得注意的实现细节:
- 子进程管理:
TabbyServer通过subprocess.Popen以 CUDA 设备、8000 端口启动tabby serve,并显式传入补全模型StarCoder-1B与聊天模型Qwen2-1.5B-Instruct; - 就绪探测:
ready()用 1 秒超时的 TCP 连接探测127.0.0.1:8000;若连接失败还会检查子进程是否已退出(launcher.poll()),一旦异常退出立即抛出RuntimeError,避免无限等待; - ASGI 挂载:
@bentoml.mount_asgi_app(app, path="/")把asgi_proxy("http://127.0.0.1:8000")创建的代理挂载到服务根路径,外部请求因此被透明转发到本地 Tabby; - 数据双向同步:
on_deployment时用rclone sync把 R2 中的users/tabby-local拉到~/.tabby;on_shutdown时反向推送(带--links保留符号链接)。这保证了索引等运行态数据在 serverless 实例生命周期间的连续性。
从服务端源码可以印证模型加载流程:serve.rs 在启动时按需调用download_model_if_needed拉取 Completion、Chat、Embedding 三类模型——这也解释了setup-docker.sh为何要预下载恰好三个模型(StarCoder-1B、Qwen2-1.5B-Instruct、Nomic-Embed-Text),分别对应补全、聊天与嵌入三类推理任务。模型落盘路径遵循{TABBY_MODEL_CACHE_ROOT}/TabbyML/{model_id}/ggml的结构(见 registry.rs 中关于模型目录的注释)。
第四步:编写部署配置(bentodeploy.yaml)
部署配置声明了部署名称、Bento 路径、鉴权开关以及运行环境变量。其中持久化数据通过 rclone 与 Cloudflare R2 对象存储同步:
name: tabby-local bento: ./ access_authorization: false envs: - name: RCLONE_CONFIG_R2_TYPE value: s3 - name: RCLONE_CONFIG_R2_ACCESS_KEY_ID value: $YOUR_R2_ACCESS_KEY_ID - name: RCLONE_CONFIG_R2_SECRET_ACCESS_KEY value: $YOUR_R2_SECRET_ACCESS_KEY - name: RCLONE_CONFIG_R2_ENDPOINT value: $YOUR_R2_ENDPOINT - name: TABBY_MODEL_CACHE_ROOT value: /home/bentoml/tabby-models配置要点:
name:部署名称tabby-local,同时作为 R2 中的对象路径(users/tabby-local);bento:指向当前目录的 Bento 构建上下文;access_authorization:设为false表示部署端点不做额外鉴权;RCLONE_CONFIG_R2_*系列环境变量:以 S3 兼容协议配置 rclone 连接 Cloudflare R2,包括 Access Key ID、Secret Access Key 与 Endpoint。R2 的访问凭证与端点值需要根据 Cloudflare R2 的 S3 API Token 文档自行获取并替换$YOUR_*占位符;TABBY_MODEL_CACHE_ROOT:必须与setup-docker.sh中下载模型的路径保持一致(/home/bentoml/tabby-models),运行时才能命中预缓存的模型。
另外需在 R2 存储中预先创建 buckettabby-cloud-managed以及users/tabby-local路径(见 service.py 的注释说明),否则on_deployment钩子中的 rclone 同步会失败。
第五步:部署并访问服务
完成上述四个文件后,在目录下执行:
bentoml deploy -f bentodeploy.yaml该命令会构建 Bento、创建 BentoCloud 部署并上线应用。部署成功后,通过控制台提供的 URL 访问服务,例如:
https://$YOUR_DEPLOYMENT_SLUG.mt-guc1.bentoml.ai将$YOUR_DEPLOYMENT_SLUG替换为你的实际部署 slug 即可。部署运行截图可参考 app-running.png,图中展示了服务上线后的状态页面。
此时可以在 IDE 客户端中将 Tabby 的服务器地址配置为上述 URL,即可开始使用云端 GPU 提供的代码补全与问答能力。
总结与注意事项
本方案的本质是一套"镜像预缓存 + 子进程托管 + 对象存储持久化"的组合:
- 镜像预缓存:利用 BentoCloud 内部文件系统在构建期下载模型,避免冷启动时的网络拉取;
- 进程托管:BentoML 服务内以子进程方式运行
tabby serve,并用 TCP 探测保证就绪后才对外服务; - 数据持久化:rclone + R2 在部署生命周期钩子中同步
~/.tabby,使索引与事件数据跨实例留存。
实操中还需注意:CUDA 版本(11.7.1)必须与 Tabby 发行版(cuda117)匹配;所有模型下载须以bentoml用户执行;R2 的 bucket 与路径需预先创建;bentodeploy.yaml中的TABBY_MODEL_CACHE_ROOT必须与构建脚本一致。完整可运行的四个文件均保留在仓库的 bentoml 部署目录 中,可直接对照参考。
【免费下载链接】tabbySelf-hosted AI coding assistant项目地址: https://gitcode.com/GitHub_Trending/tab/tabby
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考