ADG Оставить заявку
Блог Автоматизация 2 мин чтения

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.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.