Pull to refresh

Сократить бэкапы на 99.5% с hashget

Reading time8 min
Views11K

hashget — это бесплатный, оперсорсный дедупликатор — похожая на архиватор утилита, которая позволяет значительно сократить размер бэкапов, а также организовать схемы инкрементального и дифференциального бэкапа и не только.


Это обзорная статья для описания возможностей. Само использование hashget (довольно простое) описано в README проекта и wiki-документации.


Сравнение


По закону жанра, начну сразу с интриги — сравнения результатов:


Data sample unpacked size .tar.gz hashget .tar.gz
Wordpress-5.1.1 43 Mb 11 Mb ( 26% ) 155 Kb ( 0.3% )
Linux kernel 5.0.4 934 Mb 161 Mb ( 20% ) 4.7 Mb ( 0.5% )
Debian 9 (LAMP) LXC VM 724 Mb 165 Mb ( 23% ) 4.1 Mb ( 0.5% )

Предыстория, каким должен быть идеальный и эффективный бэкап


Каждый раз, когда я делал бэкап свежесозданной виртуалки, мне не давало покоя чувство, что я что-то делаю не так. Почему у меня получается увесистый бэкап от системы, где моего бесценного нетленного творчества — однострочный index.html с текстом "Hello world"?


Почему в моем бэкапе есть 16-мегабайтный /usr/sbin/mysqld? Неужели в этом мире именно мне выпала честь хранить этот важный файл, и если я не справлюсь — он будет потерян для человечества? Скорее всего нет. Он хранится на высоконадежных серверах debian (надежность и бесперебойность которых ни в какое сравнение не идет с тем, что могу обеспечить я), а также в резервных копиях (миллионах их) других админов. Точно ли нам нужно создавать для повышения надежности 10 000 000 + 1'ую копию этого важного файла?


В общем-то hashget и решает эту проблему. При запаковке — он создает очень маленький бэкап. При распаковке — полностью распакованную систему, аналогичную той, которая была бы при tar -c / tar -x. (Иными словами, это lossless упаковка)


Как работает hashget


В hashget есть понятия Package и HashPackage, с их помощью он выполняет дедупликацию.


Package (пакет). Файл (обычно архив .deb или .tar.gz), который можно надежно скачать из сети, и из которого можно получить один или более файлов.


HashPackage — небольшой JSON файл, представляющий Package, в том числе содержащий URL пакета и хеш-суммы (sha256) файлов из него. Например, для пакета mariadb-server-core размером 5 мегабайт размер hashpackage всего 6 килобайт. Примерно в тысячу раз меньше.


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


Запаковка


При запаковке просматриваются все файлы из пакуемого каталога, считаются их хеш-суммы, и если сумма найдена в одном из известных HashPackage, то метаданные о файле (имя, хеш, права доступа итд) сохраняются в особый файл .hashget-restore.json, который тоже будет включен в архив.


Сама запаковка в простейшем случае выглядит не сложнее tar'а:


hashget -zf /tmp/mybackup.tar.gz --pack /path/to/data

Распаковка


Распаковка делается в два этапа. Сначала обычная tar распаковка:


tar -xf mybackup.tar.gz -C /path/to/data

затем восстановление из сети:


hashget -u /path/to/data

При восстановлении hashget читает файл .hashget-restore.json, скачивает необходимые пакеты, распаковывает их, и извлекает необходимые файлы, устанавливая их на нужные пути, с нужными owner/group/permissions.


Более сложные вещи


То, что описано выше — уже достаточно, для тех, кому "хочу как tar, но чтобы упаковал мой Debian в 4 мегабайта". Дальше посмотрим более сложные вещи.


Индексирование


Если у hashget совсем не было б ни одного HashPackage, то он просто не смог бы ничего дедуплицировать.


Создать HashPackage можно и вручную (просто: hashget --submit https://wordpress.org/wordpress-5.1.1.zip -p my), но есть путь удобнее.


Для того чтобы получить нужные hashpackage, есть этап индексирования (он автоматически выполняется при команде --pack) и эвристики. При индексировании hashget "скармливает" каждый найденный файл всем имеющимся эвристикам, которым он интересен. Эвристики затем могут проиндексировать какой-либо Package, чтобы создать HashPackage.


Например, дебиановская эвристика любит файл /var/lib/dpkg/status и обнаруживает установленные debian пакеты, и если они не проиндексированы (для них не созданы HashPackage), скачивает и индексирует их. Получается очень приятный эффект — hashget всегда будет эффективно дедуплицировать дебиановские ОС, даже если на них есть самые новые пакеты.


Файлы-подсказки (хинты)


Если в вашей сети используется какой-то ваш проприетарный пакет или публичный пакет, который не включен в эвристики hashget, вы можете добавить в него простой hint-файл hashget-hint.json по образцу:


{
    "project": "wordpress.org",
    "url": "https://ru.wordpress.org/wordpress-5.1.1-ru_RU.zip"
}

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


Если какой-то ваш собственный пакет обновляется периодически, но изменения не очень большие, можно делать hint только для основных версий. Например, в версии 1.0 сделали хинт с указанием на mypackage-1.0.tar.gz, и она будет полностью дедуплицироваться, затем выпустили версию 1.1, которая немного отличается, а хинт не обновили. Ничего страшного. Дедуплицируются только файлы, которые совпадают (которые можно восстановить) с версией 1.0.


Эвристика, которая обрабатывает hint-файл — хороший пример для понимания внутреннего механизма работы эвристик. Он обрабатывает только файлы hashget-hint.json (или .hashget-hint.json с точкой) и игнорирует все остальные. По этому файлу он определяет, какой URL пакета должен быть проиндексирован, и hashget его индексирует (если этого не было сделано раньше)


HashServer


Было бы довольно трудоемко при создании бэкапов полностью выполнять индексирование. Для этого нужно скачать каждый пакет, распаковать, проиндексировать. Поэтому hashget использует схему с HashServer. При обнаружении установленного debian'овского пакета, если он не найдется в локальных HashPackage, сначала делается попытка просто скачать HashPackage с хеш-сервера. И только если это не получается — hashget сам скачивает и хеширует пакет (и загружает на hashserver, чтобы в дальнейшем hashserver его предоставлял).


HashServer — не обязательный элемент схемы, не критичный, служит исключительно для ускорения и снижения нагрузки на репозитории. Легко отключается (опцией --hashserver без параметров). Кроме того, легко можно сделать свой собственный hashserver.


Инкрементальные и дифференциальные бэкапы, запланированное устаревание


hashget позволяет очень просто сделать схему инкрементальных и дифференциальных бэкапов. Почему бы нам не проиндексировать сам наш бэкап (со всеми нашими уникальными файлами)? Одна команда --submit и все готово! Следующий бэкап, который создаст hashget, не будет включать файлы из этого архива.


Но это не очень хороший подход, потому что может оказаться так, что при восстановлении нам придется дергать все hashget-бэкапы за всю историю (если в каждом будет хотя бы один уникальный файл). Для этого есть механизм запланированного устаревания бэкапов. При индексировании можно указать дату устаревания HashPackage --expires 2019-06-01, и по наступлению этой даты (c 00:00), он не будет использован. Сам архив можно после этой даты не удалять (Хотя hashget может удобно показать URLы всех бекапов, которые у нас протухли/протухнут на данный момент или на любую дату).


Например, если 1го числа делать фулл бэкап, и индексировать его со временем жизни до конца месяца — мы получим схему дифференциального бэкапа.


Если так же будем индексировать и новые бэкапы — будет схема инкрементальных бэкапов.


В отличие от традиционных схем, hashget позволяет использовать несколько базовых источников. Бекап будет сокращен и за счет сокращения файлов из предыдущих бэкапов (если они есть), и за счет публичных файлов (то, что можно выкачать).


Если мы по какой-то причине не доверяем надежности дебиановских ресурсов (https://snapshot.debian.org/) или использует другой дистрибутив, мы просто можем один раз сделать полный бэкап со всеми пакетами, и далее опираться уже на него (отключив эвристику). Теперь, если все сервера наших дистрибутивов окажутся нам недоступны (в сувенирном Интернете или при зомби-апокалипсисе), но наши бэкапы будут в порядке — мы сможем восстановиться из любого короткого дифф-бэкапа, опирающегося только на наши более ранние бэкапы.


Hashget опирается только на надежные источники восстановления по ВАШЕМУ усмотрению. Какие вы считаете надежными — те и будут использованы.

FilePool и Glacier


Механизм FilePool позволяет не обращаться постоянно к внешним серверам для скачивания пакетов, а использовать пакеты из локального каталога или корпоративного сервера, например:


$ hashget -u . --pool /tmp/pool

или


$ hashget -u . --pool http://myhashdb.example.com/

Чтобы сделать пул в локальном каталоге — достаточно просто создать каталог и накидать в него файлов, hashget сам по хешам найдет то, что ему нужно. Чтобы сделать пул доступным через HTTP, нужно специальным образом создать симлинки, это делается одной командой (hashget-admin --build /var/www/html/hashdb/ --pool /tmp/pool). Сам HTTP FilePool — это статичные файлы, так что обслуживать его может любой простейший вебсервер, нагрузка на сервер почти нулевая.


Благодаря FilePool в качестве базовых ресурсов можно использовать не только ресурсы на http(s), но и, к примеру, Amazon Glacier.


После аплоада бэкапа на гласьер получаем его Upload ID и используем его в качестве URL. Например:


hashget --submit Glacier_Upload_ID --file /tmp/my-glacier-backup.tar.gz --project glacier --hashserver --expires 2019-09-01

Теперь новые (дифференциальные) бэкапы будут опираться на этот бэкап и будут короче. После tar распаковки диффбэкапа мы можем посмотреть, на какие ресурсы он опирается:


hashget --info /tmp/unpacked/ list

и просто шелл-скриптом скачать из гласьера все эти файлы в pool и запустить обычное восстановление: hashget -u /tmp/unpacked --pool /tmp/pool


Стоит ли овчинка выделки


В простейшем случае — вы просто будете меньше платить за бэкапы (если вы их храните где-то в облаке за деньги). Может быть — гораздо-гораздо меньше.


Но это не единственное. Количество переходит в качество. Можете использовать это, чтобы получить качественный апгрейд схемы бэкапов. Например, раз бэкапы у нас теперь короче — можно делать не ежемесячный бэкап, а ежедневный. Хранить их не полгода, как ранее, а 5 лет. Раньше хранили в медленном но дешевом "холодном" хранилище (Glacier), теперь можете хранить в горячем, откуда можно всегда быстро скачать бэкап и восстановиться за минуты, а не за день.


Можно увеличить надежность хранения бэкапов. Если сейчас мы их храним в одном хранилище, то сократив объем бэкапов — сможем хранить в 2-3 хранилищах и безболезненно пережить, если одно из них повредится.


Как попробовать и начать пользоваться?


Заходим на гитлаб страничку https://github.com/yaroslaff/hashget, устанавливаем одной командой (pip3 install hashget[plugins]) и просто читаем-выполняем quick-start. Думаю, все простые вещи сделать — займет минут 10-15. Затем можно попробовать пожать свои виртуалки, сделать, если надо, хинт-файлы, чтобы ужималось сильнее, поиграться с пулами, локальной базой хешей и хешсервером, если интересно будет, а на следующий день посмотреть, каков будет размер инкрементального бэкапа поверх вчерашнего.


Restic + HashGet


(Эта глава добавлена позже. Спасибо комментаторам за критику и мотивацию.)


Есть хороший удобный инструмент для бэкапов — restic. Он тоже может выполнять дедупликацию, но только в пределах репозитория, не умеет внешнюю дедупликацию, которую легко делает hashget. Но в сочетании restic + hashget у нас получается использовать плюсы обоих подходов!


Подготовка (распакуем wordpress и проиндексируем его):


# wget -q https://wordpress.org/wordpress-5.2.2.tar.gz
# hashget --submit https://wordpress.org/wordpress-5.2.2.tar.gz -p my --file wordpress-5.2.2.tar.gz --hashserver
# tar -xf wordpress-5.2.2.tar.gz
# du -sh wordpress
46M wordpress

Добавление снапшота в restic через


# hashget -X exclude-list --prepack wordpress --hashserver
Saved: 1468 files, 1 pkgs, size: 40.5M. Download: 10.7M

# restic --exclude-file exclude-list backup wordpress
password is correct
scan [/tmp/wp/wordpress]
scanned 193 directories, 367 files in 0:02
[0:04] 100.00%  700.829 KiB / 700.829 KiB  560 / 560 items  0 errors  ETA 0:00 
duration: 0:04
snapshot 76b54230 saved

# du -sh /tmp/restic-repo/
2,1M    /tmp/restic-repo/

На этом этапе мы добавили снапшот каталога (40+ Mb), и размер репозитория увеличился всего на 1 Mb.


Восстановление делается двумя командами:


# restic restore 76b54230 -t unpacked
password is correct
restoring <Snapshot 76b54230 of [/tmp/wp/wordpress] at 2019-06-19 04:30:55.760618336 +0700 +07 by root@braconnier> to unpacked
# hashget -u unpacked/wordpress/ --hashserver
Recovered 1468/1468 files 40.5M bytes (0 downloaded, 0 from pool, 10.7M cached) in 1.56s
Tags:
Hubs:
+23
Comments40

Articles