长期运行的 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 兼容性检查
        ↓
后台迁移任务
        ↓
数据库索引检查
        ↓
核心完整性检查
        ↓
日志检查
        ↓
安全检查
        ↓
正常使用验证

只有这些部分全部稳定之后,升级才真正完成。

Leave a Reply

Your email address will not be published. Required fields are marked *