1
0
0

飞牛 OS 使用要点

2026-09-13
2026-09-13
文章摘要
|

来源:halo-kb/scripts/nas/README.md(整篇)(原文 6934 字符)

Halo 备份 / 恢复 / 运维脚本(NAS 侧)

目标环境(已实测):

主机飞牛 OS(FNOS),Debian 12 用户态,systemd 252,bash 5.2.15
项目目录/vol1/1000/docker/halo
容器Haloregistry.fit2cloud.com/halo/halo-pro:2.26)、PostgreSQLpostgres:15.4
备份根目录/vol1/1000/docker/halo/backups只增不改的约定位置)
执行身份ZYJ(uid 1000,在 docker 组内,无需 sudo

一、文件清单

文件作用是否需要 root
halo-backup.sh全量备份:pg_dump -Fc + pg_dumpall --globals-only + halo2/ 归档 + compose/env 副本 + 校验和 + 清单 + 保留策略
halo-restore-verify.sh非破坏性恢复验证:把 dump 恢复到临时库、比对计数、删临时库
halo-restore.sh破坏性生产恢复(有交互确认与 --confirm-restore 双闸)
halo-healthcheck.sh只读健康检查(10 项判据),支持 --prom / --quiet
halo-backup.cron用户级 crontab:每日备份 + 每周恢复验证 + 每日两次健康检查
halo-logrotate.confhalo2/logs/halo.log 的轮转规则(copytruncate不重启容器是(装到 /etc/logrotate.d/
install-on-nas.sh部署脚本 + 语法/依赖自检 + 打印后续步骤

二、部署到 NAS

本机(Windows)把脚本目录送到 NAS。任选一条

方式 A:本机起临时 HTTP 服务,NAS 主动拉取(实测可用,最快)

# 本机(H:\Works\halo-kb\scripts\nas 目录下)
python -m http.server 8899 --bind 0.0.0.0
# NAS 上
curl -s -o /tmp/halo-scripts.tar http://<内网IP>:8899/halo-scripts.tar
mkdir -p /tmp/halo-scripts && tar -xf /tmp/halo-scripts.tar -C /tmp/halo-scripts
bash /tmp/halo-scripts/install-on-nas.sh

⚠️ 本机防火墙需放行 8899;用完把 HTTP 服务停掉。 ⚠️ 本机 IP 会变(实测 <内网IP>;WLAN 上是 <内网IP>),换网段要一起改。

方式 B:用 NAS 文件管理器 / SMB 手抄 —— 把本目录文件复制到 /vol1/1000/docker/halo/scripts/,然后

chmod 750 /vol1/1000/docker/halo/scripts/*.sh
bash /vol1/1000/docker/halo/scripts/halo-backup.sh --dry-run   # 自检

安装后目录应为:

/vol1/1000/docker/halo/scripts/
├── halo-backup.sh          (750)
├── halo-restore-verify.sh  (750)
├── halo-restore.sh         (750)
├── halo-healthcheck.sh     (750)
├── halo-backup.cron
├── halo-logrotate.conf
└── README.md

三、日常使用

cd /vol1/1000/docker/halo

./scripts/halo-backup.sh --dry-run      # 只打印计划,不落盘
./scripts/halo-backup.sh                # 正式备份(实测约 5.5 秒)
KEEP=14 ./scripts/halo-backup.sh        # 覆盖保留份数(默认 7)
INCLUDE_LOGS=1 ./scripts/halo-backup.sh # 把 halo2/logs 也打进归档(默认排除)

./scripts/halo-restore-verify.sh        # 用最新 dump 做非破坏性恢复验证(约 44 秒)
./scripts/halo-healthcheck.sh           # 10 项健康检查
./scripts/halo-healthcheck.sh --prom    # Prometheus textfile 格式
./scripts/halo-healthcheck.sh --quiet   # 只在失败时输出(给 cron 用)

产物命名(同一时间戳一套):

halo-db-<ts>.dump            PostgreSQL custom 归档(-Fc)
halo-pg-globals-<ts>.sql     全局角色(CREATE ROLE / ALTER ROLE)
halo2-<ts>.tar.gz            Halo 工作目录(排除 backups/ 与 logs/)
compose-<ts>.yaml            docker-compose.yaml 原样副本
container-env-<ts>.txt       两容器环境变量快照(口令一律脱敏)
MANIFEST-<ts>.txt            清单:计数、镜像 digest、恢复要点
MANIFEST-<ts>.sha256         四个产物的 sha256
backup.log                   追加式运行日志

回滚一次备份产物(确认无用后):

rm -f backups/halo-db-<ts>.dump* backups/halo2-<ts>.tar.gz* \
      backups/halo-pg-globals-<ts>.sql* backups/compose-<ts>.yaml* \
      backups/container-env-<ts>.txt backups/MANIFEST-<ts>.*

四、恢复步骤

4.1 先验证,再恢复(必做

./scripts/halo-restore-verify.sh

判据:输出 ✅ 恢复验证通过,且「表数量」「extensions 行」两侧一致。 这一步不改生产库,随时可跑。

4.2 灾难恢复(会覆盖生产数据)

cd /vol1/1000/docker/halo

# 0) 先给「恢复前的现状」再做一份备份,别把唯一的退路也覆盖掉
./scripts/halo-backup.sh
cp -p backups/MANIFEST-<ts>.txt /tmp/restore-plan.txt   # 记下这次的时间戳

# 1) 停 Halo(不动数据库容器)
docker compose stop halo

# 2) 恢复数据库
docker exec -i PostgreSQL pg_restore -U halo -d halo --clean --if-exists --no-owner \
  < backups/halo-db-<ts>.dump

# 3) 恢复文件(会覆盖 halo2/,故第 0 步不可省)
tar -xzf backups/halo2-<ts>.tar.gz -C /vol1/1000/docker/halo

# 4) 恢复 compose(仅在 compose 本身也丢了/被改坏时)
cp -p backups/compose-<ts>.yaml /vol1/1000/docker/halo/docker-compose.yaml

# 5) 起 Halo 并验证
docker compose up -d halo
./scripts/halo-healthcheck.sh

4.3 ⚠️ 三个会让人恢复错库/丢数据的坑(都是实测结论)

  1. 本机宿主上另有一个 PostgreSQL。

宿主 postgres 系统用户(uid 107)监听 127.0.0.1:5432,且宿主装了 /usr/bin/psql在 NAS 上裸跑 psql 会连到它,而不是 Halo 的库。 本方案所有数据库命令一律显式走 docker exec PostgreSQL ...,不要用宿主 psql。 (另有容器 n8n-postgres,当前 Exited,同样不要碰。)

  1. 口令不在脚本里,靠的是容器内本地 socket 的 trust 认证。

pg_hba.conf 里有 local all all trust,所以 docker exec PostgreSQL pg_dump -U halo ... 不需要任何口令。这也意味着任何能在该容器内执行命令的人都能读全库 —— 见报告 C-3。

  1. --clean --if-exists 是破坏性的。

它对目标库逐对象 DROP 再 CREATE。务必先确认 -d 后面的库名是 halo


五、定时任务

crontab /vol1/1000/docker/halo/scripts/halo-backup.cron
crontab -l

内容(详见 halo-backup.cron):

30 3 * * *   halo-backup.sh                              # 每天 03:30 备份
30 4 * * 1   halo-restore-verify.sh                      # 每周一 04:30 恢复验证
0 8,20 * * * halo-healthcheck.sh --quiet                 # 每天 08:00/20:00 健康检查

验证它真的会跑(三条,缺一不算验证):

crontab -l                                    # ① 条目在
date                                          # ② 宿主机时区(cron 用系统时区,不换算)
grep -n 'cron' /var/log/syslog | tail         # ③ cron 是否真的触发了该作业
ls -lt /vol1/1000/docker/halo/backups/ | head # ④ 次日看有没有新时间戳的产物

⚠️ 不能用 journalctl 验证:ZYJ 不在 systemd-journal 组,实测 journalctl 返回权限拒绝。 cron 的日志走 syslog。若 syslog 里也看不到,用一个临时作业自证: * * * * * date >> /tmp/cron-alive.log,等两分钟后检查,验完删掉该行。

systemd timer 备选(若你更愿意用 systemd)

需要 root(sudo 在这台 NAS 上要密码,本方案没有代做):

# /etc/systemd/system/halo-backup.service
[Unit]
Description=Halo backup
[Service]
Type=oneshot
User=ZYJ
Group=Users
ExecStart=/vol1/1000/docker/halo/scripts/halo-backup.sh
# /etc/systemd/system/halo-backup.timer
[Unit]
Description=Daily Halo backup
[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload && sudo systemctl enable --now halo-backup.timer
systemctl list-timers halo-backup.timer        # 验证

六、日志轮转

sudo cp /vol1/1000/docker/halo/scripts/halo-logrotate.conf /etc/logrotate.d/halo
sudo chown root:root /etc/logrotate.d/halo && sudo chmod 644 /etc/logrotate.d/halo
logrotate -d /etc/logrotate.d/halo            # 只演练,判定应显示 after 1 days
sudo logrotate -v -f /etc/logrotate.d/halo    # 强制执行一次
ls -la /vol1/1000/docker/halo/halo2/logs/     # 应出现 halo.log-YYYYMMDD

判据:-d 输出必须是 after 1 days (14 rotations) 且带 log files >= 20971520 are rotated earlier若显示 20971520 bytes (14 rotations) 则配置写错了 —— 见 halo-logrotate.conf 头部两条实测要点。

Docker 侧的 json-file 日志不需要额外处理daemon.json 已设 max-size=100m max-file=5,Halo 容器的 HostConfig.LogConfig 也显式带了同样参数。 若日后要改这两个值,必须重建容器docker compose up -d --force-recreate halo), 因为容器的日志驱动参数在创建时就固定了,restart 不生效。


七、契约

  • 本目录脚本不含任何明文口令或 token:数据库访问走容器内本地 socket(trust),

容器环境变量快照一律经 sed 脱敏后才落盘。

  • 备份产物只写 /vol1/1000/docker/halo/backups/(可用 BACKUP_DIR 覆盖)。
  • halo-backup.sh 只做新增;唯一的删除行为是保留策略清理它自己产生的旧产物。
  • 恢复验证用临时库,跑完即删,生产库全程只读。

来源:intranet-tunnel/docs/AGENTS-ARCHIVE.md### 三条可复用的判据(原文 1043 字符)

三条可复用的判据

  1. TUNNELSTUNNELS_FILE 能力不同(文档一度把两者写成同一件事):

TUNNELS 只认 name:type:local_port[:domain] 简写 —— 写 JSON 会报 config: TUNNELS 项 ... 的本地端口非法;而且它的 local_addr硬编码为 127.0.0.1client/internal/config/config.goParseInlineTunnelsLocalAddr: "127.0.0.1")。 容器里的 127.0.0.1 是容器自己 ⇒ 容器场景只能用 TUNNELS_FILE。 已回写 client/.env.dockerdeploy/docker/client/client.env.example 与该目录 README (原先两处的 JSON 示例照抄必然启动失败)。

  1. 域名按 custom_domain 全库查占用,跨客户端会被拒绝ensureStaticTunnels

GetTunnelByDomain 命中且 owner.ClientID != clientID跳过该静态隧道 (日志 域名已被其它客户端占用,忽略静态隧道)。实测 NAS 上 fn(5666)、 qb(8085)、portainer 已被离线客户端 pc1 占着,所以 NAS 客户端只能用新前缀 (本次用 fnosmoviepilot)。

  1. 落在泛域内的新隧道,云端零改动:域名按前缀声明、服务端用 TUNNEL_DOMAIN 补全,

泛域块复用 ⇒ 边缘配置指纹不变、reload 次数不增,内置反代规则也随登录自动装载 (本次没有触发 POST /api/proxies/reload,也没有重启 tunnel-server)。 改完 tunnels.json 只需 docker compose restart tunnel-client; 而改 client.env 仍必须 up -d --force-recreateenv_file 只在创建容器时读)。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

评论