1. 为什么要在 NAS 上折腾 Octopus
手里攒了好几个大模型的 API Key,这大概是过去一年里很多 AI 玩家的真实写照。DeepSeek 用来写代码,智谱用来处理中文长文本,Kimi 用来啃 PDF,偶尔还想调一下本地跑的 Ollama 模型做点隐私相关的活儿。结果就是浏览器里开着四五个标签页,每个平台一套界面、一套计费逻辑、一套调用方式,写个小脚本还得在代码里硬编码一堆base_url和api_key,改一次配置就得重新翻一遍文档。
Octopus 这个项目解决的正是这个痛点——它把自己定位成一个 LLM API 的聚合网关,你只需要在它里面配置好各个厂商的渠道,对外就暴露一套统一的 OpenAI 兼容接口。任何支持自定义base_url的客户端,比如 Chatbox、NextChat、LobeChat、沉浸式翻译,甚至你自己写的 Python 脚本,都只需要填一个地址、一个 Key,剩下的路由、负载均衡、失败重试全部交给 Octopus 处理。
那为什么非要部署在 NAS 上?我自己的考虑有三点。第一,NAS 是 7x24 小时开机的,把它当成家里的 AI 网关,手机、平板、电脑、电视盒子随时都能连上,不用每次开电脑才想起来服务没启动。第二,数据不出内网,API Key 集中存在自己家里,比散落在各个客户端里安全得多,尤其是家里有小孩或者多人共用设备的情况。第三,NAS 上跑 Docker 太方便了,群晖、绿联、飞牛这些系统现在都有图形化的容器管理,拉个镜像、映射个端口、挂个配置目录,十分钟就能跑起来,比在云服务器上还要配安全组、配域名省心。
这篇文章我会把整个部署过程拆开讲清楚:从 NAS 环境准备、Docker 部署、渠道配置、模型映射,到客户端接入和常见坑的排查。不管你是刚入手 NAS 的新手,还是已经玩了一段时间 Docker 的老玩家,都能照着做下来。核心关键词就几个:NAS、Octopus、API、大模型、LLM,围绕这几个词把该说的都说透。
2. 部署前的整体设计与选型思路
2.1 Octopus 到底是个什么东西
先把概念理清楚,不然后面配置渠道的时候容易懵。Octopus 本质上是一个反向代理 + 路由分发的服务,它自己不产生任何 AI 能力,只是站在你和各个大模型厂商之间做中转。你可以把它想象成家里的路由器:宽带只有一根,但路由器可以分给手机、电脑、电视同时用,而且还能根据设备类型分配不同的带宽策略。
它对外暴露的接口格式是OpenAI 兼容格式,也就是/v1/chat/completions这一套。这是目前事实上的行业标准,几乎所有主流客户端都支持。对内,它维护着一张"渠道表",每个渠道包含厂商地址、API Key、支持的模型列表、权重、优先级等信息。当你发来一个请求,Octopus 会根据请求里的模型名去匹配对应的渠道,然后转发出去,拿到结果再原样返回。
这个设计带来的好处很直接:客户端零改造。你不需要去研究每个厂商的 SDK 差异,也不需要为了换模型去改代码,只要在 Octopus 后台加一个渠道,所有客户端立刻就能用上新模型。
2.2 为什么选 Docker 而不是直接装
NAS 上部署软件有几种方式:套件中心安装、手动编译、Docker。Octopus 官方推荐的就是 Docker,我实测下来也确实是最省事的路径,原因有几个。
依赖隔离。Octopus 是 Go 写的,理论上编译出来是个单文件二进制,但它依赖数据库(默认 SQLite,也可以接 MySQL)和前端静态资源。直接装在 NAS 系统里,万一和系统自带的库版本冲突,排查起来非常痛苦。Docker 把这些全打包在镜像里,跟宿主机系统完全隔离。
升级方便。新版本出来,拉新镜像、删旧容器、重新起一个,配置目录挂载出来数据不丢。手动安装的话,升级还得考虑二进制替换、数据库迁移,麻烦得多。
迁移成本低。哪天你从群晖换到绿联,或者从老电脑 NAS 换到新机器,把配置目录拷过去,Docker Compose 文件改个路径就能跑,几乎零成本。
提示:如果你的 NAS 是 ARM 架构(比如某些瑞芯微芯片的机型),拉镜像前一定要确认 Octopus 有没有对应的 arm64 版本,否则会报
exec format error。x86 机型(J4105、N100、3865U 这些)基本不用担心。
2.3 硬件和系统的最低要求
Octopus 本身非常轻量,它不做推理,只做转发,所以对硬件的要求极低。我整理了一个参考表:
| 项目 | 最低要求 | 推荐配置 | 说明 |
|---|---|---|---|
| CPU | 双核 x86_64 | 四核 N100/J4105 | 转发几乎不吃 CPU |
| 内存 | 512MB 可用 | 2GB 可用 | 主要给数据库和并发连接 |
| 存储 | 1GB | 10GB | 日志和数据库会慢慢增长 |
| 系统 | 支持 Docker 的 Linux | 群晖 DSM 7.x / 飞牛 / 绿联 | 内核 4.x 以上 |
| 网络 | 内网可达 | 固定内网 IP | 建议给 NAS 设静态 IP |
这里要特别说一下内存。很多人看到"转发不吃资源"就以为 256MB 够了,实际上如果你开了请求日志、并发量又比较大,SQLite 写入会占用不少内存。我自己的机器是 J4105 + 8GB,Octopus 容器常驻内存大概在 150MB 到 300MB 之间波动,完全无压力。
2.4 网络规划:端口和访问方式
部署前先想清楚两件事:容器端口映射到宿主机哪个端口,以及怎么访问管理后台。
Octopus 默认监听容器内的3000端口。映射到宿主机时,我建议避开常用的 80、443、5000(群晖 DSM 占用)、8080(很多 NAS 的 Web 服务占用)。我自己用的是33000,好记又不容易冲突。
访问方式分两种场景。纯内网使用:直接http://NAS的IP:33000就行,简单直接。需要外网访问:这里我不展开讲具体方案,只提醒一点——管理后台的登录密码一定要设强密码,因为后台能看到你所有的 API Key。如果只是自己家里用,内网访问完全够,没必要折腾外网暴露。
3. 核心细节解析与实操要点
3.1 镜像选择与版本策略
Octopus 的镜像在 Docker Hub 上有官方仓库,标签策略一般是latest加版本号。我的建议是不要用 latest,而是锁定一个具体的版本号,比如v1.x.x。原因很简单:latest 随时可能变,某天你重启容器发现行为不一样了,排查半天才发现是自动升级了。
具体操作上,先去项目的 Release 页面看一眼最新稳定版是哪个,然后拉对应的 tag。如果你用的是群晖的 Container Manager,在"映像"里搜索的时候可以直接输入完整标签;如果用命令行,就是:
docker pull octopus/octopus:v1.x.x注意:镜像名和标签以你实际查到的为准,不同时期项目可能调整命名。拉之前先在 Docker Hub 搜一下确认。
3.2 目录挂载:数据持久化的关键
容器一删数据就没,这是新手最容易踩的坑。Octopus 需要持久化的主要是两块:数据库文件和配置文件。默认情况下它用 SQLite,数据库文件在容器内的/data目录下。
所以启动容器时必须把宿主机的某个目录挂载到/data。我在 NAS 上的做法是在 Docker 共享文件夹下建一个octopus目录,里面再分data和logs两个子目录:
/volume1/docker/octopus/ ├── data/ # 数据库和配置 └── logs/ # 日志(可选)挂载的时候把/volume1/docker/octopus/data映射到容器内的/data。这样即使你把容器删了重新拉镜像,只要这个目录还在,所有渠道配置、用户、日志都还在。
3.3 环境变量的取舍
Octopus 支持通过环境变量覆盖一些默认配置,常见的有监听端口、数据库类型、日志级别等。但我的经验是:能用后台界面配的,就别用环境变量。原因在于环境变量是启动时读取的,改了必须重启容器才生效,而后台界面改完即时生效,还能随时回滚。
只有几个场景适合用环境变量:一是首次启动时指定数据库连接(如果你要用 MySQL 而不是 SQLite),二是设置初始管理员账号密码,三是调整日志级别做排查。其他像渠道、模型映射这些,全部在后台配。
3.4 渠道配置的核心逻辑
这是整个 Octopus 最核心的部分,也是最容易配错的地方。一个"渠道"代表一个上游厂商的一个接入点,包含以下关键字段:
- 类型:OpenAI、Azure、Anthropic、自定义等,决定了请求怎么组装
- Base URL:厂商的接口地址,比如 DeepSeek 是
https://api.deepseek.com - API Key:你在该厂商申请的密钥
- 模型列表:这个渠道支持哪些模型名
- 权重和优先级:多个渠道支持同一模型时,怎么分配流量
这里有个非常重要的概念叫"模型映射"。举个例子,你希望客户端统一用gpt-4o这个名字,但实际上后端可能路由到 DeepSeek 或者智谱。这时候就在渠道里配置映射关系,把客户端请求的模型名翻译成厂商实际支持的模型名。这样做的好处是客户端配置永远不用改,后端换厂商对用户透明。
3.5 安全细节:Key 的保护
API Key 泄露的后果不用我多说,轻则被人白嫖额度,重则账单爆炸。Octopus 在这方面做了几件事:后台登录需要密码、Key 在数据库里是加密存储的、对外接口需要单独的访问令牌。但你自己也要注意几点:
第一,管理后台不要暴露到公网,内网用就内网用。第二,给客户端分发的令牌要单独生成,不要直接用后台管理员密码。第三,定期轮换上游 Key,尤其是发现某个渠道调用量异常的时候。第四,日志里不要打印完整 Key,Octopus 默认会脱敏,但你自己写脚本调用的时候要注意。
4. 实操过程与核心环节实现
4.1 第一步:确认 NAS 的 Docker 环境
不同 NAS 系统检查方式略有差异,但核心就一条:能不能正常跑容器。
群晖 DSM 7.x 的话,去套件中心确认 Container Manager 已安装并启动。绿联和飞牛一般自带 Docker 管理界面。老电脑自己装的系统,命令行执行:
docker version docker compose version两条命令都能正常输出版本号,说明环境 OK。如果docker compose报错,可能是只装了 docker 没装 compose 插件,需要单独补一下。
提示:有些 NAS 的 Docker 默认存储位置在系统盘,容器多了容易把系统盘撑满。建议在 Docker 设置里把存储位置改到大容量数据盘。
4.2 第二步:创建目录结构
SSH 登录 NAS,或者用文件管理器图形化操作都行。我习惯用命令行,一条命令搞定:
mkdir -p /volume1/docker/octopus/data mkdir -p /volume1/docker/octopus/logs注意路径要换成你自己 NAS 的实际路径。群晖一般是/volume1/,绿联可能是/mnt/或者别的,飞牛也有自己的约定。不确定的话,在 Docker 管理界面里看一下现有容器的挂载路径就知道了。
创建完之后确认一下权限。如果 Docker 以非 root 用户运行,要保证这个目录对该用户可读写:
chmod -R 755 /volume1/docker/octopus4.3 第三步:编写 Docker Compose 文件
我强烈建议用 Compose 而不是docker run,因为配置写在文件里,下次迁移或者重建直接复用,不用回忆当初敲了什么参数。在/volume1/docker/octopus/下建一个docker-compose.yml:
version: "3.8" services: octopus: image: octopus/octopus:v1.x.x container_name: octopus restart: unless-stopped ports: - "33000:3000" volumes: - /volume1/docker/octopus/data:/data - /volume1/docker/octopus/logs:/app/logs environment: - TZ=Asia/Shanghai - LOG_LEVEL=info healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/health"] interval: 30s timeout: 5s retries: 3几个参数解释一下。restart: unless-stopped保证 NAS 重启后容器自动起来,除非你手动停过。TZ设成上海时区,不然日志时间对不上,排查问题的时候很抓狂。healthcheck是可选的,但加上之后 Docker 能自动检测服务是否健康,配合一些监控工具很好用。
镜像标签记得换成你实际确认过的版本号。路径也要换成你自己的。
4.4 第四步:启动并验证
在 compose 文件所在目录执行:
docker compose up -d-d是后台运行。执行完看一下状态:
docker compose ps如果 STATUS 显示Up或者healthy,说明起来了。再看一眼日志确认没有报错:
docker compose logs -f --tail=50日志里出现类似 "server started on :3000" 的字样就对了。然后浏览器打开http://NAS的IP:33000,应该能看到登录界面。
注意:如果打不开,先检查防火墙。群晖的 DSM 防火墙默认可能拦截非标准端口,需要在"安全性-防火墙"里放行 33000。另外确认一下 NAS 的 IP 没变,建议在路由器里给 NAS 绑定静态 IP 或者 DHCP 保留。
4.5 第五步:初始化管理员账号
首次访问会引导你创建管理员账号。密码一定要设复杂一点,至少 12 位,大小写加数字加符号。这个账号能看所有 API Key,价值很高。
创建完之后登录进去,先别急着加渠道,去设置里把这几项确认一下:站点名称(随便填)、日志保留天数(建议 7 到 30 天,太长占空间)、是否允许注册(自己用就关掉)。
4.6 第六步:配置第一个渠道
以 DeepSeek 为例,走一遍完整流程。在后台找到"渠道"菜单,点新增:
- 渠道名称:DeepSeek 官方
- 渠道类型:OpenAI(因为 DeepSeek 兼容 OpenAI 格式)
- Base URL:
https://api.deepseek.com - API Key:粘贴你申请的 Key
- 模型:手动填入
deepseek-chat、deepseek-reasoner
保存之后,Octopus 一般会自动测试连通性。如果显示绿色或者"已启用",说明通了。如果报错,先检查 Key 有没有多余空格,再检查 Base URL 有没有写错(有些厂商要带/v1,有些不带,看文档)。
4.7 第七步:配置模型映射和负载均衡
假设你现在有两个渠道都支持deepseek-chat,一个是官方,一个是某个第三方中转。你希望优先用官方,官方挂了自动切到中转。这时候就在两个渠道上都填deepseek-chat,然后设置优先级:官方设 10,中转设 5。Octopus 会优先走优先级高的,失败后自动降级。
如果你希望两个渠道轮流分担流量,那就把权重设成一样,比如都是 1,它会按权重随机分配。这个功能在某个渠道有速率限制的时候特别有用。
模型映射的配置在渠道详情里,格式一般是客户端模型名:上游模型名。比如你想让客户端统一请求gpt-4o,实际路由到 DeepSeek,就填gpt-4o:deepseek-chat。这样客户端那边完全感知不到后端换厂商了。
4.8 第八步:生成访问令牌并接入客户端
渠道配好之后,去"令牌"菜单生成一个访问令牌。这个令牌是给客户端用的,跟上游 API Key 是两回事。生成的时候可以设置额度限制、过期时间、允许的模型范围,建议按客户端分别生成,方便追踪用量。
以 Chatbox 为例,接入步骤是:设置里选"自定义 OpenAI 兼容",API 地址填http://NAS的IP:33000/v1,API Key 填刚才生成的令牌,模型名填你在映射里配的那个(比如gpt-4o)。保存之后发一条消息测试,能正常回复就成功了。
LobeChat、NextChat、沉浸式翻译这些客户端的配置逻辑完全一样,都是填 base_url 和 key,区别只在界面位置。这就是统一网关的价值——配一次,处处能用。
5. 常见问题与排查技巧实录
5.1 容器起不来或者反复重启
这是最常见的问题,排查顺序如下。先看日志:
docker compose logs --tail=100 octopus如果日志里报permission denied,基本是挂载目录权限问题,执行chmod -R 777临时验证一下(生产环境别用 777,确认后改回 755)。如果报database is locked,说明 SQLite 文件被占用,可能是上次容器没正常退出,删掉data目录下的.db-wal和.db-shm文件再重启。如果报exec format error,就是架构不匹配,换对应架构的镜像。
5.2 渠道测试失败但 Key 明明是对的
这种情况我遇到过好几次,原因五花八门。整理成表格方便对照:
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 401 Unauthorized | Key 错误或过期 | 重新复制,注意首尾空格 |
| 404 Not Found | Base URL 路径不对 | 确认是否要加/v1 |
| 403 Forbidden | IP 被限制或额度耗尽 | 登录厂商后台查看 |
| 超时 | NAS 出网有问题 | 容器内ping测试 |
| 模型不存在 | 模型名写错 | 对照厂商文档核对 |
其中超时最容易被忽略。NAS 如果配了特殊的 DNS 或者网关,容器可能出不了网。进容器测试:
docker exec -it octopus sh wget -qO- https://api.deepseek.com能返回内容说明网络通,返回不了就是网络问题,检查 NAS 的 DNS 设置。
5.3 客户端能连上但回复很慢
先区分是网络慢还是上游慢。在 Octopus 的日志里看请求耗时,如果 Octopus 到上游的耗时就很长,那是厂商的问题,换渠道或者换时段。如果 Octopus 处理很快但客户端收到慢,那可能是客户端本身的问题,或者 NAS 到客户端的网络有问题。
还有一个容易被忽略的点:流式输出。有些客户端默认开流式,如果 Octopus 的某个渠道不支持流式,就会卡住。检查渠道配置里有没有开流式支持,没有的话在客户端关掉流式试试。
5.4 多个渠道同名模型怎么排查走了哪个
Octopus 的日志里一般会记录实际命中的渠道。如果日志不够详细,可以临时把某个渠道禁用,看请求是否还能成功,以此判断路由。更规范的做法是给每个渠道配不同的标签,然后在日志里按标签过滤。
5.5 数据库越来越大怎么办
SQLite 用久了会膨胀,尤其是开了详细日志的情况下。两个办法:一是定期在后台清理日志,设置里可以配自动清理;二是直接对数据库做 vacuum。进容器执行:
docker exec -it octopus sh # 找到数据库文件位置,一般在 /data 下不过更省事的做法是一开始就用 MySQL。如果你 NAS 上已经跑了 MySQL(绿联、群晖都能装),把 Octopus 的数据库指向 MySQL,性能和可维护性都好很多。配置方式是在环境变量里指定数据库连接串,具体格式看项目文档。
5.6 我踩过的几个坑
第一个坑是时区。刚开始没设TZ,日志时间全是 UTC,排查问题的时候对不上本地时间,白白多花了半小时。第二个坑是端口冲突,33000 一开始被我另一个服务占了,容器起来但访问不了,后来换成 33001 才好。第三个坑是Key 里的空格,从网页复制的时候经常带一个尾随空格,肉眼看不出来,粘贴进去就报 401,后来养成习惯先粘到记事本里看一眼。
还有一个不算坑但值得说的:别把所有渠道都设成同一个优先级。我有次配了三个渠道全是默认优先级,结果请求随机分配,某个不稳定的渠道时不时超时,体验很差。后来把稳定的设高优先级,不稳定的设低,问题就没了。
6. 进阶玩法与后续扩展
6.1 接入本地 Ollama 做兜底
如果你 NAS 性能还行,或者有另一台机器跑着 Ollama,可以把本地模型也配成一个渠道。好处是当所有云端渠道都挂了或者额度用完的时候,本地模型能兜底,虽然慢一点但至少能用。配置的时候类型选 OpenAI 兼容,Base URL 填 Ollama 的地址加/v1,模型名填你本地拉下来的模型。
6.2 用量统计和成本控制
Octopus 后台一般有用量统计功能,能看到每个令牌、每个渠道的调用次数和 token 消耗。结合各厂商的计费单价,可以大致算出成本。我的做法是给每个家庭成员或者每个用途单独生成令牌,月底一看就知道谁用得多。如果某个令牌用量异常,直接禁用,不影响其他人。
6.3 配合自动化脚本
因为 Octopus 暴露的是标准 OpenAI 接口,你现有的任何调用 OpenAI 的脚本,只需要改base_url和api_key两行就能用上所有后端模型。比如你有个批量处理文档的 Python 脚本,原来写死了 OpenAI,现在改成:
from openai import OpenAI client = OpenAI( base_url="http://NAS的IP:33000/v1", api_key="你在Octopus生成的令牌" ) response = client.chat.completions.create( model="gpt-4o", # 实际路由到哪个后端由Octopus决定 messages=[{"role": "user", "content": "你好"}] )这样后端换模型、加渠道,脚本完全不用动。对于经常做实验、对比不同模型效果的人来说,这个价值非常大。
6.4 备份策略
配置目录一定要纳入 NAS 的备份计划。群晖用 Hyper Backup,绿联和飞牛也有各自的备份工具。备份频率看你的配置变动频率,我是一周一次,保留最近 4 个版本。备份的时候注意,如果 Octopus 正在运行,SQLite 文件可能处于写入状态,最好先停容器再备份,或者用数据库自带的备份命令导出。
6.5 什么时候该考虑换 MySQL
SQLite 在单用户、低并发场景下完全够用。但如果你满足以下任意一条,就该考虑换 MySQL 了:并发请求经常超过 20、日志保留超过 30 天、数据库文件超过 500MB、或者你打算多人共用。换的过程不复杂,导出 SQLite 数据、导入 MySQL、改环境变量重启,具体步骤项目文档里有,照着做就行。
整套折腾下来,最大的感受是统一入口这件事本身就值回票价。以前每加一个新模型就要在四五个客户端里各配一遍,现在只在 Octopus 里加一个渠道,所有客户端立刻可用。NAS 常年开机这个特性又让这个网关随时在线,手机在外面也能连回家里用。如果你手里也有 NAS,又正好在玩各种大模型,这套方案值得花一个周末搭起来。