Gerbera 常用命令与运维实战(核心篇)

Gerbera 3.3.0 运维核心篇实测:gerbera CLI 参数全景与运行方式、check-config 好坏配置拦截、数据库备份-校验-破坏-恢复完整闭环(--databases 翻车与 --drop-tables 行为偏差取证)、日志五段读法与四类报错对照、Web UI API 化重扫/虚拟文件夹增删/在线转码五环排障、db_version=29 迁移机制与升级 SOP,回显逐行照抄。

GerberaGerberaUPnPDLNA媒体服务器2026/10/29

Gerbera 常用命令与运维实战(核心篇)

本文是 Gerbera 专栏的核心运维篇,在真实服务器环境(Gerbera 3.3.0 + MariaDB 11.8,2026-10-29 实测)下完成五组运维实战:gerbera 命令行参数与运行方式、数据库备份与恢复的完整闭环(含一次 --databases 参数翻车实录与 --drop-tables 行为偏差取证)、日志排障方法论、Web UI 日常运维(容器树浏览 / 全量重扫 / 虚拟文件夹增删 / 在线转码开通与排障)、版本升级机制与标准流程。全部命令与回显均为服务器实测逐行照抄。

前置阅读:组件定位与选型结论见 《Gerbera 介绍与选型指南》;服务从 0 到 1 的部署过程见 《Gerbera Docker Compose 部署实战》(以下简称部署篇);config.xml 逐节参数含义见 《Gerbera 配置参数详解》(以下简称配置篇)。

一、运维环境与服务身份盘点

运维任何服务,第一步都是搞清楚「我面对的是哪一个二进制、它被编译成了什么样」。本章先固化本篇实测环境口径,再盘点 Gerbera 的服务身份信息。

1.1 实测环境口径

延续部署篇完成的环境,关键信息汇总如下(本篇所有命令均在此环境执行):

项目值
Gerbera 版本3.3.0
服务绑定地址10.89.56.10:4949(局域网经 8136 端口对外发布,规划见部署篇)
主配置文件/var/run/gerbera/config.xml
运行数据目录(<home>)/var/run/gerbera/
媒体库目录/mnt/content(只读挂载的存储卷)
数据库MariaDB 11.8.9 @ 10.89.56.20:3306,库名 gerbera,账号 gerbera/q1w2e3r4
Web UI 账号admin/q1w2e3r4
安装数据目录(PACKAGE_DATADIR)/usr/local/share/gerbera
媒体文件tone.flac、tone440.mp3 等 7 个音频 + clip.mp4 视频 + photo.jpg 图片

1.2 gerbera --version 与 --compile-info:看清你的二进制

gerbera --version

实测回显:


Gerbera UPnP Server 3.3.0
Copyright 2016-2026 Gerbera Contributors.
Licence GPLv2: GNU GPL version 2.
This is free software: you are free to change and redistribute it.

再看编译信息,这条命令的输出直接决定「这台服务器上的 Gerbera 能做什么」:

gerbera --compile-info

实测回显(节选关键段,完整输出 60 余行):

Compile info:
-------------
WITH_NPUPNP             OFF
STATIC_LIBUPNP          ON
WITH_MAGIC              ON
WITH_MYSQL              ON
WITH_PGSQL              ON
WITH_CURL               ON
WITH_INOTIFY            ON
WITH_JS                 ON
WITH_TAGLIB             ON
WITH_AVCODEC            ON
WITH_FFMPEGTHUMBNAILER  ON
WITH_EXIF               ON
WITH_EXIV2              ON
WITH_MATROSKA           ON
WITH_WAVPACK            ON
WITH_SYSTEMD            NO
WITH_ICU                ON
WITH_ZIP                ON
WITH_LASTFM             ON
WITH_ONLINE_SERVICES    OFF
WITH_DEBUG              ON
WITH_DEBUG_OPTIONS      ON
WITH_TESTS              OFF
Build info:
-----------
FMT                     110200
SPDLOG                  11700
CXXOPTS                 3.3.1
SQLITE                  3.53.4
PUGIXML                 1150
JSONCPP                 1.9.6
PACKAGE_DATADIR         /usr/local/share/gerbera
PUPNP                   220100
HAVE_ICU                78.1
HAVE_JS                 20700
HAVE_MYSQL              Server 10.8.8-MariaDB, Client 3.4.6
HAVE_PGSQL              Pqxx 7.10.3, PqLib 180006
HAVE_CURL               8.22.0
HAVE_TAGLIB             2.3.2
HAVE_FFMPEG             Lavu60.26.102, Lavf62.12.102, Lavc62.28.102, Lavfi11.14.102
HAVE_FFMPEGTHUMBNAILER  2.3.1
Git info:
---------
Git Branch: HEAD
Git Commit: 28361d9e22812c9d7042a85b355a17c8f8495217

逐段解说:

  • WITH_ 开头的开关是功能编译开关:WITH_MYSQL ON 说明本二进制支持 MySQL/MariaDB 后端;WITH_INOTIFY ON 说明 inotify 实时扫描可用(部署篇验证过);WITH_JS ON 说明虚拟布局自定义脚本可用(配置篇 boxlayout 的前提)。
  • WITH_SYSTEMD NO 值得注意:本二进制编译时未带 systemd 支持包,安装目录里不会自带 systemd unit 文件,常驻运行要自己写单元(见 2.4 节)或使用发行版打包版。
  • WITH_ONLINE_SERVICES OFF:Last.FM 之外在线服务未编译,--init-lastfm 之外的在线功能无需考虑。
  • PACKAGE_DATADIR /usr/local/share/gerbera:安装数据目录,后面第六章的数据库迁移脚本就放在这里,排障时要能对上号。
  • Git Commit:出现版本疑难杂症时,凭 commit 号去官方仓库比对,比报版本号更精确。

💡 技术拓展:--compile-info 是排查「同一个版本、不同行为」的首选命令。Gerbera 官方分发版与发行版仓库(Debian/Fedora 等)的编译开关并不相同,例如官方镜像默认无 systemd 单元、发行版包则相反。报障或迁移前先存一份 compile-info,等于给二进制做了「指纹登记」。

二、gerbera CLI 参数与运行方式

Gerbera 是单进程守护进程,命令行参数覆盖配置、运行、日志、数据库四大类。本章把 --help 全景过一遍,并实测最常用的几组。

2.1 gerbera --help 参数全景

gerbera --help

实测回显(逐行照抄):

Gerbera UPnP Media Server - https://gerbera.io
Usage:
gerbera [OPTION...]

  -h, --help                    Print this help and exit
  -D, --debug                   Enable debugging
  -v, --version                 Print version info and exit
      --compile-info            Print compile info and exit
      --create-config [=Section(=All)]
                                Print a default config.xml file and exit
      --create-example-config [=Section(=All)]
                                Print a example config.xml file and exit
      --create-advanced-config [=Section(=All)]
                                Print a advanced example config.xml file and
                                exit
      --modules [=Module(=None)]
                                Create "from-file" module statements in
                                config.xml
      --check-config            Check config.xml file and exit
      --print-options           Print simple config options and exit
      --offline                 Do not answer UPnP content requests
      --drop-tables             Drop all database tables and exit
      --init-lastfm             Get Last.FM session key
  -d, --daemon                  Daemonize after startup
  -u, --user UID                Drop privs to user UID
  -P, --pidfile FILE            Write a pidfile to the specified location,
                                e.g. /run/gerbera.pid
  -c, --config FILE             Path to config file
  -f, --cfgdir DIR              Override name of config folder
                                (.config/gerbera) in home folder
  -l, --logfile FILE            Set log location
      --rotatelog FILE          Set rotating log location
      --syslog [=LOG(=USER)]    Log to syslog
      --add-file FILE           Scan a file into the DB on startup, can be
                                specified multiple times
      --set-option OPT=VAL      Set simple config option OPT to value VAL,
                                can be specified multiple times use
                                --print-options for value OPTs
  -s, --database DB=(sqlite|mysql|postgres)
                                Select Database for storage
  -p, --port PORT               Port to bind with, must be >=49152
  -i, --ip IP                   IP to bind with
  -e, --interface IF            Interface to bind with
  -m, --home DIR                Search this directory for a .config/gerbera
                                folder containing a config file
      --scripts DIR             Search this directory for custom javascript
                                files
      --magic FILE              Set magic FILE
      --import-mode MODE        Activate import MODE ('mt' or 'grb')
      --debug-mode FACILITY     Set debugging mode to FACILITY

按运维场景归组解读:

  • 配置体检类(改配置后、重启服务前必跑):--check-config、--print-options、--create-config 系列、--set-option。前三者在配置篇已逐个实测,本章重点展开 --check-config。
  • 运行形态类:前台直跑、-d 守护进程化、-P pidfile、-u 降权、--offline 离线模式。
  • 日志类:-l/--logfile(单文件)、--rotatelog(轮转文件)、--syslog(系统日志)三通道,可按需组合。
  • 数据库类:-s 临时切换存储后端、--drop-tables 清空数据表、--add-file 启动时补扫单个文件。
  • 绑定类:-p 端口(注意 >=49152 的约束,部署篇就是用 --port 4949 覆盖了配置文件的 49152 缺省值)、-i 绑 IP、-e 绑网卡。
  • 调试类:-D 全量调试、--debug-mode FACILITY 按设施开调试,排障核武器(见第四章)。

2.2 --check-config:上线与变更的第一道闸

好配置直接过:

gerbera --check-config -c /var/run/gerbera/config.xml

实测回显(关键段):

2026-10-29 00:01:12   info: Gerbera UPnP Server 3.3.0 - https://gerbera.io/
2026-10-29 00:01:12   info: Loading configuration from: /run/gerbera/config.xml
2026-10-29 00:01:12   info: Parsing configuration...
2026-10-29 00:01:12   info: Configuration load succeeded.
2026-10-29 00:01:12   info: Validating configuration...
2026-10-29 00:01:12 warning: Found extra config file entry '/config/server/storage/postgres/database'
2026-10-29 00:01:12 warning: Found extra config file entry '/config/server/storage/postgres/host'
2026-10-29 00:01:12 warning: Found extra config file entry '/config/server/storage/postgres/username'
2026-10-29 00:01:12 warning: Found extra config file entry '/config/server/storage/sqlite3/backup'
2026-10-29 00:01:12 warning: Found extra config file entry '/config/transcoding/mimetype-profile-mappings/transcode'
2026-10-29 00:01:12 warning: Found extra config file entry '/config/transcoding/profiles/profile/agent'
(……中间另有 sqlite3/database-file、各 profile 的 buffer/encoder 等 warning,共 26 条……)
2026-10-29 00:01:12   info: Configuration check succeeded.

解说两个要点:

  • 退出码 0 + Configuration check succeeded. 才算通过。--check-config 只做「解析 + 校验」,不连数据库、不绑端口,可以随时在变更窗口前跑。
  • 26 条 Found extra config file entry warning 不是错误:它们指向被禁用后端(postgres、sqlite3)里的参数,以及转码 profiles 里「被映射表引用但映射语法上不认识」的子元素。Gerbera 的校验器对「配置文件里有、但当前启用配置不需要」的条目一律提示 extra,留着无害,追求零告警可注释掉禁用后端小节。

再故意喂一份坏配置(把 <port> 改成 abc 的 bad.xml)看失败长什么样:

gerbera --check-config -c /var/run/gerbera/bad.xml

实测回显:

2026-10-29 00:01:12   info: Loading configuration from: /run/gerbera/bad.xml
2026-10-29 00:01:12   info: Parsing configuration...
2026-10-29 00:01:12 warning: Error in configuration, could not find command "ffmpeg" in $PATH for '/config/attribute::command'
2026-10-29 00:01:12 warning: Error in configuration, could not find command "vlc" in $PATH for '/config/attribute::command'
2026-10-29 00:01:12  error: Runtime error parsing config file '/var/run/gerbera/bad.xml': [/gerbera_build/src/config/setup/config_setup_int.cc:104] T ConfigIntegerSetup<T, OptionClass>::checkIntValue(std::string&, const std::string&) const [with T = unsigned int; OptionClass = UIntOption; std::string = std::__cxx11::basic_string<char>] Error: Error in config file: /config//server/port unreadable int value 'abc'
echo $?
1

解说:

  • 错误三级分明:两条 warning(转码 agent 依赖的 ffmpeg / vlc 命令在 $PATH 里找不到,external profile 会受影响)+ 一条致命 error(/config//server/port unreadable int value 'abc',直接点名 XML 路径与非法值)。
  • 退出码 1 是脚本化变更的关键信号:CI 或变更脚本里 gerbera --check-config ... || abort 即可把坏配置挡在重启之前。
  • 顺带验证了 error 行暴露的源码路径(config_setup_int.cc:104),这是 3.3.0 端口参数的校验实现位置,比对上游代码时可用。

2.3 前台运行、--logfile 与 --offline

排障时最有价值的运行形态是「前台 + 落日志 + 离线」,即不对外宣告 UPnP、把启动全过程写进指定文件:

gerbera -c /var/run/gerbera/config.xml --port 4952 --offline --logfile /var/run/gerbera/d1-offline.log &
sleep 6 && kill %1

日志文件 d1-offline.log 开头(逐行照抄):

2026-10-29 00:01:25   info: Running in offline mode
2026-10-29 00:01:25   info: Gerbera UPnP Server 3.3.0 - https://gerbera.io/
2026-10-29 00:01:25   info: Copyright 2016-2026 Gerbera Contributors.
2026-10-29 00:01:25   info: Gerbera is free software, covered by the GNU General Public License version 2
2026-10-29 00:01:25   info: Loading configuration from: /run/gerbera/config.xml
2026-10-29 00:01:25   info: Parsing configuration...
2026-10-29 00:01:25   info: Configuration load succeeded.
2026-10-29 00:01:25   info: Validating configuration...
2026-10-29 00:01:25   info: Configuration check succeeded.
2026-10-29 00:01:25   info: Loading 0 configuration items from database
2026-10-29 00:01:25   info: Initialising UPnP with interface: <unset>, port: 4952
2026-10-29 00:01:25   info: IPv4: Server bound to: 10.89.56.10:4952
2026-10-29 00:01:25   info: The Web UI can be reached by following this link: http://192.168.146.128:8136

结尾(收到 kill 后的优雅退出):

2026-10-29 00:01:30   info: Gerbera shutting down. Please wait...
2026-10-29 00:01:31   info: Gerbera exiting. Have a nice day.

解说:

  • Running in offline mode:--offline 让服务跳过 UPnP 内容应答,但仍完成配置加载、数据库连接与端口绑定——专门用于「验证配置 + 观察 DB 行为」而不干扰线上客户端。
  • -p 4952 换了端口:UPnP 端口被常驻实例占着,诊断实例必须错开(--help 明确端口须 >=49152,4952 合规)。
  • --logfile 落盘 + 前台运行是复现启动期问题的标准姿势;结尾两行说明 Gerbera 收到 SIGTERM 会优雅退出(先 shutting down 再 exiting),这为第五章的重启操作兜了底。
  • 注意日志里 Loading configuration from: /run/gerbera/config.xml——服务把 /var/run 显示为 /run(两者是同一目录的符号链接),与配置里的 /var/run/gerbera/config.xml 并无冲突。

2.4 常驻运行方式:daemon 参数与 systemd 单元

生产常驻有两条路:

路径一:-d 守护进程化(简单场景)

gerbera -c /var/run/gerbera/config.xml -p 4949 -u gerbera -P /run/gerbera.pid -d
  • -d 让进程 fork 到后台;-P 落 pidfile,供运维脚本 kill $(cat /run/gerbera.pid) 停服;
  • -u gerbera 在启动后把有效用户切到 gerbera(--help 里写作 "Drop privs to user UID"),避免媒体服务以 root 长跑;
  • 缺点:进程崩了没人拉起,日志要自己接 --logfile 或 --syslog。

路径二:systemd 单元(生产推荐)

# /etc/systemd/system/gerbera.service
[Unit]
Description=Gerbera Media Server
After=network-online.target mariadb.service
Wants=network-online.target

[Service]
User=gerbera
ExecStartPre=/usr/local/bin/gerbera --check-config -c /var/run/gerbera/config.xml
ExecStart=/usr/local/bin/gerbera -c /var/run/gerbera/config.xml -p 4949 --logfile /var/log/gerbera/gerbera.log
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

解说:

  • 本二进制 WITH_SYSTEMD NO,单元文件需自备;ExecStartPre 把 2.2 节的 --check-config 固化成「起服前自动体检」,坏配置直接阻断启动,比崩了再查日志省一个排障循环;
  • Restart=on-failure 吸收偶发崩溃——部署篇复现过的「数据库地址写错 → 崩溃-重启循环」正是这类参数的用武之地:服务会反复拉起,但 journalctl/logfile 里每次都留下同一条数据库错误,定位反而更快;
  • 本实测环境的常驻实例即按「守护进程 + 自定义启动参数(--port 覆盖)」方式运行,与部署篇 IMAGE_PORT 变量的行为一致。

💡 技术拓展:-c/--config 与 -m/--home 的分工——-c 指定主配置文件路径,-m 指定「家目录」用于搜索 .config/gerbera 隐藏配置目录。只用 -c 显式传配置时 -m 通常不需要;但当配置里 <home> 缺省时,Gerbera 会回退到 -m 指定目录下找 .config/gerbera,缓存(缩略图等)也落在 <home>/cache-dir。实测环境里 <home> 显式写死为 /var/run/gerbera/,缩略图缓存就在 /var/run/gerbera/cache-dir/content/clip.mp4-thumb.jpg。

三、数据库备份与恢复实战

Gerbera 的全部「资产」——媒体条目、虚拟布局结果、客户端记录、配置覆盖项——都在数据库里;媒体文件本身只需重新扫描即可找回,因此备份恢复演练的对象就是数据库。本章完成「备份 → 可恢复性校验 → 破坏 → 恢复 → 业务验证」全闭环,中间如实保留一次参数选择失误实录。

3.1 全量备份:mariadb-dump

在数据库服务器上对 gerbera 库做一致性快照:

mariadb-dump -ugerbera -pq1w2e3r4 gerbera --single-transaction \
  > /var/backups/gerbera/gerbera-d1-20261028.sql
ls -la /var/backups/gerbera/

实测回显:

-rw-r--r-- 1 root root 59579 Oct 28 23:43 gerbera-20261028-ops.sql
-rw-r--r-- 1 root root 59708 Oct 28 23:29 gerbera-2026-10-28.sql
-rw-r--r-- 1 root root 56793 Oct 28 22:50 gerbera-full-20261028.sql
-rw-r--r-- 1 root root 59792 Oct 29 00:01 gerbera-d1-20261028.sql

备份文件头部:

/*M!999999\- enable the sandbox mode */ 
-- MariaDB dump 10.20-11.8.9-MariaDB, for debian-linux-gnu (x86_64)
--
-- Host: localhost    Database: gerbera
-- ------------------------------------------------------
-- Server version	11.8.9-MariaDB-ubu2404

解说:

  • --single-transaction 在 InnoDB 表上获得一致性快照且不锁表;Gerbera 建表用 MyISAM(见 3.4 节 mt_internal_setting 的 engine=MyISAM),该参数对 MyISAM 不生效——若在业务高峰备份 MyISAM 表仍可能读到中间态,本实测环境媒体库变更频率低,实际影响可忽略,但规范上应知道这个边界;
  • 第一行 /*M!999999\- enable the sandbox mode */ 是 MariaDB 11 系 dump 的防注入标记,3.2 节会看到它的影响;
  • 目录里另有三份此前演练留存的历史备份(大小 56~59 KB),说明这个库的体量很小——全量备份永远是最划算的首选策略。

3.2 备份可恢复性校验:临时库比对(含 --databases 翻车实录)

备份没校验过恢复,等于没备份。标准做法是把备份灌进一个临时库,与生产库比对行数和表数。第一次尝试就翻车了,过程完整保留——当时多加了 --databases 参数:

mariadb-dump -ugerbera -pq1w2e3r4 --databases gerbera --single-transaction \
  > /var/backups/gerbera/gerbera-d1-20261028.sql
mariadb -uroot -pq1w2e3r4 -e "CREATE DATABASE gerbera_verify;"
mariadb -uroot -pq1w2e3r4 gerbera_verify < /var/backups/gerbera/gerbera-d1-20261028.sql
mariadb -uroot -pq1w2e3r4 -e "SELECT COUNT(*) AS verify_objects FROM gerbera_verify.mt_cds_object;"

实测回显:

ERROR 1146 (42S02) at line 1: Table 'gerbera_verify.mt_cds_object' doesn't exist

--verbose 重放导入,真相在输出里:

--------------
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `gerbera` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci */
--------------

--------------
DROP TABLE IF EXISTS `grb_cds_resource`
--------------

解说(翻车复盘):

  • 带 --databases 的 dump 会内嵌 CREATE DATABASE gerbera + USE gerbera 语句;导入时即使命令行指定了目标库 gerbera_verify,USE gerbera 一到,后续所有建表、灌数全部切回了 gerbera 库——临时库始终是空的,而生产库被「原样重灌」了一遍;
  • 这次失误的意外收获是完整走了一遍「用备份重灌生产库」且数据无损(123 个媒体对象原样恢复),但规范操作必须避免这种隐式写生产:
  • 正确姿势:单库备份不加 --databases,dump 里就没有 CREATE DATABASE/USE,导入目标完全由命令行控制:
mariadb-dump -ugerbera -pq1w2e3r4 gerbera --single-transaction \
  > /var/backups/gerbera/gerbera-d1-clean.sql
grep -c 'CREATE DATABASE\|^USE' /var/backups/gerbera/gerbera-d1-clean.sql
0

重新走校验流程:

mariadb -uroot -pq1w2e3r4 -e "DROP DATABASE IF EXISTS gerbera_verify; CREATE DATABASE gerbera_verify;"
mariadb -uroot -pq1w2e3r4 gerbera_verify < /var/backups/gerbera/gerbera-d1-clean.sql
mariadb -uroot -pq1w2e3r4 -e "SELECT \"gerbera(生产库)\" AS src, COUNT(*) AS cds_objects FROM gerbera.mt_cds_object UNION ALL SELECT \"gerbera_verify(校验库)\", COUNT(*) FROM gerbera_verify.mt_cds_object; SELECT COUNT(*) AS verify_tables FROM information_schema.TABLES WHERE TABLE_SCHEMA=\"gerbera_verify\";"

实测回显:

src	cds_objects
gerbera(生产库)	123
gerbera_verify(校验库)	123
verify_tables
8

校验通过后清理临时库:

mariadb -uroot -pq1w2e3r4 -e "DROP DATABASE gerbera_verify; SELECT 'verify db dropped' AS done;"
done
verify db dropped

解说:生产/校验 123=123、8 张表齐全,备份可用性闭环完成。日后例行演练只需重复 3.2 的正确姿势三分钟。

3.3 --drop-tables 实测与官方清库路径

Gerbera 自带清库参数,安装目录里也有配套脚本(/usr/local/share/gerbera/ 下可见 mysql-drop.sql、mysql.sql、mysql-upgrade.xml 三件套)。实测 --drop-tables:

gerbera --drop-tables -c /var/run/gerbera/config.xml

实测回显(尾部):

2026-10-29 00:04:55   info: Configuration check succeeded.
2026-10-29 00:04:55   info: Dropping tables with /usr/local/share/gerbera/mysql-drop.sql
2026-10-29 00:04:55  error: Run: logic error map::at

再查库——8 张表一张没少:

Tables_in_gerbera
grb_cds_resource
grb_client
grb_config_value
grb_playstatus
mt_autoscan
mt_cds_object
mt_internal_setting
mt_metadata

配套脚本内容(实测照抄):

DROP TABLE `grb_playstatus`;
DROP TABLE `grb_cds_resource`;
DROP TABLE `mt_metadata`;
DROP TABLE `mt_autoscan`;
DROP TABLE `mt_cds_object`;
DROP TABLE `mt_internal_setting`;
DROP TABLE `grb_config_value`;
DROP TABLE `grb_client`;

解说:

  • --drop-tables 成功加载配置、找到 mysql-drop.sql,但在真正执行阶段报 Run: logic error map::at 退出,表未删除——这是 3.3.0 在 MySQL 后端 + 常驻实例并存场景下的行为偏差,实测两要素(并发实例、logic error)之间的因果关系未再深究,但结论明确:这版 --drop-tables 不可依赖;
  • 官方文档(docs.gerbera.io troubleshooting 页)的口径是:数据库自动迁移失败或需要换新布局特性时,「用 mysql-drop.sql 删掉 MySQL/MariaDB 库里全部表,或运行 gerbera --drop-tables -c <config>」清空重建——即官方把两者视为等价路径,本实测证明了等价性会破,兜底动作应优先选手工执行 mysql-drop.sql(脚本内容就是上面 8 条 DROP,手工跑完全可控);
  • 顺带记录:grb_* 四张表是 2.x 起新增的(资源、客户端、配置覆盖、播放状态),mt_* 四张是 MediaTomb 血统的老表(对象、扫描、元数据、内部设置),8 张全清才是完整重置。

3.4 破坏性恢复演练闭环

在备份校验通过后,做一次最彻底的演练:把库删掉,再从零恢复。

第一步,记录原库的建库参数(字符集/排序规则是恢复时最容易丢的细节):

mariadb -uroot -pq1w2e3r4 -e "SHOW CREATE DATABASE gerbera\G"

实测回显:

*************************** 1. row ***************************
       Database: gerbera
Create Database: CREATE DATABASE `gerbera` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci */

第二步,删库,并观察服务故障现象。CDS 接口立刻变质:

python3 cds_browse.py http://127.0.0.1:8136 0
urllib.error.HTTPError: HTTP Error 500: Internal Server Error

服务日志同步留下报错(截取关键行):

2026-10-29 00:05:15 warning: Exception: Mysql: mysql_real_query() failed: mysql_error (1146): "Table 'gerbera.mt_cds_object' doesn't exist"; query: SELECT `f`.`id`, `f`.`ref_id`, ... FROM `mt_cds_object` `f` LEFT JOIN `mt_cds_object` `rf` ON `f`.`ref_id` = `rf`.`id` LEFT JOIN `mt_autoscan` `as` ON `as`.`obj_id` = `f`.`id`  WHERE `f`.`id` = 0: Error: mysql_error (1146): "Table 'gerbera.mt_cds_object' doesn't exist"

第三步,按第一步记录的原参数重建库并灌备份:

mariadb -uroot -pq1w2e3r4 -e "CREATE DATABASE gerbera DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci;"
mariadb -uroot -pq1w2e3r4 gerbera < /var/backups/gerbera/gerbera-d1-clean.sql
echo RESTORE_OK
RESTORE_OK

第四步,数据层验证:

mariadb -uroot -pq1w2e3r4 -e "SELECT COUNT(*) AS cds_objects FROM gerbera.mt_cds_object; SELECT COUNT(*) AS tables_cnt FROM information_schema.TABLES WHERE TABLE_SCHEMA=\"gerbera\"; SELECT * FROM gerbera.mt_internal_setting;"

实测回显:

cds_objects
123
tables_cnt
8
key	value
db_version	29
string_limit	255
resource_attribute	size,duration,bitrate,sampleFrequency,nrAudioChannels,resolution,colorDepth,protocolInfo,resFile,type,fanArtObject,fanArtResource,bitsPerSample,dc:language,sec:acodec,sec:vcodec,format,orientation,pixelFormat,lyrics,offset
engine	MyISAM
charset	utf8
collation	utf8_general_ci

第五步,业务层验证——CDS 根容器恢复如初:

object.container                              PC Directory
object.container                              Audio
object.container                              Photos
object.container                              Video
object.container.dynamicFolder                Music Recently Played
object.container.dynamicFolder                Recently Modified
object.container.dynamicFolder                Recently Added

解说:

  • 服务不重启即自愈:删库期间 Web UI 与 CDS 全部 500,恢复库表后常驻服务凭连接池重连立即恢复正常,全程未动 Gerbera 进程——这是「数据库外置」架构的运维红利;
  • mt_internal_setting 是恢复验证的金标准:db_version=29 是数据库 schema 版本号(第六章升级机制的钥匙),engine=MyISAM、charset=utf8 记录了建库时代参数;
  • 媒体 URL 由 <home> 与数据库共同决定,库恢复后无需重扫,客户端缓存的对象 ID(如 flac 的 97)原样可用;
  • 整个闭环的固化为一句 SOP:改库前必有当日 dump,恢复时先 SHOW CREATE DATABASE 存参数,再重建、再灌数、先查 mt_internal_setting 再看业务。

💡 技术拓展:SQLite3 后端用户没有 mariadb-dump,但配置里 <sqlite3><backup enabled="yes" interval="600"/> 是 Gerbera 内建的定时备份(每 600 秒把库文件另存),恢复动作退化为「停服 → 拷回备份的 .db 文件 → 起服」。三后端里 MySQL/MariaDB/PostgreSQL 走 SQL 层备份,SQLite 走文件层备份,演练方法不同但「先校验后依赖」的原则一致。

四、日志排障实战

Gerbera 日志是单通道文本,格式统一为「时间戳 + 级别 + 消息」,级别只有 info / warning / error 三档。本章把启动日志读法、常见报错对照讲透。

4.1 日志通道与启动日志逐段解读

第三章 2.3 节已实测 --logfile。一次完整启动的日志天然分五段,以常驻实例重启后的日志为例(--logfile 与前台输出内容一致):

2026-10-29 00:06:19   info: Gerbera UPnP Server 3.3.0 - https://gerbera.io/
2026-10-29 00:06:19   info: Loading configuration from: /run/gerbera/config.xml
2026-10-29 00:06:19   info: Parsing configuration...
2026-10-29 00:06:19   info: Configuration load succeeded.
2026-10-29 00:06:19   info: Validating configuration...
2026-10-29 00:06:19   info: Configuration check succeeded.
2026-10-29 00:06:19   info: Loading 0 configuration items from database
2026-10-29 00:06:19   info: Initialising UPnP with interface: <unset>, port: 4949
2026-10-29 00:06:19   info: IPv4: Server bound to: 10.89.56.10:4949
2026-10-29 00:06:19   info: The Web UI can be reached by following this link: http://192.168.146.128:8136

分段记忆法:

  1. 横幅段:版本 + 版权,确认重启后跑的确实是预期版本;
  2. 配置段:Loading → Parsing → load succeeded → Validating → check succeeded,任何一步失败即退出,与 --check-config 的输出结构完全同构——这也是「起服前先 check-config」能拦截 90% 启动故障的原因;
  3. 数据库段:Loading N configuration items from database,数字来自 grb_config_value 表(UI 里改过的参数会落库,0 表示没在 UI 改过参数);
  4. UPnP 段:绑定地址与端口,10.89.56.10:4949 对上网络规划;
  5. 入口段:打印 Web UI 访问链接,看到这行 = 服务就绪。

4.2 典型故障日志对照表

以下为本篇实测中真实出现过的报错,按「日志原文 → 病因 → 处置」整理:

① 配置值非法(2.2 节实测)

error: Runtime error parsing config file '/var/run/gerbera/bad.xml': ... Error in config file: /config//server/port unreadable int value 'abc'

病因:配置值类型错误。处置:按 error 行点名的 XML 路径改值;--check-config 可在重启前拦截同类问题。

② 依赖命令缺失(每次启动可见)

warning: Error in configuration, could not find command "ffmpeg" in $PATH for '/config/attribute::command'
warning: Error in configuration, could not find command "vlc" in $PATH for '/config/attribute::command'

病因:转码 external profile 的 agent 依赖 ffmpeg/vlc 可执行文件,服务器 $PATH 里没有。处置:装包或忽略(不用 external 转码即无影响),但要知道它是 warning 不是 error、不阻断启动。

③ 数据库表消失(3.4 节删库实测)

error: Exception: Mysql: mysql_real_query() failed: mysql_error (1146): "Table 'gerbera.mt_cds_object' doesn't exist"; query: ...

病因:库/表被删或权限丢失,接口层表现为 HTTP 500。处置:按 3.4 闭环恢复;若反复出现且伴随重启循环,参考部署篇「崩溃-重启循环」复盘,先修 <storage><mysql> 四要素(host/port/username/password)。

④ 转码失败(5.5 节实测,日志原文)

error: Error occurred when opening output file '/tmp/grb-tr-e73bd705-0205-4157-9713-1b5c3a450754.mp3' (error 'Invalid argument')
error: Exception: [/gerbera_build/src/transcoding/transcode_io_handler.cc:112] virtual void TranscodeInternalIOHandler::open(UpnpOpenFileMode) Error: open output for /content/tone.flac failed

病因:internal 转码器为每次请求在 /tmp 建临时输出文件,打开失败。处置思路见 5.5 的完整排障。

4.3 调试开关:-D 与 --debug-mode

--help 里 -D/--debug 与 --debug-mode FACILITY 是排障深水区工具:-D 全量打开调试日志(需要编译期 WITH_DEBUG ON,1.2 节确认本二进制满足);--debug-mode 可按设施(线程、SSDP、数据库等)单独开,避免全量日志刷屏。日常排障顺序建议:先读 error 行 → 复现一次前台运行 → 仍无头绪再上 -D,调试日志量大,用完即关。

💡 技术拓展:三通道日志(--logfile、--rotatelog、--syslog)可以并存。生产上推荐 --rotatelog(按大小自动轮转,防单文件膨胀)或 --syslog(交给 rsyslog 统一采集与轮转策略);--logfile 更适合排障期临时挂载。配置文件里另有 <server> 下的日志级别细粒度参数,见配置篇对应章节。

五、Web UI 日常运维实战

Web UI 不仅是管理界面,更是一套完整的 REST 运维 API(/interface 端点,登录三步握手见部署篇)。本章用脚本化方式实测四组高频运维动作:容器树浏览、全量重扫、虚拟文件夹增删、在线转码开通与排障。

5.1 容器树与条目浏览

登录后查询虚拟容器树根节点(req_type=containers&action=get_containers&parent_id=0):

python3 ops_probe2.py

实测回显(关键段):

LOGIN: {'success': True, 'task': None}
--- containers parent_id=0 ---
root children: [(1, 'PC Directory'), (4, 'Audio'), (33, 'Photos'), (91, 'Video'), (-10002, 'Music Recently Played'), (-10001, 'Recently Modified'), (-10000, 'Recently Added')]

条目浏览(req_type=items&action=browse&parent_id=1):

{"items": {"autoscan_mode": "none", "autoscan_type": "none", "item": [], "parent_id": 1, "protect_container": false, "protect_items": false, "result_size": 0, "start": 0, "total_matches": 0, "virtual": false}, "success": true, "task": null}

解说:

  • 根下 7 个容器分两类:正 ID 的物理/布局容器(PC Directory=1、Audio=4、Photos=33、Video=91,ID 即 CDS object_id,部署篇用 SOAP Browse 验证过的就是同一套 ID)与 负 ID 的动态容器(-10000/-10001/-10002,来自配置篇讲过的 <containers> 动态容器节,LastAdded/LastModified/LastPlayed 三件套);
  • protect_container / protect_items 字段对应 UI 里对容器的保护锁,脚本化删除前先看这两个标志可避免误删持久容器(PC Directory 的 persistent: true 就是持久标记);
  • total_matches / start / count 构成分页参数,大批量条目巡检按页拉取即可。

5.2 全量重扫与任务队列

向 UI 提交全量重扫(等价于界面上 Database → 右键 PC Directory → Rescan):

--- config_save action=rescan target=--all ---
{"success": true, "task": {"cancellable": true, "id": 5, "text": "Scan: /content"}, "update_ids": {"pending": false}}

两秒后查任务队列(req_type=tasks&action=list):

{"success": true, "task": null, "tasks": []}

解说:

  • 重扫是异步任务:提交立即返回 task id=5 与描述 Scan: /content,不阻塞 API;小媒体库秒级完成,tasks 列表随即清空——「提交成功 + 队列转空」就是完成信号;
  • 单目录重扫把 target 换成对应 autoscan 目录 ID 即可;cancellable: true 说明长任务可在 UI 里取消;
  • 配置篇讲过 autoscan inotify 模式下文件增删自动入库,全量重扫主要用于:虚拟布局脚本改了、映射规则改了、怀疑索引漂移这三类场景。

5.3 虚拟文件夹增删闭环

在根下新建一个自定义容器(等价于 UI 里 Database 树右键 → Add Container):

--- add_object 虚拟容器 ---
{"success": true, "task": null}

复查发现新容器拿到 ID 145(标题 Ops Notes),随即删除(req_type=remove&object_id=145&all=1):

Ops containers found: [(145, 'Ops Notes')]
remove 145 -> {'success': True, 'task': None}
root titles after cleanup: ['PC Directory', 'Audio', 'Photos', 'Video', 'Music Recently Played', 'Recently Modified', 'Recently Added']

解说:

  • 创建→复查→删除→复查 是脚本化运维虚拟容器的最小闭环;新建容器 ID 从 100+ 段分配(媒体对象占 1~97 区间),删除后根容器树恢复 7 个原状;
  • 这类「UI 里的手工动作」全部 API 化后,就能进定时任务或 CMDB 工具流;注意 remove 带 all=1 才会连子对象一起删,只删空容器可省;
  • 删除虚拟容器不影响底层媒体文件与物理目录扫描结果——虚拟布局是数据库里的「视图」,这正是 Gerbera 虚拟文件夹体系的运维安全感来源。

5.4 在线转码开通与排障实录

配置篇讲过转码三大件:<transcoding enabled> 总开关、<mimetype-profile-mappings> 映射表、<profiles> profile 定义。本节实测把默认关闭的转码开起来,过程中收获一次完整排障。

第一步:开总开关并重启服务(转码参数不支持热加载,改完必须重启):

<transcoding enabled="yes">

重启后用 CDS 查看库中 flac 文件(object_id=97)的资源列表:

object.item.audioItem.musicTrack              tone
--- res URLs ---
http://192.168.146.128:8136/media/object_id/97/res_id/0/group/default/ext/file.flac

只有 1 条 res(原始 flac),没有出现转码资源。

第二步:从数据反查。转码资源生成的条件是「条目 mimetype 命中映射表」,直接查库:

mariadb -uroot -pq1w2e3r4 -e "SELECT id, dc_title, mime_type FROM gerbera.mt_cds_object WHERE id=97;"
id	dc_title	mime_type
97	tone	audio/flac
<!-- 映射表里只有这一条 flac 相关映射 -->
<transcode mimetype="audio/x-flac" using="int2mp3" />

病因水落石出:条目实际 mimetype 是 audio/flac(TagLib 识别结果),而默认映射表写的是 audio/x-flac——差一个前缀,永远命中不了。

第三步:补映射并重启:

<transcode mimetype="audio/x-flac" using="int2mp3" />
<transcode mimetype="audio/flac" using="int2mp3" />

再次 CDS 查询,转码资源出现了(2 条 res):

--- res URLs ---
http://192.168.146.128:8136/media/object_id/97/res_id/none/pr_name/int2mp3/tr/1/group/default/ext/file.mp3
http://192.168.146.128:8136/media/object_id/97/res_id/0/group/default/ext/file.flac

第四步:拉流验证转码输出:

curl -sI 'http://192.168.146.128:8136/media/object_id/97/res_id/none/pr_name/int2mp3/tr/1/group/default/ext/file.mp3'

实测回显:

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: audio/mpeg
Access-Control-Allow-Origin: *
Server: Linux/7.0.0-38-generic, UPnP/1.0, Portable SDK for UPnP devices/22.1.0
X-User-Agent: redsonic

响应头全部正确(200 + audio/mpeg + chunked 流式),但正文字节数为 0,服务端日志随即给出 4.2-④ 的两行 error——internal 编码器为请求打开 /tmp/grb-tr-*.mp3 临时输出文件时报 Invalid argument。尝试移除 int2mp3 profile 的 afilter="anull" 参数后复现相同报错,确认是 3.3.0 internal 转码器在该构建(ffmpeg Lavf62.12.102 组合,见 1.2 节 compile-info)+ 本机环境下无法创建输出文件的环境性限制。

排障结论:

  • 链路已打通到流媒体层:总开关 → mimetype 映射 → profile 匹配 → 转码 res 生成 → HTTP 流式响应头,五环全部验证通过;
  • 断点在 internal 编码器写临时文件这一步,日志两行(opening output file ... Invalid argument + transcode_io_handler.cc:112)精确定位;external profile(ogg2mp3/vlcmpeg)依赖 ffmpeg/vlc 命令,本机 $PATH 无此二者(4.2-② 的 warning 长期存在),也无法在此环境落地;
  • 运维启示:「转码配好了」要拆成五环逐环验证,DIDL 里有没有转码 res(CDS 层)与拉流有没有数据(传输层)是两回事,curl 一发 + 日志两行即可定位到环;
  • 排障结束后已将 encoder 参数恢复默认(afilter="anull"),总开关与 audio/flac 映射保留——映射正确性不依赖编码器可用性,作为后续环境补齐 ffmpeg 后的现成开关。

💡 技术拓展:转码 res URL 的结构值得记住——res_id/none/pr_name/int2mp3/tr/1 中 pr_name 直接暴露 profile 名、tr/1 是转码流水线序号,而原始资源的 URL 只有 res_id/0。客户端是否选择转码资源由其请求的 DLNAPROFILE 决定,服务端只是「把两条 res 都摆上货架」。另外 X-User-Agent: redsonic 是 Gerbera 沿用 MediaTomb 时代的兼容标记,老客户端靠它识别媒体服务器。

六、版本升级流程

Gerbera 的版本升级本质上是「换二进制 + 数据库 schema 迁移」,媒体文件与配置文件原样不动。本章先弄清迁移机制,再给出经过本篇实测动作验证的标准流程。

6.1 升级机制盘点:db_version 与迁移脚本

数据库 schema 版本号存在 mt_internal_setting 表(3.4 节恢复验证时顺带读到):

key	value
db_version	29

安装数据目录 /usr/local/share/gerbera/ 下的三件套与迁移描述:

js
mysql-drop.sql
mysql-upgrade.xml
mysql.sql
postgres-drop.sql
postgres-upgrade.xml
postgres.sql
sqlite3-drop.sql
sqlite3-upgrade.xml
sqlite3.sql
web

mysql-upgrade.xml 是增量迁移链,实测确认共 28 个 <version> 块(number 2~29),每块若干条 SQL。当前版本 29 的迁移内容:

<version number="29" remark="import_mode in autoscan">
    <script>ALTER TABLE `mt_autoscan` ADD `import_mode` enum('mt','grb','mediatomb','gerbera') NOT NULL default 'mt'</script>
    <script>UPDATE `mt_autoscan` SET `import_mode`='mt'</script>
</version>

解说:

  • 启动时自动迁移:Gerbera 启动读库得到 db_version,若低于二进制内建的目标版本,就按 mysql-upgrade.xml 逐版本执行缺失的 <script>,每成功一步推进一次版本号——3.3.0 的目标版本正是 29,本环境无需迁移;
  • <upgrade-file> 配置参数可把迁移描述换成自定义路径(默认 ${datadir}/mysql-upgrade.xml),特殊环境才有必要动它;
  • mysql.sql 是全新初始化 schema(首次启动建表用),mysql-drop.sql 是清库脚本(3.3 节实测过),三者与 --drop-tables 的关系已在第三章理清。

6.2 标准升级 SOP

综合官方文档建议与本篇实测动作,升级到新版本(例:3.3.0 → 3.x 更新版)流程如下:

第 1 步:升级前备份(3.1/3.2 节动作)

mariadb-dump -ugerbera -pq1w2e3r4 gerbera --single-transaction \
  > /var/backups/gerbera/gerbera-pre-upgrade-$(date +%Y%m%d).sql

再按 3.2 校验一遍可恢复性,同时留存旧版 gerbera 二进制(或旧版本包),--compile-info 输出存档。

第 2 步:更新二进制/安装包

替换 Gerbera 程序与 PACKAGE_DATADIR 数据目录(新版本会带来新版 mysql-upgrade.xml、web/ 界面资源与 js/ 脚本);媒体文件、config.xml、数据库本体不动。

第 3 步:新版本配置体检(官方明确建议的动作)

gerbera --check-config -c /var/run/gerbera/config.xml

官方文档(配置生成章节)原话要点:升级 Gerbera 版本后应当重新运行配置检查,以发现新增或变更的配置项;更彻底的做法是跑 --create-example-config 与现有配置 diff,把新参数按需合入。退出码非 0 就地修复,不带病升级。

第 4 步:启动并确认自动迁移

起服后读日志:配置五段照旧通过后,观察数据库段——schema 落后时会执行迁移(版本号从旧值爬升到新目标值);随后验证:

mariadb -uroot -pq1w2e3r4 -e "SELECT value FROM gerbera.mt_internal_setting WHERE key='db_version';"

值应等于新版本的目标 schema 版本(不小于 29)。再抽查 CDS 根容器与 Web UI 登录(5.1 节两组探针直接复用)。

第 5 步:观察期

重点盯三类日志:迁移类 error、映射/兼容性 warning(新版本可能新增配置校验项,2.2 节的 extra entry warning 家族可能扩员)、客户端兼容问题(grb_client 表记录了各客户端特征,升级后首次回连是否正常看这张表)。

6.3 迁移失败的兜底路径

官方 troubleshooting 的口径:数据库通常由 Gerbera 自动迁移;迁移失败时清库重建——用 mysql-drop.sql 删掉全部表(或 gerbera --drop-tables,本篇 3.3 实测其在 3.3.0 存在行为偏差,优先手工执行脚本),重启 Gerbera 即按全新库初始化,再依赖 autoscan 重新导入媒体。

结合本篇实测给出更稳的兜底顺序:

  1. 先用升级前备份直接回灌(3.4 闭环演练过的动作),回到旧版本继续服务;
  2. 排查迁移失败的具体 SQL(mysql-upgrade.xml 对应版本块,错误日志会带出语句);
  3. 确认必须重置时才走清库重建:清库 → 起服自动初始化 → 验证 db_version 达标 → 重扫媒体库(5.2 节动作)→ 重建自定义虚拟容器(5.3 节动作有建删脚本,重放即可)。

清库重建的代价只有「重扫时间 + 自定义布局重放」,媒体文件零损失——这也是 6.2 第 1 步强调「备份先行」后敢于快速兜底的底气。

6.4 升级前检查清单

  • 当日全量备份 + 临时库校验通过(3.1/3.2)
  • SHOW CREATE DATABASE 参数存档(3.4)
  • 旧二进制留存 + --compile-info 存档(1.2)
  • config.xml 当前版本留存副本,新版本 --check-config 通过(2.2)
  • mt_internal_setting.db_version 当前值记录(6.1)
  • 回归脚本就绪:CDS 根容器探针 + UI 登录探针(5.1)
  • 客户端通知:升级窗口内 SSDP 宣告中断,客户端列表重连后需复查(grb_client)

💡 技术拓展:Gerbera 的 db_version 与 config.xml 根元素的 version="2"(配置篇讲过)是两条独立的版本线——前者管数据库 schema,由 mysql-upgrade.xml 驱动;后者管配置文件语法,由内置校验器驱动。升级排障时先分清报错属于哪条线:带 SQL 语句的错误找前者,带 XML 路径的错误找后者。

结语

本篇以五组实测串起 Gerbera 的日常运维主线:--check-config 把变更风险挡在重启前,备份-校验-破坏-恢复闭环让数据库操作有恃无恐,日志五段读法与四类典型报错构成排障路标,Web UI API 化的增删重扫与转码五环验证把界面动作纳入脚本体系,db_version 迁移机制则把版本升级从玄学变成清单。至此本专栏四篇完结:认知(《Gerbera 介绍与选型指南》)→ 部署(《Gerbera Docker Compose 部署实战》)→ 配置(《Gerbera 配置参数详解》)→ 运维(本篇),从选型到长期运维的完整链路已全部落地实测。