个人内网备份方案
点击展开更新日志
2026
2026-02-13
【创建】本文
nexttime
会有些什么呢(❁´◡`❁)
¶源起
这篇应该就是2025年最后一篇文章了,也或许有机会入选年度好文😏(当然是自评)。经过差不多一年多时间的折腾,内网环境基本已经稳定,各方面使用也差不多可以满足需求了,于是后面开始折腾备份,中间试过几种备份方案,不过都不甚满意,最近借助GPT的帮助,重新理了一遍,自我感觉还不错,供友友们参考。
¶内网架构
先来介绍下我的内网环境:

-
物理及虚机服务器:
- 软路由n7505:
- PVE底层:Debian13
- iKuai主路由:主要做限速
- ImmortalWrt:透明代理
- DockerHost:计算服务器,承载多个Docker服务
- PVE底层:Debian13
- 交换机:
- PC主机(代号:Icarus):主力PC,Win11,同时有一块10T机械开了SMB做备份服务器(BackupServer)
- 迷你主机(代号:Delta):7940HS,Win11,目前做模拟器、日常备机
- MacminiM4(代号:Beta):最低配,计算服务器,承载多个Docker服务
- NAS:威联通C462C2,仅做存储。
- 软路由n7505:
¶备份内容&要求
我把我的所有数据分成两部分:
- 绝对不可丢失数据:不计成本多副本多介质存储,比如照片、工作资料、个人知识库、密码本等
- 可丢失数据:成本受限仅保留本地,允许丢失,可以重复获取/丢失影响可控,比如涩图和片
备份目标如下,目前仍存在差距:
- 所有不可丢失数据存在可恢复副本,本地+NAS+加密上传云盘保存;
- 定期备份校验数据可用性;
- 以上。
¶PC主机
D盘(10T)作为备份盘,除有一个Temp目录作为日常使用存放下载的临时大文件,其他目录作为C盘及其他client的备份目录不做手动更改,目录结构:
1 | D:\ |
备份内容
- C盘整机备份
- Documents等独立文件数据
备份工具
- Macrium/Acronis:每月一次仅保留最近1-3个备份(每天备份磁盘读写太大,做了一年放弃了)
- GoodSync:手动设置排除
¶PVE
备份内容
- pve配置
- 虚拟机(除DockerHost)
备份工具
- restic
- smbclient
¶(可选)前置操作
¶青龙面板
为例方便定时任务管理,这里是使用青龙面板做定时任务管理,ssh到客户端执行脚本。
¶配置SSH登录
为了让青龙能执行远程主机上的脚本,需要先在宿主机配置SSH免密登录,然后将密钥挂载到容器。
-
创建密钥对:
1
ssh-keygen # 假设文件名为 id_ed25519
-
拷贝公钥到服务器:
1
ssh-copy-id -i ~/.ssh/id_ed25519 root@<IP>
¶部署
-
创建数据目录:
1
mkdir /data/docker/qinglong
-
拷贝密钥对:
1
2
3mkdir /data/docker/qinglong/ssh
cp ~/.ssh/id_ed25519 /data/docker/qinglong/ssh
cp ~/.ssh/known_hosts /data/docker/qinglong/ssh -
准备compose文件:
1
2
3
4
5
6
7
8
9
10
11
12services:
qinglong:
image: whyour/qinglong:latest # 基于 Debian 的版本:whyour/qinglong:debian
container_name: Qinglong
volumes:
- ./data:/ql/data
- ./ssh:/root/.ssh
ports:
- "5700:5700"
environment:
QlBaseUrl: '/' # 部署路径非必须,以斜杠开头和结尾,比如 /test/
restart: unless-stopped😶🌫️:
-
挂载
.ssh是为了 ssh 免密登录到客户端执行备份脚本 -
这里之所以没有加
traefik标签,是因为是部署在另一台机器上的。。。
-
-
启动部署:
1
2
3docker compose up -d
# 手动进入容器执行免密登录做一次保存,因为第一次连接会做Host Key Verification(主机指纹校验),后续写入到 known_hosts 就不会再出现了。 -
访问地址:
http://localhost:5700
¶备份配置
我的方案是本地备份+中心备份的方式,客户端使用 restic 备份,通过 SMB/SFTP 的方式同步到中心服务器。
¶BackupServer
作为备份管理中枢,配置如下:
-
创建备份目录:
1
2
3
4D:\BackupServer\repos # restic仓库
# 可选,暂时没用到
D:\BackupServer\config # 配置
D:\BackupServer\staging # 其他文件 -
安装工具:
- restic · Backups done right! :Windows下的安装方式很简单,下载解压放到舒服的位置,修改程序名为
restic.exe(去掉版本号),添加PATH环境变量。 - Rclone :同上
- restic · Backups done right! :Windows下的安装方式很简单,下载解压放到舒服的位置,修改程序名为
-
创建SMB用户:
- 从【设置】-【账户】-【其他用户】添加一个本地账户,选择“没有这个用户的微软账户信息”,然后禁用登录,之后也就不会在登录界面显示影响美观;
- 设置
BackupServer目录共享给SMB用户,完全控制。
¶PVE
PVE上需要将备份服务器的目录挂载到本地,然后挂给vm,默认情况下vm是不能直接使用SMB挂载的。
-
安装工具:
1
# 忘了有没有装过,如果报错就装一下,后面再补
-
创建挂载目录:
1
mkdir -p /mnt/backupserver
-
创建登录凭据:
1
2
3
4
5
6
7vim /root/smb_credential
# 写入刚才创建的SMB用户信息
# /root/smb_credential
username=smbShare
password=smbShare -
挂载SMB目录:
1
2
3
4
5
6mount -t cifs //192.168.1.7/BackupServer /mnt/backupserver -o credentials=/root/smb_credential,vers=3.1.1,sec=ntlmssp,iocharset=utf8,serverino,nosharesock,soft,_netdev,nounix,file_mode=0644,dir_mode=0777,rsize=1048576,wsize=1048576,cache=none
# /etc/fstab
//192.168.1.7/BackupServer /mnt/backupserver cifs \
credentials=/root/smb_credentials,vers=3.1.1,iocharset=utf8,sec=ntlmssp,serverino,nosharesock,soft,uid=1000,gid=1000,file_mode=0664,dir_mode=0775,nounix,_netdev 0 0这里可能会遇到的一个问题,
dmesg提示报错STATUS_ACCOUNT_LOCKED_OUT,这是因为SMB/CIFS 在认证失败多次后,Windows 会触发 Account Lockout Policy,之后任何正确密码都会被拒绝,返回这个错误。Win + R → lusrmgr.msc
Users → 找到你的账户
右键 → Properties → 取消勾选 Account is locked out -
创建备份目录:
1
2
3
4# restic 仓库
mkdir /mnt/backupserver/repos/pve
# 临时备份目录
mkdir /data/backup/config{pve/network} -
初始化备份仓库:
1
2
3
4
5
6
7
8
9vim /root/restic.env
# /root/restic.env & /root/.bash_profile
export RESTIC_REPOSITORY="/mnt/backupserver/repos/pve"
export RESTIC_PASSWORD="<restic_password>"
# 初始化仓库
source /root/.bash_profile
restic init -
创建虚拟机备份:
到PVE web界面,服务器视图,选择【数据中心】-【存储】,添加备份目录:- 类型:目录
- ID:backup
- 目录:
/data/backup - 内容:只选择备份
再添加备份计划,保存位置选择
backup,建议每周/每月备份一次即可,比如我的DockerHost虚拟机特别大(300G),每次备份时间很长,因此每月备份一次,然后对上面的Docker数据每周单独备份。 -
编写备份脚本:
1
vim /root/restic_backup_pve.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27!/usr/bin/env bash
set -e
BACKUP_DIR="/data/backup"
读取restic环境变量
source /root/restic.env
echo "===== PVE restic backup start $(date) ====="
1. 防止并发执行
exec 9>/tmp/restic-pve.lock
flock -n 9 || { echo "Another restic job running"; exit 1; }
2. 清理僵尸锁(非常重要)
restic unlock || true
3. 备份 PVE 配置(先更新一份)
rsync -aL /etc/pve/ ${BACKUP_DIR}/config/pve2/
cp /etc/network/interfaces ${BACKUP_DIR}/config/network/
4. 执行备份(只备份PVE自己的目录)
restic backup ${BACKUP_DIR}
5. 保留策略:仅保留最近3个
restic forget --keep-last 3 --prune
echo "===== PVE restic backup finished $(date) ====="添加执行权限并手动执行一次看看是否报错。
1
chmod +x restic_backup_pve.sh
-
配置定时任务:
并没有直接使用crontab的方式,并不是不好,只是最近装了青龙面板,干脆想着定时任务都放一起方便管理。如果不想用,直接用crontab添加定时任务即可:1
2
3
4crontab -e
# 每周五晚上19:00执行备份,主要是想着出问题了有时间排障
00 19 * * 5 /root/restic_backup_pve.sh如果使用青龙,创建定时任务:
- 名称:PVE 定时备份
- 命令:
ssh -i /root/id_ed25519 root@<PVE_IP> '/root/restic_backup_pve.sh' - 定时类型:常规定时
- 定时规则:
00 19 * * 5
¶DockerHost
这个是创建的LXC虚拟机,系统用的 Debian12 :
-
挂载备份目录:
-
在PVE上创建单独的备份目录:
1
mkdir /mnt/backupserver/repos/dockerHost
-
挂载给容器:
1
2
3
4vim /etc/pve/lxc/<vmid>.conf
# 添加以下配置
mp2: /mnt/backupserver/repos/dockerHost,mp=/mnt/backup -
重启容器:
1
2pct stop <vmid>
pct start <vmid>
-
-
初始化仓库:
1
2
3
4
5
6
7
8
9
10
11apt install restic
vim /root/restic.env
# /root/restic.env & /root/.bash_profile
export RESTIC_REPOSITORY="/mnt/backup"
export RESTIC_PASSWORD="<restic_password>"
# 初始化仓库
source /root/.bash_profile
restic init -
创建备份脚本:
⚠Warnging:
使用此脚本,假设:- docker 数据目录:
/data/docker - 脚本都已经给了执行权限
1
2
3
4
5
6# 文件结构
/
├── backup_volumes.sh
├── restic.env
├── rsync_exclude_patterns.txt
└── restic_backup_dockerHost.sh1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30!/bin/bash
restic_backup_dockerHost.sh
加载环境变量
set -e
source /root/restic.env
restic unlock || true
compose 文件源
COMPOSE_SOURCE="/data/docker"
BACKUP_DIR="/data/backup/containers_data/docker_compose"
日志
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
docker compose backup
mkdir -p "${BACKUP_DIR}"
rsync -av --delete --exclude-from='/root/rsync_exclude_patterns.txt' "${COMPOSE_SOURCE}/" "${BACKUP_DIR}/"
volumes backup
bash /root/backup_volumes.sh
执行备份
restic backup /data/backup
执行保留策略(保留最近 7 天、4 周、6 个月的备份,清理不需要的数据)
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147!/usr/bin/env bash
backup_volumes.sh
set -Eeuo pipefail
#######################################
配置区
#######################################
是否 Dry Run(true=只演练不执行删除/停止/启动)
DRY_RUN=false
备份根目录
BACKUP_DIR_ROOT="/data/backup/containers_data/volumes"
保留天数(可外部覆盖:RETENTION_DAYS=30 ./backup_volumes.sh)
RETENTION_DAYS=${RETENTION_DAYS:-7}
日期后缀
DATE_SUFFIX=$(date +%Y%m%d)
BACKUP_DIR="${BACKUP_DIR_ROOT}/${DATE_SUFFIX}"
排除卷(支持通配符)
BLACKLIST=(
"homepage_logs"
"immich_model_cache"
)
#######################################
工具函数
#######################################
log() {
echo "[$(date '+%F %T')] $*"
}
run_cmd() {
if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] $*"
else
eval "$@"
fi
}
is_blacklisted() {
local vol="$1"
for pattern in "${BLACKLIST[@]}"; do
[[ "$vol" == $pattern ]] && return 0
done
return 1
}
#######################################
创建备份目录
#######################################
mkdir -p "$BACKUP_DIR"
log "备份目录: $BACKUP_DIR"
#######################################
获取所有命名卷(排除匿名卷)
#######################################
mapfile -t VOLUMES < <(docker volume ls -q | grep -vE '^[0-9a-f]{64}$')
#######################################
找出使用卷的运行中容器
#######################################
log "扫描使用 volumes 的容器..."
RUNNING_CONTAINERS=$(docker ps -q)
CONTAINERS_TO_STOP=()
for CID in $RUNNING_CONTAINERS; do
for VOL in "${VOLUMES[@]}"; do
if docker inspect "$CID" | grep -q "\"Name\": \"${VOL}\""; then
CONTAINERS_TO_STOP+=("$CID")
break
fi
done
done
#######################################
停止容器(保证数据一致性)
#######################################
if [ ${#CONTAINERS_TO_STOP[@]} -gt 0 ]; then
log "需要暂停的容器: ${CONTAINERS_TO_STOP[*]}"
run_cmd "docker stop ${CONTAINERS_TO_STOP[*]}"
else
log "没有需要暂停的容器"
fi
#######################################
开始备份 volumes
#######################################
log "开始备份 Docker Volumes..."
for VOL in "${VOLUMES[@]}"; do
if is_blacklisted "$VOL"; then
log "[跳过] $VOL 在黑名单中"
continue
fi
backup_file="${VOL}_${DATE_SUFFIX}.tar.gz"
log "备份 volume: $VOL"
if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] docker run backup $VOL"
continue
fi
docker run --rm --pull=never --log-driver=none \
-v "$VOL":/source:ro \
-v "$BACKUP_DIR":/dest \
alpine \
tar -zcf "/dest/${backup_file}" -C /source .
if [ -f "$BACKUP_DIR/$backup_file" ]; then
SIZE=$(du -h "$BACKUP_DIR/$backup_file" | cut -f1)
log "[成功] $backup_file ($SIZE)"
else
log "[失败] $VOL 备份失败!"
fi
done
#######################################
恢复容器运行
#######################################
if [ ${#CONTAINERS_TO_STOP[@]} -gt 0 ]; then
log "恢复容器运行..."
run_cmd "docker start ${CONTAINERS_TO_STOP[*]}"
fi
#######################################
清理旧备份
#######################################
log "清理 $RETENTION_DAYS 天前的备份..."
if [ "$DRY_RUN" = true ]; then
find "$BACKUP_DIR_ROOT" -maxdepth 1 -type d -mtime +"$RETENTION_DAYS" \
-name "[0-9]*" -print
else
find "$BACKUP_DIR_ROOT" -maxdepth 1 -type d -mtime +"$RETENTION_DAYS" \
-name "[0-9]*" -exec rm -rf {} \;
fi
log "备份流程完成 ✅"- 使用前设置
DRY_RUN=true试一下看看效果,确认没问题了再改成false使用。主要功能:- 备份使用到的docker volume 卷,支持黑名单
- 备份前暂停容器,自动跳过已停止容器,备份完成后恢复
- 自动清理历史备份,默认7天
- docker 数据目录:
-
配置定时任务
如果使用crontab添加任务即可。
如果使用青龙,参照上方 PVE 配置即可。
¶其他
¶restic 更新
¶rclone 更新
1 | rclone selfupdate |

