Простой скрипт для инкрементального бекапа директорий

    С чего все начиналось


    «Есть 2 типа админов — те, кто ещё не делают бекапы, и те, кто уже делают.»
    Будучи первым типом админа, после случайного rm -rf * в директории с проектом, очень печально осознавать, что последняя резервная копия была сделана около полугода назад. Попав в такую ситуацию, я начал поиск простого и не ресурсоемкого средства для резервного копирования информации.
    Критерии:
    1) Возможность делать бекапы на лету в условиях ограниченных системных ресурсов (например VPS)
    2) Возможность нативно удалять устаревшие бекапы
    3) Возможность работать с примонтированной файловой системой

    Процесс


    После некоторого гугления/яндексения выбор пал на утилиту rdiff-backup (офф. сайт).
    Все возможности данной утилиты вместе с примерами и документацией есть на сайте, скажу вкратце, что всем трем пунктам она соответствует.
    В качестве хранилища для файлов я использую домашний роутер с внешним хардом (монтирую к серверу посредством sshfs), поэтому в скрипте использую именно ее. Кроме того, хотелось получать уведомления о результатах бекапа на почту — это также предусмотрено.

    Результат


    #!/bin/sh
     
    REMOTE_ADDR='user@storage:/remote_path' # Путь до удаленного хранилища
    MOUNTPOINT='/backup_remote' # Точка монтирования бекапного раздела
    BACKUP_DIR='/somedir' # Директория которую хотим бекапить
    MAILFROM='root@server' # Адрес, с которого посылать отчеты
    MAILTO='mail@example.com' # Адрес, на который будут приходить отчеты
    EXPIRE="1W" # Время, которое хранить инкрементальные файлы
     
    TMP='/tmp/backup_tmp.tmp'
     
    sshfs $REMOTE_ADDR $MOUNTPOINT > /dev/null 2>&1
     
    if [ `mount | grep $MOUNTPOINT | grep -vc grep` = "0" ]then
        echo "Error mounting $MOUNTPOINT at `date +'%d/%m/%Y %H:%M'`" | mail -a "From: $MAILFROM" -s "Backup ERROR" $MAILTO
        exit 1
    fi
     
    if [ ! -d $MOUNTPOINT/$BACKUP_DIR ]then
        mkdir -p $MOUNTPOINT/$BACKUP_DIR > /dev/null 2>&1
    fi
     
    printf "Processing $BACKUP_DIR... \n\n" >> $TMP
    rdiff-backup --force --exclude-symbolic-links --exclude-sockets --exclude-special-files --exclude-fifos --exclude-device-files --no-hard-links --print-statistics $BACKUP_DIR $MOUNTPOINT/$BACKUP_DIR >> $TMP 2>&1
    rdiff-backup --force --no-hard-links --remove-older-than $EXPIRE $MOUNTPOINT/$BACKUP_DIR >> $TMP 2>&1
    printf "\n-----------------------\n\n" >> $TMP 
     
    ERRORS="no errors"
     
    if [ `cat $TMP | grep 'Error' | grep -v 'Errors 0' | grep -cv grep` !"0" ]then
        ERRORS="errors detected"
    fi
     
    cat $TMP | mail -a "From: $MAILFROM" -s "Backup report (${ERRORS})" $MAILTO
    rm -f $TMP
    umount $MOUNTPOINT
     
    exit 0
     

    Скрипт одним файлом можно взять здесь.

    Итог


    Поместив скрипт в крон получаем инкрементальные бекапы заданной директории с настраиваемым сроком хранения и отчетами о выполнении.
    Восстановление файлов из последней версии можно производить простым копированием, для восстановления до определенной версии пользуемся утилитой rdiff-backup.

    Приятного пользования!
    Share post

    Comments 34

      +7
      YET ANOTHER BACKUP script :)
        +1
        парсер такой парсер
          0
          Yanbacks =)
          Нет, у нас тут все несколько посложнее, это для себя писал =)
          +3
          Как только не изгаляются люди не желая использовать VCS.
            –14
            Не VCS а CVS (SVN), и никто не говорит что не использую. Правда для других задач — как бы системы контроля версий несколько для иных целей создавались имхо.
              +6
              VCS — Version Control System — наиболее общее определение для всех систем контроля версий. Это так, для справки.
                –7
                Для справки — я в курсе. И в данном контексте rdiff-backup в таком случае является таковой системой, isn't it?)
              +1
              причем тут vsc, если нужно бекапить файловое хранилище?
                0
                > в директории с проектом

                Не уточнено, что за проект. Я как программист, под проектом подразумеваю толпу исходников, которые лежат в директории.

                Если проект- это обработаное видео, то да, другой разговор.
                  0
                  Обычно, далеко не весь проект (в программистском смысле) находится под VCS. Например, локальные настройки и файлы, относящиеся к данным.
                  +2
                  VCS помог бы, но при условии что у него можно было бы чистить историю как в rdiff-backup:
                  rdiff-backup --force --remove-older-than 20D /backups
                +1
                А '--exclude-special-files' не синоним для '--exclude-device-files --exclude-fifos --exclude-sockets --exclude-symbolic-links'?
                  0
                  Да и симлинки, ИМХО, иногда полезно хранить.
                  0
                  А если хочу бэкапить не одну, а несколько директорий за раз? Делать кучу копий скрипта?
                    +1
                    я делаю вот так:

                    скрипт раскладывает по папочкам каждый день изменений в отдельности и держит текущую версию в папке CURRENT. После чего просто по ssh заливаю на сторейдж папочку текущего дня в архиве.

                    do_rsync()
                    {
                    /usr/local/bin/rsync ${OPTIONS} --backup-dir=${DSTDIR}/${INCDIR} ${SRCDIR} ${DSTDIR}/${CURRENT}
                    }

                    OPTIONS="--delete-excluded --delete-after --backup -logthvrpF"
                    INCDIR=`date +%Y-%m-%d`
                    CURRENT=«current»

                    SRCDIR="/etc"
                    DSTDIR="/opt/backup/rsync/etc"
                    do_rsync

                    SRCDIR="/home/"
                    DSTDIR="/opt/backup/rsync/home"
                    do_rsync

                    SRCDIR="/opt/www/"
                    DSTDIR="/usr/var/backup/www"
                    do_rsync
                      0
                      Блин, может я придираюсь потому что программист, но передавать функции аргументы через глобальные переменные, это как-то не очень.
                        0
                        Согласен. Это просто издержки предыдущей версии скрипта, он раньше был вообще без аргументов.
                          0
                          Бывает и такое, понимаю.
                          Вот сейчас сам сижу и переписываю свои бекапные срипты (их не мало и они сложные), чтобы новый админ понял, что там написано и как оно работает. :-)
                      0
                      for i in `echo $BACKUP_DIR`; do
                      ...
                      fi

                      Внутри блок, начинающийся с if [ ! -d $MOUNTPOINT/$BACKUP_DIR ]; then... и заканчавающийся перед ERRORS=…
                      Внутри блока $BACKUP_DIR на $i поменять, разумеется.
                      0
                      «grep -vc grep» -> «wc -l»
                      портабельней будет.
                        0
                        Да, черт, привык вывод ps парсить, никак от привычки не избавлюсь.
                        +1
                        Рекомендую Bacula
                        Ресурсов почти не жрет, кстати. Разобраться в настройке можно побыстрее чем написать «YET ANOTHER BACKUP script».
                          0
                          Бакула не удобна тем, что на хранилище надо иметь как минимум bacula-sd, я уж не говорю про наличие fd и director'a.
                          А у меня на VPS память не резиновая.
                            0
                            1) ну, могу сказать что все демоны могут находиться даже на одной машине а sd может с таким же успехом складывать архив на удаленную ФС, хотя это извращение конечно и не так безопасно.

                            2) ресурсов на самом деле потребляет минимум. клиентский fd у меня на VPS кушает ~1.5Мб оперативки

                            Кстати, вон в комментариях многие жалуются что при смене админа почти всегда приходится менять бекап-скрипты т.к. в чужих никто разобраться не сможет. А бакула промышленное решение, там с этим проблем не должно быть. Если админ бакулу не осилит — в топку такого админа)) Если под себя все делается то этот момент не так актуален конечно.

                            Хотя в целом конечно по ситуации надо смотреть. Если у вас 1-2 сервера, а то и просто виртуальный хостинг, то можно скриптами отделаться.
                            Но если что-то посерьезнее — то нужно и решение соответствующее.
                          0
                          Когда дело касается баша, мне проще самому для своих задач написать, чем разбирать чужие скрипты.
                          А так да, использую rdiff-backup. И ещё rsync, так как он быстрее синхронизирует дерево, а это важно для соблюдения целостности также бекапящейся базы данных, которая ссылается на файловую.
                            0
                            Не так давно выбирал себе что-нибудь для бэкапа, и rsnapshot понравился больше, чем rdiff-backup. Настройка несколько прозрачнее и удобнее. Несколько уровней копий (ежечасные, ежедневные, и т. д.) с независимыми расписаниями, восстановление до любого состояния простым копированием, и работает, как мне показалось, быстрее.
                              +1
                              Всё, что генерирует много файлов, плохо для хранения бэкапа на внешнем сервере. Как, например, эти файлы перенести на FTP сервер? Никак, очень будет медленно. Восстановление пофайловое с внешнего сервера тоже небыстрое.
                              Самый простой способ — основывать бэкапную систему не на rdiff-backup, а на dar, конечным продуктом будет один файл на первую копию (full) и по одному файлу на инкрементальные. В качестве обвязки можно использовать www.backup-manager.org/. Кстати, в случае с dar инкрементальную копию можно получить, не храня на компьютере full копию, можно хранить только метаинформацию от неё.
                                0
                                Спасибо, возьму на заметку.
                                  0
                                  Если есть возможность (а обычно для данных задач она есть), много файлов очень элегантно заменяются одним:
                                  # big sparse container — 1Gb
                                  dd if=/dev/zero of=/big-backup-container.raw bs=1024 count=1 seek=1024k
                                  # mkfs
                                  yes|mkfs.ext2 /big-backup-container.raw
                                  # each time backup
                                  mount -o loop,noatime,rw /big-backup-container.raw /mnt/backup
                                  rdiff-backup /very-important-files /mnt/backup
                                  umount /mnt/backup
                                  p.s. надеюсь это объяснять никому не надо? чуть более сложно используется в моих скриптах резервного копирования
                                    0
                                    ну нам-то надо иметь возможность отправить на ftp сервер только изменения, а не весь архив.
                                  0
                                  есть еще третяя категория — те кто видел слёзы первых и проникся.

                                  а вообще инфа существующая в единственном экземпляре — заведомо потеряна.
                                    0
                                    для меня проблема бэкапа решилась с написанием github.com/astrails/safe
                                      +1
                                      А я отношусь еще к более параноидальной категории — делаю бэкапы в 5 точек сразу. :)
                                      А то был печальный опыт с умиранием диска с бэкапом как раз в момент падения сервера (при этом, продакшн и бэкап сервер находились в разных концах города). Я уже не говорю про ситуации, когда в бэкап-сервер находится в одном ДЦ с продакшном…
                                        0
                                        Сурово)
                                        Ну я все-же верю, ч то вероятность подобного стремиться к 0, делаю бекапы в одно место.

                                      Only users with full accounts can post comments. Log in, please.