Restic - это простой, надежный, быстрый и эффективный способ резервного копирования. Простой в установке и настройке, с поддержкой большого количества бэкендов хранения, надежным шифрованием и дедупликацией. Это прекрасный инструмент для резервного копирования в современном ИТ-ландшафте. Тут я расскажу, зачем он нужен, как его поставить и начать им пользоваться.

Теоретическая часть

Предыстория

Исторически сложилось, что системы резервного копирования устроены очень сложно. Их сложно устанавливать, настраивать и сопровождать. Руководство к Veritas NetBackup (в дальнейшем - Symantec) имеет размеры приличной книги, по этой системе можно сдать экзамен и получить сертификат. Конфиг Amanda или Bacula весит под сотню килобайт, а официальное руководство занимает больше 500 страниц. Разумеется - у этой поражающей сложности есть простая, понятная, логичная причина, даже две:

  • ненадежность медиа
  • сложность процедуры бэкапа

Ненадежные медиа

Резервные копии надо где-то хранить. Традиционно используются цифровые кассеты (DAT/DLT), CD (в дальнейшем - DVD и BD) ROM/RW и жесткие диски. Проблема физических медиа в их не абсолютной надежности: кассеты размагничиваются и осыпаются, диски - царапаются и страдают от деградаций красочного слоя, про жесткие диски и говорить нечего. Из-за этого “классические” системы резервного копирования имеют сложные структуры расписаний и управление медиа-пулом. Медианосители надо постоянно перетасовывать, чтобы нагрузка на чтение и запись была равномерной и чтобы не оказалось, что весь огромный многотеррабайтный бэкап зависит от одного-единственного диска, который только что рассыпался в дисководе. Сложное расписание умножается на сложную структуру бэкапов (полный, инкрементальный, дифференциальный) где каждый слой зависит от другого. Это делается для экономия места и времени бэкапа. Кроме этого медиа ограничены в размере, а потому - нужно иметь возможность точно отфильтровать файлы и писать только то, что реально необходимо.

Сложная процедура бэкапа

В классическом традиционном ИТ-подходе бэкап - это очень сложная процедура. Бэкапим мы файлы, но данные нельзя воспринимать как файлы - они меняются. Бэкап файлов “в лоб” провоцирует инконсистентные данные, когда что-то поменялось на ходу и часть файлов находится в состоянии “до”, а часть - уже “после”. В том же Veritas NetBackup львиную долю цены лицензий состовляла цена агентов - сущностей, которые помогали бэкапить конкретные базы (Oracle, MSSQL) или состояния конкретных продуктов (SAP, BAAN). Агент знал, как “заморозить” состояние системы и получить консистентный бэкап, без него резервное копирование превращалось в лотерею. В opensource решениях агентов обычно нет, но родовые травмы в виде PreTask/PostTask остались - нужно подготовить данные к резервному копированию, а потом - вывести систему из этого состояния.

Restic: новый подход

Restic задуман как резервное копирование для современного ИТ-мира. Он имеет простой (предельно простой) интерфейс и минимум настроек. Одно из главных нововведений Restic - отказ от сложных медиа-операций. В современном ИТ в качестве системы хранения используется внешний сервис хранения, доступный обычно по S3 (Amazon S3, Azure BlobStorage, DigitalOcean Spaces, Minio на собственном оборудовании) или через прослойку в виде rclone (dropbox, Yandex.Disk). Restic предполагает такой бэкенд небезопасным, но надежным. Разумеется, данные внутри Amazon S3 хранятся на медиа, и медиа эти ненадежны. Однако между пользователем и медиа - несколько слоев абстракции, которые защищают данные, следят за их целостностью и верификацией. RAID-контроллеры, Erasure Codes, репликация между серверами-стойками-датацентрами, media scrubbing, check-суммы блоков - технологий десятки, если не сотни. По словам Dropbox - 80% трафика внутри их датацентров составляет служебный трафик, верификация, репликация и проверки данных, и только 20% - это собственно пользовательские данные. Restic исходит из идеи, что данные во внешнем хранилище содержатся вполне надежно и шансы получить битый или потеряный блок - минимальны. Именно по этому в Restic нет разделения на типы бэкапов - полные, инкрементальные и дифференциальные. Вместо этого предложен механизм snapshot-тов. Это просто снимок состояния системы в момент времени.

Как хранятся данные

Так как restic предполагает, что медиа - небезопасно - все данные шифруются AES256-CTR. Ключ AES шифруется паролем. Содержимое файлов нарезается на блоки (blobs) переменной длины (от 512кб до 8Мб), при этом каждый blob имеет контрольную сумму (CDC). Блоки упаковываются в пачки (pack). Пачка хранит в себе содержимое файла с метаданными или дерево (то есть - хранит в себе ссылки на другие пачки - файлы). Разные пачки могут ссылаться на один и тот же blob, что позволяет значительно экономить место. На самом верху этой пирамиды восседает snapshot, который хранит метаданные о, собственно, бэкапе (кто, когда, где) и ссылку на пачку с деревом верхнего уровня. При этом разные snapshot-ы могут ссылаться на одни и те же пачки деревьев, а пачки деревьев - на одни и те же пачки файлов (если файлы не менялись). Restic имеет удобную команду restic cat blob <blobId>, которая позволяет посмотреть в содержимое конкретного блоба и выяснить, что там лежит.

Практическая часть

Установка и инициализация repository

Restic написан на go и потому распространяется в виде единственно бинарного файла, который содержит все необходимое. В Debian и Ubuntu он есть в виде пакета, так что поставить можно традиционно:

apt-get install -y restic

Ну или просто скачать готовый binary отсюда.

После установки нужно инициализировать repo. При этом restic сгенерирует ключ для шифрования и создаст структуру каталогов для хранения blob-ов, паков и snapshot-ов. Тут нужно сделать небольшое отступление: в рамках одного repo можно хранить бэкапы разных каталогов и даже разных хостов. Структура repo restic-а позволяет это делать и бэкапы не будут пересекаться друг с другом (hostname и path бэкапа - обязательный атрибут snapshot-а, так что можно легко отфильтровать нужный). Блобы общие в рамках repo, по этому если три разных хоста хранят одинаковый файл - в repo он будет хранится в одном блобе, что позволит сэкономить место.

Инициализация выполняется командой init

# для локальной файловой системы:
restic init --repo /mnt/disk0/backup-repo

# для S3-совместимых серверов (Minio, CEPH ObjGateway)
export AWS_ACCESS_KEY_ID=<MY_ACCESS_KEY>
export AWS_SECRET_ACCESS_KEY=<MY_SECRET_ACCESS_KEY>
restic init --repo s3:https://my.local.server/backup-repo

# для Amazon S3 (European bucket)
export AWS_ACCESS_KEY_ID=<MY_ACCESS_KEY>
export AWS_SECRET_ACCESS_KEY=<MY_SECRET_ACCESS_KEY>
restic init -o s3.region="eu-west-1" --repo s3://s3.eu-west-1.amazonaws.com/bucketName/backupRepo

Главное - не забыть пароль, про который спросит restic. Именно этим паролем шифруется ключ и если пароль посеять - шансов на распаковку будет примерно ноль.

Типовые операции

Бэкап

Бэкап - основная операция, которая выполняется (по-хорошему), чаще всего:

restic -r /mnt/disk0/backup-repo backup /home/projects

Не всегда нужны все файлы. Для таких случаев есть ключ --exclude-file. В этот можно добавить как конкретные файлы и папки (путь должен быть относительным, от корня бэкапа), так и regexp. Для парсинга файла используется filepath.Glob, так что синтаксис в деталях можно посмотреть тут.

Пример файла:

# просто папки
tmp
out
# файлы по расширению
*.deb
*.rpm
*.pyc
*.pyo
# файлы по пути. К примеру /home/projects/a/b/c/temp /home/projects/a/temp /home/projects/temp
/home/projects/**/temp

Получение списка snapshot-ов (из которых можно восстанавливать):

restic -r /mnt/disk0/backup-repo snaphots

repository 365c035e opened successfully, password is correct
ID        Time                 Host        Tags         Paths
----------------------------------------------------------------------
119a1eb2  2020-04-30 15:48:20  mynote      mynote,work  /home/projects
ee597f8e  2020-05-31 00:30:01  mynote      mynote,work  /home/projects
a43ffe6a  2020-06-07 22:12:33  mynote      mynote,work  /home/projects
1d70de01  2020-06-10 00:32:43  mynote      mynote,work  /home/projects
0c236089  2020-06-12 18:44:56  mynote      mynote,work  /home/projects
e557bc74  2020-06-15 23:53:01  mynote      mynote,work  /home/projects
b39cf3de  2020-06-17 23:07:33  mynote      mynote,work  /home/projects
677b00d2  2020-06-19 11:40:50  mynote      mynote,work  /home/projects
8b781249  2020-06-20 00:07:52  mynote      mynote,work  /home/projects
d7c588a3  2020-06-23 10:15:06  mynote      mynote,work  /home/projects
----------------------------------------------------------------------

Сравнение двух snapshot-ов (дельта):

restic -r /mnt/disk0/backup-repo diff 119a1eb2 ee597f8e
password is correct
comparing snapshot 119a1eb2 to ee597f8e:

 C   /home/projects/cmd_diff.go
+    /home/projects/foo
 C   /home/projects/restic

Files:           0 new,     0 removed,     2 changed
Dirs:            1 new,     0 removed
Others:          0 new,     0 removed
Data Blobs:     14 new,    15 removed
Tree Blobs:      2 new,     1 removed
  Added:   16.403 MiB
  Removed: 16.402 MiB

Просмотр списка файлов в snapshot-е

Так можно посмотреть все файлы, которые можно найти в snapshot-е

restic -r /mnt/disk0/backup-repo ls -l latest

snapshot d7c588a3 of [/home/projects] filtered by [] at 2020-06-23 10:15:06.972887989 +0300 MSK):
/home
/home/projects
/home/projects/.ICEauthority
/home/projects/.PyCharmCE2019.3
/home/projects/.PyCharmCE2019.3/config
/home/projects/.PyCharmCE2019.3/config/codestyles
/home/projects/.PyCharmCE2019.3/config/codestyles/Default.xml
/home/projects/.PyCharmCE2019.3/config/inspection
/home/projects/.PyCharmCE2019.3/config/inspection/Default.xml
/home/projects/.PyCharmCE2019.3/config/options
/home/projects/.PyCharmCE2019.3/config/options/colors.scheme.xml
/home/projects/.PyCharmCE2019.3/config/options/debugger.xml
[...]

А так можно найти определенный (если восстановить хочется не все, а только определенные файлы):

restic find "**/blog/**/*.md" --snapshot latest

repository 365c035e opened successfully, password is correct
Found matching entries in snapshot d7c588a3
/home/projects/blog/_drafts/2017-11-06-deadend.md
/home/projects/blog/_drafts/2018-02-06-newway.md
/home/projects/blog/_posts/2005-01-14-openssh-key-auth.md
[...]

Восстановление из бэкапа

Самый простой вариант - восстановить все файлы из определенного snapshot-а. Latest как ID snapshot-а автоматически преобразуется в последний доступный snapshot

restic -r /mnt/disk0/backup-repo restore latest --target=tmptest/

repository 365c035e opened successfully, password is correct
restoring <Snapshot d7c588a3 of [/home/projects] at 2020-06-23 10:15:06.972887989 +0300 MSK by [email protected]> to tmptest/

Если восстанавливать хочется не все - есть ключ --include:

restic -r /mnt/disk0/backup-repo restore latest --target=tmptest/ --include ".PyCharmCE2019.3"

В этом случае будет восстановлена только папка .PyCharmCE2019.3 со всем ее содержимым. Обратный фокус с ключом --exclude сработает тоже.

Проверки snapshot-ов

Целосность данных критически важна. Если будет потерян blob - все snapshot-ы, которые ссылаются на него - станут невалидными и не смогут восстановится. Для этой цели есть команда check:

restic -r /mnt/disk0/backup-repo check

using temporary cache in /tmp/restic-check-cache-830311414
repository 365c035e opened successfully, password is correct
created new cache in /tmp/restic-check-cache-830311414
create exclusive lock for repository
load indexes
check all packs
check snapshots, trees and blobs
no errors were found

Уборка в репозитории

Restic по умолчанию не имеет политик очистки (retention policy), а значит - репозиторий будет расти вечно, с каждым изменением файла. Чтобы этого избежать - репозиторий надо чистить. Это делается в два шага:

  • команда forget удаляет ненужные snapshot-ы из репозитория. Однако она не трогает сами блобы, на которые ссылаются эти snapshot-ы
  • команда prune удаляет блобы, на которые никто не ссылается. Именно в этот момент происходит реальная чистка репозитория.

Пример использования:

restic -r /mnt/disk0/backup-repo forget --keep-daily 7 --keep-weekly 5 --keep-monthly 12
restic -r /mnt/disk0/backup-repo prune

В этом примере мы храним 7 последних ежедневных snapshot-ов, 4 snapshot-а недельных (что покрывает месяц) и 12 месячных. Это позволит нам вытащить бэкап за весь последний год, причем за последнюю неделю он ежедневный, за месяц - еженедельный а дальше - помесячный. Почему keep-weekly в этом примере равен 5? Потому что последний из 7 keep-daily попадает на недельный (это первый по-недельный snapshot). Чтобы проверить, какие snapshot-ы будут удалены - можно вызвать forget с ключом --dry-run. Никакого удаления при этом не производится и можно по выводу команды понять, отрабатывает ли политика так, как задумано.

Авторы restic рекомендуют запускать check после prune, чтобы убедится, что не произошло повреждения данных.

Использование в скриптах

Restic прекрасно работает в скриптах, в нем нет интерактивных элементов. Пароль можно задать с помощью переменных окружающей среды:

  • RESTIC_PASSWORD - пароль repo “как есть”, открытым текстом без шифрования
  • RESTIC_PASSWORD_FILE - файл с паролем repo. Без шифрования. Учитывается только первая строка. Имеет более высокий приоритет, чем RESTIC_PASSWORD (если задан RESTIC_PASSWORD_FILE, RESTIC_PASSWORD не учитывается)
  • RESTIC_PASSWORD_COMMAND - команда, выполнение которой вернет пароль. Задавать ее в виде субпроцесса $(command) не нужно. Хороший вариант, если вы используете для шифрования PGP или менеджер паролей (pass, gopass, 1password-cli). Наивысший приоритет

Заключение

Restic - простой, быстрый и при правильном применении - вполне надежный инструмент. Простота настройки снижает риск ошибок, а скорость работы - повышает шансы, что все будет забэкаплено вовремя и как надо. Я очень рекомендую этот инструмент, он показал себя простым, гибким, удобным и надежным. И помните - системные администраторы делятся на две группы: те, кто не делают бэкапы и те, кто уже делают бэкапы :)