☰
Conduwuit 升级 Continuwuity:数据、密钥与联邦迁移
2026/10/28 5:51:59 网站建设 项目流程

Conduwuit 迁移到 Continuwuity 这件事,我上个月刚从零走了一遍,中间停了两次服务、回滚过一次,最后才算把数据连同联邦身份一起平滑搬过去。这篇就把整个过程掰开说清楚:从 Conduwuit 换到 Continuwuity,本质上不是"换个二进制重启一下",而是一次涉及数据库 schema、签名密钥、媒体仓库和联邦声誉的搬迁工程。哪些数据能原样带走、哪些必须重建、升级路径怎么排、出问题怎么退回,我会一条条写明白。适合正在跑 Conduwuit 想升级的运维、自己搭服务端玩到一定深度的爱好者,以及手上管着几十号人日常沟通、不敢随便动手的团队负责人。哪怕你只是刚装好没几天,看完也能知道哪些东西现在就该备份好。

1. 先想清楚:这次迁移到底在换什么

很多人对这类迁移的第一反应是"停服务、换程序、起服务",三分钟搞定。我第一次也是这么想的,结果启动日志刷了两屏就直接退出,数据库文件被写了个新版本号,旧版二进制再也读不进去,只能靠备份回滚。所以动手之前,得先弄明白 Conduwuit 和 Continuwuity 之间到底共享什么、改变了什么,以及你手上这套部署属于哪一类。

1.1 两个项目的关系与"同源不同枝"的现实

Conduwuit 和 Continuwuity 是同一条技术血脉上的两个节点,都用 Rust 写,都面向联邦式即时通信服务端这个场景,都支持 RocksDB 和 SQLite 两种后端存储。Continuwuity 可以理解为在 Conduwuit 的架构基础上继续往前推的一支,它在配置组织方式、默认路径、部分模块拆分上做了调整,数据库 schema 也往上提了版本。

这个"同源"关系带来一个很实际的后果:大部分数据结构和语义是通的,但版本号和配置键名不一定通。你直接拿 Conduwuit 的配置文件丢给 Continuwuity,大概率能启动,但会有一堆配置项被忽略、默认值悄悄生效——这比直接报错更危险,因为服务看着是好的,实际监听地址、上传大小限制、联邦开关全变了。

我在第一次试跑时就吃了这个亏:配置文件里旧的端口写法没被识别,Continuwuity 退回默认端口启动,我的反向代理还指在原来那个端口上,外部访问一片 502,而服务端日志里干干净净,什么都没报。所以第一条经验是:永远不要相信"能启动就是配对了",启动之后必须逐项核对生效值。

1.2 三种部署形态,对应三种完全不同的迁移策略

同样是 Conduwuit 换 Continuwuity,单人实例和几十人团队实例的迁移难度差着量级。我把常见情况归成三类,你可以先对号入座:

部署形态典型特征数据量级推荐策略可接受停机
单人自用实例自己一个人用,房间数少,联邦连接不多几百 MB 到几 GB停机全量搬迁30 分钟到 2 小时
小团队实例十几到几十人,有若干活跃房间,联邦往来频繁几 GB 到几十 GB停机搬迁 + 只读窗口1 到 4 小时
多人长期实例百人以上,媒体仓库巨大,历史消息多几十 GB 以上影子实例预演 + 正式切换两阶段4 小时以上或分两次

单人实例最舒服,直接停、直接搬、直接起,出问题就回滚,反正没人催你。小团队实例的难点不在技术,在沟通——你得提前告诉大家"今晚十点后消息可能发不出去",并且真的在承诺时间内恢复。

多人长期实例最麻烦的是媒体体积。消息数据库可能只有几 GB,但媒体仓库动辄几十上百 GB,全量拷贝一次动辄几小时。这种场景我建议分两次做:第一次先把配置迁移和 schema 升级在影子实例上跑通,把媒体用增量同步工具预热一遍;第二次才是正式停机,只同步差值部分,把停机时间压到最短。

1.3 迁移窗口怎么定,回滚线画在哪

定窗口有个朴素但有效的原则:把最坏情况的时间乘以二,再找个大家最不活跃的时段。如果你估计迁移要一小时,就留两小时窗口;如果估计要三小时,就别在工作日下午动。

真正决定成败的不是窗口多长,而是你有没有一条明确的、验证过的回滚线。我的做法是画三条线:

  • 软回滚线:新版本启动成功但自检项有失败(比如媒体 404、联邦握手失败),先不切流量,就地排查,超过 30 分钟没进展就回退。
  • 硬回滚线:新版本起不来,或者起来之后数据库被写坏、旧版读不进去,直接恢复备份,不纠缠。
  • 放弃线:回滚两次仍然失败,说明准备工作有结构性缺失(比如密钥备份丢了),这时候停下来重新收集信息,不要在同一个晚上反复折腾。

注意:硬回滚线必须建立在"备份真的可用"的前提上。我见过太多次备份文件躺在那里,真要恢复时发现备份的是数据目录但漏了密钥文件,或者备份时数据库还在写入导致文件损坏。备份完必须验证,验证方式很简单——把备份解到一个临时目录,用旧版二进制挂上去启动一次,能起来才算数。

还有一点容易被忽略:回滚的时间点。如果新版本已经跑了几小时,接收到了新的联邦消息和本地消息,这时候再回滚到旧备份,这几个小时的消息就丢了。联邦协议本身有一定的事件追补能力,对端会重新推一些状态过来,但本地发出的消息是补不回来的。所以要么在切换后尽快确认稳定,要么接受"这几小时的消息可能丢"这个代价,提前跟大家说清楚。

2. 数据兼容性拆解:哪些能原样带走,哪些必须重建

这一章是整篇的核心。迁移失败的案例里,八成以上不是操作失误,而是对"哪些数据是硬的、哪些是软的"判断错了。我把 Conduwuit 的数据分成四层来看:身份层、数据库层、媒体层、配置与环境层,每层的兼容性风险完全不同。

2.1 数据库层:schema 版本、存储引擎与降级禁区

Conduwuit 支持 RocksDB 和 SQLite 两种后端。两者在迁移中的表现差别很大,得分开说。

RocksDB 是默认选项,优势是写入性能好、适合高频小写入,劣势是版本兼容性极其敏感。RocksDB 的存储格式有内部版本,Continuwuity 升级 schema 时会顺手把存储层的一些参数也更新掉,一旦新版打开过这个目录,旧版二进制再打开就可能直接报错退出,甚至出现部分数据不可读的情况。这就是所谓的"降级禁区"——升级是单向门,走过去就回不来,除非你有备份。

SQLite 相对温和一些。schema 升级会改表结构、加列、加索引,旧版打开新库通常会因为缺列或少表而报错,但至少数据文件本身不会被底层格式破坏,理论上可以手工降级(删掉新加的列、回退版本表里的数字)。不过我不建议把这当成常规手段,手工改 schema 的风险比恢复备份高得多。

我的实际做法是:迁移前记录当前数据库的 schema 版本号,把它写进变更记录里。Continuwuity 启动时会打印自己的版本和检测到的数据库版本,第一个成功启动的日志一定要存下来。以后再遇到任何问题,这个日志是判断"是从哪个版本升上来的"的唯一依据。

# 迁移前记录版本信息(示意,具体命令以你的版本文档为准) conduwuit --version > /root/migration/pre-version.txt ls -la /var/lib/conduwuit/ >> /root/migration/pre-version.txt du -sh /var/lib/conduwuit/ >> /root/migration/pre-version.txt

顺手把数据目录的完整列表和体积也记下来,后面比对"有没有少文件"时非常有用。

还有一个细节值得单独提:RocksDB 的 LOCK 文件和临时文件。如果旧进程没完全退出就启动新进程,新进程会因为拿不到锁而报错;反过来,如果旧进程异常退出留下了锁文件,新进程也可能卡住。迁移前务必确认旧进程是真的退干净了,用进程列表确认,不要只看启动脚本返回。

2.2 身份根基:server_name 与签名密钥为什么碰不得

这是整个迁移里最不能出错的地方,没有之一。

服务端身份由两样东西决定:服务器名(server_name)和签名密钥(signing key)。服务器名是你在这个联邦网络里的"门牌号",一旦定下来,它在所有对端节点、所有房间成员记录、所有历史事件里都被引用。签名密钥则是你的"公章",你发出的每一条事件、每一个联邦请求都带着它的签名。

迁移中最常见的两个致命错误:

第一,改了配置文件里的 server_name。有些人觉得"我换了个新程序,正好把域名也换一下",这一改,整个身份就变了。对端节点看到的是一个陌生服务器在声称自己是原来那个房间的成员,直接拒绝;本地数据库里的房间记录也会因为服务器名不匹配而出现大量状态不一致。这种情况下唯一的解法是把 server_name 改回去,没有第二条路。

第二,没有迁移签名密钥,或者迁移时重新生成了一份。Continuwuity 首次启动如果发现没有密钥文件,会自己生成一份新的。服务能起来,本地发消息也正常,但一旦涉及联邦——对端验证签名失败,所有出站事件被拒绝,你的消息发不出去,对端的消息也推不过来。更麻烦的是,这个错误不会在启动时报出来,可能过几小时才被发现。

所以迁移清单里,密钥文件的优先级应该和数据库同级,甚至更高:

文件类型路径示例(以实际为准)是否可重建迁移优先级
签名密钥/var/lib/conduwuit/*.ed25519*否,重建等于换身份最高
TLS 证书与私钥/etc/letsencrypt/live/...是,可重新签发高
客户端发现文件站点根目录下.well-known相关文件是,但要保证内容一致高
数据库目录/var/lib/conduwuit/db否最高
媒体仓库/var/lib/conduwuit/media部分可重建中
日志/var/log/...是低

提示:密钥文件迁移过去之后,记得核对文件权限和属主。Continuwuity 以什么用户运行,密钥文件就必须对该用户可读。我见过因为权限不对导致"密钥文件在但读不到",程序默默地生成了一份新的,踩坑时间长达两天。

另外补充一点关于密钥轮换的常识,这对长期运维有用:如果你确实需要换密钥,正规做法是保留旧密钥一段时间,让新密钥和旧密钥同时存在于密钥列表中,等对端节点都更新过你的密钥信息之后,再移除旧的。直接删掉旧密钥等于宣布"以前那些签名都不是我发的",后果很严重。

2.3 媒体仓库与派生数据:能重建的别搬,搬不动的先算体积

媒体仓库是体积最大、也最容易被想复杂的一块。先说结论:原始媒体必须搬,派生数据可以重建,缓存可以直接删。

原始媒体就是你和其他人上传的图片、文件、语音,这些是用户数据,丢了补不回来。派生数据指的是缩略图、预览图、转码后的版本,这些是服务端根据原图生成的,理论上可以重新生成,只是要花 CPU 时间。缓存则是各种临时文件,直接删掉最省事。

Continuwuity 对媒体目录的组织方式可能和 Conduwuit 有差异,比如分片规则、文件名哈希算法、元数据索引的位置。这里有个关键判断:如果你的媒体元数据存在数据库里,那么数据库和媒体目录必须成对迁移,不能分开。因为数据库里存的是"哪条消息对应哪个媒体文件、文件的 MIME 类型、大小、哈希",媒体目录里存的是文件本体,两边对不上就会出现"消息里的图片链接 404"或者"文件在但打不开"。

我在第一次迁移时就犯了这个错:为了省时间,先把数据库搬过去启动了服务,媒体目录打算第二天再同步。结果那一晚上所有图片都是裂的,用户疯狂反馈,我只好晚上十点又跑回去补同步。教训就是:数据库和媒体必须同时到位,宁可多等两小时,也不要分两次。

体积估算也要提前做,不然容易在深夜手忙脚乱:

# 统计各部分体积,心里有个数 du -sh /var/lib/conduwuit/db du -sh /var/lib/conduwuit/media du -sh /var/lib/conduwuit/ # 总量 find /var/lib/conduwuit/media -type f | wc -l # 文件数量

文件数量比总体积更能反映同步耗时。几十 GB 但只有几万个文件,同步很快;几 GB 但有上百万个小文件,光是遍历目录就能拖很久,这种时候用支持并发的同步工具(比如 rsync 加多线程,或者专门的增量同步方案)比单线程拷贝快一个数量级。

2.4 兼容性速查矩阵

把上面几层汇总成一张表,迁移前对着过一遍,能避开大部分结构性错误:

数据类别兼容性判断迁移方式风险点备注
server_name 配置必须完全一致手工核对改了就无法联邦最优先确认
签名密钥文件必须原样迁移文件拷贝 + 权限核对重新生成等于换身份迁移后核对指纹
数据库(RocksDB)单向升级停机拷贝 + 新版打开降级不可行必须有备份
数据库(SQLite)单向升级,可手工降级停机拷贝表结构不匹配不建议手工改
原始媒体文件兼容,需与库配对同步工具搬运元数据对不上就 404与数据库同时到位
缩略图等派生数据不保证兼容可删除重建重建耗 CPU建议直接删
TLS 证书兼容拷贝或重签权限与路径注意自动续期
发现文件格式兼容,内容需核对手工重写路径与端口要一致客户端依赖它
日志与缓存不需要丢弃无别浪费迁移时间

这张表我在每次迁移前都会重新过一遍,因为它能强迫我确认"这个文件到底属于哪一类"。判断标准就一条:它是不是不可再生。不可再生的,全部当最高优先级对待。

3. 六步走:从备份到切流量的完整升级路径

前面讲的是判断,这一章讲动作。我把整个迁移拆成六步,每步都有明确的完成标准和检查点,做完一步确认一步,不要图快跳过。这套流程在小团队实例上跑过两次,第二次全程不到两小时,包括验证。

3.1 第一步:环境清点与三层备份

环境清点要回答三个问题:现在跑的是哪个版本、数据在哪、服务是怎么被访问的。

版本信息从二进制拿,数据路径从配置文件和服务实际运行状态拿(这两个不一定一致,配置文件写着 A,实际可能通过命令行参数指向 B),访问链路要搞清楚有没有反向代理、代理配置里的端口和路径怎么写的。这一步偷懒,后面排查问题会加倍还回来。

备份我建议做三层,别嫌麻烦:

第一层是快照。如果你的机器支持卷快照或者虚拟机快照,迁移前打一个。这是最快的回滚手段,几秒钟就能回到迁移前的状态,代价是要占一份存储空间。

第二层是文件级全量备份。把数据目录、密钥目录、配置目录打包,复制到另一块盘或者另一台机器上。打包时一定要停服务,或者在数据库层面做一致性导出,运行中直接 tar 一个 RocksDB 目录,很可能拿到一个损坏的副本。

第三层是配置和密钥的单独副本。这一层看着冗余,但在最坏情况下救命——比如数据全丢了,至少密钥还在,你还能用同一个身份重建一个空实例,联邦关系不会断。

# 停止服务,确保没有写入 systemctl stop conduwuit sleep 5 systemctl status conduwuit # 确认已停止 # 全量打包(路径以实际为准) mkdir -p /root/migration/backup tar -czf /root/migration/backup/full-$(date +%F-%H%M).tar.gz \ /var/lib/conduwuit \ /etc/conduwuit # 单独备份密钥,权限收紧 cp -a /var/lib/conduwuit/*.ed25519* /root/migration/backup/ chmod 600 /root/migration/backup/*.ed25519*

备份完必须验证,我把验证当成一个独立步骤而不是"顺手看看"。验证方式:把备份解压到临时目录,用旧版二进制以只读方式挂一下,能读到数据库、能列出房间,就算通过。这一步花十分钟,可能省掉一整晚。

3.2 第二步:配置文件差异比对与改写

Continuwuity 的配置文件结构和 Conduwuit 有出入,键名、分组方式、默认值都可能变。最忌讳的做法是拿旧配置直接启动,然后靠"能跑就行"来判断。

正确做法是拿两份官方示例配置做基线比对。Continuwuity 一般会附带一份带详细注释的示例配置,把它和你的现有配置放在一起,逐段过。重点关注这几类键:

  • 监听相关:地址、端口、是否启用 TLS。这类键被忽略的话,服务会退到默认端口,外部访问直接断。
  • 数据库相关:路径、后端类型(RocksDB 还是 SQLite)、连接池大小。路径写错会直接创建一个空库,看着启动成功,实际上是全新的空实例。
  • 联邦相关:是否开启联邦、出站连接的超时和重试、允许的服务器列表。
  • 注册与访问控制:是否开放注册、邀请策略、房间创建权限。这类配置一旦默认值生效,可能出现"突然谁都能注册"的尴尬局面。
  • 上传与媒体:单文件大小上限、媒体保留策略、缩略图生成开关。

我实际的做法是把新配置当成一份新文件重写,而不是在旧文件上改。理由很实在:改的时候容易漏,重写的时候每一行都要经过脑子。重写完之后,把新旧两份并排放着再对一遍,确认所有非默认值都体现在新文件里。

# 示意片段,键名请以你所用版本的示例配置为准 [global] server_name = "chat.example.net" # 必须与旧实例完全一致 [listen] address = "0.0.0.0" port = 8008 [database] backend = "rocksdb" path = "/var/lib/conduwuit/db" [media] path = "/var/lib/conduwuit/media" max_upload_size = 52428800

提示:改写配置时,把每一处"我有意保留默认值"的地方都写个注释说明。下次再迁移或者排查问题时,这个注释能帮你想起来"当初为什么这么设"。

3.3 第三步:数据目录搬迁与数据库迁移

这一步的核心是把旧的数据库目录放到新实例能读到的地方,然后让新版本完成 schema 升级。

如果新旧实例在同一台机器上跑,最省事的方式是先把旧目录改名成备份名,再把备份复制一份到目标路径。为什么是"复制"而不是"移动"?因为复制保留了原始的、未升级的数据库副本,万一 schema 升级中间崩了,你手上还有一份干净的旧库。

如果是跨机器迁移,先用同步工具把数据传过去,传完之后校验文件数量和总大小,不一致就重传。传输过程中不要启动任何实例,避免一边写一边传。

数据库升级本身是新版本首次启动时自动完成的,不需要额外命令(具体版本可能有单独的命令行工具,按官方说明走)。升级过程中不要中断进程,不要断电,不要在升级期间手动去翻数据目录。我一般会把这一步放在机房稳定、网络没波动的时间段做,虽然它不依赖网络,但心理上踏实。

升级完成后立刻做一次"升级后备份"。这一点很容易被忽略——很多人只备份了升级前的状态,升级后不备份。结果升级成功跑了两天,第三天出问题时发现:回滚只能回到两天前,中间两天的消息全丢。每次重大状态变更后都备份一次,这是我踩过最疼的坑之一。

3.4 第四步:灰度启动与自检命令

所谓灰度启动,不是指流量灰度,而是指先用最小配置跑起来,确认基础功能,再逐步放开。

我的启动顺序是这样的:

  1. 先只监听本地回环地址启动,确认进程不崩、日志没有异常、数据库能打开、密钥能加载。
  2. 用本机命令行工具或 API 查询服务器版本和配置生效值,确认监听端口、数据库路径、联邦开关都是预期值。
  3. 放开到内网,用客户端登录一个测试账号,发一条消息、传一张图、建一个房间,验证基本读写。
  4. 确认无误后再对外放开,走反向代理。

自检清单我通常写成一张表格,每项打勾才往下走:

自检项检查方式通过标准
进程稳定观察启动日志 5 分钟无 error 级日志,无重启
数据库版本查看启动日志中的版本行显示已完成升级
密钥加载日志中确认密钥指纹指纹与备份中的一致
配置生效查询生效配置关键项与预期一致
账号登录客户端登录测试账号成功,无报错
消息收发测试房间发消息本端可见,延迟正常
媒体上传上传一张图片并打开图片显示,链接可访问
关键文件检查数据库与密钥文件权限属主和权限正确

这里有个细节:核对密钥指纹。Continuwuity 的日志或管理接口一般能看到当前使用的签名密钥的公钥信息,把它和备份里的旧密钥算出来的值对一下,一致才算真的复用了旧身份。这一步能提前发现"密钥没读到、程序自己生成了新的"这个隐蔽问题。

3.5 第五步:客户端与联邦双向验证

本地能发消息不等于一切正常,联邦方向的问题往往更隐蔽,需要主动验证。

客户端方向相对简单:用不同平台的客户端各登录一次,看看能不能同步历史消息、能不能正常上传下载、推送是否正常。特别注意一下旧会话是否还有效——有些客户端保存的登录态在服务端升级后可能失效,需要重新登录,这不算故障,但要提前跟使用者说清楚,免得一堆人以为账号出问题了。

联邦方向要主动造场景验证,不能等它自己出问题:

  • 出站验证:找一个你确认在线的、有联邦关系的对端房间,发一条消息,让对方确认能否收到。这条消息的签名用的是你的密钥,对方能验证通过,说明出站正常。
  • 入站验证:让对端在那个房间里也发一条,看你这边能否收到。入站涉及你的服务器能否正确拉取和验证对方的事件。
  • 媒体跨服验证:在对端服务器发一张图片,看你能不能正常加载。跨服媒体加载走的是另一条路径,经常会单独出问题。
  • 新房间联合:找一个对端用户,尝试创建一个双方都在的房间,验证状态同步。

如果这几项里有一项失败,先别急着改配置,去看日志。联邦问题的日志通常写得很具体,会告诉你卡在哪一步:是 DNS 解析不到、是连接超时、是 TLS 证书验证失败、还是签名验证不通过。这四类原因的处理方式完全不同。

3.6 第六步:观察期、回滚判定与旧节点下线

服务切过去不等于迁移结束,我一般会留 3 到 7 天的观察期。观察期里旧实例不删、备份不动、配置文件保留,随时可退。

观察期重点看几个指标:进程有没有异常重启、内存占用是否稳定、数据库体积增长速度是否正常、错误日志有没有新的高频条目。内存这一项特别值得关注,因为不同版本的内存管理策略可能不同,迁移后内存曲线突然爬升是常见现象,需要判断是缓存预热还是真的泄漏。

旧节点下线要分两步走。第一步是停掉旧服务但保留数据,观察一周确认没人再访问、没有遗漏的定时任务指向它。第二步才是清理数据和配置,清理前再做一次最终备份,归档到冷存储。

还有一件容易被忘的事:把旧的定时任务和监控告警改过来。备份脚本、证书续期、日志轮转、健康检查,这些外围设施如果还指向旧路径或旧服务名,会在几周后突然给你一个"备份一直是空的"或者"证书过期了没人知道"的惊吓。

4. 踩坑实录:六个真实会遇到的故障与排查路径

这一章全是我自己或身边人实际撞过的问题,按出现频率排序。每个问题我都写了现象、判断依据和解决方法,你可以当成故障速查用。

4.1 启动即退出:数据库版本与锁文件

现象:新版本启动,日志刷几行之后退出,退出码非零,日志里出现存储引擎相关的错误。

最常见的三个原因:一是旧进程没退干净,数据库锁还在;二是数据目录权限不对,新版本以另一个用户运行;三是数据目录不完整,比如只复制了部分文件。

排查顺序建议是:先确认没有残留进程,再确认目录属主和权限,再看日志里的具体错误行。RocksDB 的错误信息里通常会带文件路径,照着路径去看文件在不在。如果是权限问题,ls -la看一眼就能发现——比如目录属主是 root,但服务以专用用户运行,直接读写失败。

还有一种情况是配置文件里的数据库路径写错了,程序在空目录上创建了一个新库然后正常启动。这种"假成功"最坑,因为日志看起来一切正常,你却在用一个空实例。判断方法很简单:看数据目录里有没有新创建的数据库文件、体积是不是从零开始。迁移后第一件事永远是对比数据体积。

4.2 能登录、能发消息,但对端收不到

现象:本地一切正常,自己发的消息自己能看见,联邦房间里的对端用户完全收不到。

这是典型的出站联邦故障,可能原因按概率排序:签名密钥不对、服务器名不对、DNS 或发现记录指向错误、出站连接被防火墙拦、对端把你加入了黑名单。

排查的第一步是确认密钥。如果迁移时密钥没带过来,程序自动生成了新的,本地一切正常但签名对不上,对端会静默丢弃(有些实现会回一个错误,有些不回)。确认方式是对比密钥指纹,前面说过。

第二步是确认服务器名。配置里的 server_name 必须和旧实例、和发现记录、和证书上的名字三者一致。我当时就是因为发现文件里写的是旧端口,导致对端解析到的地址连不上,卡了整整一个下午。

第三步是用外部工具手动请求一下对端的联邦接口,看返回什么状态码。能返回具体错误码就说明网络是通的,问题在协议层;连不上就是网络层的问题。

4.3 头像和图片全 404

现象:文字消息正常,所有图片、头像、附件都显示不出来,点开链接是 404 或者空白。

这个问题的根源几乎总是媒体目录和数据库不匹配。可能情况有三种:媒体目录没同步、同步不完整、媒体元数据里的路径和实际存放路径不一致。

判断方法:找一条具体的消息,从数据库或管理接口查出它引用的媒体标识,然后去媒体目录里找对应文件。找得到说明是路径映射问题,找不到说明是文件缺失。这两种处理方式完全不同——前者改配置,后者补文件。

我遇到的一次是同步工具跳过了某些扩展名的文件(配置里有个排除规则),导致一部分媒体没传过去。这种问题不会报错,只会静默丢失。所以同步完一定要比对文件数量,而不是只看体积。

4.4 联邦握手失败与证书链问题

现象:对端主动连你的时候失败,或者你主动连对端的时候握手报错。

这类问题的日志通常比较明确,会提到 TLS 相关错误。常见原因:证书过期、证书链不完整(只配了站点证书没配中间证书)、证书域名和 server_name 不匹配、反向代理没有正确转发。

证书链不完整这个坑特别隐蔽,因为浏览器访问时通常会自动补全,看起来一切正常,但服务端之间的连接不会做这种补全,直接就失败了。验证方法是拿命令行 TLS 工具连一下你的联邦端口,看能不能拿到完整的证书链。

反向代理配置也是高频问题:联邦流量走的路径和客户端流量可能不同,代理如果只配了客户端路径没配联邦路径,就会出现"用户能用,服务器之间不通"的诡异现象。

4.5 内存与文件句柄:迁移后第一周的性能反直觉

现象:迁移后前几天好好的,第三四天开始变慢,内存占用持续走高,偶尔出现连接失败。

这种情况多数不是 bug,而是新版本的缓存策略和旧版不同。不同版本对联邦事件缓存、媒体缓存、数据库块缓存的大小默认值可能不一样,旧版在小实例上刚好够用,新版默认值更大,在内存有限的机器上就会开始吃紧。

处理方式很直接:限制缓存相关的配置项,把缓存大小调到和机器内存匹配的值。一般建议给服务端留出的内存不超过物理内存的 60%,剩下的留给系统缓存和数据库本身。文件句柄同理,检查一下系统的句柄上限,长期运行的实例句柄数会缓慢增长,上限太低就会在某个时间点突然开始报错。

4.6 故障速查表

把上面这些整理成一张表,出问题时从上往下对:

现象最可能原因快速验证处理方向
启动即退出数据库锁 / 权限 / 路径错看日志路径行,ls -la查权限杀残留进程、修权限、改路径
启动成功但数据是空的数据库路径写错,新建了空库比对数据目录体积改路径,恢复数据
本地正常,联邦收不到密钥不对 / 服务器名不一致核对密钥指纹与 server_name恢复密钥、改回名字
图片全 404媒体目录缺失或不完整按媒体标识查找文件补同步,比对文件数
对端连不上你证书链不完整 / 代理未转发命令行 TLS 工具检查补证书链、改代理
能发不能收入站路径被拦或 DNS 问题手动请求本机联邦端口检查防火墙与解析
用几天后变慢缓存过大 / 句柄耗尽看内存曲线与句柄数调低缓存、提高上限
客户端掉登录会话表结构变化重新登录是否正常提前通知使用者
备份恢复失败备份时数据库在写入用旧版挂载备份试启动改为停机备份

速查表的价值在于缩短"从发现现象到定位方向"的时间,具体的修复还是得看日志。我一般会建议在迁移期间把日志级别调到比平时更详细一档,多打点日志不花钱,出问题时能省几小时。

5. 迁移之后:把这次折腾变成长期能力

迁移做完,多数人就不管了,直到下次再换版本时重新踩一遍同样的坑。我个人的习惯是把每次迁移都变成一次资产沉淀,具体做两件事。

5.1 配置版本化与变更记录

配置文件不要只放在服务器上,放进版本管理里。每次改动都留一条记录:改了什么、为什么改、改完之后验证了什么。这样做的好处是,下次迁移时你可以直接 diff 出"从当前版本到目标版本,配置需要改哪些"。

变更记录我写得比较土,就是一个纯文本文件,按时间倒序排列,每条三行:时间、改了什么、验证结果。看着简陋,但真出事的时候,翻记录比翻记忆可靠得多。

5.2 备份、监控与下一次迁移的准备

备份策略要在迁移之后重新校一遍。重点确认三件事:备份任务真的在跑(不是配了但从没成功过)、备份文件真的能恢复(不是打不开的压缩包)、密钥文件在备份里(不是只有数据库)。

监控方面,我建议至少关注四个指标:进程存活、内存占用、数据库体积增长、联邦出站成功率。前三个容易做,第四个需要一点配置,但它是唯一能提前发现"联邦悄悄断了"的手段——联邦断了不会影响本地使用,可能几天都没人发现,等发现时已经积压了大量待同步事件。

最后说一句关于"下次迁移"的准备。这次迁移积累的东西——版本记录、配置 diff、备份验证流程、故障速查表——全部存好。自托管服务的版本迭代不会停,明年可能又是一次 Conduwuit 到 Continuwuity 式的搬迁,到时候你手上有一份跑通过的流程,成本能压到现在的一小半。

我个人在实际操作中的体会是:这类迁移最难的从来不是技术细节,而是在动手之前把"什么是不可再生的"想清楚。密钥、数据库、原始媒体,这三样只要保护好、同时到位、验证过指纹和版本,剩下的都是可以在线慢慢修的小问题。反过来,这三样里任何一样出岔子,都不是加班能解决的。所以真要给一条建议,就是:备份做完先别急着升级,花十分钟验证一下备份能不能用,这十分钟大概率是你整晚最值钱的十分钟。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询