跳到主要内容

Kopia 部署教程:网站文件快照、加密仓库与恢复

用 Docker 部署 Kopia,区分来源目录和备份仓库,创建快照并恢复文件。

为网站文件保留多个时间点

Kopia 可以为目录创建加密、去重快照。附件、配置和数据库导出纳入来源目录,误删或改错文件后可以选择旧快照恢复。本篇先用本地仓库演示,再说明切换远端存储的步骤。

建立配置和数据目录

VPS 需要 Docker 与 Compose。先建立配置、缓存、日志、演示仓库和恢复目录:

sh;使用前请核对本文前提。
mkdir -p ~/kopia/{config,cache,logs,repository,restore}
cd ~/kopia

准备已有来源目录 /srv/example-files。创建 .env,分别填写 KOPIA_PASSWORD 和 KOPIA_UI_PASSWORD,可用 openssl rand -hex 32 生成独立密码。仓库密码负责解密备份,Web 密码负责登录界面。

启动私人管理界面

保存下面的 compose.yaml。来源目录映射为 /data,只允许读取;恢复目录是 /restore。

示例固定为 0.23.1。已有仓库先记录备份时的版本与镜像摘要,并用同版本恢复;升级单独安排,不为了照抄示例给现有仓库降级。

yaml;使用前请核对本文前提。
services:
  kopia:
    image: kopia/kopia:0.23.1
    hostname: vps-backup
    ports:
      - "127.0.0.1:51515:51515"
    environment:
      KOPIA_PASSWORD: ${KOPIA_PASSWORD:?set repository password}
      USER: backup
    command:
      - server
      - start
      - --insecure
      - --address=0.0.0.0:51515
      - --server-username=operator
      - --server-password=${KOPIA_UI_PASSWORD:?set web password}
    volumes:
      - ./config:/app/config
      - ./cache:/app/cache
      - ./logs:/app/logs
      - /srv/example-files:/data:ro
      - ./repository:/repository
      - ./restore:/restore
    restart: unless-stopped

--insecure 在这里表示容器内使用 HTTP;宿主机只映射 127.0.0.1,远程通过 SSH 隧道访问。固定 hostname 可避免默认随机主机名,来源用户名、主机名和路径仍要以 snapshot list 的实际输出为准。下方恢复命令会显式沿用这组身份,不把 USER 环境变量单独当作身份证明。

sh;使用前请核对本文前提。
chmod 600 .env
docker compose config --quiet
docker compose up -d
docker compose logs --tail=100 kopia
ssh -L 15151:127.0.0.1:51515 user@your-server

本机打开 http://127.0.0.1:15151,用 operator 和 Web 密码登录。

创建仓库并做第一份快照

在界面新建文件系统仓库,路径填写 /repository。来源选择 /data,先备份少量文本与图片;修改、删除其中一份后创建第二次快照,再查看第一个时间点。

本地仓库和来源同处一台 VPS,适合演示。正式备份换到独立存储,可选择官方支持的对象存储、SFTP 等后端。仓库密码、连接参数与存储凭据另存。

恢复文件与数据库导出

选取快照,将文件恢复到 /restore 下的独立目录,打开配置和图片,检查内容与权限。数据库先生成一致的导出,再随文件快照备份;恢复时导入独立数据库。

应用数据库与附件需要对应同一恢复时点时,在短维护窗口暂停写入并生成配套备份。挂载目录变化后,检查 Compose 是否仍覆盖所需来源。

本地配置丢失时,先找回完整仓库和原仓库口令。重新设置一个 Web 登录密码,不能解密旧备份。下面用全新的配置、缓存和恢复目录连接仓库副本,原实例和原仓库都保留。

下面在 Bash 中演练 0.23.1 文件系统仓库的恢复;这部分使用一次性 CLI 容器,先停止原 GUI 实例的写入和所有共用仓库的计划任务,并核对备份时的版本。

先取得停止所有仓库写入后的完整副本;尚未备份时按第6节保存。把下方 /path/to/kopia-cold-backup.tar 换成自己生成并核对过的归档,先确认其中的 repository 成员完整,再只解出仓库到新的空工作目录。执行账号须能读取仓库,并能写入新配置及恢复目录;复制或解包报错就先停下处理,不能继续。已有同名工作目录时换一个新名称,避免覆盖上次恢复结果。

bash;使用前请核对本文前提。
set -e
umask 077
mkdir -m 700 "$HOME/kopia-recovery"
cd "$HOME/kopia-recovery"
mkdir -m 700 config cache logs restore source
tar -tf /path/to/kopia-cold-backup.tar
tar -xpf /path/to/kopia-cold-backup.tar repository
test -f repository/kopia.repository

read -r -s -p '输入原仓库口令:' repository_password
printf '\n'
printf '%s\n' "$repository_password" > repository-password
unset repository_password
chmod 600 repository-password

下方函数只运行一次性 CLI 容器,不启动管理界面或计划任务。口令文件只读挂载,由容器内进程读取;新 config、cache、logs 不使用原客户端的目录。显式固定来源身份为 backup@vps-backup:/data,便于恢复后继续整理同一来源的历史。若原实例使用其他身份,按自己的记录替换。

bash;使用前请核对本文前提。
kopia_recovery() {
  docker run --rm --network none \
    --user "$(id -u):$(id -g)" --hostname vps-backup \
    --env HOME=/tmp --env USER=backup \
    --mount "type=bind,src=$PWD/config,dst=/app/config" \
    --mount "type=bind,src=$PWD/cache,dst=/app/cache" \
    --mount "type=bind,src=$PWD/logs,dst=/app/logs" \
    --mount "type=bind,src=$PWD/repository,dst=/repository" \
    --mount "type=bind,src=$PWD/source,dst=/data,readonly" \
    --mount "type=bind,src=$PWD/restore,dst=/restore" \
    --mount "type=bind,src=$PWD/repository-password,dst=/run/kopia-secret,readonly" \
    --entrypoint /bin/sh kopia/kopia:0.23.1 \
    -c 'IFS= read -r KOPIA_PASSWORD < /run/kopia-secret; export KOPIA_PASSWORD; exec /bin/kopia "$@"' sh "$@"
}

kopia_recovery repository connect filesystem --path /repository \
  --override-hostname vps-backup --override-username backup \
  --no-check-for-updates
kopia_recovery snapshot list --all --json --no-retention

连接已有仓库使用 repository connect,不是 repository create。列表中 id 是快照清单 ID,rootEntry.obj 是该快照的根目录对象 ID。先按 source、startTime、endTime 找到要恢复的时间点,再把对应的两个 ID 分别填入下面变量。restore 使用根目录对象 ID,verify 使用快照清单 ID。

bash;使用前请核对本文前提。
read -r -p '所选快照的 id:' snapshot_id
read -r -p '同一快照的 rootEntry.obj:' root_id

kopia_recovery snapshot restore "$root_id" /restore/old-version \
  --parallel 1 --no-ignore-permission-errors
kopia_recovery snapshot verify "$snapshot_id" \
  --verify-files-percent 100 --parallel 1 --file-parallelism 1

find restore/old-version -type f
sha256sum restore/old-version/example.txt

example.txt 换成该时间点确实存在的文件,并与备份前保存的校验和比较。恢复另一个时间点时,重新选取其 ID,使用 /restore/another-version 这类不同的空目标目录,检查修改、删除和新增文件是否符合那个时间点。不要把恢复目标指向正在运行的网站目录。

verify 默认不选择全部文件内容做读取验证,上面的 100 会选择这份小快照的全部文件,过程中可能复用缓存。大仓库还要安排足够的读取时间和存储流量预算。命令成功仍需核对实际恢复出的文件清单、哈希与权限;数据库导出要导入独立数据库,核对记录和附件对应关系,不能只看导出文件的哈希。

确认旧文件恢复正确后,可以在演练副本上验证下一份快照。下面把恢复内容复制到本工作目录的 source,再新增一个检查文件;不会改原网站目录。

bash;使用前请核对本文前提。
cp -a restore/old-version/. source/
printf 'recovery check\n' > source/recovery-check.txt
kopia_recovery snapshot create /data --parallel 1
kopia_recovery snapshot list --all --json --no-retention

核对新清单的来源身份和创建时间,确认原来的快照仍在,再按新 rootEntry.obj 恢复到第三个空目录,检查新文件。正式接回备份计划前,重新核对实际来源挂载、保留策略和仓库目标;不要启用演练副本中的旧计划。

一次本地 ARM64、Kopia 0.23.1 的小样本实验,已用空配置和空缓存重连仓库副本,恢复两个旧时间点的全部文件,并继续建立、恢复第三个快照。三个时间点的文件校验和、权限和属主清单均匹配,两个旧版本的合成 SQLite 导出也分别导入独立新库并核对了记录。实验没有验证网页界面、远端存储、跨版本恢复或大仓库容量,不能据此给出生产最低配置。

安排自动快照和维护

根据文件变化频率设置快照周期和保留策略,检查下一次自动快照是否完成。缓存、配置、仓库分别维护;删除历史快照后,由 Kopia 的仓库维护处理不再使用的数据。

切换远端仓库后,确认策略实际写入新目标,再恢复一份文件。升级前记录镜像版本与仓库配置,保存密码和配置目录,更新后检查快照、维护和恢复。

本例 repository 是文件系统仓库。复制它之前,除了停止本机 Web 服务,还要停下所有向同一仓库写入的客户端和计划任务,等正在运行的快照、维护结束。只停界面不等于所有写入已经停止。

bash;使用前请核对本文前提。
set -e
cd "$HOME/kopia"
umask 077
docker compose stop kopia
mkdir -p "$HOME/kopia-cold-backups"
chmod 700 "$HOME/kopia-cold-backups"
tar -cpf "$HOME/kopia-cold-backups/kopia-$(date +%Y%m%d-%H%M%S).tar" \
  compose.yaml .env config cache logs repository

执行账号须能读取这些目录并保留所需属主、权限。tar 按链接本身保存日志软链接,不加 --dereference 跟随它们;归档失败或漏文件时不能把它当成完整备份。口令、配置和仓库归档都应限制访问,并保存到这台机器之外。完成备份检查后,可恢复原服务;恢复演练使用独立副本。

恢复盘要容纳所选快照解密后的文件,仓库副本和缓存还要另留空间,不能只按压缩后的仓库大小准备磁盘。本机演练仓库与来源处在同一台机器,不提供真正的异地防灾;选用独立存储时,再核对容量、读取费用和自己的恢复窗口。