Beszel 运维手册

覆盖 Beszel 上线后的日常巡检、备份恢复(含 integrity_check 演练)、阈值告警创建与触发实测、节点扩缩容、升级回滚与七类故障排查实录——全部操作已在 Ubuntu 22.04(Beszel 0.21.0)实测验证。

BeszelBeszel监控2026/10/17

Beszel 运维手册

‍‍​​‌​> 目标:覆盖 Beszel 监控平台上线后的日常巡检、备份恢复、告警管理、节点扩缩容、升级回滚与故障排查——所有命令与输出均已在 Ubuntu 22.04 服务器(Beszel Hub/Agent 0.21.0,Docker 29.8.1)实测验证。

一、环境信息

本手册所有操作在以下环境实测完成(数据采集时间 2026-10-17):

  • 操作系统:Ubuntu 22.04.4 LTS(x86_64)
  • Beszel Hub:henrygd/beszel:latest(实测 0.21.0),端口 8090,数据目录 /opt/data/beszel
  • Beszel Agent:henrygd/beszel-agent:latest(实测 0.21.0),端口 45876
  • 编排目录:/opt/data/beszel-lab(compose.yml、.env,见《Beszel Docker Compose 部署指南》)
  • 接入节点:beszel-agent-node1(本机)、beszel-agent-node2(第二节点,45878 端口)
  • 演示管理员:admin@quzuo.net(认证 Token 获取方式见第二章,不落明文)

后续所有 API 命令依赖登录 Token,统一用以下方式取得(每节示例中的 $TOKEN 均指它):

TOKEN=$(curl -s -X POST http://localhost:8090/api/collections/users/auth-with-password \
  -H 'Content-Type: application/json' \
  -d '{"identity":"admin@quzuo.net","password":"q1w2e3r4"}' \
  | python3 -c "import sys,json;print(json.load(sys.stdin)['token'])")

实测无输出(变量赋值),可用 echo ${#TOKEN} 验证长度:

echo ${#TOKEN}

实测输出:

224

二、日常巡检

日常巡检的目标是四件事:服务活着、版本正确、节点在报数、数据在落盘。以下四项各给一条命令与实测输出,建议纳入每日巡检清单。

2.1 服务运行状态与版本

cd /opt/data/beszel-lab && docker compose ps --format 'table {{.Name}}\t{{.Status}}'

实测输出:

NAME                  STATUS
beszel-0.21.0-agent   Up 15 minutes
beszel-0.21.0-hub     Up 15 minutes
curl -s http://localhost:8090/api/beszel/info -H "Authorization: $TOKEN"

实测输出:

{"key":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIPLUxo4t9n4W86eZ4QBzKgtWQhl3namsh5kaWcBHD8j","v":"0.21.0","cu":false}

判读:两个服务 Up、v 为预期版本即通过;key 字段可顺便与 .env 中 Agent 配置的公钥核对(防私钥误换,见 7.1 节)。

2.2 节点在线状态

curl -s 'http://localhost:8090/api/collections/systems/records?perPage=5&fields=name,status,updated' \
  -H "Authorization: $TOKEN" | python3 -m json.tool --no-ensure-ascii

实测输出:

{
    "items": [
        {
            "status": "up",
            "name": "beszel-agent-node2",
            "updated": "2026-10-17 11:35:31.412Z"
        },
        {
            "status": "up",
            "updated": "2026-10-17 11:35:29.582Z",
            "name": "beszel-agent-node1"
        }
    ],
    "page": 1,
    "perPage": 5,
    "totalItems": 2,
    "totalPages": 1
}

判读:status 应全为 up;updated 与当前时间差超过 1 分钟即说明上报停滞(正常每分钟写入新统计记录)。

2.3 指标采集时延

curl -s 'http://localhost:8090/api/collections/container_stats/records?perPage=1&sort=-created&fields=created' \
  -H "Authorization: $TOKEN"

实测输出:

{"perPage":1,"totalItems":117,"totalPages":117,"items":[{"created":"2026-10-17 11:35:29.582Z"}],"page":1}

判读:最新记录 created 距今应在 1 分钟内(采集周期为 1 分钟);totalItems 持续增长说明历史数据正常入库。

2.4 数据目录容量

du -sh /opt/data/beszel/ && ls -la /opt/data/beszel/data.db*

实测输出(运行数小时后):

2.5M    /opt/data/beszel/
-rw-r--r-- 1 root root  790528 10月 17 19:20 /opt/data/beszel/data.db
-rw-r--r-- 1 root root   32768 10月 17 19:35 /opt/data/beszel/data.db-shm
-rw-r--r-- 1 root root 1734552 10月 17 19:36 /opt/data/beszel/data.db-wal

判读:SQLite 数据库极小(2 台节点、2 个统计集合,数小时约 2.5 MB),按本机 30 个 Docker 统计对象 + 分钟粒度估算,一年量级仍在数十 MB,磁盘压力可忽略;巡检时确认 -wal 文件随运行增长属正常(检查点机制自动合并)。

三、备份与恢复

3.1 备份范围

全部持久化状态都在 Hub 数据目录 /opt/data/beszel(挂载自宿主机),包括主库 data.db、连接 Agent 的 SSH 私钥 id_ed25519、辅助库 auxiliary.db 与通知数据 .notify/。私钥丢失会导致所有 Agent 拒绝连接(7.1 节),因此备份必须包含整个目录。Agent 侧无状态,无需备份。

3.2 备份实测(tar 打包)

tar czf /opt/data/quzuo/beszel-lab/beszel-backup-$(date +%Y%m%d).tar.gz -C /opt/data beszel/

确认备份包:

ls -la /opt/data/quzuo/beszel-lab/beszel-backup-*.tar.gz

实测输出:

-rw-r--r-- 1 root root 401774 10月 17 19:36 /opt/data/quzuo/beszel-lab/beszel-backup-20261017.tar.gz
tar tzf /opt/data/quzuo/beszel-lab/beszel-backup-20261017.tar.gz

实测输出:

beszel/
beszel/.keep
beszel/id_ed25519
beszel/data.db-shm
beszel/data.db-wal
beszel/auxiliary.db
beszel/data.db
beszel/.notify/

要点:包内 data.db-wal 是运行中数据库的未合并写入,必须连同主库一起备份(本命令 tar 的是运行中目录,天然包含);如需绝对一致的快照,可先 docker compose stop hub 再打包。

3.3 恢复演练实测

恢复 = 停 Hub → 解包覆盖数据目录 → 起 Hub → 校验。先在临时目录演练解包与库完整性校验:

mkdir -p /tmp/beszel-restore-test && tar xzf /opt/data/quzuo/beszel-lab/beszel-backup-20261017.tar.gz -C /tmp/beszel-restore-test
python3 -c "
import sqlite3
con = sqlite3.connect('/tmp/beszel-restore-test/beszel/data.db')
print('integrity_check:', con.execute('PRAGMA integrity_check').fetchone()[0])
print('systems:', con.execute('SELECT COUNT(*) FROM systems').fetchone()[0])
print('users:', con.execute('SELECT COUNT(*) FROM users').fetchone()[0])
con.close()"

实测输出:

integrity_check: ok
systems: 2
users: 1

判读:integrity_check: ok 表示备份中的库完整可用,系统与用户记录齐全。真实恢复时执行 docker compose stop hub → rm -rf /opt/data/beszel && tar xzf <备份包> -C /opt/data → docker compose start hub,随后按 2.1/2.2 节复查版本与节点状态即可。

3.4 定时备份

crontab 追加一行(每日 03:30 打包到备份目录,保留策略由备份盘容量决定):

crontab -l 2>/dev/null; echo '30 3 * * * tar czf /data/backup/beszel-backup-$(date +\%Y\%m\%d).tar.gz -C /opt/data beszel/'

⚠️ 注意:date +%Y%m%d 在 crontab 中 % 必须转义为 \%(crontab 把裸 % 当换行),否则该行会被截断、备份静默失败。

四、监控告警

4.1 告警能力总览

Beszel 内置基于阈值的告警:对每个节点可按指标类型设置阈值,越限并持续指定分钟数后触发,通过页面通知或 SMTP/Webhook 外发。

💡 技术拓展:监控告警的判定方式不止「静态阈值」一种。常见方式对比——

方式原理优点缺点
静态阈值指标超过固定值即告警简单直观、可预期阈值需人工维护,易误报/漏报
同比/环比与历史同期或前 N 分钟对比自适应业务波动冷启动期不准
异常检测统计/ML 模型判定偏离免维护阈值黑盒、调参成本高
黑盒拨测从外部定期探测服务可用性直接度量用户体验看不到内部指标

Beszel 实测提供静态阈值 + 持续时长(min 字段)组合,属于第一类;对轻量自托管场景够用,复杂告警需求可把 Webhook 对接 Alertmanager 等外部系统再加工。

4.2 告警规则类型与字段

规则类型为固定枚举(0.21.0 实测从数据字典读出):Status、CPU、Memory、Disk、Temperature、Bandwidth、GPU、LoadAvg1、LoadAvg5、LoadAvg15、Battery、ContainerHealth、SystemdFailed、CPUIOWait、CPUSteal、NetworkMonitorLoss。

字段说明(实测 schema):

字段类型必填含义
name枚举是告警指标类型(上表枚举之一)
value数字否触发阈值(如 CPU 百分比、Disk 百分比;Status 不需要)
min数字否持续分钟数:越限持续该时长才触发
system关联是所属节点记录 ID
user关联是归属用户 ID(接收告警通知的用户)
triggered布尔自动当前是否处于触发态

唯一约束:(user, system, name) 组合唯一——同一用户对同一节点的同一指标只能建一条规则。

4.3 创建告警规则实测

为 node1 创建 Memory 告警(阈值 90%、持续 5 分钟):

curl -s -X POST http://localhost:8090/api/collections/alerts/records \
  -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"Memory","value":90,"min":5,"system":"u3q4gtml3pv3us0","user":"egb5322xmdbhc47"}'

实测输出:

{"collectionId":"elngm8x1l60zi2v","collectionName":"alerts","created":"2026-10-17 11:38:46.512Z","id":"w2b6oozxh5cks5t","min":5,"name":"Memory","system":"u3q4gtml3pv3us0","triggered":false,"updated":"2026-10-17 11:38:46.512Z","user":"egb5322xmdbhc47","value":90}

查询规则清单与触发态:

curl -s 'http://localhost:8090/api/collections/alerts/records?perPage=10&fields=name,value,min,triggered,created' \
  -H "Authorization: $TOKEN" | python3 -c "import sys,json; [print(i) for i in json.load(sys.stdin)['items']]"

实测输出:

{'triggered': True, 'created': '2026-10-17 08:14:32.254Z', 'min': 0, 'name': 'CPU', 'value': 1}
{'triggered': False, 'created': '2026-10-17 11:38:46.512Z', 'min': 5, 'name': 'Memory', 'value': 90}

⚠️ 注意(实测踩坑):创建规则时两个字段极易写错——user 必须用当前 Hub 的用户记录 ID 且字段名是单数 user(写成 users 数组返回 400);value 必须是数字(写成字符串 "85" 同样 400,回显为 {"data":{},"message":"Failed to create record.","status":400},不提示具体原因)。遇到 400 先核对这两处。

4.4 触发行为实测

实测一条低阈值 CPU 规则(阈值 1%、持续 0 分钟):创建于 08:14:32.254Z,13 秒后(08:14:45.636Z)即翻转为 triggered: true(本机 CPU 3.2% > 1%)——验证了「越限即触发」链路真实可用。min 字段生效时,需越限持续满指定分钟数才触发,实测 2.2 节的 Memory 规则(90%/5min)在内存 28% 时长期保持 triggered: false,未误报。

解除触发:指标回落到阈值以下并保持后,triggered 自动翻回 false;页面规则列表的铃铛图标同步消警。删除规则(实测删除一条 Status 规则,ID edz9mzic8yx5a1g):

curl -s -o /dev/null -w 'http_code=%{http_code}\n' \
  -X DELETE http://localhost:8090/api/collections/alerts/records/edz9mzic8yx5a1g -H "Authorization: $TOKEN"

实测输出:

http_code=204

删除后复核清单,该规则已消失:

curl -s 'http://localhost:8090/api/collections/alerts/records?perPage=10&fields=name,value,triggered' \
  -H "Authorization: $TOKEN" | python3 -c "import sys,json; [print(i) for i in json.load(sys.stdin)['items']]"

实测输出:

{'name': 'CPU', 'triggered': True, 'value': 1}
{'name': 'Memory', 'triggered': False, 'value': 90}

4.5 Status 告警的行为边界(实测)

Status 类型用于节点失联告警,实测发现一个重要行为:在节点已经处于 down 状态时新建 Status 规则,不会补触发(triggered 保持 false,等待状态恢复后再按「正常→失联」跃迁判定)。因此 Status 规则应在节点健康时预先创建,不要等故障来了再建。节点失联→恢复的完整演练实录见 7.7 节。

五、扩容缩容

5.1 扩容:新增被监控节点

以第二节点(主机名 agent2,监听 45878)为例。在被监控机器上按《Beszel Docker Compose 部署指南》4.1/4.4/4.5 节启动 Agent(KEY 填 Hub 公钥、PORT 填 45878),然后在 Hub 侧添加节点:

curl -s -X POST http://localhost:8090/api/collections/systems/records \
  -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"beszel-agent-node2","host":"agent2","port":"45878","users":["egb5322xmdbhc47"]}'

实测输出:

{"id":"guzs42rlb9mshds","name":"beszel-agent-node2","host":"agent2","port":"45878","status":"pending"}

约 20 秒后验证上线:

curl -s "http://localhost:8090/api/collections/systems/records/guzs42rlb9mshds?fields=status,info.v" \
  -H "Authorization: $TOKEN"

实测输出:

{"status":"up","info":{"v":"0.21.0"}}

实测经验:从 pending 到 up 约需 8~25 秒(Hub 周期性发起 SSH 连接);若持续 pending/down,按 7.1 节排查 KEY 与网络可达性。

5.2 缩容:摘除节点

摘除 = 删除 Hub 上的系统记录(可选:在被监控机器停掉 Agent 进程)。实测先建一条演示记录再摘除,完整呈现缩容闭环:

curl -s -X POST http://localhost:8090/api/collections/systems/records \
  -H "Authorization: $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"beszel-agent-docdemo","host":"agent-demo","port":"45879","users":["egb5322xmdbhc47"]}'

实测输出:

{"id":"7w7qlumlwhetfic","name":"beszel-agent-docdemo","host":"agent-demo","port":"45879","status":"pending"}

摘除该记录:

curl -s -o /dev/null -w 'delete http_code=%{http_code}\n' \
  -X DELETE http://localhost:8090/api/collections/systems/records/7w7qlumlwhetfic -H "Authorization: $TOKEN"

实测输出:

delete http_code=204

该节点的指标历史随级联删除一并清理。删除后核对剩余节点:

curl -s 'http://localhost:8090/api/collections/systems/records?perPage=10&fields=name,status' \
  -H "Authorization: $TOKEN" | python3 -c "import sys,json; [print(i['name'], i['status']) for i in json.load(sys.stdin)['items']]"

实测输出:

beszel-agent-node2 up
beszel-agent-node1 up

⚠️ 注意:实测中曾把 Hub 专用网络上的节点摘除,但该 Agent 仍挂在网络上,导致 docker compose down 报 Network ... Resource is still in use(见 7.4 节)。缩容时先 docker network disconnect 或直接停容器再删记录。

六、升级与回滚

6.1 升级流程

Beszel 升级 = 拉新镜像 → 重建容器 → 核对版本。数据在宿主机目录,重建不影响:

cd /opt/data/beszel-lab && docker compose pull

实测输出:

 Image henrygd/beszel-agent:latest  Pulling
 Image henrygd/beszel:latest  Pulling
 Image henrygd/beszel:latest  Pulled
 Image henrygd/beszel-agent:latest  Pulled
cd /opt/data/beszel-lab && docker compose up -d --force-recreate

实测输出:

 Container beszel-0.21.0-hub  Recreated
 Container beszel-0.21.0-agent  Recreated
 Container beszel-0.21.0-agent  Starting
 Container beszel-0.21.0-hub  Starting
 Container beszel-0.21.0-agent  Started
 Container beszel-0.21.0-hub  Started

核对版本与数据完好:

curl -s http://localhost:8090/api/beszel/info -H "Authorization: $TOKEN"

实测输出(重建后):

{"key":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIPLUxo4t9n4W86eZ4QBzKgtWQhl3namsh5kaWcBHD8j","v":"0.21.0","cu":false}

系统记录与告警历史重建后全部保留(实测 systems 2 条 up、alerts total: 1,命令与输出见《Beszel Docker Compose 部署指南》5.2 节,本机复核一致)。

💡 说明:Agent 升级需逐台把新镜像拉到对应机器并重建 Agent(多节点时在各节点分别执行 pull 与重建,每台单独操作);Hub 全局一个,重建即全局生效。0.21.0 实测 Hub 与 Agent 版本需保持一致,升级后按 2.2 节确认所有节点 info.v 为新版本。

6.2 回滚方案

回滚 = 把镜像标签固定回旧版本再重建。编辑 compose.yml 把 henrygd/beszel:latest 与 henrygd/beszel-agent:latest 改为上一版本的语义化标签(如 henrygd/beszel:v0.21.0),然后:

cd /opt/data/beszel-lab && docker compose pull && docker compose up -d --force-recreate

再用 6.1 节的 info 接口核对 v 已回到旧版本。两处实测注意:

  1. 国内镜像加速源可能未同步历史版本标签,实测拉取 v0.21.0 时返回:
Error response from daemon: error from registry: denied

解决:重试、更换加速源或直连 Docker Hub(见 7.5 节)。

  1. 回滚前先按第三章做一次备份——跨版本回滚如遇数据结构变更,可直接回退到升级前状态。

七、故障排查手册

本章收录实测环境出现过的全部故障与处置实录,按现象索引。

7.1 节点状态 down,Agent 日志 Invalid SSH key

现象:Hub 页面节点持续 down,Agent 日志出现:

2026/10/17 11:17:03 WARN Invalid SSH key addr=172.31.0.2:48742

定位:Agent 端 KEY 与 Hub 当前公钥不匹配(Hub 重装、数据目录更换或 id_ed25519 被误删重生后必然发生)。

解决:从 Hub 重新取公钥(2.1 节 info 接口的 key 字段),更新 Agent 的 KEY 环境变量后重建 Agent。实测修复后 Agent 日志恢复:

2026/10/17 08:28:45 INFO SSH connected addr=172.31.0.2:45290

7.2 API 返回 401

现象:curl /api/beszel/info 返回:

{"data":{},"message":"The request requires valid record authorization token.","status":401}

解决:0.21.0 该接口需认证。按第一章方式取 $TOKEN 后重试,加 -H "Authorization: $TOKEN"。

7.3 Hub 日志 Error getting container stats

现象(实测三类,均为个别统计对象的瞬态错误):

ERROR Error getting container stats err="infallible_lichterman - bad memory stats - see https://github.com/henrygd/beszel/issues/144"
ERROR Error getting container stats err="container stats request failed: 404 Not Found"
WARN Bad network delta container=surreal-curl-helper

结论:第一条对应内核未提供有效 memory 统计(cgroup 差异,官方 issue #144),第二条对应统计瞬间容器已退出,第三条为网络计数增量异常。实测均不影响其他对象采集——同时刻 Docker 统计仍有 30 个对象正常入库。无需处理。

7.4 docker compose down 报网络被占用

现象:

 Network beszel-lab_beszel-net  Removing
 Network beszel-lab_beszel-net  Resource is still in use

定位:该网络上还有不属于本编排的容器(本文实测是排障用的第三只 Agent)。

解决:docker network inspect beszel-lab_beszel-net 查看占用者,逐一 docker stop 或 docker network disconnect 后重试;或保留网络仅 docker compose stop。该保护行为防止误删他人网络,属预期设计。

7.5 镜像拉取 denied / 502

现象:实测拉取非 latest 标签时加速源返回:

Error response from daemon: error from registry: denied

解决:重试(实测数分钟后同一源拉 latest 成功)、更换加速源或直连 Docker Hub。升级操作前建议先手动 docker pull 验证标签可得。

7.6 页面无温度数据

定位:运行环境(虚拟机/受限环境)内无 hwmon 传感器设备。

解决:按部署指南 8.3 节挂载宿主机传感器目录并设置 SYS_SENSORS。实测开启后指标记录出现温度字段:

{"t":{"coretemp_package_id_0":45.5}}

真机(裸金属)部署一般无需任何配置。

7.7 节点失联与恢复演练实录

注入:停掉第二节点 Agent,观察 Hub 判定:

docker stop beszel-0.21.0-agent-node2

实测输出:

beszel-0.21.0-agent-node2

等待约 70 秒后查询节点状态:

curl -s 'http://localhost:8090/api/collections/systems/records?perPage=5&fields=name,status,updated' \
  -H "Authorization: $TOKEN" | python3 -c "import sys,json; [print(i['name'], i['status'], i['updated']) for i in json.load(sys.stdin)['items']]"

实测输出:

beszel-agent-node2 down 2026-10-17 11:40:31.543Z
beszel-agent-node1 up 2026-10-17 11:40:29.589Z

实测结论:Agent 停止后约 1 分钟内 Hub 将节点判为 down(失联判定周期分钟级),其余节点不受影响。

恢复:

docker start beszel-0.21.0-agent-node2

等待约 40 秒后复查:

curl -s 'http://localhost:8090/api/collections/systems/records?perPage=5&fields=name,status' \
  -H "Authorization: $TOKEN" | python3 -c "import sys,json; [print(i['name'], i['status']) for i in json.load(sys.stdin)['items']]"

实测输出:

beszel-agent-node2 up
beszel-agent-node1 up

节点自动回归 up,期间历史数据不丢(失联时段仅缺数,不产生空洞补齐)。配合 4.2 节的 Status 规则,这一跃迁会触发失联告警——但注意 4.5 节实测结论:规则须在健康时预先创建。

八、常见问题(FAQ)

Q1:巡检发现某节点一直 pending,怎么排查?

按顺序核对三处:Agent 进程存活(对应机器上 docker compose ps)、Agent 端口对 Hub 可达(在 Hub 侧执行 curl -s --max-time 3 telnet://<节点IP>:45878 或 telnet 测试)、KEY 与 Hub 公钥一致(7.1 节)。三者都正常仍在 pending,重启 Agent 后观察 1 分钟。

Q2:告警触发后如何确认通知已外发?

先确认规则 triggered: true(4.3 节查询),再检查用户通知渠道配置(页面后台 Notifications 设置 SMTP/Webhook)。实测 API 层面 triggered 翻转即代表告警事件成立,外发失败常见原因是 SMTP 凭据错误,查看 Hub 日志 docker logs beszel-0.21.0-hub | grep -i notify 定位。

Q3:数据目录磁盘增长过快怎么办?

先 du -sh /opt/data/beszel/(2.4 节)确认量级;实测本场景增长主因是 Docker 统计对象多的机器分钟级入库。Beszel 页面后台可设置数据保留时长(Settings → 数据清理),按需缩短保留期;同时核对 -wal 文件是否异常巨大(正常随检查点合并回落)。

九、参考链接