Скрипт резервного копирования через rsync

    Возникла необходимость как-то и куда-то бэкапится. Причём чтобы процессоры не грузились и место не занималось, а бэкапы ротэйтились и удобно доставались. Раньше всегда пользовался fsbackup, но захотелось отказаться от архивирования. Для решения задачи была использована rsync и механизм жёстких ссылок (так называемых хардлинков) файловой системы.

    Архитектура: есть отдельно стоящий сервер с большим винтом — на нём и работает скрипт. Есть много разных серверов с доступом по ssh, на которых в ~/.ssh/authorized_keys добавлен публичный ключ пользователя, под которым работает скрипт резервного копирования.

    Логика работы: в определённое время скрипт по ssh синхронизирует содержимое папки на удалённом сервере с папкой domain.com/latest, а потом копирует её в папку с сегодняшней датой, создавая при этом жёсткие ссылки на файлы, затем удаляет папки, дата создания которых старше 7 дней. Т.к. синхронизируется только содержимое каталога, дампить базу по крону нужно на клиентской машине перед тем, как rsync заберёт файлы.

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

    Вопрос:
    — т.к. скрипт был первоначально опубликован в бложике, так и не удалось услышать авторитетное мнение относительно эффективности такого подхода — был бы рад, если бы вы поделились мыслями…

    
    #!/bin/sh
    # simple rsync backup script written by farmal.in 2011-01-21
    #
    # latest backup is always in $SDIR/domains/$domain/latest folder
    # all backups which are older than 7 days would be deleted
    # backup.ini file can't contain comments, empty lines and spaces in domain names
    #
    # example of a GOOD backup.ini:
    # mydomain.com user@mydomain.com:/path/to/public_html
    #
     
    SDIR="/usr/local/backup"
    SKEY="$SDIR/.ssh/id_rsa"
    SLOG="$SDIR/backup.log"
    PID_FILE="$SDIR/backup.pid"
    ADMIN_EMAIL="email@domain.com"
     
     
    if [ -e $PID_FILE ]; then
            echo "this task is already running or previous run was completed with errors on `hostname`" | mail -s "Some mess with backups on `hostname`..." $ADMIN_EMAIL
            exit
    fi
     
    touch $PID_FILE
     
    # redirecting all output to logfile
    exec >> $SLOG 2>&1
     
    # parsing backup.ini file into $domain and $from variables
    cat backup.ini | while read domain from ; do
    	destination="$SDIR/domains/$domain"
    	# downloading a fresh copy in 'latest' directory
    	echo -e "`date` *** $domain backup started">>$SLOG
     
    	# start counting rsync worktime
    	start=$(date +%s)
    	rsync --archive --one-file-system --delete -e "ssh -i $SKEY" "$from" "$destination/latest" || (echo -e "Error when rsyncing $domain. \n\n For more information see $SLOG:\n\n `tail $SLOG`" | mail -s "rsync error" $ADMIN_EMAIL & continue)
    	finish=$(date +%s)
    	echo -e "`date` *** RSYNC worked for $((finish - start)) seconds">>$SLOG
     
        # cloning the fresh copy by hardlinking
    	cp --archive --link "$destination/latest" "$destination/`date +%F`"
    	# deleting all previous copies which are older than 7 days by creation date, but not 'latest'
    	find "$destination" -maxdepth 1 -ctime +7 -type d -path "$destination/????-??-??" -exec rm -r -f {} \;
    	echo "`date` *** The size of $domain/latest is now `du -sh $destination/latest | awk '{print $1}'` ">>$SLOG
    	echo -e "`date` *** $domain backup ended">>$SLOG
    	echo -e "`date` *** Total allocated `du -sh $destination | awk '{print $1}'`">>$SLOG
    	echo -e "------------------------------------------------------------------">>$SLOG
    done
     
    rm $PID_FILE
    
    Метки:
    Поделиться публикацией
    Похожие публикации
    Комментарии 36
    • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        да, действительно использовать logger правильнее, при возможности разберусь с его документацией

        про ошибку при наличии pid файла — дело в том, что в конце работы скрипта он удаляется безусловно. т.е. если скрипт доработал до конца, его не будет. таким образом наличие этого файла может свидетельстовавать о 1) процесс идёт 2) скрипт убили 3) во время последнего запуска сервера произошла перезагрузка системы. Или как Вы предлагаете?
        • +1
          а использовать конструкцию типа
          if [ -e $PID_FILE ]; than
          if [ -z `ps ax | awk '{ print $1;}' | grep $PID_FILE`]; then
          echo " process $PID_FILE died"
          rm $PID_FILE
          else
          echo " process $PID_FILE still working"
          exit
          fi
    • +7
      Это неплохо, но не смотрели на rsnapshot? Тот же rsync, + perl.
      • 0
        спасибо — действительно раньше не видел (если бы настроил rsnapshot, отпала бы необходимость писать свой). у него больше возможностей, а принцип работы аналогичен.
      • 0
        А базы mysql можно бекапить rsync-ом?
        • 0
          >дампить базу по крону нужно на клиентской машине перед тем, как rsync заберёт файлы.
          • –2
            вы имеете в виду изпользующиеся в данный момент .MYD .MYI и .frm файлы MySQL?
            • 0
              man mysqldump
              • 0
                как показывает практика дампить базы по 10 гб — немного гиморно. выручает mysqlhotcopy
            • +1
              если вы имеете ввиду сами файлы баз, то можно, но перед «горячим» копированием нужно «заморозить» состояние базы и сбросить кеши. Т.е. в любом случае одним рсинком не обойтись, нужно предварительно совершить некоторые действия с самой базой. Более подробно все расписано в главе 6 официального мануала.
            • +3
              Что только ни придумают, лишь бы не использовать Bacula
              • 0
                Бакула очень тяжело переживает наличие большого количества файлов.
                При бекапе 6 серверов и кол-ве файлов больше миллиона пользоваться бакулой стало просто нереально.
                • 0
                  В бакуле есть возможность выполнять скрипты до/после бэкапа. Если места на диске достаточно, то можно все файлы засунуть в архив, скопировать этот архив, затем удалить этот архив. Логичный минус — то что копируется всё скопом, а не выборочно исходя из изменений. Но если места хватает, это даже плюс.
                  Проверено, работает.
              • +3
                Уже больше 3х месяцев использую duplicity — масса положительных впечатлений.
                Поддерживает инкрементальные бекапы, указание списка каталогов и исключений (откуда бекапить), шифрацию получившихся файлов (через gnupg), при этом на сервер бекапов отправляет только изменения.

                В итоге работает такая схема:
                1. Выделенный бекап сервер, на него есть доступ с основных серверов
                2. Каждый сервер по крону ежесуточно запускает инкрементальный бекап, еженедельно — полный

                p.s. Получение доступа злоумышленниками к бекап серверу не особо опасно с учётом того, что бекапы зашифрованы.
                • +1
                  у duplicity немного другой подход. насколько я знаю, она оперирует только tar файлами, что не всегда удобно (согласен, что с другой стороны не всегда удобно деревья файловой системы в тысячу файлов :) )

                  Получение доступа злоумышленниками к бекап серверу не особо опасно с учётом того, что бекапы зашифрованы.
                  у них там право только на запись, а не на удаление? я к тому что если сайт дефейснули, не смогут ли злоумышленники просто удалить все бэкапы на удалённом сервере?
                  • 0
                    Мне намного проще иметь на бекап-сервере десяток больших файлов вместо сотен-тысяч мелких.

                    По поводу удаления злоумышленниками — в принципе, могут. Хотя никто не мешает по cron'у на backup сервере файлам менять owner'а (через какое-то время после создания) и оставлять возможность только чтения.
                    Конкретно у меня так бекапятся виртуальные сервера, бекапятся из хост-системы, так что риски значительно ниже (linux + openVZ).
                    • 0
                      в обычных условиях это правильно, а у себя как раз продумываю возможность на несколько часов быстро превратить бэкап-сервер в резервный, на который можно моментально перекинуть днс в случае падения продакшн. хотя это всё на уровне сферического отказоустойчивого сервера в вакууме — игры разума.

                      тоже вариант
                • +1
                  Эффективнее использовать перенос подняв rsync-демон на бакапном сервере и использовать для авторизации rsync password-file, вместо ssh-ключей. Передача данных в таком случае значительно вырастет, особенно актуально при больших объемах передаваемых данных.
                  • 0
                    интересно, что там password-file никак не шифруется… а из-за чего рост производительности?
                    организовать бы ещё port-knocking...
                    • 0
                      За счет того, что через протокол rsync'a данные не шифруются ssl
                      • +1
                        да, но не ssl
                        rsa, dsa зависит какой ключ был сгенерирован
                  • +1
                    duplicity + S3
                    • +1
                      Пробовали много разных скриптиков бекапа в том числе и fsbackup (больше года на нем сидели), но все это фигня по сравнению с Bacula, попробуйте очень рекомендую
                      • +1
                        Есть rdiff-backup — делает то же самое, только отлажен и с несколькими плюсами.
                        • 0
                          • 0
                            dirvish.org делает тоже самое, только еще умеет еще удалять старые бэкапы. правда не уверен, что программа все еще поддерживается.
                            • 0
                              а что значит «удалять старые бэкапы»? тут тоже по истечении срока (7 дней по умолчанию) старые удаляются
                              • 0
                                агга, сейчас заметил удаление. там настройка более гибкая, типа как в time machine
                            • 0
                              Прикольно. Пару лет назад я сделал подобное на Perl+rsync. С поддержкой команд на удалённом сервере, чтобы можно было сделать экспорт баз, с параллельным запуском rsync-ов для разных серверов и прочими гибкими настройками. Быкаперы запускаются по крону с разными конфигами, в каждом конфиге описываются сервера, методы доступа к ним, если не дефолтные и количество копий, что надо держать.
                              • 0
                                Bontmia — работает как часы
                                • 0
                                  Я правильно понимаю, что в случае изменения какого-либо файла в папке latest, он изменится также во всех копиях (в папках названных датами)? То есть папка latest будет отличаться от копий только наличием новых файлов или отсутствием старых?
                                  • +1
                                    Вопрос очень хороший, в своё время я сам занимался его изучением. Чтобы на него ответить надо понимать механизм работы rsync по-умолчанию c параметром --delete. man говорит что:

                                    --delete-before receiver deletes before transfer (default)
                                    --delete-during receiver deletes during xfer, not before
                                    --delete-delay find deletions during, delete after
                                    --delete-after receiver deletes after transfer, not before

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

                                    • +1
                                      написал предыдущий коммент и понял что он ничего не объясняет :) вернее всё что там написано относится к файлам, которых больше нет в папке-источнике :)

                                      а то о чём Вы спрашиваете, происходит из-за механизма работы хардлинков. при модификации одного из файлов, на диске создаётся его модифицированная копия, т.е. происходит так называемый detach хардлинка.

                                      Допустим есть 2 бэкапа Backup1, Backup 2. Они содержат File1, File2 которые не изменялись и File3, который изменился. Это будет выглядеть так:

                                      image
                                    • 0
                                      Проверил. Всё работает отлично. Гениальное в простоте.
                                      Было бы еще хорошо если б какой-нть такой же вариант с базами MySql провернуть, а то они получаются архивированными дампами лежат, хотя меняется пара-тройка записей в одной таблице.

                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.