К счастью, фотографии удалось восстановить. Это был дешёвый урок о работе с данными. Никогда не храни важное только в одном месте. Вещей, которые могут пойти не так, существует множество: диск может отказать, его можно украсть, биты могут испортиться при длительном хранении (магнитные частицы на жёстких дисках со временем смещаются, а NAND-транзисторы на SSD теряют заряд и повреждают данные).
Первый принцип прост — нужна резервная копия, то есть копия файлов в другом месте.
Но это решает только часть проблемы. Подключённый диск сам по себе может стать источником опасности: вирусы-шифровальщики способны зашифровать файлы, а простая ошибка может привести к удалению нужных данных или запуску скрипта, который перепишет всё нулями.
Резервная копия не должна быть зеркалом исходного диска — нужна возможность вернуться в прошлое и отменить ошибку. Это означает, что зеркалирование через RAID 1 не подойдёт. Нужен другой подход, который создаёт снимки состояния (snapshots) в разные моменты времени.
Как часто нужно создавать эти снимки? Для семейных фотографий еженедельное резервное копирование было бы достаточным — потеря данных за 6 дней и 23 часа приемлема. В IT это называется Recovery Point Objective (RPO) и колеблется от менее чем 30 секунд для критичных финансовых систем до 24 часов и более для небольших компаний (если они вообще имеют стратегию восстановления).
Создание снимков приводит к растущему потреблению хранилища. При RPO в 24 часа получится 7 снимков в неделю, 30 в месяц и 365 в год. Нужна ротация резервных копий.
Наивный подход — хранить снимки последних 14 дней. При создании нового снимка удалять старейший. Просто, но требует внимания. Если в данных есть повреждение, обнаружить его можно будет максимум две недели. С другой стороны, хранить год резервных копий неэффективно — события, произошедшие на 2-й и 3-й день года, почти не актуальны на 364-й день. Поэтому частота снимков должна измениться: чем ближе к сегодня, тем чаще; чем дальше в прошлое, тем реже.
Один из вариантов: ежедневные снимки ротируются каждые 14 дней, еженедельные — каждые 7 недель, ежемесячные — каждые 12 месяцев. Это намного эффективнее, но сложность растёт. Теперь это называется GFS-rotated, snapshot-based backup. Список определений будет только расти.
Вспомнив, как MPEG сжимает видео, можно заметить интересную закономерность: спокойные сцены, где актер говорит на фоне неподвижного пейзажа, хорошо сжимаются, потому что кадры почти идентичны, а изменения можно описать как векторы движения. То же верно и для резервных копий! Изменения файлов следуют распределению с тяжелым хвостом: большинство файлов вообще не меняются, а крошечное меньшинство изменяется постоянно.
Логично не хранить идентичные копии файлов, а применить дедупликацию. Можно использовать жёсткие ссылки для ссылки на существующий файл. На диске хранится один файл, а каждый снимок на него ссылается. Это хорошо сочетается с ротацией копий, так как удаляются только записи в директориях, а не сами файлы. Именно этот подход использует rsnapshot, и он называется инкрементальное резервное копирование, потому что хранятся только изменения между соседними снимками (в отличие от дифференциального копирования, где сохраняются все изменения с момента последней полной копии).
Сбережение хранилища — не единственный плюс. Данные хранятся не на одной машине, поэтому нужна сеть для передачи файлов. Дедупликация экономит пропускную способность, что особенно важно при использовании облачного сервиса — это напрямую влияет на финансовые расходы.
К этому моменту получается инкрементальное, дедупликированное, GFS-rotated, snapshot-based резервное копирование. Для передачи файлов можно использовать rsync, а для планирования — cronjobs. Можно создавать резервные копии произвольного количества машин. Добавление нового источника тривиально. Метаданные файлов сохраняются — права доступа и владелец остаются на месте.
После успеха этого решения появляется идея применить его ко всему homelabу с его 10 контейнерами Docker. Но потом в логах обнаруживаются ошибки резервного копирования. Причина: многие Docker-контейнеры создают файлы от root, а cronjob запускается от обычного пользователя.
Усложнение: почти каждое веб-приложение использует базу данных. БД часто хранят данные в памяти и сбрасывают их на диск партиями для производительности. Восстановление из резервной копии может привести к повреждению данных, если попасть в неудачный момент. Нужно добавить дамп базы в процесс резервного копирования и дать полные права на Docker-тома. Вот, должно работать!
Потом читаешь о случаях, когда модель жёсткого диска имела чрезмерно высокий уровень отказов, и начинаешь думать: может быть, хранить резервные копии на двух машинах с разными типами носителей? Тогда специфичный отказ одного типа оборудования не уничтожит все резервные копии. А добавить одну копию в облако или на машину у родственников? Это защитит от скачков напряжения, наводнений и пожаров. Вот откуда берётся правило 3-2-1: 3 копии, 2 типа носителей, 1 оффсайт.
Выбран облачный провайдер, например Amazon S3. Быстро обнаруживается, что текущая схема не работает. Во-первых, файлы теряют метаданные при загрузке в S3. Во-вторых, цена за загрузку множества маленьких файлов запредельна (распределение размеров файлов снова с тяжелым хвостом). Оптимально упаковать всё в tarball — так сохранятся метаданные и затраты будут низкими. Но как это правильно сделать? Один гигантский tarball? Нет, тогда инкрементальные копии с жёсткими ссылками теряют смысл. Лучше разделить на чистые 50 МБ куски, но как гарантировать безопасность процесса?
До этого момента создавал собственное решение для резервного копирования казалось выполнимым в один день, но здесь лучше остановиться. Умственная нагрузка слишком велика. Проще использовать проверенные инструменты вроде Borg или Restic, которые справляются со всем этим и даже больше (шифрование, дедупликация на уровне чанков, контрольные суммы). Стоит выразить глубокую благодарность open-source сообществу за создание, поддержку и live-тестирование этих инструментов — объём проб и ошибок, необходимый для такой абстракции сложности, огромен.
Но всё это теряет смысл, если не тестировать восстановление. Это нужно добавить в список цифровой гигиены. Если регулярно проверять восстановление каждые 6 месяцев, можно наслаждаться:
зашифрованным, дедупликированным на уровне чанков, GFS-rotated, point-in-time архивированным, облачным, 3-2-1 решением для резервного копирования
И не забыть: не запускать резервные копии в 2 или 3 часа ночи, иначе может быть страшновато.