Today

Restic: резервное копирование на Mac по схеме 3-2-1

У меня есть макбук, на котором я пишу программы, тексты и книги, работаю с документами. Здесь лежат проекты за несколько лет, заметки в Obsidian, рукописи, конфиги, переписка. Если диск умрёт или ноутбук украдут — пропадёт всё, что нажито непосильным трудом. Поэтому мне нужно было организовать качественное резервное копирование по схеме 3-2-1, о которой я подробно рассказывал в одном из предыдущих постов.

Напомню суть: три копии данных, на двух разных типах носителей, одна из которых хранится за пределами дома. Звучит как паранойя, но на самом деле это минимальный здравый смысл. Пожар, кража, случайное rm -rf — и одной копии может не хватить.

Изучив решения для реализации такого копирования, я выбрал restic — и хочу рассказать, почему именно его и как всё реализовано.

Почему restic

Когда выбираешь инструмент для бэкапов, хочется найти что-то одновременно простое и надёжное. Я перебрал несколько вариантов — BorgBackup, Duplicacy, Kopia — и в итоге остановился на restic. Вот что в нём подкупило.

Restic — это программа с открытым исходным кодом, написанная на Go. Один бинарный файл, никаких зависимостей, работает на macOS, Linux, Windows и даже FreeBSD. Ставится через brew install restic и сразу готова к работе.

Мне понравилось, что restic шифрует всё. Не отдельные файлы, а вообще всё: содержимое, имена файлов, структуру каталогов, метаданные. Используется AES-256 с аутентификацией Poly1305, ключи выводятся через scrypt. Это значит, что даже если кто-то получит доступ к хранилищу с бэкапами — без пароля он увидит только набор зашифрованных блоков.

Второе важное свойство — дедупликация. Restic разбивает файлы на блоки переменной длины и сохраняет только уникальные блоки. Если я изменил три строчки в большом файле, в бэкап попадёт только изменённый кусок, а не весь файл заново. На практике это экономит место в разы, особенно когда бэкапы делаются каждый день.

Третье — restic работает с кучей бэкендов для хранения. Локальная папка, внешний диск, SFTP-сервер, Amazon S3, Backblaze B2, REST-сервер — всё это поддерживается из коробки. Мне это позволяет хранить копии в трёх разных местах, используя один и тот же инструмент.

И наконец — простота. У restic понятный набор команд: init для создания репозитория, backup для копирования, restore для восстановления, snapshots для просмотра истории, forget для удаления старых снапшотов. Не нужно читать документацию неделями, чтобы начать пользоваться.

Что копируется и куда

Я собрал все настройки в одном конфигурационном файле — restic-config.sh. В нём описано три вещи: какие папки копировать, куда копировать и что исключить.

Список папок — это просто массив путей. У меня туда входят рабочие проекты, заметки, сайты, рукописи и блог. Выглядит это примерно так:

BACKUP_SOURCES=(
  "$HOME/Documents/Notes"
  "$HOME/Projects"
  "$HOME/Sites"
  "$HOME/Documents/Book"
  "$HOME/Documents/Blog"
)

Дальше — список репозиториев, то есть мест, куда restic складывает бэкапы. У каждого репозитория есть имя, путь и необязательная проверка доступности. Например, для внешнего диска проверка — это наличие точки монтирования. Если диск не подключён, скрипт это поймёт и пропустит.

RESTIC_REPOS=(
  "local|$HOME/backup/restic_repo|$HOME/backup"
  "remote|sftp://user@myserver/restic_repo|"
  "disk|/Volumes/BackupDisk/restic_repo|/Volumes/BackupDisk"
)

Три репозитория — три копии. Локальная папка на встроенном диске, удалённый сервер по SFTP и внешний USB-диск. Это и есть схема 3-2-1: три копии, два типа носителей (встроенный SSD и внешний диск), одна копия удалённо (сервер).

Исключения тоже задаются в конфиге. Нет смысла копировать node_modules, vendor, кеши сборки и прочий мусор, который легко восстановить. Для этого используются glob-паттерны, а ещё есть механизм файлов-маркеров: если в папке лежит файл .nobackup, её содержимое не копируется.

Пароль для шифрования хранится в отдельном файле. Все репозитории используют один и тот же пароль — это важно, потому что иначе копирование между ними не будет работать.

Как устроено копирование

Скрипт backup.sh — это обёртка над restic, которая делает всю работу автоматически. Запускаешь его, и он последовательно копирует данные во все настроенные репозитории.

Но делает он это умнее, чем просто «запустить restic backup три раза». Есть два режима работы.

В основном режиме скрипт сначала делает бэкап в «основной» репозиторий — у меня это локальная папка. Это самый быстрый вариант, потому что данные пишутся на локальный диск. Restic создаёт снапшот, запоминает его идентификатор. Потом скрипт берёт этот снапшот и копирует его в «зеркала» — на удалённый сервер и на внешний диск — с помощью команды restic copy. Это экономит время: данные собираются с диска один раз, а не три, и restic передаёт в зеркала только недостающие блоки.

Есть и «прямой» режим (--direct), когда каждый репозиторий бэкапится независимо. Это полезно, если основной репозиторий по какой-то причине недоступен.

После копирования скрипт применяет политику хранения. В моём случае хранятся ежедневные снапшоты за последнюю неделю, еженедельные — за последний месяц и ежемесячные — за полгода. Всё, что старше, удаляется автоматически, а с флагом --prune освобождается и место на диске.

RETENTION_KEEP_DAILY=7
RETENTION_KEEP_WEEKLY=4
RETENTION_KEEP_MONTHLY=6

Скрипт защищён от параллельного запуска через блокировку. Если бэкап уже работает, второй экземпляр не запустится. Если какой-то репозиторий недоступен (диск не подключён, сервер не отвечает), скрипт может либо упасть с ошибкой, либо пропустить его и продолжить — в зависимости от флагов.

Есть режим --dry-run, который показывает, что было бы сделано, но ничего не записывает. Полезно при первой настройке, чтобы убедиться, что всё сконфигурировано правильно.

Как устроено восстановление

Скрипт restore.sh — это удобная обёртка для восстановления данных. Он читает тот же конфиг, знает обо всех репозиториях и предлагает набор подкоманд.

Чтобы посмотреть список доступных репозиториев, достаточно набрать ./restore.sh repos. Чтобы увидеть все снапшоты в репозитории — ./restore.sh snapshots. По умолчанию используется первый репозиторий из конфига, но можно явно указать любой через --repo.

Для восстановления нужно указать идентификатор снапшота (или latest для последнего) и целевую папку:

./restore.sh restore latest --target ~/restore

Можно восстановить не весь снапшот, а конкретную папку из него — это удобно, когда нужен только один проект или одна заметка:

./restore.sh restore latest /Users/me/Documents/Notes --target ~/restore/notes

Есть команда ls для просмотра содержимого снапшота — можно заглянуть внутрь, не восстанавливая ничего на диск. И команда dump для извлечения одного файла — она выводит содержимое прямо в терминал, что бывает полезно для быстрой проверки.

Флаг --verify после восстановления проверяет целостность восстановленных файлов. Флаг --dry-run показывает, что будет восстановлено, без фактической записи на диск.

Что в итоге получилось

В результате у меня работает полноценная схема 3-2-1. Скрипт backup.sh запускается регулярно, делает снапшот в локальный репозиторий на SSD, затем копирует его на удалённый сервер по SFTP и на внешний USB-диск, если тот подключён.

Все три копии зашифрованы одним паролем. Дедупликация делает так, что ежедневные бэкапы занимают мало места — сохраняются только изменения. Политика хранения автоматически чистит старые снапшоты, не давая репозиториям разрастаться бесконечно.

Если встроенный диск умрёт, я восстановлюсь с внешнего. Если ноутбук украдут вместе с внешним диском (маловероятно, но бывает) — восстановлюсь с сервера. Данные зашифрованы, поэтому даже если кто-то получит физический доступ к любому из хранилищ, без пароля он ничего не увидит.

Восстановление проверено и работает. Это, пожалуй, самый важный момент во всей истории. Бэкап, который ни разу не восстанавливали — это не бэкап, а самоуспокоение. Периодически я делаю тестовое восстановление в отдельную папку и проверяю, что все файлы на месте и читаются. Скрипт restore.sh делает это в одну команду, так что нет причин не проверять.

Restic оказался именно тем инструментом, который мне был нужен: простым, надёжным и достаточно гибким, чтобы реализовать схему 3-2-1 парой скриптов. Если вы до сих пор не делаете бэкапы — самое время начать. А если делаете — проверьте, что умеете из них восстанавливаться.