restic: бэкапы, которые не подводят
Я потерял данные дважды. Первый раз — когда жёсткий диск на старом ноутбуке начал сыпать ошибками, а я думал «потом сделаю бэкап». Диск умирал постепенно — сначала редкие сбои при чтении, потом система начала зависать при загрузке, а в один прекрасный день она просто не включилась. Я потащил ноут в сервис, но восстановление данных стоило 15 тысяч рублей, и то не всё спасли. Второй раз — когда случайно запустил rm -rf не в том контейнере. Думал, что чищу временные файлы в /tmp, а оказалось, что я в корне проекта с клиентской базой. Хорошо, что был бекап недельной давности — потерял всего 5 дней работы. После второго раза я решил: хватит. Нужна автоматическая система бэкапов, которая работает без моего участия и не требует вспоминать про неё каждый день.
Перебрал кучу инструментов. rsync — надёжно, но нет версионирования. Если файл испортился, rsync просто скопирует испорченную версию поверх хорошей. А если случайно удалил файл — rsync послушно удалит его и в бекапе. duplicity — глючный и тормозной, я с ним мучился месяц: то ключи GPG теряются, то инкрементальные бекапы внезапно становятся полными и занимают в 10 раз больше места. Я так и не понял, от чего это зависит. Ещё duplicity требует устанавливать GPG и настраивать ключи — лишняя головная боль. Borg — хорош, но на Windows не заведёшь, а у меня есть ноутбук с Windows для работы с графикой и клиентскими проектами. В итоге остановился на restic. Почему? Он кроссплатформенный — один бинарник работает на Linux, Windows и macOS без зависимостей. Шифрует данные на клиенте, умеет дедупликацию и работает с кучей хранилищ: локальные папки, S3, Backblaze B2, SFTP, даже Google Cloud. И да, он написан на Go — один исполняемый файл без внешних библиотек.
Ставится restic смешно просто. На Linux:
sudo apt install restic
На Windows — качаешь exe с GitHub и кидаешь в PATH. На macOS — brew install restic. Версия для Windows, кстати, работает отлично — я через PowerShell запускаю бекап рабочих файлов раз в час. Никаких проблем с русскими именами файлов, всё корректно бэкапится. Единственное — на Windows нужно не забыть добавить restic в исключения антивируса, иначе Windows Defender может заблокировать запись в сетевую папку. У меня так было — Defender решил что restic подозрительный, и бекапы молча не работали неделю, пока я не заметил.
Дальше нужен bucket в S3 или в Backblaze B2. Я выбрал B2 — у них 10 ГБ бесплатно, а дальше копейки. Создал bucket, сгенерировал ключи доступа в панели Backblaze. Важный момент: не используй русские буквы в имени bucket. Я сначала назвал «мои-бекапы», и restic с ним работал, но когда я захотел посмотреть данные через веб-интерфейс B2 — url выдавал ошибку. Пришлось пересоздавать bucket с латинским именем и перезаливать все бекапы. Ключи доступа в B2 бывают master (полный доступ) и application (ограниченные). Для restic лучше создать отдельный application key с доступом только к конкретному bucket — если ключ утечёт, злоумышленник не получит доступ ко всем твоим данным в B2.
Первый раз инициализируешь репозиторий:
restic init —repo b2:my-bucket-name:/restic
Также нужно передать ключи через переменные окружения или параметры командной строки. Я предпочитаю переменные окружения, чтобы ключи не светились в истории bash и в логах cron:
export B2_ACCOUNT_ID=ваш_application_key_id
export B2_ACCOUNT_KEY=ваш_application_key
Создастся зашифрованное хранилище. Restic попросит пароль — запомни его. Если потеряешь — данные не восстановить никак. Это не шутка. Без пароля restic не расшифрует данные даже если у тебя есть доступ к bucket. Шифрование у restic на базе AES-256 с аутентификацией через Poly1305 — взломать это невозможно без пароля. Я храню пароль в KeePassXC и в бумажке под клавиатурой. Пароль должен быть сложным — не «password123», а генерация через менеджер паролей, символов 30-40.
Сам бэкап делаю одной командой:
restic backup /home/user/docker —repo b2:my-bucket-name:/restic
Restic сканирует папку, делит файлы на чанки по несколько мегабайт, дедуплицирует — сравнивает чанки с уже сохранёнными — и шифрует перед отправкой. Первый бэкап занял у меня 20 минут (где-то 50 ГБ данных). Каждый следующий — секунды, потому что restic помнит, что уже загружено. Дедупликация работает на уровне чанков: если в папке поменялся один файл, restic загрузит только изменённые чанки. За месяц использования дедупликация сэкономила мне где-то 70 процентов места в B2. Особенно эффективно для бэкапов баз данных — дампы MySQL каждый час отличаются на несколько процентов, и restic умно хранит только разницу. Для логов это вообще сказка — 100 ГБ логов сжимаются до 5 ГБ в репозитории restic.
Восстановление — ещё проще:
restic restore latest —target /tmp/restore —repo b2:…
Но я предпочитаю монтировать снапшот как папку и выборочно копировать файлы:
restic mount /mnt/restic —repo b2:…
Открываешь /mnt/restic — там снапшоты по датам, как папки. Можно зайти внутрь и cp нужные файлы. Удобно, когда нужно восстановить не всё, а только один конфиг. Я так однажды восстанавливал файл docker-compose.yml, который случайно удалил при рефакторинге. Зашёл в снапшот, скопировал — заняло 10 секунд. Для mount нужен FUSE — на Ubuntu он уже установлен, на Windows придётся ставить доп софт. Поэтому на Windows я использую restore вместо mount.
Автоматизацию я сделал через cron. Раз в день в 4 утра:
0 4 * * * restic backup /home/user/docker —repo b2:… —tag daily >> /var/log/restic.log 2>&1
Раз в неделю — проверка целостности. Это важно — restic check проверяет, что все чанки на месте и не повреждены:
0 5 * * 0 restic check —repo b2:… >> /var/log/restic-check.log 2>&1
Раз в месяц — забываю старые снапшоты. Оставляю только последние 30. Политика хранения — дело вкуса. Кто-то хранит годовые снапшоты, кто-то — только за неделю. Я выбрал 30 дней, потому что за месяц ошибка обычно замечается, а дальше хранить смысла нет:
0 6 1 * * restic forget —keep-last 30 —prune —repo b2:…
Команда forget с флагом —prune физически удаляет старые данные из хранилища. Без —prune они висят мёртвым грузом и жрут место в B2. Забыл добавить —prune в первый месяц — пришёл счёт от Backblaze на 5 долларов вместо обычного 1 доллара. Урок усвоен. Кстати, —prune может выполняться долго — у меня на 200 ГБ репозитория занимает около часа. Поэтому я запускаю его раз в месяц, а не каждую неделю.
Сейчас у меня настроено три репозитория: один для Docker-контейнеров с volumes, второй для баз данных (дамплю PostgreSQL раз в час в папку, restic бэкапит её раз в 4 часа), третий — для конфигов системы. Каждый репозиторий со своим паролем. Да, хранить три пароля неудобно, но если один скомпрометируют — остальные данные в безопасности. Пароли я храню в переменных окружения в ~/.bashrc, чтобы не вводить их каждый раз вручную.
Пара грабель, на которые я наступил. Первое — не ставь теги с пробелами, используй дефисы. Второе — обязательно проверяй восстановление. У меня был случай, когда репозиторий был цел, но ключи доступа к B2 протухли, и restic не мог подключиться. Сейчас я раз в месяц делаю тестовое восстановление самого важного файла. Третье — настрой мониторинг. Я добавил в скрипт отправку уведомления в Telegram, если бекап не выполнился. Четвёртое — не бэкапь /tmp и кеш браузера. Я случайно забекапил 50 ГБ кеша Chrome, и B2 радостно это съел за мои деньги. Используй файлы —exclude для исключения ненужных директорий. Пятое — проверяй, что репозиторий доступен перед бэкапом. Если B2 временно недоступен, restic может выдать ошибку, а cron промолчит. Я добавил проверку через curl перед запуском restic.
Личный вердикт: restic — лучший инструмент для бэкапов, который я пробовал. За два года ни одного потерянного файла, ни одного сбойного восстановления. А когда полетел SSD на домашнем сервере — я восстановил всё за час, включая базы данных и конфиги каждого сервиса. Без restic я бы потратил неделю на переустановку и настройку всего с нуля, а многие конфиги пришлось бы восстанавливать по памяти — какие-то настройки nginx, переменные окружения, параметры PostgreSQL. С restic я просто смонтировал последний снапшот и скопировал всё как было.
Из планов на будущее: хочу настроить бэкап по схеме grandfather-father-son через restic forget с разными политиками для ежедневных, еженедельных и ежемесячных снапшотов. restic поддерживает —keep-daily, —keep-weekly, —keep-monthly, —keep-yearly. Планирую хранить ежедневные за 7 дней, еженедельные за месяц, ежемесячные за год. Ещё хочу попробовать restic в связке с systemd таймерами вместо cron — systemd надёжнее логирует и умеет дожидаться завершения предыдущего запуска. А ещё есть resticprofile — это враппер, который управляет несколькими профилями restic из одного YAML-файла. Можно описать все репозитории, расписания и политики хранения в одном месте. Пока не внедрил, но присматриваюсь.
Что касается стоимости — Backblaze B2 обходится мне примерно в 1-2 доллара в месяц за 50 ГБ данных с учётом дедупликации и сжатия. Для сравнения, S3 от AWS стоил бы долларов 7-8 за такой же объём плюс плата за запросы API при каждом бекапе. Google Cloud Nearline — около 3 долларов, но берёт деньги за чтение — если восстанавливать данные часто, выйдет дороже B2. Быстрое чтение из B2 стоит $0.01 за ГБ, что копейки. Ещё есть Wasabi — $6.99 в месяц за неограниченное хранилище, но без платы за чтение и с минимальным объёмом хранения 90 дней. Для бэкапов сервера это дороже B2.
Для тех, кто хочет попробовать restic без регистрации на B2 — можно начать с локального репозитория. Просто restic init —repo /mnt/backup/restic-repo. Все функции те же: дедупликация, шифрование, снапшоты. Потом, когда разберёшься, добавляешь B2 или S3. Я так и сделал — неделю тестировал локально, гонял backup и restore, проверял —check, смотрел как работает forget —prune. Убедился, что данные восстанавливаются корректно, и только потом подключал удалённое хранилище. Это спасло меня от ошибок в начале — я случайно удалил тестовый репозиторий командой restic forget —prune с неправильными флагами, но потерял только локальные данные, не успевшие дойти до B2.