编程 Beszel 部署排障笔记:两条连接通道、unix socket 与几处静默失败

2026-10-27 00:05:02

Beszel 部署排障笔记:两条连接通道、unix socket 与几处静默失败

Beszel 的 hub 和 agent 之间有两条彼此独立的连接通道:hub 主动发起 TCP/SSH 连接,连到 agent 的 45876 端口;或者 agent 主动通过 WebSocket 连到 hub 的 /api/beszel/agent-connect。只要这两条通道中任意一条通畅,监控就能工作。所以部署卡住时,第一件事是先分清当前走的是哪条通道,再去查那条通道上的防火墙、反向代理和证书;如果顺序反过来,就会浪费很多时间。

1. 组成与版本

hub 基于 PocketBase,前端用 Svelte。镜像为 henrygd/beszel:latest,监听 8090,需要两个卷:/beszel_data 存数据,/beszel_socket 用于与 agent 共享 unix socket。环境变量可以带 BESZEL_HUB_ 前缀。第一个用户可以用 USER_EMAIL / USER_PASSWORD 直接创建。

hub 侧的其余开关:

  • APP_URL:hub 对外地址,反代场景要设对。
  • MFA_OTP:先配好 SMTP 才能用。
  • CHECK_UPDATES:默认 false。
  • CONTAINER_DETAILS:默认 true,开着才能在 UI 里看容器 inspect/logs。

agent 是纯 Go 单二进制,支持 Docker/Podman、Homebrew、WinGet/Scoop、Home Assistant add-on。文档版本 0.21.1,协议 MIT。

2. hub 与 agent 怎么连

agent 侧必需三个值:KEY(hub 添加系统时给出的公钥)、TOKEN(/settings/tokens 里的通用 token)、HUB_URL(主动 WebSocket 用,SSH 模式不需要)。

  • LISTEN 默认 45876,旧名 PORT 已废弃。
  • NETWORK 依据地址前缀推断:以 / 开头当 unix socket,否则当 tcp;IPv6 必须写方括号。
  • ALL_PROXY 只认 socks5:// 和 socks5h://。
  • hub 挂在 Cloudflare Access / Pangolin 后面时,用 HUB_HEADERS 带认证头;X-Token 和 X-Beszel 是保留头,不能用。

hub 侧默认行为是:agent 没有活跃 WebSocket 时,hub 会尝试用 SSH 连过去。DISABLE_SSH=true 关掉的是这个「hub 主动 SSH」动作。agent 侧也有同名变量,语义是关掉 agent 自己的 SSH server,两者不是一回事,配错会以为关了其实没关。

一个典型的静默失败:走 SSH 模式,而 hub 宿主机上 iptables -P FORWARD DROP。Docker 的端口映射依赖 FORWARD 链,于是 hub 连不进去,日志里也没有明确报错。要么改用 WebSocket,要么放开转发。

排查顺序:

  1. 看 PocketBase 日志 http://:8090/_/#/logs。
  2. 用 telnet 45876 验证 TCP 通道。
  3. 走 WebSocket 的话,确认反代支持 WebSocket 升级。

另外,月/周聚合是从短期数据平均出来的。agent 中断超过一分钟,或者被暂停超过一分钟,当前区间就会重算——别随手 pause 容器。

3. 同机部署:localhost 不通,用 unix socket

hub 和 agent 是两个容器,各自在独立的网络命名空间里,agent 填 localhost 或 127.0.0.1 连不到 hub,反过来也一样。官方推荐的做法是让两者共享一个目录,通过 unix socket 通信:

services:
beszel:
image: henrygd/beszel:latest
ports:
- "8090:8090"
volumes:
- ./beszel_data:/beszel_data
- ./beszel_socket:/beszel_socket
healthcheck:
test: ["CMD", "/beszel", "health", "--url", "http://localhost:8090"]
interval: 120s

beszel-agent:
image: henrygd/beszel-agent:latest
network_mode: host
volumes:
- ./beszel_socket:/beszel_socket
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
LISTEN: /beszel_socket/beszel.sock
KEY: ""
TOKEN: ""
healthcheck:
test: ["CMD", "/agent", "health"]

在 Web UI 添加系统时,Host/IP 一栏直接填 /beszel_socket/beszel.sock,不要填 IP。

4. host network、端口、rootless 与 USE_CGROUP

agent 必须用 host 网络模式,才能读到宿主网卡的统计。副作用是 agent 端口直接暴露在宿主上;要换端口改 LISTEN 即可。如果不关心网络统计,可以去掉 host 网络、手动映射端口。

rootless 容器读不到一堆宿主级指标:温度、风扇、电池、GPU、ZFS、SMART、systemd 服务。容器自身的指标需要正确的 socket 路径加 cgroup 委托。文档的建议很直接——要这些指标,就用二进制 agent 跑成 systemd 服务。

USE_CGROUP=true 时,agent 上报的是自己容器的 CPU/内存和限额,需要容器有独立的 cgroup namespace。如果容器用了 --cgroupns=host,agent 看到的是宿主 root cgroup 并给出告警。这个模式下不显示单核用量,MEM_CALC 也不适用。MEM_CALC=htop 则是让内存算法和 htop 对齐。

容器图表一片空白时,先 docker stats 看内存是不是恒为 0,需要打开 cgroup memory accounting(树莓派上常见)。Docker 24 有容器数据填充不全的 bug,agent 侧有 workaround,但不完美。

5. 必须显式设置的采集项

FILESYSTEM:容器里自动找 / 挂载分区经常找错,必须显式指定。可填 lsblk 的 NAME、df -h 的 Filesystem 或 fdisk -l 的 Device。

EXTRA_FILESYSTEMS:额外磁盘以只读方式挂到 /extra-filesystems/ 下监控。配合 DISK_USAGE_CACHE=5m 之类的值,可以避免反复唤醒休眠盘。

NICS:默认是白名单,前缀加 - 变成黑名单,空字符串表示关闭网卡带宽监控。

SENSORS / PRIMARY_SENSOR:同样的白名单语义;PRIMARY_SENSOR 未设时取最高温度。

SMART_DEVICES:覆盖 smartctl --scan 的结果,格式 /dev/nvme0:nvme,/dev/sda:sat,设成空字符串关闭 SMART。SMART_INTERVAL 默认 1h。

SERVICE_PATTERNS:例如 "beszel*,docker*,kubelet*";SKIP_SYSTEMD / SKIP_SYSTEMD_LOGS 可整体关掉。

PACKAGE_UPDATES_INTERVAL 默认 1h,设 0 关闭。DOCKER_IMAGE_CHECK 默认 true,会访问镜像仓库查新版本,可以关。DOCKER_TIMEOUT 默认 2100ms,慢盘或慢机器要调大。EXCLUDE_CONTAINERS 支持通配符。

CONTAINER_NAME_LABEL 解决 Coolify 这类容器名带随机后缀、每次重新部署就新开一条曲线的问题。Compose 环境里 $ 要写成 $$。

6. 反代 SSE 与自签 CA

实时数据不刷新、改了设置存不住,先查代理层的压缩。text/event-stream 一旦被 gzip 之类的压缩处理,SSE 就断了。Coolify 上要去掉 hub 服务的 gzip。

自签证书分两种情况:

hub 作为容器去连自签的服务时,把 CA 挂进容器,否则报 tls: failed to verify certificate: x509: certificate signed by unknown authority:

volumes:
- /path/custom-ca.crt:/etc/ssl/certs/custom-ca.crt:ro

agent 连私有 CA 签的 hub 时,用 CA_CERT_FILE 指向 PEM 文件。证书必须包含与 HUB_URL 匹配的 SAN。文件缺失、读不了或格式不对,agent 直接起不来。

7. 安全边界

TRUSTED_AUTH_HEADER 默认对所有来源的请求都生效,必须同时配 TRUSTED_PROXY_IPS(逗号分隔的 IP/CIDR)。判断依据是 TCP 连接来源地址,不看 X-Forwarded-For。不配的话,任何能直连 hub 的人都能自己伪造认证头。

AUTO_LOGIN 会完全绕过认证,只在完全可信的环境里开。DISABLE_PASSWORD_AUTH 只是禁用密码登录(改用 OAuth),并不等于关闭认证,别混。

HEARTBEAT_URL 做 dead man's switch,默认 POST,60s 一次,JSON 含 status、systems、down_systems、triggered_alerts、beszel_version;也支持 GET/HEAD。

密钥不要写进环境变量:KEY_FILE / TOKEN_FILE 用于从文件读取。

Docker socket 是另一个边界。把 /var/run/docker.sock 直接挂给 agent,等于给了容器宿主 root 权限。更窄的做法是用 DOCKER_HOST 指向 docker-socket-proxy,proxy 上只开放只读端点。

走 Authelia 4.39.15+ 的 forward-auth 时,要给 authz.forward-auth 加 authn_strategies: CookieSession。

8. 和 node_exporter + Prometheus + Grafana 的取舍

Beszel 自带 UI、告警、容器与服务状态,部署成本低——一个 hub 加每台机器一个 agent,十几台以内的小集群和自托管场景很合适。REST API 也能对接外部系统。

长期时序保留、跨实例 PromQL 查询、复杂面板,仍然是 Prometheus 生态更合适。

两者可以共存:Beszel 回答「这台机器活着吗、资源正常吗」,Prometheus 管历史和容量规划。

复制全文 生成海报 Beszel 服务器监控 Docker 运维排障 Go

推荐文章

程序员茄子在线接单