Что самое важное для бекапов? Правильно, воспроизводимость. Поэтому давайте сделаем велосипед на коленке и на опции --link-dest у rsync. У нашего велосипеда не будет сложной структуры данных в стиле git как у restic, ни кучи бекендов как у duplicity. Но мы сможем восстановить его работу по памяти даже под стрессом.


Опция --link-dest позволяет указывать предудущую версию бекапа, на которую rsync будет ставить жёсткие ссылки, если файлы с прошлого раза не поменялись.


То есть, rsync --link-dest=/var/backups/www.1 remote:/var/www /var/backups/www.0 скопирует с удалённого сервера в папку /var/backups/www.0 только те файлы, что поменялись, а на оставшиеся поставит жёсткую ссылку в /var/backups/www.1


Теперь дело за малым: обернуть вызов rsync в код, который сдвигает бекапы на один назад и освобождает место для нового бекапа в /var/backups/www.0, а также удаляет по необходимости последнюю копию /var/backups/www.9.


#отсортировать папочки
find /var/www/backups/ -maxdepth 1 -type d -name '*.[0-9]'| sort -rn| while read dir 
do
    #выделить циферку из имени папки
    this=`expr match "$dir" '.*\([0-9]\)'`;  
    # увеличить на 1, не забывая, что у нас ограничение на 10 копий
    let next=($this+1)%$10; 
    basedirname=${dir%.[0-9]}
    if [ $next -eq 0 ] ; then
         rm -rf $dir
    else
         mv $dir $basedirname.$next
    fi
done

Этот код переименует /var/backups/www.1 в /var/backups/www.2 а /var/backups/www.0 переименует в /var/backups/www.1.


Осталось только запустить rsync --link-dest=/var/backups/www.1 remote:/var/www /var/backups/www.0, добавив опции по вкусу. Так опция --delete удаляет файлы в последней копии (rsync этого по умолчанию не делает), опция -C игнорирует папки .svn, .git, артефакты patch и ещё несколько часто встречающихся видов временных файлов.


Всё вместе:


#!/bin/bash
FROM=$1 # откуда
TO=$2 # куда
LINKTO=--link-dest=$TO/`basename $FROM`.1 # предыдущая копия
OPTS="-Ca --delete" # полезные опции для rsync
NUMBER_OF_BACKUPS=10 # столько копий мы храним 
# шаманим с переименованием папок, чтобы предыдущие версии сохранялись под именами
# dir.1, dir.2, dir.3 и так далее до dir.9
find $TO -maxdepth 1 -type d -name '*.[0-9]'| sort -rn| while read dir
do
    this=`expr match "$dir" '.*\([0-9]\)'`; 
    let next=($this+1)%$NUMBER_OF_BACKUPS;
    basedirname=${dir%.[0-9]}
    if [ $next -eq 0 ] ; then
         rm -rf $dir
    else
         mv $dir $basedirname.$next
    fi
done
# шаманство закончилось, запускаем rsync
rsync $OPTS $LINKTO $FROM/ $TO/`basename $FROM.0`

За скобками осталась обработка ошибок (см. set -e) и уведомления, да и количество копий можно сделать настраиваемым.


Но ведь работает же!

Комментарии (15)


  1. snp
    12.11.2019 15:56

    apt-get install rsnapshot


    1. mikhailian Автор
      12.11.2019 16:05

      Более половины решений в списке альтернатив restic используют rsync. А вот мы с вами взяли — и использовали его напрямую. We need to go deeper. В следующий раз будем реализовывать rolling hash на голом шелле и netcat.


  1. jim945
    12.11.2019 23:01

    Можно же проще.
    Пример из скрипта.
    snapshotdir="`date +\%Y-\%m-\%d\%R`"
    mkdir $backup_dir/$snapshot_dir
    rsync -avHXE --delete --delete-excluded --exclude-from=$exclude_file --progress --stats --numeric-ids --link-dest=$backup_dir/last $mnt_dir/ $backup_dir/$snapshot_dir/


    rm $backup_dir/last
    ln -s $backup_dir/$snapshot_dir $backup_dir/last


    1. mikhailian Автор
      12.11.2019 23:15

      Можно по-всякому. Можно даже без ln -s, просто искать в $backup_dir последний по дате снапшот с помощью ну скажем find $backup_dir -maxdepth 1 -exec stat -c '%n' {} +|tail -n1 и линковать на него.


  1. saboteur_kiev
    13.11.2019 02:04

    А зачем для переименовывания такой большой и ненужный цикл, если можно (echo для наглядности)

    for file in {0..9}; do echo mv www.$file www.$(($file+1)); done


    1. mikhailian Автор
      13.11.2019 23:19

      Потому, что бекапов может быть меньше, чем 10.


      1. saboteur_kiev
        16.11.2019 01:14

        Ох, какое непреодолимое препятствие.
        for file in {0..9}; do echo if [ -d www.$file ]; then mv www.$file www.$(($file+1)); fi; done


  1. Pinkerton42
    13.11.2019 10:34

    А зачем такой огород, если rsync умеет --backup-dir=$(date +%Y%m%d), который держит актуальную копию данных в отдельном каталоге, а изменившиеся (с момента создания предыдущей копии) файлы складывает в другой с сохранением всей иерархии? Т.е. в результате имеем полную копию всего и рядом историю изменений (между копиями) каждого файла. Без всяких скриптов


    1. Pinkerton42
      13.11.2019 11:26

      поправлюсь… не "… изменившиеся файлы складывает...", а старые версии изменившихся файлов кладет рядом, а в основном снапшоте — всегда актуальные файлы.


      1. mikhailian Автор
        13.11.2019 23:23

        Так ведь хардлинков нет. Они нужны для того, чтобы внутри каждой из папочек www.0, www.1, www.2 содержимое соответствовало копии на момент копирования. Ну и для дедупликации. Вам же не хочется 10 копий одного и того же файла хранить.


        1. Pinkerton42
          14.11.2019 04:29

          Не нужна дедупликация, т.к. ничего "лишнего" не хранится. Рядом по каталогам рассовываются только изменившиеся файлы. В итоге получается, что есть всегда актуальный спапшот каталога-источника и рядом "история" прошлых версий изменившихся файлов:


          main
            folder
              file1
            file2
            file3
          20191112
            file2
          20191113
            folder  
              file1
            file3

          Т.е., чтобы получить данные на какую-то конкретную дату, берется каталог "main" и сверху него кладется с заменой каталог с нужной датой.


          1. Pinkerton42
            14.11.2019 06:43

            т.е. последовательно "отмотать" все даты… что-то я туплю с утра.


  1. dphilipo
    13.11.2019 10:52

    вот так я делаю бэкапы.
    cp -al делает не копирует файлы, а создает жесткие ссылки на них.
    rsync потом копирует и создает только изменившиеся файлы.
    В результате получаются директории в которых находятся "снапшоты" копируемой директории.
    Если вы копируете какой-нибудь офисный файловый ресурс, то корневую директорию резервных копий можно подключить через плагин теневых копий samba и пользователи будут видеть состояние ресурса на каждый день.


    basepath=/path/to/backup
    today=`date +%Y%m%d`
    yesterday=`date --date=«yesterday» +%Y%m%d`
    monthago=`date --date=«30 day ago» +%Y%m%d`
    
    todaydir=$basepath/$today
    yesterdaydir=$basepath/$yesterday
    monthagodir=$basepath/$monthago
    
    mkdir -p $todaydir
    
    cp -al $yesterdaydir $todaydir
    
    rsync -a --delete /path/to/files/ $todaydir
    
    rm -rf $monthagodir


  1. Netbioz
    13.11.2019 23:25
    +1

    Т.е. получается если похерился или случайно удалил мастер-бекап, все остальные будут нерабочими?


    1. terantul
      14.11.2019 06:09

      скорее частично рабочими.
      Теже Бакула\Бареос тоже при потере Фулла, по Инкрементам не всегда смогут восстановить актуальную копию.
      ЗЫ. хотя были случаи когда с момента создания Фулла все файлы менялись как минимум один раз. В итоге получилось восстановить актуальную копию только по Инкрементам.