长期运行的 Nextcloud 实例,升级通常不是简单地执行一次:
php updater.phar
就可以结束。
随着运行时间增加,实例会逐渐积累:
第三方 App
禁用 App
历史数据库版本
旧配置
旧缓存
核心文件改动
MIME 类型扩展
后台迁移任务
应用数据库版本差异
因此,一次跨越多个主要版本的升级,本质上更像是一次系统整理。
这次升级最终依次完成:
Nextcloud 31
↓
Nextcloud 32
↓
Nextcloud 33
↓
Nextcloud 34
最终版本为:
Nextcloud 34.0.4
一、没有跨主要版本跳跃
整个升级过程采用官方支持的逐版本路线。
例如:
31.0.x
↓
32.0.x
↓
33.0.x
↓
34.0.x
这样做的主要原因不是保守,而是每一个主要版本都可能包含自己的:
数据库迁移
App 兼容逻辑
Background Job
配置转换
文件升级逻辑
如果跳过中间版本,就可能绕过原本应该执行的升级阶段。
二、数据目录分离显著改变了 updater 的行为
在早期升级过程中,一个非常明显的瓶颈出现在:
Delete old files
某次升级中,这一步曾经持续:
约 1 小时 53 分钟
后来数据目录从应用程序目录中彻底分离。
原来的结构类似:
<NEXTCLOUD_ROOT>/
├── apps/
├── core/
├── config/
└── data/
调整后变成:
<NEXTCLOUD_ROOT>/
├── apps/
├── core/
└── config/
<NEXTCLOUD_DATA>/
└── 用户数据
Nextcloud 配置中的:
'datadirectory' => '<NEXTCLOUD_DATA>',
也同步修改。
而且没有通过符号链接伪装旧数据目录。
之后再次进行:
32 → 33
33 → 34
升级时,
Delete old files
几乎变成瞬时完成。
这说明此前 updater 很大一部分时间很可能消耗在庞大的数据目录树检查上。
应用代码和数据目录分离后,更新器只需要处理真正属于程序的文件。
三、升级使用 updater.phar,并明确关闭 updater 自己的备份
其中一次升级命令类似:
sudo -E -u <WEB_USER> \
php <NEXTCLOUD_ROOT>/updater/updater.phar --no-backup
这里使用:
--no-backup
并不是表示系统不需要备份。
而是因为该实例已经有独立的备份策略,不需要 updater 再额外生成一份完整程序备份。
更新完成后,继续执行:
sudo -E -u <WEB_USER> php occ upgrade
最终状态为:
installed: true
maintenance: false
needsDbUpgrade: false
version: 34.0.4.x
versionstring: 34.0.4
四、第三方 App 是升级中最需要单独处理的部分
Nextcloud Core 与第三方 App 的生命周期并不完全同步。
当主版本升级到 NC34 时,部分 App 尚未提供兼容版本。
因此一些应用需要暂时禁用,例如:
metadata
unsplash
onlyoffice
其中:
metadata
unsplash
在当时没有更高兼容版本,因此继续保持禁用。
而:
onlyoffice
后来出现新版本,可以在仍然保持禁用的情况下升级代码。
五、禁用 App 的“代码版本”和“installed 版本”可能不同
升级完成后执行:
php occ app:list --disabled
可以看到类似:
circles: 34.0.0 (installed 30.0.0)
comments: 1.24.0 (installed 1.21.0)
files_external: 1.26.0 (installed 1.19.0)
这很容易让人误以为:
这些 App 没有升级完成
实际上不是。
对于 Nextcloud 自带但长期禁用的 App:
磁盘上的 App 代码
会随着 Nextcloud Core 更新。
但:
数据库中的 installed_version
可能仍然保持它最后一次启用时的版本。
因为 App 自己的数据库升级流程通常只在启用状态下执行。
因此这种状态并不等于错误。
也不应该为了把两个数字“刷成一样”而临时启用几十个不需要的 App。
六、检查真正存在的 App 更新
可以执行:
php occ app:update --showonly
当时只显示:
passman new version available: 2.6.2
onlyoffice new version available: 10.2.0
于是直接执行:
php occ app:update --all
结果:
passman updated
onlyoffice updated
这两个 App 更新之后仍然保持:
Disabled
没有因为更新代码而自动启用。
这是比较理想的维护方式。
七、升级后发现缺失数据库索引
进入 NC34 后,管理页面提示存在缺失数据库索引。
Nextcloud 提供官方修复命令:
sudo -E -u <WEB_USER> php occ db:add-missing-indices
其中补充了一个缺失索引:
taskp_status_type_upd
这种问题在大版本升级后并不少见。
Nextcloud 有时为了缩短正式升级时间,并不会在升级过程中自动创建所有可能耗时的索引,而是要求管理员之后手动执行维护命令。
八、Core Integrity 出现一个 JavaScript 文件哈希不一致
升级后的完整性检查发现:
core/js/mimetypelist.js
哈希值与官方版本不一致。
最开始尝试:
php occ maintenance:mimetype:update-js
重新生成 MIME JavaScript 文件。
但重新生成之后,Integrity Check 仍然失败。
原因是:
当前生成结果
≠
官方 Nextcloud 34.0.4 发布包中的原始文件
因此最终没有继续尝试生成,而是直接下载对应版本的官方 Nextcloud Release ZIP。
从官方安装包中提取:
core/js/mimetypelist.js
然后替换当前文件。
官方文件的 SHA-512 得到确认之后,再次运行:
php occ integrity:check-core
没有任何输出。
在 Nextcloud 中:
无输出
即表示核心完整性检查通过。
九、为什么这个文件会发生变化
进一步排查发现,实例此前安装过一个与特殊文件类型相关的 App。
该 App 会扩展 MIME Type。
即使 App 后来已经删除,它之前修改或生成的 MIME Type JavaScript 文件仍然可能保留下来。
因此:
Integrity Check 失败
并不自动意味着:
服务器被入侵
它也可能来自:
第三方 App
历史脚本
MIME 扩展
人工修改
旧版本残留
正确方法是比较实际文件与官方 Release,而不是关闭完整性检查。
十、日志等级也在升级后重新整理
升级期间曾经发现:
'loglevel' => 0
这意味着系统处于 DEBUG 级别。
结果是:
nextcloud.log
产生大量低价值日志。
最终将日志等级调整为:
2
也就是 Warning。
同时保留:
admin_audit
用于独立审计。
之后:
nextcloud.log
和历史轮转日志被清理,建立新的正常运行基线。
十一、升级后的最终状态
最终系统状态为:
Nextcloud 34.0.4
Stable channel
maintenance = false
needsDbUpgrade = false
Core Integrity = OK
Missing DB indices = fixed
正常 cron = enabled
额外 Preview Generator cron = disabled
Preview migration = completed
第三方 App = 根据兼容性分别保持启用或禁用
管理页面只剩一些功能性提示,例如:
未强制要求所有用户使用 2FA
Talk 未部署 High Performance Backend
这类提示不属于系统故障。
十二、版本号升级成功,只是升级的一半
真正完整的 Nextcloud 升级流程应该理解为:
程序文件升级
↓
数据库 Schema 升级
↓
App 兼容性检查
↓
后台迁移任务
↓
数据库索引检查
↓
核心完整性检查
↓
日志检查
↓
安全检查
↓
正常使用验证
只有这些部分全部稳定之后,升级才真正完成。