系列最后一篇讲部署:一个个人工具怎么从”Mac 上跑个进程”进化到”Docker 多架构镜像 + NAS 常驻 + Tailscale 远程访问 + 按配额窗口智能挂机”。这部分的坑全部来自真实世界:交叉编译、国内网络、免费额度的排队、以及 Mac 合盖就断的物理限制。
为什么要部署:Mac 写工具的物理天花板
工具在 Mac 上跑得好好的,为什么非要折腾部署?三个真实痛点:
- Mac 合盖就断——写书是 8 小时起步的批量任务,总不能为这个不合盖
- Mac 是开发机——调试时重启服务、改配置、切 provider 是常态,跑着的长任务随时被误伤(真实发生过:测试切 mock 时忘了服务端缓存,用户真实项目的任务被牵连)
- 家里有台不吃电的常驻设备——极空间 NAS,Docker 环境现成,24 小时在线
开发环境和生产环境分离,个人工具也逃不开这条工程铁律。
Docker 化:从一个 Dockerfile 到多架构发布
容器化的关键决策:
# ARG 全局前置(吃了顺序亏:ARG 必须在 FROM 前才能进构建阶段) |
- 数据外置:
AINOVELS_DATA环境变量派生全部数据目录(projects/config/cache),容器无状态,升级 = pull 新镜像重建容器,数据在卷里纹丝不动 - 国内网络友好:
PIP_INDEX_URL可注入国内源;健康检查用 python urllib(镜像里没有 curl,不想为它 apt) - 多架构:Mac 是 arm64、NAS 是 amd64,buildx 交叉构建 + manifest 合并双架构镜像
推送脚本沉淀成一页纸的工程诗(节选):
# 网络策略(自动): |
三条实战注释值千金:
- pipefail:脚本被管道调用时,没有它退出码是 tee/grep 的(永远成功)——推送失败也显示成功
- 基础镜像恒走镜像源:buildkit 拉基础镜像的元数据解析不走代理环境变量,缓存冷时直连必被 DNS 污染超时——注释里的”两轮推送失败实锤”是用时间换的
- host.docker.internal 代替 VM 网关 IP:Docker 跑在 Colima 虚拟机里时,代理在宿主机——走 VM 网关 IP 的话虚拟机重启换网段就断,docker internal 域名一劳永逸
Clash 代理还有个大文件上传的 broken pipe 问题(推镜像层时断流),解法是备一个直连构建器:代理路线推不动时脚本自动切 xbuilder-direct——直连慢(静默期 8 分钟无日志,不是卡死)但稳。
NAS 部署:compose 一把梭
NAS 端的交付物是一份 docker-compose.yml——照着用户 NAS 上已有的 Tailscale 容器的 compose 风格写的(家庭服务器上保持配置风格一致是隐形福利)。数据卷指向 NAS 的存储池路径,restart: always 常驻,8348 端口。
升级闭环就此定型:
# Mac 端发版 |
远程访问走 Tailscale——NAS 在异地也能通过虚拟内网安全访问,不暴露公网。远程质检只做只读操作:GET 版本号确认容器跑的是新镜像(/api/version 404 = 旧镜像无疑)、GET 体检报告看写作质量,绝不远程触发任何 LLM 消耗。
配额协议:免费额度的智能挂机
部署解决”在哪跑”,配额协议解决”怎么跑得久”。LLM 走的是 Coding Plan 套餐额度——5 小时一个窗口,窗口重置才能继续。夜间挂机的原始做法是定时重试,蠢且浪费。配额协议的做法:
1. 应用暴露 /api/monitor/usage/quota/limit 查询当前窗口用量与重置时间 |
配套的加固都是真实事故喂出来的:
- CogVideoX 轮询超时 900s → 1800s:免费档夜间排队 7+ 分钟是常态,超时太短会把”排队中”误判为”失败”
- 成片过期检测:重生成过素材的集(成片比素材旧)自动纳入夜间批次重新合成——不然会出现”图是新的、视频是旧的”的静默错位
- 每集耗时波动大:任务列表的进度预估改为按集粒度,不做时间承诺
Mac ↔ NAS:设定包工作流
部署后的最后一公里:用户在 Mac 上调试各种设定(骨架/硬规则/文风/知识库组合),调好在 NAS 上长期生产。人肉重复配置太蠢,做了设定包导入导出——JSON 打包参数 + 规则 + 知识库全文,纯前端实现零新端点。安全边界明确:不含 LLM 密钥、不含原著源挂接(密钥留在各自机器,源在 NAS 重新挂一次)。
于是完整的工作流闭环成型:
Mac:调试设定 → 导出设定包 |
踩坑小结
- 开发环境和生产环境分离,个人工具也不例外。测试误伤生产任务的教训,比任何理论都直观
- shell 脚本被管道调用必加 pipefail,不然失败永远显示成功
- 代理方案要分层:buildkit 元数据不走代理 env(基础镜像恒走镜像源)、上传走代理(快)、代理断流备直连(稳)——一层代理策略打天下是不可能的
- 免费额度挂机要读真实重置时间,盲重试既浪费又吵
- 远程操作原则:只读免费随便做,花钱操作必须在场——这条原则让你敢在千里之外动生产环境
系列终章:这个项目教会我的事
九篇写完,回头看整个项目最深的三个认知:
- 长篇 AI 写作是系统工程,不是模型测评。产出质量 = 记忆架构 × 质量闭环 × 度量反馈 × 部署形态,模型只是其中一个变量。同一个人用同一个模型,裸聊和管线化的产出差距是断崖的
- 个人工具的架构美学是”跟得上变化”。文件即数据库、零构建起点、照抄成熟设计、按痛点重构——每一步都为迭代速度服务,不为简历服务
- AI 时代个人开发者的价值在调度和裁决。代码产出可以外包给 AI,但”合并时盯三处”的重叠预判、”度量不是表演”的品味坚持、”我要一爽到底”背后的用户立场——这些判断无一可以外包
工具现在已经在我家 NAS 上安静地跑着,夜间写书白天验收。至于那个科幻同人项目写完是什么样——等它完本了,或许会有第十篇。