从备份到满血复原:Docker 服务 + 反向代理 + 密钥 的备份与灾难恢复 SOP

目标:当服务器故障/误删/迁移时,能按步骤快速恢复:博客、RSS 聚合、密码库、文件同步、状态监控、导航页等。

范围:以 Docker + Compose 部署、Nginx 反向代理 + HTTPS 出网的通用栈为例。

脱敏说明:文中使用占位符(如 SERVER_IPapp.example.com/srv/app),不包含你的真实 IP/域名/口令。


【实操教学】

一、备份策略设计(先定“备什么、在哪、多久一次”)

1)分层备份清单(最重要放最上)

  • 密钥与账号

    • 本机 SSH 私钥(例如 ~/.ssh/id_ed25519)→ 用加密压缩备份

    • 服务器 admin 用户sudoers 片段(/etc/sudoers.d/90-admin)

  • 反向代理与证书

    • Nginx 站点配置(或面板导出的反代配置)

    • HTTPS 证书(Let’s Encrypt 可重签;也可一并备)

  • Compose 项目与持久化数据

    • docker-compose.yml.env、自定义脚本

    • 数据卷/挂载目录(下表给出示例路径)

服务

必备数据目录(示例)

说明

WordPress

/srv/wordpress/html + 数据库备份

网站文件与数据库都要有

FreshRSS

/srv/rss_stack/freshrss_data/

订阅、用户、配置

RSSHub

可选(多为无状态)

如有自定义路由/配置则备

Vaultwarden

/srv/vaultwarden/data/

核心数据,必须备

Syncthing

/srv/syncthing/config/ + 各资料库路径

配置 + 你要同步的文件

Uptime Kuma

/srv/uptime-kuma/data/

监控项与通知通道

Homer

/srv/homer/assets/

config.yml + custom.css

说明:路径只作示例——你可以把所有项目统一放到 /srv/<project>/home/admin/<project>,便于备份。


2)备份周期与保留(建议起点)

  • 每日:留 7 份(近一周)

  • 每周:留 4 份(近一月)

  • 每月:留 6 份(近半年)

  • 关键变更:手动打一个 里程碑备份(例如“升级前/大改动前”)


二、基础工具与目录

# 建立统一的本地备份目录
mkdir -p /srv/_backups/daily          # mkdir:创建目录;-p:父目录不存在则一并创建
mkdir -p /srv/_backups/weekly
mkdir -p /srv/_backups/monthly

云端存储:如果你使用对象存储(如 S3/COS 等),建议在服务器装对应 CLI(如 aws s3 / coscli),把本地备份同步到云端冷存。


三、一次性脚本:打“整包”备份(可直接用)

把你实际的项目路径替换到脚本里;每条命令都含中文注释,便于理解。

nano /usr/local/bin/stack-backup.sh   # nano:新建脚本文件

粘贴以下内容(按需调整项目列表与路径):

#!/usr/bin/env bash
set -euo pipefail
# set -e:遇错退出;-u:未定义变量即报错;-o pipefail:管道中任一命令失败即失败

NOW=$(date +%F_%H%M%S)                 # date:取当前日期时间,格式 2025-08-17_123456
HOST=$(hostname -s)                    # hostname -s:主机短名,便于区分多台机器
DEST_BASE="/srv/_backups"              # 本地备份根目录
TIER="${1:-daily}"                     # 备份层级:daily/weekly/monthly,默认 daily
DEST_DIR="$DEST_BASE/$TIER"            # 目标子目录

# 需要备份的项目(按你的实际修改)
PROJECTS=(
  "/srv/wordpress"                     # WordPress(含 compose 与 html)
  "/srv/rss_stack"                     # FreshRSS / RSSHub
  "/srv/vaultwarden"                   # Vaultwarden
  "/srv/syncthing"                     # Syncthing
  "/srv/uptime-kuma"                   # Uptime Kuma
  "/srv/homer"                         # Homer
)

mkdir -p "$DEST_DIR"                   # 确保目标目录存在

# 1) 冷冻所有 compose 项目到“干净状态”(可选:减少正在写入)
#   仅当你非常在意一致性时使用;正常家庭/轻量服务可跳过。
# for dir in "${PROJECTS[@]}"; do
#   [ -f "$dir/docker-compose.yml" ] && (cd "$dir" && docker compose pause || true)
# done

# 2) 打包每个项目
for dir in "${PROJECTS[@]}"; do
  name=$(basename "$dir")              # basename:取目录名作为包名的一部分
  tarball="${DEST_DIR}/${HOST}_${name}_${NOW}.tar.gz"
  # tar -czf:打包并压缩为 .tar.gz
  # --exclude:可按需排除缓存/临时文件
  tar -czf "$tarball" \
    --exclude='*.log' \
    --exclude='node_modules' \
    -C "$dir" .                         # -C:先切换到目录,再把“当前目录下所有内容”打进包
  # 生成校验和(sha256sum:计算文件哈希)
  sha256sum "$tarball" > "${tarball}.sha256"
done

# 3) 恢复运行(若之前 pause 了)
# for dir in "${PROJECTS[@]}"; do
#   [ -f "$dir/docker-compose.yml" ] && (cd "$dir" && docker compose unpause || true)
# done

# 4) 本地清理策略(保留一定数量的包)
#   这里按“每层保留 30 天”为例;你也可以按数量清理(-mtime 改为 -printf + sort)
find "$DEST_DIR" -type f -name "*.tar.gz" -mtime +30 -delete
find "$DEST_DIR" -type f -name "*.sha256" -mtime +30 -delete

echo "[OK] backup done: $DEST_DIR"

保存(Ctrl+O 回车)→ 退出(Ctrl+X)→ 赋予执行权限:

chmod +x /usr/local/bin/stack-backup.sh   # chmod +x:给脚本可执行权限

四、定时任务(crontab)

crontab -e
# 在文件末尾追加三行(每日/每周/每月各一次;时间可自行调整)

# 每天 凌晨 02:10 做一次“每日备份”
10 2 * * *  /usr/local/bin/stack-backup.sh daily   >> /var/log/stack-backup.log 2>&1

# 每周 周日 凌晨 02:20 做一次“周备份”
20 2 * * 0  /usr/local/bin/stack-backup.sh weekly  >> /var/log/stack-backup.log 2>&1

# 每月 1 号 凌晨 02:30 做一次“月备份”
30 2 1 * *  /usr/local/bin/stack-backup.sh monthly >> /var/log/stack-backup.log 2>&1

若要同步到对象存储,可在脚本末尾追加一段(以 S3 语法为例,COS/其他厂商类同):

# aws s3:把备份同步上云;--delete:让云端与本地一致(谨慎使用)
aws s3 sync "$DEST_DIR" "s3://your-bucket/stack/$TIER/" --delete

五、私钥的安全备份(只针对

私钥文件

# 1) 压缩并加密(zip 会提示输入加密口令)
cd ~/.ssh
zip --encrypt admin_ssh_key_${HOST}_$(date +%F).zip id_ed25519
# zip --encrypt:加密压缩;文件名中带日期与主机名便于辨识

# 2) 把生成的 zip 放入你的密码库 / 云盘(受信、受控)
#   切记:绝不把“明文私钥”丢在公开或弱权限的位置

六、灾难恢复(最短路径:从裸机到上线)

场景:原服务器不可用,新机器上快速恢复同一套服务

0)新服务器最小化初始化

# 新建 admin 用户并给 bash
useradd -m -s /bin/bash admin      # -m:创建家目录;-s:登录 shell
passwd admin                       # 设置 admin 口令(仅作本地应急)

# 配置 admin 的 SSH 公钥(把你本机公钥粘贴进去)
su - admin
mkdir -p ~/.ssh && chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys        # 粘贴你的公钥(.pub 文件内容,一行)
chmod 600 ~/.ssh/authorized_keys

# 给 admin 配置 sudo 片段(免密或提示输入密码按需选择)
sudo visudo -f /etc/sudoers.d/90-admin
# 粘贴其中一行:
# admin ALL=(ALL) ALL
# 或
# admin ALL=(ALL) NOPASSWD:ALL
sudo chmod 440 /etc/sudoers.d/90-admin
sudo visudo -c                     # -c:检查 sudoers 语法

1)安装 Docker 与 Compose

# 不同发行版命令略有差异,这里给出通用参考(已简化)
sudo dnf install -y docker
sudo systemctl enable --now docker
docker -v                           # 验证 Docker 可用
docker compose version              # 验证 Compose 子命令可用

2)恢复反向代理与证书

  • 使用面板或手工安装 Nginx。

  • 为每个子域名添加站点,目标指向 本机回环端口(稍后容器起来即可连接)。

  • 申请 Let’s Encrypt 并强制 HTTPS。

  • 站点反代里加入六行黄金请求头(避免样式/JS丢失):

proxy_set_header Host              $host;
proxy_set_header X-Real-IP         $remote_addr;
proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host  $host;
proxy_set_header X-Forwarded-Port  $server_port;

3)拉回备份并解包到原路径

# 假设从对象存储或另一台机器取回最近的备份包
mkdir -p /srv/_restore && cd /srv/_restore
# 把 *.tar.gz 下载到本目录(命令略)

# 校验完整性:对每个包做 sha256sum 校验
sha256sum -c *.sha256               # -c:逐个校验哈希,OK 表示文件未损坏

# 选择一个包(示例:vaultwarden),解压回目标目录
mkdir -p /srv/vaultwarden
tar -xzf HOST_vaultwarden_YYYY-MM-DD_HHMMSS.tar.gz -C /srv/vaultwarden
# tar -xzf:解压 .tar.gz;-C:解压到指定目录

权限复位(重要):如果容器需要以 admin 身份写文件,确保属主一致:

sudo chown -R admin:admin /srv/vaultwarden
# 按项目依次处理 /srv/rss_stack /srv/syncthing /srv/uptime-kuma /srv/homer 等

4)逐项启动各服务(按你原来的项目目录)

# 先起反代依赖少的(例如 Homer)
cd /srv/homer && docker compose up -d && docker compose ps

# 再起 Kuma(便于在恢复过程中“自我监控”)
cd /srv/uptime-kuma && docker compose up -d

# 再起 FreshRSS/RSSHub
cd /srv/rss_stack && docker compose up -d

# Vaultwarden(核心)
cd /srv/vaultwarden && docker compose up -d

# Syncthing(文件同步)
cd /srv/syncthing && docker compose up -d

# WordPress(如你用 Docker 方式)
cd /srv/wordpress && docker compose up -d

若 WordPress 用的是面板安装而非 Docker:

  • 先建空站点 → 导入网站文件(备份包里的 html 目录)

  • 导入数据库.sql 或面板的数据库备份);

  • 若域名变化,记得更新 wp_options 里的 siteurl/home(或 wp-config.php 里定义 WP_HOME/WP_SITEURL)。

5)逐项验证(从“站点入口”到“服务本体”)

# 只在服务器本机测后端端口
curl -I http://127.0.0.1:3002      # Homer 示例
curl -I http://127.0.0.1:3001      # Kuma
curl -I http://127.0.0.1:8082      # FreshRSS
curl -I http://127.0.0.1:8384      # Syncthing GUI
# -I:只取响应头;200/302 即后端正常

# 外网浏览器访问域名(反代 + HTTPS)
https://home.example.com
https://status.example.com
https://rss.example.com
# ……

6)Syncthing 特有步骤

  • 打开 https://sync.example.com:检查设备 ID 是否与本地设备互认;

  • 如果资料库路径不同,按 GUI 提示**“重新配对路径”**;

  • 22000/TCP+UDP 放行后,观察连接类型是否为 TCP/QUIC(非 Relay)。

7)Vaultwarden 特有步骤

  • 恢复后若域名变化,客户端可能需要重新登录

  • 确认环境变量/配置里的 DOMAIN(如果有)与当前域名一致;

  • 强制 HTTPS 的反代要正确传递请求头。

8)Kuma 特有步骤

  • 打开 https://status.example.com:确认监控项在线;

  • 测试通知:给 QQ SMTP 通道点“测试”;

  • 如监控的域名有变化,批量修改目标地址或重建。


七、演练建议(别等出事才第一次恢复)

  • 季度进行一次桌面演练:选 1~2 个服务,在同一台机器的备用路径(如 /srv/_drill)解包 + up -d,验证能打开;

  • 演练完成后删除临时容器与数据,避免混淆与资源占用;

  • 每次演练都更新一份 “恢复核对清单” 记录问题与改进点。


八、恢复核对清单(贴墙上就能照做)

  • admin 账号可 SSH 登录,sudoers 片段有效(visudo -c 通过)

  • Docker/Compose 能正常运行(docker compose version OK)

  • Nginx 反代 + HTTPS 就绪(站点存在、证书有效、六行请求头已配置)

  • 备份包校验通过(sha256sum -c 全 OK)

  • 各项目目录解包到位,属主为 admin(chown -R admin:admin

  • 各服务 docker compose up -d 无报错,curl -I 127.0.0.1:<port> 200/302

  • 外网访问各域名正常

  • Syncthing 设备互认、资料库路径匹配、22000 直连可用

  • Vaultwarden 登录正常;若域名变化,客户端已重新登录

  • Uptime Kuma 监控与通知均恢复

  • Homer 首页导航链接全部可点开


【深度解析与知识扩展】

1)为什么“备份目录 + Compose + 数据卷”三位一体?

  • Compose 文件定义“如何跑”;

  • 数据卷/挂载目录保存“跑出来的状态”;

  • 反代与证书决定“别人如何访问”。

    缺一不可:只有数据没 Compose,会忘记端口/环境变量;只有 Compose 没数据,站点会“空壳启动”。

2)一致性 vs 可用性

  • 对数据库/高并发写入服务,一致性备份最好在短暂停机或只读窗口进行(或用热备工具)。

  • 对你的轻量服务,结合“快速冷备 + 多副本”通常更实用:即便单次备份不完美,也能靠近几天的多份回滚。

3)校验和(Checksum)的意义

  • 传输/存储过程中可能损坏;

  • sha256sum 给你“数学级”的确定性:校验通过才可用于恢复

4)为什么密钥一定要“加密备份”

  • 私钥一旦泄露 = 任何人都可冒充你登录服务器;

  • 加密压缩 + 强口令 + 放入密码库,才能“可用又安全”。

5)“六行黄金请求头”的通用性

  • 很多“反代后没样式/JS”的根因,是后端不知道自己在 https/哪个 Host

  • 这六行把关键信息补齐,适用于 Homer / Kuma / FreshRSS / Vaultwarden / Syncthing GUI 等多数 Web 服务。

6)为什么建议把项目统一放

/srv/<project>

/home/admin/<project>

  • 备份/迁移/权限管理都极其直观;

  • 你只需要记住“一棵树”,脚本里循环打包即可。


到此为止,你已经拥有一套可操作、可演练、能在出事时救命的“备份与灾备 SOP”。

发表回复

Your email address will not be published. Required fields are marked *.

*
*

Copyright © 2025 十两东日的随想. All Rights Reserved.

鄂ICP备2025140583号-1 | 鄂公网安备42011102005804号