升级 Specivo,就是切换到更新的 specivo/specivo Docker 镜像。容器入口脚本会在启动时自动执行新的数据库迁移,所以整个流程很直接:备份、固定目标版本、拉取镜像、重启、验证。本文以从 0.1.9 升级到 0.1.10 为例,操作环境与安装指南相同,是一台 1 vCPU / 2 GB 内存的 Ubuntu 24.04 云主机。
全文的工作目录都是 /opt/specivo,安装指南把 Compose 文件、.env 和 specivo-data/ 目录都放在这里。
你将完成的步骤
- 动手前先确认整个服务栈运行正常
- 导出数据库并打包数据目录
- 在
.env中固定目标版本 - 拉取新镜像并重启服务栈
- 验证所有组件都已在新版本上正常运行
开始之前
你需要一套已安装好的 Specivo(按安装指南部署的环境)、root 的 shell 访问权限,以及几分钟时间。拉取任何东西之前,先阅读目标版本的发布说明:CHANGELOG 在代码仓库里,每个 GitHub Release 也都链接到它。先弄清变更内容,再执行升级。
第 1 步:确认当前运行的版本
SSH 登录服务器,进入安装目录:
cd /opt/specivo
确认所有容器都处于健康状态:
docker compose ps
你应该看到六个容器:nginx、api、db、redis、celery-worker 和 celery-beat,状态全部为 Up (healthy)。如果有容器在反复重启或不健康,先解决这个问题再继续。
查看当前的数据库 schema 版本:
docker compose exec -T api alembic current
记下输出。本文示例中是 0020 (head)。升级完成后还要再查一次。
检查磁盘空间。拉取会下载新的镜像层,备份归档在转移到其他主机之前也会一直占用磁盘:
df -h /
本文用的这台主机 48 GB 磁盘还剩 43 GB,足够容纳拉取的镜像和大约 280 MB 的备份归档。
第 2 步:先做备份
每次升级都必须执行这一步。迁移可能会修改数据库 schema,而且不一定可逆。如果迁移已经执行、升级却出了问题,备份是唯一的退路。不要跳过。
如果还没有备份目录,先创建一个:
mkdir -p /opt/specivo-backups
导出数据库。必须加 -T 参数,这样才不会分配伪终端,输出才能正确通过管道写入文件:
docker compose exec -T db pg_dump -U specivo specivo \
> /opt/specivo-backups/backup-$(date +%Y%m%d-%H%M%S).sql
本文导出的文件约 210 KB,是一份完整的纯 SQL 快照,包含每一个工作项(Issue)、维基(Wiki)页面、评论和历史记录。
打包数据目录。这会把附件、日志、主题、内置数据库文件以及磁盘上的嵌入模型一并打包:
tar czf /opt/specivo-backups/specivo-data-$(date +%Y%m%d-%H%M%S).tar.gz specivo-data
本文的归档约 279 MB,其中大部分是约 400 MB 的嵌入模型(在磁盘上本身已是压缩格式)和 PostgreSQL 数据文件。
继续之前,把这两个文件转移到主机之外:对象存储、外接硬盘或远程服务器都可以。只放在待升级这台机器上的备份,不算真正的备份。
第 3 步:固定目标版本
用编辑器打开 .env,把 SPECIVO_VERSION 从当前值改成你要升级到的版本:
SPECIVO_VERSION=0.1.10
如果你的 .env 还保留着初次安装时的 SPECIVO_VERSION=latest,现在正是改掉的好时机。固定到明确的标签,意味着每次升级的时机由你决定,而且你始终清楚运行的是哪个镜像。回滚也同样简单:重新固定到旧标签即可。
第 4 步:拉取新镜像
docker compose pull
Compose 只会下载有变化的镜像层。拉取结束时会输出类似这样的一行:
Image specivo/specivo:0.1.10 Pulled
db(pgvector)、redis 和 nginx 镜像不属于 Specivo 的发布周期,除非你改过它们固定的版本,否则这一步不会重新拉取。
如果你在 .env 中设置了 SPECIVO_IMAGE=ghcr.io/specivo/specivo(用于无法访问 Docker Hub 的服务器),同一镜像会从 GitHub Container Registry 拉取,输出行会显示为 Image ghcr.io/specivo/specivo:<version> Pulled。从 0.12.0 起,每个版本都会以相同的标签同时发布到两个镜像仓库。
第 5 步:重启服务栈
docker compose up -d
Compose 会把运行中的容器与 Compose 文件对比,只重建镜像发生变化的容器。本次升级重建了三个:specivo-api-1、specivo-celery-worker-1 和 specivo-celery-beat-1。db、redis 和 nginx 的镜像没有变化,容器原样保留。
Compose 会等 db 和 redis 报告健康后再启动 api,启动顺序不需要你自己管理。
迁移会自动执行。入口脚本每次启动都会运行 alembic upgrade head。你会在 API 日志中看到类似这样的输出:
Running database migrations...
INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Will assume transactional DDL.
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
如果某个版本确实包含 schema 变更,这些行之间还会出现 Running upgrade <rev> -> <rev>,也就是 alembic 按顺序逐个执行新迁移。
你永远不需要手动运行迁移命令。盯着日志,确认启动过程没有报错:
docker compose logs -f api
看到 Application startup complete. 后按 Ctrl-C 停止跟踪日志。
有一点需要说明:这次从 0.1.9 到 0.1.10 的升级没有新增 schema 迁移,alembic current 在升级前后都是 0020 (head)。入口脚本依然执行了迁移步骤,这一步是幂等的。其他版本之间的升级可能包含 schema 迁移,正因如此,无论你是否预期有 schema 变更,升级前的备份每次都是必做项。
第 6 步:验证
确认所有容器都已使用新镜像:
docker ps
api、celery-worker 和 celery-beat 三行都应显示 specivo/specivo:0.1.10。api 容器应报告 Up (healthy)。
检查登录页能否响应:
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:9933/
返回 302 并重定向到登录页,就是正常的健康响应。
再次确认 schema 版本一致:
docker compose exec -T api alembic current
最后在浏览器中打开应用,登录,打开一个工作项,再执行一次搜索。三项都正常,升级就完成了。
回滚
如果新版本出现异常,在 .env 中重新固定到之前的标签即可回滚:
SPECIVO_VERSION=0.1.9
然后拉取并重启:
docker compose pull
docker compose up -d
如果失败的升级已经执行了 schema 迁移,仅重新固定标签是不够的。数据库 schema 可能已经领先于旧镜像,旧镜像会拒绝启动或运行异常。这种情况下,你必须恢复升级前的数据库导出文件和
specivo-data/归档,再启动旧镜像。先恢复数据目录,再把 SQL 导出文件载入数据库:docker compose down tar xzf /opt/specivo-backups/specivo-data-<timestamp>.tar.gz docker compose up -d db docker compose exec -T db psql -U specivo specivo \ < /opt/specivo-backups/backup-<timestamp>.sql docker compose up -d这正是升级前备份每次都必须执行的原因。
好习惯
- 每次只升级一个小版本,不要一次跨越多个版本。迁移序列是按逐步递增的方式设计的。
- 拉取每个版本之前,都先阅读它的发布说明。
- 每次都备份,哪怕看起来只是补丁版本。
- 在
.env中始终把SPECIVO_VERSION固定到明确的标签。生产实例绝不要跑在latest上。 - 每次升级后做一次快速冒烟测试:登录、打开一个工作项、执行一次搜索。