rsync --link-dest: инкрементальные снимки с жёсткими ссылками вместо tar на cron
Переходим с tar-архивов на rsync --link-dest: 30 дней ежедневных снимков, каждый выглядит как полный бекап, а на диске занимает только дельту.
rsync 2.6.8 актуален; инструмент становится де-facto стандартом резервного копирования в Linux-средах
Года три мы жили на tar. Скрипт на cron, архив с датой в имени файла, ротация руками или простым find с удалением по возрасту. Выглядело разумно ровно до момента, когда кто-то спрашивал: «а покажи, что было в этом каталоге неделю назад». Разворачивать tar ради одного файла - это примерно как вытаскивать одну вещь из упакованного чемодана, не трогая остальное. Возможно, но неприятно.
Несколько недель назад мы начали переводить клиентские Linux-серверы под сопровождением на rsync с флагом --link-dest. Расскажем, что получилось.
Почему tar перестал устраивать
С tar есть несколько неудобств, которые начинаешь замечать только когда что-то идёт не так.
Полный архив каждый раз. Даже если за сутки изменилось три процента данных, tar упакует всё. Это и время, и место на диске, и нагрузка на I/O в окно бекапа.
Восстановление - отдельный квест. Нужен конкретный файл двухнедельной давности? Надо найти нужный tar, распаковать во временный каталог, скопировать файл, убрать за собой. Умеет делать каждый, но никому не нравится.
Инкрементальные tar - отдельная боль. Флаг --newer есть, но итоговые архивы неудобно склеивать при восстановлении. Нужно знать порядок, не пропустить ни один файл из цепочки.
Как работает rsync --link-dest
Идея простая, и именно поэтому элегантная.
rsync при синхронизации умеет сравнивать файл-приёмник с образцом из предыдущего снимка. Если файл не изменился - вместо копирования rsync создаёт жёсткую ссылку на существующий файл в предыдущем снимке. Жёсткая ссылка занимает место в файловой системе, но не занимает места на диске - это просто ещё одна запись в директории, указывающая на те же блоки данных.
В итоге получается:
- Каждый снимок в своей директории (например,
2006-10-05/,2006-10-04/, ...) и выглядит как полное дерево файлов. - Физически новые блоки данных - только у изменившихся файлов.
- 30 снимков подряд при типичной ежедневной дельте занимают на диске примерно 120-130% от объёма одной полной копии - а не 3000%.
Схема, с которой мы стартовали
Структура на бекап-сервере:
/backup/
client-name/
2006-10-05/ <- сегодняшний снимок
2006-10-04/
2006-10-03/
...
2006-09-06/ <- 30 дней назад
Скрипт, который запускается по cron:
#!/bin/bash
SRC="user@host:/data/"
DST="/backup/client-name"
TODAY=$(date +%Y-%m-%d)
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
rsync -a --delete \
--link-dest="${DST}/${YESTERDAY}" \
"${SRC}" \
"${DST}/${TODAY}/"
# удаляем снимки старше 30 дней
find "${DST}" -maxdepth 1 -type d -name '????-??-??' \
| sort | head -n -30 | xargs rm -rf
-a - архивный режим (рекурсия, сохранение прав, времён, симлинков). --delete - удаляет из приёмника файлы, которых нет в источнике. --link-dest - указывает на предыдущий снимок для дедупликации через жёсткие ссылки.
Первый запуск - полная передача. Каждый последующий - только дельта по сети и только реально изменившиеся файлы на диске.
Что получается на практике
Восстановить один файл - зайти в директорию нужного снимка и скопировать. Без распаковки, без временных каталогов. Это ощутимо меняет отношение к процессу восстановления: из «процедуры с бумажкой» превращается в рутинную операцию.
Место на диске - примерно 20% дополнительно на каждую неделю истории при типичном офисном сервере. Само собой, зависит от того, как часто и насколько меняются данные. У баз данных картина другая - там каждый день дельта большая, жёсткие ссылки помогают меньше. Для файловых серверов и конфигов - отлично работает.
Нагрузка на сеть и диск в окно бекапа - заметно ниже, чем при полном tar. Для клиентов, где бекап идёт по медленному каналу, это существенно.
Что пока требует внимания
Жёсткие ссылки работают только в пределах одной файловой системы. Если бекап-сервер и хранилище снимков разделены - надо за этим следить при разметке дисков. Один раз мы споткнулись именно на этом: снимок создался, но --link-dest тихо проигнорировал оптимизацию, и место закончилось быстрее, чем ожидали.
--link-dest принимает ровно одну директорию для сравнения. Если вчерашний снимок по какой-то причине не создался (скрипт упал, диск был полон), то сегодня ссылки будут сравниваться с позавчерашним - что корректно с точки зрения данных, но займёт больше места, чем обычно. Мониторинг выполнения скрипта обязателен.
Удаление старого снимка через rm -rf освобождает место только если на файлы нет других жёстких ссылок. Пока снимки связаны между собой через --link-dest - неизменённые файлы освобождаются постепенно, по мере того как все ссылающиеся снимки удаляются. На практике это значит: после удаления снимка место освобождается не сразу и не всё. Ожидаемо, но надо понимать механику.
Схема работает. Клиенты уже не спрашивают «а можно посмотреть, что было три дня назад» с тоном, подразумевающим «это, наверное, сложно». Теперь это просто ls и cp.
- Ленточные бекапы и ротация дед-отец-сын: почему нельзя делать на авось · 12 декабря 2000
- VMware snapshot - не бекап: выясняем на практике с VI3 · 31 июля 2006