印度孟买某公司开发的 Virtualizor 面板,让不少 VPS 服务商都栽了跟头。从去年的 ColorCrossing 到最近的 CloudCone,黑客利用漏洞入侵服务器并进行勒索,导致大量用户数据全灭。
面对此类「黑天鹅事件」,谁也没法独善其身,是时候完善一下我的数据自动化备份方案了。
几年前我一直使用 Duplicacy CLI,甚至还写过两篇又臭又长的教程。但它的开源许可证不够清晰,且已停更近一年,我开始寻找更好的替代品。我找到了:
最终的选择是 Kopia ,它支持增量快照、自带端到端 加密、且上手极其简单。
Kopia 支持几乎所有主流的存储服务和协议:
对象存储我选择了 Backblaze B2 ,按量计费计划 仅需 41.15 元/TB。A 类操作免费且每天赠送一定额度的 B/C 类操作次数,免费出口流量是 3 倍存储量。对于增量备份,成本几乎可以忽略不计了。

假设我每月存储 100GB,费用大约为 4.11 元/月左右,实际根本用不了这么多。
创建访问机密
在 B2 创建存储桶,在 Application Keys 里生成访问机密,记下 keyID 和 applicationKey 即可开始配置。
在 Debian GNU/Linux 上,最省心的安装方式是添加官方的 APT 仓库。
# 导入 GPG 密钥
curl -s https://kopia.io/signing-key | sudo gpg --dearmor -o /etc/apt/keyrings/kopia-keyring.gpg
# 添加软件源
echo "deb [signed-by=/etc/apt/keyrings/kopia-keyring.gpg] http://packages.kopia.io/apt/ stable main" | sudo tee /etc/apt/sources.list.d/kopia.list
# 安装
sudo apt update && sudo apt install kopia
# 验证安装
kopia --version
执行 repository create 命令初始化存储库:
sudo kopia repository create b2 \
--bucket=<存储桶名称> \
--key-id=<keyID> \
--key=<applicationKey> \
--prefix=<目录前缀>/
务必牢记设置的密码,一旦遗忘,备份数据将彻底锁定,没有找回或重置密码的可能。
Enter password to create new repository:
Re-enter password for verification:
Initializing repository with:
block hash: BLAKE2B-256-128
encryption: AES256-GCM-HMAC-SHA256
key derivation: scrypt-65536-8-1
splitter: DYNAMIC-4M-BUZHASH
Connected to repository.
NOTICE: Kopia will check for updates on GitHub every 7 days, starting 24 hours after first use.
To disable this behavior, set environment variable KOPIA_CHECK_FOR_UPDATES=false
Alternatively you can remove the file "/root/.config/kopia/repository.config.update-info.json".
Retention:
Annual snapshots: 3 (defined for this target)
Monthly snapshots: 24 (defined for this target)
Weekly snapshots: 4 (defined for this target)
Daily snapshots: 7 (defined for this target)
Hourly snapshots: 48 (defined for this target)
Latest snapshots: 10 (defined for this target)
Ignore identical snapshots: false (defined for this target)
Compression disabled.
To find more information about default policy run 'kopia policy get'.
To change the policy use 'kopia policy set' command.
NOTE: Kopia will perform quick maintenance of the repository automatically every 1h0m0s
and full maintenance every 24h0m0s when running as root@vps-micro.
See https://kopia.io/docs/advanced/maintenance/ for more information.
NOTE: To validate that your provider is compatible with Kopia, please run:
$ kopia repository validate-provider
查看连接状态
sudo kopia repository status
设置全局压缩算法,推荐 zstd 选项,平衡压缩率和速度
sudo kopia policy set --global --compression=zstd
配置全局保留策略,作为备份项目的回退配置
# 保留最新的 8 份快照
sudo kopia policy set --global --keep-latest 8
检查全局策略是否生效
sudo kopia policy list
以 /home/dejavu/warp 下的 Docker 容器为例,该容器使用了绑定挂载。
# 细致化单个备份项目保留策略
# 对于配置基本不动的容器,保留 3 份快照足矣
sudo kopia policy set /home/dejavu/warp \
--keep-latest 3 \
--keep-hourly 0 \
--keep-daily 0 \
--keep-weekly 0 \
--keep-monthly 0 \
--keep-annual 0
通过 .kopiaignore 文件,排除掉日志、缓存等无用数据,比如:
# 忽略日志文件
*.log
logs/
# 忽略临时目录
tmp/
temp/
# 排除缓存
.cache/
检查具体备份项目的策略
sudo kopia policy get /home/dejavu/warp
Actions 是 Kopia 内置的一种 钩子(Hooks)机制。能在快照执行前后自动触发命令,但对我而言,这种「黑盒」机制调试起来不够直观,本文不使用该方式。
我选择编写一个简单的 Shell 脚本,配合 Crontab 定时执行。
# 创建日志目录
sudo mkdir -p /root/kopia/logs
# 创建备份脚本
sudo vim /root/kopia/backup-warp.sh
示例脚本如下:
#!/bin/bash
SRC_DIR="/home/dejavu/warp"
LOG_DIR="/root/kopia/logs"
CURRENT_DATE=$(date +%Y%m%d)
LOG_FILE="$LOG_DIR/${CURRENT_DATE}-warp.log"
# 必须修改 Kopia 存储库密码(使用单引号包裹)
export KOPIA_PASSWORD=''
# [可选] 禁用 Kopia 检查更新(适合国内机器)
# export KOPIA_CHECK_FOR_UPDATES=false
log() {
echo "[$(date '+%H:%M:%S')] $1" >> "$LOG_FILE"
}
# 回退
fallback_service() {
# 检查容器状态
if ! docker compose -f "$SRC_DIR/compose.yml" ps --services --filter "status=running" | grep -q "warp"; then
log "恢复容器启动..."
docker compose -f "$SRC_DIR/compose.yml" up -d >> "$LOG_FILE" 2>&1
fi
}
trap fallback_service EXIT
log "=== 备份开始 ==="
cd "$SRC_DIR" || { log "致命错误:找不到目录 $SRC_DIR"; exit 1; }
log "1. 停止容器..."
docker compose down >> "$LOG_FILE" 2>&1
if [ $? -ne 0 ]; then
log "❌ 容器停止失败,跳过备份以保数据安全。"
exit 1
fi
# 4.3 创建快照 (耗时操作)
log "开始创建快照..."
kopia snapshot create "$SRC_DIR" --description "Weekly Backup" >> "$LOG_FILE" 2>&1
SNAPSHOT_STATUS=$?
# 启动服务
log "3. 正在恢复服务..."
docker compose up -d >> "$LOG_FILE" 2>&1
if [ $SNAPSHOT_STATUS -eq 0 ]; then
log "✅ 快照创建成功"
else
log "❌ 快照创建失败!"
exit 1
fi
# 维护任务
log "执行存储库维护 (GC)..."
kopia maintenance run --full >> "$LOG_FILE" 2>&1
log "=== 备份结束 ==="
设置脚本权限
sudo chmod 700 /root/kopia/backup-warp.sh
手动执行一次
sudo /root/kopia/backup-warp.sh
检查首次运行生成的日志
cat /root/kopia/logs/20260201-warp.log
查看了 B2 存储桶,快照已同步,日志输出也符合预期:
[10:26:27] === 备份开始 ===
[10:26:27] 1. 停止容器...
Container warp Stopping
Container warp Stopped
Container warp Removing
Container warp Removed
Network warp-tunnel Removing
Network warp-tunnel Resource is still in use
[10:26:37] 开始创建快照...
Snapshotting root@vps-micro:/home/dejavu/warp ...
* 0 hashing, 40 hashed (23.4 MB), 0 cached (0 B), uploaded 193 B, estimated 23.4 MB (100.0%) 0s left
Created snapshot with root ka463aa955f638a00aed18d636e77e60c and ID 72c3275ef74463396ad6092d49c84ff0 in 1s
Running quick maintenance...
Compacting an eligible uncompacted epoch...
Advancing epoch markers...
Finished quick maintenance.
[10:26:46] 3. 正在恢复服务...
Container warp Creating
Container warp Created
Container warp Starting
Container warp Started
[10:26:47] ✅ 快照创建成功
[10:26:47] 执行存储库维护 (GC)...
Running full maintenance...
GC found 0 unused contents (0 B)
GC found 0 unused contents that are too recent to delete (0 B)
GC found 47 in-use contents (1.3 MB)
GC found 5 in-use system-contents (2.7 KB)
GC undeleted 0 contents (0 B)
Compacting an eligible uncompacted epoch...
Advancing epoch markers...
Attempting to compact a range of epoch indexes ...
Cleaning up unneeded epoch markers...
Cleaning up old index blobs which have already been compacted...
Cleaned up 0 logs.
Finished full maintenance.
[10:26:56] === 备份结束 ===
这是个可选配置,但感觉蛮实用的。我们使用 Apprise 推送通知,下面以 Telegram 为例。
使用 Docker Compose 启动一个 Apprise 服务
services:
apprise:
image: caronc/apprise:latest
container_name: apprise
restart: unless-stopped
user: "1000"
networks:
- apprise
environment:
APPRISE_STATEFUL_MODE: simple
APPRISE_WORKER_COUNT: "1"
volumes:
- ./config:/config
- ./plugin:/plugin
- ./attach:/attach
networks:
apprise:
name: apprise
driver: bridge
enable_ipv6: true
修改自动化脚本
#!/bin/bash
SRC_DIR="/home/dejavu/warp"
LOG_DIR="/root/kopia/logs"
CURRENT_DATE=$(date +%Y%m%d)
LOG_FILE="$LOG_DIR/${CURRENT_DATE}-warp.log"
# ================= 配置区域 =================
# Kopia 存储库密码
export KOPIA_PASSWORD=''
# [可选] 禁用 Kopia 检查更新
# export KOPIA_CHECK_FOR_UPDATES=false
# Apprise 通知配置
NOTIFICATION_URL="tgram://<botToken>/<group>/"
APPRISE_NET="apprise"
APPRISE_API="http://apprise:8000/notify"
# ===========================================
log() {
echo "[$(date '+%H:%M:%S')] $1" >> "$LOG_FILE"
}
# 发送通知函数
send_notify() {
local STATUS_ICON=$1
local STATUS_MSG=$2
local DETAIL_MSG=$3
local DATE_STR=$(date +%Y-%m-%d)
local TITLE="Kopia备份 - Warp"
local BODY="${STATUS_ICON} ${DATE_STR}-warp-${STATUS_MSG}\n\n${DETAIL_MSG}"
local JSON_PAYLOAD=$(cat <<EOF
{
"urls": "$NOTIFICATION_URL",
"title": "$TITLE",
"body": "$BODY"
}
EOF
)
log "正在发送通知..."
docker run --rm --network "$APPRISE_NET" curlimages/curl:8.18.0 \
-s -o /dev/null -X POST \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD" \
"$APPRISE_API"
}
# 回退函数
fallback_service() {
if ! docker compose -f "$SRC_DIR/compose.yml" ps --services --filter "status=running" | grep -q "warp"; then
log "恢复容器启动..."
docker compose -f "$SRC_DIR/compose.yml" up -d >> "$LOG_FILE" 2>&1
fi
}
trap fallback_service EXIT
log "=== 备份开始 ==="
cd "$SRC_DIR" || { log "致命错误:找不到目录 $SRC_DIR"; exit 1; }
log "1. 停止容器..."
docker compose down >> "$LOG_FILE" 2>&1
if [ $? -ne 0 ]; then
log "❌ 容器停止失败,跳过备份以保数据安全。"
send_notify "❌" "备份中断" "原因:容器无法正常停止,未执行备份。"
exit 1
fi
# 创建快照
log "开始创建快照..."
# 捕获输出以便在通知中显示部分信息
SNAPSHOT_OUTPUT=$(kopia snapshot create "$SRC_DIR" --description "Weekly Backup" 2>&1 | tee -a "$LOG_FILE")
SNAPSHOT_STATUS=${PIPESTATUS[0]}
# 启动服务
log "正在恢复服务..."
docker compose up -d >> "$LOG_FILE" 2>&1
# 获取单纯的日志文件名 (例如: 20260202-warp.log)
LOG_FILENAME=$(basename "$LOG_FILE")
if [ $SNAPSHOT_STATUS -eq 0 ]; then
log "✅ 快照创建成功"
# 提取快照 ID
SNAP_ID=$(grep "Created snapshot with root" "$LOG_FILE" | tail -n 1 | awk '{print $5}')
[ -z "$SNAP_ID" ] && SNAP_ID="ID提取异常"
# === 修改点:这里的日志位置变量改为了 $LOG_FILENAME ===
send_notify "✅" "备份成功!" "快照ID: $SNAP_ID\n服务已恢复运行。\n日志位置: $LOG_FILENAME"
else
log "❌ 快照创建失败!"
# 失败时也只显示文件名,保持整洁
send_notify "❌" "备份失败!" "Kopia 返回了错误代码。\n请立即检查服务器日志: $LOG_FILENAME"
exit 1
fi
# 维护任务
log "执行存储库维护 (GC)..."
kopia maintenance run --full >> "$LOG_FILE" 2>&1
log "=== 备份结束 ==="
示例效果:
Kopia备份 - Warp
✅ 2026-02-02-warp-备份成功!
快照ID: ke5e0d06612cb9850a1172cf0242983fb
服务已恢复运行。
日志位置: 20260202-warp.log
通过 Crontab 自动化执行任务
sudo crontab -e
末尾添加一项任务
# 由于我的服务器时区设置为 UTC 世界协调时
# 预期:北京时间每周一的 01:00 执行
0 17 * * 0 /bin/bash /root/kopia/backup-warp.sh
新建一个测试目录,验证一遍恢复流程。
mkdir -p /tmp/warp_test_restore
如果需要找回最近的数据,直接执行:
# 格式:snapshot restore <源路径> <恢复目标>
sudo kopia snapshot restore /home/dejavu/warp /tmp/warp_test_restore/latest
# 验证恢复
ls -lah /tmp/warp_test_restore/latest
回溯到特定时间点的历史快照版本:
# 查看快照历史
sudo kopia snapshot list /home/dejavu/warp
# 示例输出:
root@vps-micro:/home/dejavu/warp
2026-02-01 10:26:40 UTC ka463aa955f638a00aed18d636e77e60c 23.4 MB drwxrwxr-x files:40 dirs:6 (latest-1)
# 使用 ID 指定快照版本
sudo kopia snapshot restore ka463aa955f638a00aed18d636e77e60c /tmp/warp_test_restore/history
# 验证恢复
ls -lah /tmp/warp_test_restore/history
目前,看起来一切都工作的很好。
现在我们要考虑极端情况,若服务器数据被完全损坏,如何在新环境下进行灾难恢复呢?
在新机器上安装 Kopia
使用相同的 B2 配置连接现有的存储桶
sudo kopia repository connect b2 \
--bucket=<存储桶名称> \
--key-id=<keyID> \
--key=<applicationKey> \
--prefix=<目录前缀>/
新机器的主机名可能不同,需查看所有已存在快照
sudo kopia snapshot list --all
# 找到之前机器的<用户>@<主机名>:<备份目录>
root@vps-micro:/home/dejavu/warp
2026-02-01 10:26:40 UTC ka463aa955f638a00aed18d636e77e60c 23.4 MB drwxrwxr-x files:40 dirs:6 (latest-1)
恢复数据
# 用法:snapshot restore <用户>@<旧主机名>:<原路径> <目标路径>
sudo kopia snapshot restore ka463aa955f638a00aed18d636e77e60c root@vps-micro:/home/dejavu/warp /tmp/warp_restore_test
若想让新机器 彻底接管 旧机器的 增量备份历史,连接时需「伪装」身份。
# 断开并使用 override 参数重新连接
sudo kopia repository disconnect
sudo kopia repository connect b2 \
--bucket=<存储桶名称> \
--key-id=<keyID> \
--key=<applicationKey> \
--prefix=<目录前缀>/
--override-hostname=<原主机名> \
--override-username=<原用户名>
验证完成,删除测试路径
sudo rm -rf /tmp/warp_test_restore
至此,我们的自动化备份方案就实施完成了,后续添加新的自动备份任务,只需要:
# 细致化策略
sudo kopia policy set /home/dejavu/wakapi \
--keep-latest 3 \
--keep-hourly 0 \
--keep-daily 0 \
--keep-weekly 0 \
--keep-monthly 0 \
--keep-annual 0
# 验证策略
sudo kopia policy get /home/dejavu/wakapi
# 编辑自动化任务
sudo vim /root/kopia/backup-wakapi.sh
# 设置权限
sudo chmod 700 /root/kopia/backup-wakapi.sh
# 首次备份
sudo /bin/bash /root/kopia/backup-wakapi.sh
# 定时任务
sudo crontab -e
当然,你还可以考虑做这些优化:
印度孟买某公司开发的 Virtualizor 面板,让不少 VPS 服务商都栽了跟头。从去年的 ColorCrossing 到最近的 CloudCone,黑客利用漏洞入侵服务器并进行勒索,导致大量用户数据全灭。
面对此类「黑天鹅事件」,谁也没法独善其身,是时候完善一下我的数据自动化备份方案了。
几年前我一直使用 Duplicacy CLI,甚至还写过两篇又臭又长的教程。但它的开源许可证不够清晰,且已停更近一年,我开始寻找更好的替代品。我找到了:
最终的选择是 Kopia ,它支持增量快照、自带端到端 加密、且上手极其简单。
Kopia 支持几乎所有主流的存储服务和协议:
对象存储我选择了 Backblaze B2 ,按量计费计划 仅需 41.15 元/TB。A 类操作免费且每天赠送一定额度的 B/C 类操作次数,免费出口流量是 3 倍存储量。对于增量备份,成本几乎可以忽略不计了。

假设我每月存储 100GB,费用大约为 4.11 元/月左右,实际根本用不了这么多。
创建访问机密
在 B2 创建存储桶,在 Application Keys 里生成访问机密,记下 keyID 和 applicationKey 即可开始配置。
在 Debian GNU/Linux 上,最省心的安装方式是添加官方的 APT 仓库。
# 导入 GPG 密钥
curl -s https://kopia.io/signing-key | sudo gpg --dearmor -o /etc/apt/keyrings/kopia-keyring.gpg
# 添加软件源
echo "deb [signed-by=/etc/apt/keyrings/kopia-keyring.gpg] http://packages.kopia.io/apt/ stable main" | sudo tee /etc/apt/sources.list.d/kopia.list
# 安装
sudo apt update && sudo apt install kopia
# 验证安装
kopia --version
执行 repository create 命令初始化存储库:
sudo kopia repository create b2 \
--bucket=<存储桶名称> \
--key-id=<keyID> \
--key=<applicationKey> \
--prefix=<目录前缀>/
务必牢记设置的密码,一旦遗忘,备份数据将彻底锁定,没有找回或重置密码的可能。
Enter password to create new repository:
Re-enter password for verification:
Initializing repository with:
block hash: BLAKE2B-256-128
encryption: AES256-GCM-HMAC-SHA256
key derivation: scrypt-65536-8-1
splitter: DYNAMIC-4M-BUZHASH
Connected to repository.
NOTICE: Kopia will check for updates on GitHub every 7 days, starting 24 hours after first use.
To disable this behavior, set environment variable KOPIA_CHECK_FOR_UPDATES=false
Alternatively you can remove the file "/root/.config/kopia/repository.config.update-info.json".
Retention:
Annual snapshots: 3 (defined for this target)
Monthly snapshots: 24 (defined for this target)
Weekly snapshots: 4 (defined for this target)
Daily snapshots: 7 (defined for this target)
Hourly snapshots: 48 (defined for this target)
Latest snapshots: 10 (defined for this target)
Ignore identical snapshots: false (defined for this target)
Compression disabled.
To find more information about default policy run 'kopia policy get'.
To change the policy use 'kopia policy set' command.
NOTE: Kopia will perform quick maintenance of the repository automatically every 1h0m0s
and full maintenance every 24h0m0s when running as root@vps-micro.
See https://kopia.io/docs/advanced/maintenance/ for more information.
NOTE: To validate that your provider is compatible with Kopia, please run:
$ kopia repository validate-provider
查看连接状态
sudo kopia repository status
设置全局压缩算法,推荐 zstd 选项,平衡压缩率和速度
sudo kopia policy set --global --compression=zstd
配置全局保留策略,作为备份项目的回退配置
# 保留最新的 8 份快照
sudo kopia policy set --global --keep-latest 8
检查全局策略是否生效
sudo kopia policy list
以 /home/dejavu/warp 下的 Docker 容器为例,该容器使用了绑定挂载。
# 细致化单个备份项目保留策略
# 对于配置基本不动的容器,保留 3 份快照足矣
sudo kopia policy set /home/dejavu/warp \
--keep-latest 3 \
--keep-hourly 0 \
--keep-daily 0 \
--keep-weekly 0 \
--keep-monthly 0 \
--keep-annual 0
通过 .kopiaignore 文件,排除掉日志、缓存等无用数据,比如:
# 忽略日志文件
*.log
logs/
# 忽略临时目录
tmp/
temp/
# 排除缓存
.cache/
检查具体备份项目的策略
sudo kopia policy get /home/dejavu/warp
Actions 是 Kopia 内置的一种 钩子(Hooks)机制。能在快照执行前后自动触发命令,但对我而言,这种「黑盒」机制调试起来不够直观,本文不使用该方式。
我选择编写一个简单的 Shell 脚本,配合 Crontab 定时执行。
# 创建日志目录
sudo mkdir -p /root/kopia/logs
# 创建备份脚本
sudo vim /root/kopia/backup-warp.sh
示例脚本如下:
#!/bin/bash
SRC_DIR="/home/dejavu/warp"
LOG_DIR="/root/kopia/logs"
CURRENT_DATE=$(date +%Y%m%d)
LOG_FILE="$LOG_DIR/${CURRENT_DATE}-warp.log"
# 必须修改 Kopia 存储库密码(使用单引号包裹)
export KOPIA_PASSWORD=''
# [可选] 禁用 Kopia 检查更新(适合国内机器)
# export KOPIA_CHECK_FOR_UPDATES=false
log() {
echo "[$(date '+%H:%M:%S')] $1" >> "$LOG_FILE"
}
# 回退
fallback_service() {
# 检查容器状态
if ! docker compose -f "$SRC_DIR/compose.yml" ps --services --filter "status=running" | grep -q "warp"; then
log "恢复容器启动..."
docker compose -f "$SRC_DIR/compose.yml" up -d >> "$LOG_FILE" 2>&1
fi
}
trap fallback_service EXIT
log "=== 备份开始 ==="
cd "$SRC_DIR" || { log "致命错误:找不到目录 $SRC_DIR"; exit 1; }
log "1. 停止容器..."
docker compose down >> "$LOG_FILE" 2>&1
if [ $? -ne 0 ]; then
log "❌ 容器停止失败,跳过备份以保数据安全。"
exit 1
fi
# 4.3 创建快照 (耗时操作)
log "开始创建快照..."
kopia snapshot create "$SRC_DIR" --description "Weekly Backup" >> "$LOG_FILE" 2>&1
SNAPSHOT_STATUS=$?
# 启动服务
log "3. 正在恢复服务..."
docker compose up -d >> "$LOG_FILE" 2>&1
if [ $SNAPSHOT_STATUS -eq 0 ]; then
log "✅ 快照创建成功"
else
log "❌ 快照创建失败!"
exit 1
fi
# 维护任务
log "执行存储库维护 (GC)..."
kopia maintenance run --full >> "$LOG_FILE" 2>&1
log "=== 备份结束 ==="
设置脚本权限
sudo chmod 700 /root/kopia/backup-warp.sh
手动执行一次
sudo /root/kopia/backup-warp.sh
检查首次运行生成的日志
cat /root/kopia/logs/20260201-warp.log
查看了 B2 存储桶,快照已同步,日志输出也符合预期:
[10:26:27] === 备份开始 ===
[10:26:27] 1. 停止容器...
Container warp Stopping
Container warp Stopped
Container warp Removing
Container warp Removed
Network warp-tunnel Removing
Network warp-tunnel Resource is still in use
[10:26:37] 开始创建快照...
Snapshotting root@vps-micro:/home/dejavu/warp ...
* 0 hashing, 40 hashed (23.4 MB), 0 cached (0 B), uploaded 193 B, estimated 23.4 MB (100.0%) 0s left
Created snapshot with root ka463aa955f638a00aed18d636e77e60c and ID 72c3275ef74463396ad6092d49c84ff0 in 1s
Running quick maintenance...
Compacting an eligible uncompacted epoch...
Advancing epoch markers...
Finished quick maintenance.
[10:26:46] 3. 正在恢复服务...
Container warp Creating
Container warp Created
Container warp Starting
Container warp Started
[10:26:47] ✅ 快照创建成功
[10:26:47] 执行存储库维护 (GC)...
Running full maintenance...
GC found 0 unused contents (0 B)
GC found 0 unused contents that are too recent to delete (0 B)
GC found 47 in-use contents (1.3 MB)
GC found 5 in-use system-contents (2.7 KB)
GC undeleted 0 contents (0 B)
Compacting an eligible uncompacted epoch...
Advancing epoch markers...
Attempting to compact a range of epoch indexes ...
Cleaning up unneeded epoch markers...
Cleaning up old index blobs which have already been compacted...
Cleaned up 0 logs.
Finished full maintenance.
[10:26:56] === 备份结束 ===
这是个可选配置,但感觉蛮实用的。我们使用 Apprise 推送通知,下面以 Telegram 为例。
使用 Docker Compose 启动一个 Apprise 服务
services:
apprise:
image: caronc/apprise:latest
container_name: apprise
restart: unless-stopped
user: "1000"
networks:
- apprise
environment:
APPRISE_STATEFUL_MODE: simple
APPRISE_WORKER_COUNT: "1"
volumes:
- ./config:/config
- ./plugin:/plugin
- ./attach:/attach
networks:
apprise:
name: apprise
driver: bridge
enable_ipv6: true
修改自动化脚本
#!/bin/bash
SRC_DIR="/home/dejavu/warp"
LOG_DIR="/root/kopia/logs"
CURRENT_DATE=$(date +%Y%m%d)
LOG_FILE="$LOG_DIR/${CURRENT_DATE}-warp.log"
# ================= 配置区域 =================
# Kopia 存储库密码
export KOPIA_PASSWORD=''
# [可选] 禁用 Kopia 检查更新
# export KOPIA_CHECK_FOR_UPDATES=false
# Apprise 通知配置
NOTIFICATION_URL="tgram://<botToken>/<group>/"
APPRISE_NET="apprise"
APPRISE_API="http://apprise:8000/notify"
# ===========================================
log() {
echo "[$(date '+%H:%M:%S')] $1" >> "$LOG_FILE"
}
# 发送通知函数
send_notify() {
local STATUS_ICON=$1
local STATUS_MSG=$2
local DETAIL_MSG=$3
local DATE_STR=$(date +%Y-%m-%d)
local TITLE="Kopia备份 - Warp"
local BODY="${STATUS_ICON} ${DATE_STR}-warp-${STATUS_MSG}\n\n${DETAIL_MSG}"
local JSON_PAYLOAD=$(cat <<EOF
{
"urls": "$NOTIFICATION_URL",
"title": "$TITLE",
"body": "$BODY"
}
EOF
)
log "正在发送通知..."
docker run --rm --network "$APPRISE_NET" curlimages/curl:8.18.0 \
-s -o /dev/null -X POST \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD" \
"$APPRISE_API"
}
# 回退函数
fallback_service() {
if ! docker compose -f "$SRC_DIR/compose.yml" ps --services --filter "status=running" | grep -q "warp"; then
log "恢复容器启动..."
docker compose -f "$SRC_DIR/compose.yml" up -d >> "$LOG_FILE" 2>&1
fi
}
trap fallback_service EXIT
log "=== 备份开始 ==="
cd "$SRC_DIR" || { log "致命错误:找不到目录 $SRC_DIR"; exit 1; }
log "1. 停止容器..."
docker compose down >> "$LOG_FILE" 2>&1
if [ $? -ne 0 ]; then
log "❌ 容器停止失败,跳过备份以保数据安全。"
send_notify "❌" "备份中断" "原因:容器无法正常停止,未执行备份。"
exit 1
fi
# 创建快照
log "开始创建快照..."
# 捕获输出以便在通知中显示部分信息
SNAPSHOT_OUTPUT=$(kopia snapshot create "$SRC_DIR" --description "Weekly Backup" 2>&1 | tee -a "$LOG_FILE")
SNAPSHOT_STATUS=${PIPESTATUS[0]}
# 启动服务
log "正在恢复服务..."
docker compose up -d >> "$LOG_FILE" 2>&1
# 获取单纯的日志文件名 (例如: 20260202-warp.log)
LOG_FILENAME=$(basename "$LOG_FILE")
if [ $SNAPSHOT_STATUS -eq 0 ]; then
log "✅ 快照创建成功"
# 提取快照 ID
SNAP_ID=$(grep "Created snapshot with root" "$LOG_FILE" | tail -n 1 | awk '{print $5}')
[ -z "$SNAP_ID" ] && SNAP_ID="ID提取异常"
# === 修改点:这里的日志位置变量改为了 $LOG_FILENAME ===
send_notify "✅" "备份成功!" "快照ID: $SNAP_ID\n服务已恢复运行。\n日志位置: $LOG_FILENAME"
else
log "❌ 快照创建失败!"
# 失败时也只显示文件名,保持整洁
send_notify "❌" "备份失败!" "Kopia 返回了错误代码。\n请立即检查服务器日志: $LOG_FILENAME"
exit 1
fi
# 维护任务
log "执行存储库维护 (GC)..."
kopia maintenance run --full >> "$LOG_FILE" 2>&1
log "=== 备份结束 ==="
示例效果:
Kopia备份 - Warp
✅ 2026-02-02-warp-备份成功!
快照ID: ke5e0d06612cb9850a1172cf0242983fb
服务已恢复运行。
日志位置: 20260202-warp.log
通过 Crontab 自动化执行任务
sudo crontab -e
末尾添加一项任务
# 由于我的服务器时区设置为 UTC 世界协调时
# 预期:北京时间每周一的 01:00 执行
0 17 * * 0 /bin/bash /root/kopia/backup-warp.sh
新建一个测试目录,验证一遍恢复流程。
mkdir -p /tmp/warp_test_restore
如果需要找回最近的数据,直接执行:
# 格式:snapshot restore <源路径> <恢复目标>
sudo kopia snapshot restore /home/dejavu/warp /tmp/warp_test_restore/latest
# 验证恢复
ls -lah /tmp/warp_test_restore/latest
回溯到特定时间点的历史快照版本:
# 查看快照历史
sudo kopia snapshot list /home/dejavu/warp
# 示例输出:
root@vps-micro:/home/dejavu/warp
2026-02-01 10:26:40 UTC ka463aa955f638a00aed18d636e77e60c 23.4 MB drwxrwxr-x files:40 dirs:6 (latest-1)
# 使用 ID 指定快照版本
sudo kopia snapshot restore ka463aa955f638a00aed18d636e77e60c /tmp/warp_test_restore/history
# 验证恢复
ls -lah /tmp/warp_test_restore/history
目前,看起来一切都工作的很好。
现在我们要考虑极端情况,若服务器数据被完全损坏,如何在新环境下进行灾难恢复呢?
在新机器上安装 Kopia
使用相同的 B2 配置连接现有的存储桶
sudo kopia repository connect b2 \
--bucket=<存储桶名称> \
--key-id=<keyID> \
--key=<applicationKey> \
--prefix=<目录前缀>/
新机器的主机名可能不同,需查看所有已存在快照
sudo kopia snapshot list --all
# 找到之前机器的<用户>@<主机名>:<备份目录>
root@vps-micro:/home/dejavu/warp
2026-02-01 10:26:40 UTC ka463aa955f638a00aed18d636e77e60c 23.4 MB drwxrwxr-x files:40 dirs:6 (latest-1)
恢复数据
# 用法:snapshot restore <用户>@<旧主机名>:<原路径> <目标路径>
sudo kopia snapshot restore ka463aa955f638a00aed18d636e77e60c root@vps-micro:/home/dejavu/warp /tmp/warp_restore_test
若想让新机器 彻底接管 旧机器的 增量备份历史,连接时需「伪装」身份。
# 断开并使用 override 参数重新连接
sudo kopia repository disconnect
sudo kopia repository connect b2 \
--bucket=<存储桶名称> \
--key-id=<keyID> \
--key=<applicationKey> \
--prefix=<目录前缀>/
--override-hostname=<原主机名> \
--override-username=<原用户名>
验证完成,删除测试路径
sudo rm -rf /tmp/warp_test_restore
至此,我们的自动化备份方案就实施完成了,后续添加新的自动备份任务,只需要:
# 细致化策略
sudo kopia policy set /home/dejavu/wakapi \
--keep-latest 3 \
--keep-hourly 0 \
--keep-daily 0 \
--keep-weekly 0 \
--keep-monthly 0 \
--keep-annual 0
# 验证策略
sudo kopia policy get /home/dejavu/wakapi
# 编辑自动化任务
sudo vim /root/kopia/backup-wakapi.sh
# 设置权限
sudo chmod 700 /root/kopia/backup-wakapi.sh
# 首次备份
sudo /bin/bash /root/kopia/backup-wakapi.sh
# 定时任务
sudo crontab -e
当然,你还可以考虑做这些优化: