AsmBB 日常运维手册

AsmBB v3.0 运维闭环:五面巡检、SQLite WAL 备份大坑实录与 .backup 正确姿势、恢复流程、监控指标、983.9 req/s 压测基线、二进制换装与 v2.9→v3.0 迁移、八类故障排查实录,全部实测回显。

AsmBBAsmBBUbuntunginxSQLite论坛社区2026/11/02

AsmBB 日常运维手册

目标:给出 AsmBB v3.0 上线后的日常运维闭环——巡检、备份与恢复(含 SQLite WAL 大坑实录)、监控告警、容量评估、升级回滚与七类故障排查,全部命令与回显来自 Ubuntu 22.04 生产形态环境实测。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(x86-64)
  • 论坛引擎:AsmBB v3.0(manifest.uuid 3df85ed0b218e51a59fe3f02650ef7467050cc46),systemd 单元 asmbb.service,运行用户 www-data
  • Web 服务器:Nginx 1.18.0 (Ubuntu)(80/443),备用 Lighttpd 1.4.63(8081)
  • 站点目录:/var/www/asmbb;数据库:/var/www/asmbb/board.sqlite(WAL 模式);备份目录:/var/www/asmbb/backup
  • 引擎套接字:/var/www/asmbb/engine.sock
  • 邮件中继:Postfix 3.6.4(127.0.0.1:25)
  • 部署过程见 《Ubuntu 22.04 安装 AsmBB v3.0 指南》

二、日常巡检

巡检目标:确认引擎进程、套接字、HTTP 出口、数据库完整性与容量五个面都健康。以下命令构成最小巡检集,建议每日执行。

2.1 服务与进程状态

root@forum:~# systemctl is-active asmbb nginx
active
active
root@forum:~# systemctl status asmbb | head -8
● asmbb.service - AsmBB forum engine FastCGI script.
     Loaded: loaded (/etc/systemd/system/asmbb.service; disabled; vendor preset: enabled)
     Active: active (running) since Mon 2026-11-02 00:15:31 UTC; 9min ago
   Main PID: 3214 (engine)
      Tasks: 2 (limit: 2506)
     Memory: 3.1M
        CPU: 66ms

输出节选(实测首尾)。要点:Active: active (running)、Tasks: 2(主进程+工作线程组)、Memory 应为个位数 MB——若持续涨到数百 MB 属异常,按 7.3 处理。

root@forum:~# ps -eo pid,user,rss,vsz,cmd | grep [e]ngine
   3214 www-data  2664   3208 /var/www/asmbb/engine

RSS 2664 KB 即引擎常态内存。若出现多行 engine 进程,说明 Lighttpd spawn 实例也在运行(两方案共存形态,属正常)。

2.2 套接字与 HTTP 出口

root@forum:~# ls -l /var/www/asmbb/engine.sock
srw-rw-rw- 1 www-data www-data 0 Nov  2 00:15 /var/www/asmbb/engine.sock
root@forum:~# curl -s -o /dev/null -w "http %{http_code} %{time_total}s\n" http://127.0.0.1/
http 200 0.002300s

判定标准:套接字存在且为 srw 开头;HTTP 200 且整页耗时毫秒级。页面页脚还自带耗时统计,可直接作为探针指标:

root@forum:~# curl -s http://127.0.0.1/ | grep -oE "Page processing time: [0-9.]+ ms"
Page processing time: 2.533 ms

2.3 数据库完整性与业务量

root@forum:~# sqlite3 /var/www/asmbb/board.sqlite "PRAGMA integrity_check;"
ok
root@forum:~# sqlite3 /var/www/asmbb/board.sqlite "select (select count(*) from threads),(select count(*) from posts),(select count(*) from users),(select count(*) from eventqueue),(select count(*) from waitingactivation),(select count(*) from userlog);"
1|1|1|2|0|37

六列依次为:主题数、帖子数、用户数、事件队列积压、待激活用户、用户操作日志数。eventqueue 持续增大说明后台任务(如邮件投递)在积压,结合 4.2 看 mail.log。

2.4 引擎日志

引擎自身不写日志文件,异常经 systemd/journald 收集:

root@forum:~# journalctl -u asmbb --no-pager | tail -4
Nov 02 00:58:57 forum.quzuo.local systemd[1]: asmbb.service: Deactivated successfully.
Nov 02 00:58:57 forum.quzuo.local systemd[1]: Stopped AsmBB forum engine FastCGI script..
Nov 02 00:58:57 forum.quzuo.local systemd[1]: asmbb.service: Consumed 3.959s CPU time.
Nov 02 00:58:58 forum.quzuo.local systemd[1]: Started AsmBB forum engine FastCGI script..

三、备份与恢复

AsmBB 全部数据就是 board.sqlite 一个文件,但它是 WAL 日志模式——直接热拷贝主库文件会丢数据,本章先给踩坑实录,再给正确姿势。

3.1 踩坑实录:cp 热拷贝丢 WAL 数据

WAL 模式下最新事务先写 board.sqlite-wal,未合并回主库。实测拷贝走热备份再恢复后,论坛数据全部丢失:

root@forum:~# cp /var/www/asmbb/board.sqlite /var/www/asmbb/backup/board-20261102-004357.sqlite
root@forum:~# ls -la /var/www/asmbb/backup/
total 396
drwxr-xr-x 2 root     root       4096 Nov  2 00:43 .
drwxr-xr-x 5 www-data www-data   4096 Nov  2 00:43 ..
-rw-r--r-- 1 root     root     397312 Nov  2 00:43 board-20261102-004357.sqlite

用该备份恢复后页面变成:

root@forum:~# curl -s http://127.0.0.1/ | grep -o '<title>[^<]*</title>'
<title>AsmBB: Create the admin account!</title>
root@forum:~# sqlite3 /var/www/asmbb/backup/board-20261102-004357.sqlite "select count(*) from threads;"
0

定位:备份时刻 -wal 里还压着管理员与帖子数据(对照 2.2 节环境:主库 397312 字节时 -wal 一度达 1495592 字节,比主库还大)。结论:严禁对运行中的 AsmBB 用 cp 做热备份。

3.2 正确姿势一:sqlite3 .backup 在线热备份

root@forum:~# TS=$(date +%Y%m%d-%H%M%S); sqlite3 /var/www/asmbb/board.sqlite ".backup /var/www/asmbb/backup/board-live-$TS.sqlite"
root@forum:~# ls -la /var/www/asmbb/backup/
total 784
drwxr-xr-x 2 root     root       4096 Nov  2 00:45 .
drwxr-xr-x 5 www-data www-data   4096 Nov  2 00:45 ..
-rw-r--r-- 1 root     root     397312 Nov  2 00:43 board-20261102-004357.sqlite
-rw-r--r-- 1 root     root     397312 Nov  2 00:45 board-proper-004551.sqlite
-rw-r--r-- 1 root     root      32768 Nov  2 00:45 board-proper-004551.sqlite-shm
-rw-r--r-- 1 root     root          0 Nov  2 00:45 board-proper-004551.sqlite-wal
-rw-r--r-- 1 root     root     397312 Nov  2 00:59 board-live-20261102-005915.sqlite
root@forum:~# sqlite3 /var/www/asmbb/backup/board-live-20261102-005915.sqlite "select count(*) from threads;"
1

.backup 走 SQLite 官方在线备份 API,引擎不停机即可拿到一致快照(实测备份中论坛正常 200)。注意备份库本身也是 WAL 模式,用 sqlite3 打开查询会给备份文件生成 -shm/-wal(上表 board-proper 两行即是),归档前可再执行 sqlite3 备份文件 "PRAGMA journal_mode=delete;" 收拢为单文件。

3.3 正确姿势二:停引擎冷备份

强一致需求(如升级前)用冷备份:systemctl stop asmbb 后直接 cp board.sqlite board.sqlite-wal board.sqlite-shm 三件套,或先停再 .backup。备份完成后 systemctl start asmbb。

3.4 定时备份

root@forum:~# cat /etc/cron.d/asmbb-backup
0 3 * * * root sqlite3 /var/www/asmbb/board.sqlite ".backup /var/www/asmbb/backup/board-$(date +%Y%m%d).sqlite"

每天 03:00 一份,按保留策略轮转旧文件即可。

3.5 恢复流程(含双引擎教训)

恢复必须先停掉所有引擎实例——3.1 那次恢复失败还有一个叠加因素:Lighttpd spawn 实例仍在运行,旧引擎进程继续持有原库句柄,恢复时出现「双引擎写同一目录」的混乱。标准流程:

root@forum:~# systemctl stop asmbb lighttpd nginx
root@forum:~# ps -eo pid,cmd | grep [e]ngine        # 确认无残留进程,有则 kill
root@forum:~# rm -f /var/www/asmbb/board.sqlite-wal /var/www/asmbb/board.sqlite-shm
root@forum:~# cp /var/www/asmbb/backup/board-live-20261102-005915.sqlite /var/www/asmbb/board.sqlite
root@forum:~# chown www-data:www-data /var/www/asmbb/board.sqlite
root@forum:~# systemctl start nginx asmbb
root@forum:~# curl -s http://127.0.0.1/ | grep -o '<title>[^<]*</title>'
<title>AsmBB: Threads list /</title>
root@forum:~# sqlite3 /var/www/asmbb/board.sqlite "select count(*) from threads;"
1

要点:删除旧 -wal/-shm 是必须的——残留 WAL 会把旧数据"重放"到新库上造成错乱;恢复后页面标题应为 Threads list,若出现 Create the admin account! 说明恢复的库不含用户数据(回到 3.1 检查备份方式)。

四、监控告警

4.1 建议指标集

指标采集命令告警阈值建议
引擎存活systemctl is-active asmbb非 active 即告警
HTTP 出口curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1/≠200 告警
页面耗时curl -s http://127.0.0.1/ | grep -oE "Page processing time: [0-9.]+ ms">50ms 预警
常驻内存ps -eo rss,cmd | grep [e]ngineRSS>100MB 预警
队列积压sqlite3 /var/www/asmbb/board.sqlite "select count(*) from eventqueue;">100 预警
邮件投递tail /var/log/mail.logreject/defer 关键字告警
库体积ls -l /var/www/asmbb/board.sqlite*结合磁盘水位

4.2 邮件链路抽查

激活邮件是否真的在发,看 Postfix 队列日志即可(组合配置背景见 《AsmBB+Nginx+Postfix 组合实战指南》):

root@forum:~# tail -3 /var/log/mail.log
Nov  2 01:08:09 forum postfix/smtpd[8825]: connect from localhost[127.0.0.1]
Nov  2 01:08:09 forum postfix/smtpd[8825]: NOQUEUE: reject: RCPT from localhost[127.0.0.1]: 550 5.1.1 <tester4@forum.quzuo.local>: Recipient address rejected: User unknown in local recipient table; from=<"admin@forum.quzuo.local"@forum.quzuo.local> to=<tester4@forum.quzuo.local> proto=SMTP helo=<forum.quzuo.local>
Nov  2 01:08:09 forum postfix/smtpd[8825]: disconnect from localhost[127.0.0.1] helo=1 mail=1 rcpt=0/1 quit=1 commands=3/4

(此条即 7.5 的现场:引擎在发信,问题出在收件人不存在。)

五、扩容缩容

  • 引擎并发上限是编译期常量:源码 source/fcgi.asm 定义 MAX_THREAD_CNT=20,单进程内最多 20 个工作线程,没有运行时参数可调。
  • 压测基线(实测):ab 对首页 2000 请求压测:
root@forum:~# ab -n 2000 -c 20 http://127.0.0.1/
Complete requests:      2000
Requests per second:    983.90 [#/sec] (mean)
Time per request:       50.818 [ms] (mean)
Time per request:       1.016 [ms] (mean, across all concurrent requests)
Transfer rate:          23359.92 [Kbytes/sec] received

压测期间进程内存(两行分别为 systemd 常驻实例与 spawn 实例):

root@forum:~# ps -eo pid,user,rss,vsz,cmd | grep [e]ngine
   6033 www-data  1912   2372 /var/www/asmbb/engine
   6051 www-data  6356  10816 /var/www/asmbb/engine

复测一轮(n=500 c=20)为 823.15 req/s;Failed requests 454 全部是 Length 差异——动态页每请求长度不同(页脚耗时、会话 token),并非错误,ab 按长度判失败属工具口径:

root@forum:~# ab -n 500 -c 20 http://127.0.0.1/
Complete requests:      500
Failed requests:        454
   (Connect: 0, Receive: 0, Length: 454, Exceptions: 0)
Requests per second:    823.15 [#/sec] (mean)
Time per request:       24.297 [ms] (mean)
Transfer rate:          19510.24 [Kbytes/sec] received
  • 扩容路径:单节点纵向加 CPU 即可(引擎 RSS 压测峰值也仅 6 MB 量级,瓶颈在 CPU 与 SQLite 写盘)。不支持多写节点水平扩展:board.sqlite 是本地单文件 + WAL,跨机共享文件系统跑多引擎会损坏数据库。若需多实例分摊读压,可用 Lighttpd max-procs 多派生(同机),或前置缓存只读页。
  • 缩容:引擎无配置内存上限,天然适合 512MB 级小机器;缩容后主要盯 board.sqlite-wal 体积与磁盘水位。

六、升级与回滚

6.1 版本锁定与二进制换装演练

永久链接包永远指向最新构建,升级前先下载新版并比对 sha256 与 manifest.uuid,与现网不同才执行换装。官方升级动作 = 停引擎 → 替换 engine、ld-musl-i386.so、libsqlite3.so 三个文件(v3.0 发布公告原文步骤 4)→ 整目录替换 templates/(步骤 5)→ 起引擎。实测换装流程(本次以同版文件演练全流程):

root@forum:~# systemctl stop asmbb
root@forum:~# cp -a /var/www/asmbb/engine /var/www/asmbb/backup/engine.bak
root@forum:~# cp -f /root/dl/asmbb/engine /var/www/asmbb/engine
root@forum:~# cp -f /root/dl/asmbb/libsqlite3.so /var/www/asmbb/libsqlite3.so
root@forum:~# cp -f /root/dl/asmbb/ld-musl-i386.so /var/www/asmbb/ld-musl-i386.so
root@forum:~# chown www-data:www-data /var/www/asmbb/engine /var/www/asmbb/libsqlite3.so /var/www/asmbb/ld-musl-i386.so
root@forum:~# systemctl start asmbb
root@forum:~# curl -s -o /dev/null -w "换装后 HTTP %{http_code}\n" http://127.0.0.1/
换装后 HTTP 200
root@forum:~# sqlite3 /var/www/asmbb/board.sqlite "select count(*) from threads;"
1

6.2 跨大版本(v2.9.1 → v3.0)迁移要点

v3.0 与 v2.x 数据库结构不兼容,官方发布公告给出八步流程:①先解密数据库(SQLeet 与 v3.0 的 SQLite3MultipleCiphers 不兼容,加密库必须先解密)②备份 ③在 SQLite 控制台执行官方迁移 SQL ④停引擎替换三个二进制 ⑤整目录替换 templates/ ⑥启动 ⑦检查 ⑧需要时在设置页重新加密。迁移 SQL 核心节选(官方发布公告原文,完整脚本以公告页为准):

insert into Params values ('nu_post_interval', 0);
insert into Params values ('nu_post_interval_inc', 0);
insert into Params values ('nu_max_post_length', 0);
alter table Users add column LastPostTime integer default 0;
alter table Threads add column Rating integer default 0;
create table ThreadVoters (
 threadID integer references Threads(id) on delete cascade on update cascade,
 userID integer references Users(id) on delete cascade on update cascade,
 Vote integer
);
-- 以及 PostsAI/PostsAD 两个触发器的重建(含发帖间隔统计逻辑)

执行顺序红线:迁移 SQL 必须在引擎停止状态下执行(避免触发器与旧引擎并发写),且第 3.2 节的热备份不能替代步骤②——升级备份请用 3.3 冷备份。

6.3 回滚

回滚 = 反向换装:systemctl stop asmbb → 把 backup/engine.bak 等旧文件放回 → 若做过跨版本迁移,同时按 3.5 恢复迁移前的冷备份库 → systemctl start asmbb → 验证 HTTP 200 与业务计数。因此升级前冷备份 + 旧二进制留存(backup 目录)两个条件必须同时满足。

七、故障排查手册

7.1 502 Bad Gateway

现象:Nginx 502。定位链:systemctl is-active asmbb → 引擎停了;ls -l /var/www/asmbb/engine.sock → 套接字没了。实测:

root@forum:~# systemctl stop asmbb
root@forum:~# curl -s -o /dev/null -w "stop后 HTTP %{http_code}\n" http://127.0.0.1/
stop后 HTTP 502
root@forum:~# systemctl start asmbb
root@forum:~# curl -s -o /dev/null -w "start后 HTTP %{http_code}\n" http://127.0.0.1/
start后 HTTP 200

7.2 引擎报「No such file or directory」但文件存在

root@forum:~# cd / && /var/www/asmbb/engine
bash: line 1: /var/www/asmbb/engine: No such file or directory
exit=127

真凶是相对路径解释器 ./ld-musl-i386.so(file engine 可见 interpreter ./ld-musl-i386.so)。任何启动方式(手工、systemd WorkingDirectory、Lighttpd enter-directory)都必须把工作目录设为站点目录。

7.3 kill -9 后套接字残留与自愈

引擎被 kill -9 后 engine.sock 残留为死文件,但 Restart=on-failure 让 systemd 立即拉起新实例,新实例重建套接字自恢复:

root@forum:~# ls -l /var/www/asmbb/engine.sock
srw-rw-rw- 1 www-data www-data 0 Nov  2 00:43 /var/www/asmbb/engine.sock
root@forum:~# systemctl status asmbb | head -3
     Active: active (running) since Mon 2026-11-02 00:43:35 UTC; 742ms ago
   Main PID: 5946 (engine)
root@forum:~# curl -s -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1/
HTTP 200

若未见自愈,检查单元是否带 Restart=on-failure,或手工 systemctl restart asmbb。

7.4 直改 Params 表不生效

引擎启动时把 Params 缓存进内存,sqlite3 直改库后论坛表现不变,必须重启引擎。实测写入 SMTP 参数后重启生效:

root@forum:~# sqlite3 /var/www/asmbb/board.sqlite "select id,val from params where id in ('email_confirm','forum_title','host','smtp_addr','smtp_port');"
email_confirm|1
forum_title|AsmBB Production Demo
host|forum.quzuo.local
smtp_addr|127.0.0.1
smtp_port|25
root@forum:~# systemctl restart asmbb && systemctl is-active asmbb
active

7.5 注册了却收不到激活邮件(smtp_user 为空静默不发)

实录:email_confirm=1、SMTP 地址端口都对,注册返回 302 user_created,但 mail.log 毫无动静——smtp_user 为空时引擎静默跳过发信。补上 smtp_user(如 admin@forum.quzuo.local)并重启引擎后,此前积压的注册事件经 EventQueue 自动重投,三封激活邮件一次性入队:

root@forum:~# sqlite3 /var/www/asmbb/board.sqlite "select id,val from params where id in ('smtp_user','smtp_addr','smtp_port','email_confirm');"
email_confirm|1
smtp_addr|127.0.0.1
smtp_port|25
smtp_user|admin@forum.quzuo.local
root@forum:~# tail -3 /var/log/mail.log
Nov  2 00:42:19 forum postfix/qmgr[5785]: 7DED0160F0B8: from=<"admin@forum.quzuo.local"@forum.quzuo.local>, size=857, nrcpt=1 (queue active)
Nov  2 00:42:19 forum postfix/qmgr[5785]: 7F1CE160F0BB: from=<"admin@forum.quzuo.local"@forum.quzuo.local>, size=860, nrcpt=1 (queue active)
Nov  2 00:42:19 forum postfix/qmgr[5785]: 810FE160F0B8: from=<"admin@forum.quzuo.local"@forum.quzuo.local>, size=860, nrcpt=1 (queue active)

注意发件人呈现为 "admin@forum.quzuo.local"@forum.quzuo.local(smtp_user 被当作显示名包了一层),属引擎行为而非配置错误。排查顺序:smtp_user 非空 → 引擎重启过 → mail.log 有无 reject(如 4.2 的 550 收件人不存在)。

7.6 恢复备份后显示「Create the admin account!」

两个原因:备份用了 cp 热拷贝丢了 WAL 数据(见 3.1);或恢复时没停全部引擎/没清 -wal/-shm(见 3.5)。用 .backup 产物按 3.5 流程重做即可。

7.7 Lighttpd 403 Forbidden

lighttpd HTTP 403 size 341 = FastCGI 没接上。依次检查:90-asmbb.conf 是否加载 mod_fastcgi(缺模块时报 unknown config-key fastcgi.server)、fastcgi.server 的 bin-path/enter-directory 是否指向站点目录。完整实录见 《Ubuntu 22.04 安装 AsmBB v3.0 指南》 5.2/5.3。

7.8 Nginx 起不来:cannot load certificate

root@forum:~# nginx -t
nginx: [emerg] cannot load certificate "/etc/nginx/ssl/asmbb.crt": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory:calling fopen(/etc/nginx/ssl/asmbb.crt, r) error:10000080:BIO routines::no such file)
nginx: configuration file /etc/nginx/nginx.conf test failed

证书目录 /etc/nginx/ssl 不存在导致 openssl 与 nginx 连环失败。mkdir -p /etc/nginx/ssl 后重新签发自签证书即可(完整 TLS 配置实录见 《AsmBB+Nginx+Postfix 组合实战指南》)。

八、常见问题

Q1:AsmBB 有没有自己的日志文件? 没有。引擎无独立日志,进程级事件看 journalctl -u asmbb(见 2.4),业务级操作审计看数据库 UserLog 表(巡检 2.3 实测当前 37 条),邮件看 /var/log/mail.log。

Q2:board.sqlite 能不能收缩体积? 可以。停引擎后执行 VACUUM,实测流程与回显:

root@forum:~# systemctl stop asmbb
root@forum:~# sqlite3 /var/www/asmbb/board.sqlite "VACUUM;"
root@forum:~# ls -l /var/www/asmbb/board.sqlite
-rw-r--r-- 1 www-data www-data 397312 Nov  2 00:44 /var/www/asmbb/board.sqlite
root@forum:~# systemctl start asmbb
root@forum:~# curl -s -o /dev/null -w "vacuum后 HTTP %{http_code}\n" http://127.0.0.1/
vacuum后 HTTP 200

Q3:待激活用户堆着不动怎么办? 查 WaitingActivation 表与 mail.log:多为 7.5(smtp_user 空)或收件域不可达。手动激活可直接把激活链接(用户注册时引擎生成 !activate/<secret>)发给用户,或管理员在用户页处理。

Q4:ARM 服务器能跑吗? 官方二进制是 i386 ELF,不能。需自行用 FASM 从 Fossil 源码重编译(官方有编译安装帖),成本高,选型前先确认架构(见 《AsmBB 介绍与选型指南》 第六章限制)。

九、参考链接

AsmBB 日常运维手册 - Quzuo