Создание скриптов резервного копирования всегда представляется простой, нудной и очень обычной задачей. Напиши скрипт, поставь его в крон, проверь, что он сработал — казалось бы все, да? Но это только верхушка айсберга, а под водой скрывается огромное количество проблем. Все помнят недавную проблему на gitlab, когда оказалось, что операция по удалению данных была проведена не на резервном, а на основном сервере БД, бэкапы оказались размером в 0 байт, бэкапы в S3 недоступны, но, на счастье, резервная копия оказалась на одном из других серверов.

image

Как быть уверенным, что резервное копирование действительно работает? И что даже если скрипты работают, то данные в архивах есть? Что бэкапится именно то, что нужно? По нашей статистике, проблемы с резервным копированием происходят раз в 21 день. Если вы не проверяли ваши бэкапы дольше этого времени — возможно, у вас есть проблемы. В посте мы расскажем о своем опыте по созданию системы резервного копирования в гетерогенной инфраструктуре из 2000 машин, 20 терабайт ежедневных бэкапов самых разных систем, проблемах, которые мы встречали на своем пути, и как мы их решаем.

Долгое время мы не занимались методичной организацией резервного копирования у клиентов. Кто-то говорил, что делает его сам, кто-то просил нас настроить «пару скриптов» в их хранилище, у кого-то скрипты остались от старого администратора — мы проверяли, что они действительно существуют и дальше спокойно жили с этим знанием. Так было, пока количество клиентов не перевалило за сотню. Почти все проекты живут активной жизнью, и, как и в любом инженерном проекте, главной причиной проблем у клиентов является человеческий фактор. Случайное удаление данных — редкий, но самый неприятный его пример.

Пока клиентов было немного, это случалось очень редко, и ситуация спокойно обрабатывалась в индивидуальном порядке, но заработала теория больших чисел. Кто-то, как и gitlab, путал production и failover системы, у кого-то неверно работали скрипты обсчета статистики, в интернет-магазинах неверно происходила выгрузка товаров (загружались неверные цены, удалялись товары, которые были в наличии, приходила неверная информация о количестве товаров). С каждым разом рутина усложнялась: у кого-то бэкапы были на ftp-хранилище хостера, у кого-то — лежали в S3 или на том же сервере, где кто-то случайно выполнил rm -rf.

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

Мы начали писать свои скрипты. Как и все, мы любим изобретать колесо. Почему мы не воспользовались уже существующими сервисами? Существующие проекты довольно перегружены ненужными функциями, зачастую в них любят мудрить, они требуют установки каких-нибудь внешних пакетов, что при нашем разнообразии видов и форм конфигураций серверов было немалым минусом.

Написать свои бэкап-скрипты было намного проще, чем разбираться в зоопарке уже существующих. Писать было решено на bash, в первую очередь так как bash един для всех, в отличие от, например, python, не требует никаких зависимостей, и не привязывает к нам — bash понятен любому системному администратору.

Изначально был только один скрипт, в котором последовательно делались сначала дампы БД, а потом бэкап файлов сайта. Со временем мы разделили его на три: если один из них свалится с ошибкой, то другой независимо от первого все равно запустится.

Мы начали с создания трех простейших скриптов:

  • тот, что делает бэкап конфигов сервера, то есть /etc с исключением логов, кроны, нестандартное ПО (со временем туда добавились список активных процессов и список установленных пакетов);
  • бэкап БД, дамп mysql или postgres/mongo/redis;
  • бэкап файлов сайта (условно /var/www).

О мониторинге


Так как у нас есть своя система мониторинга, то неплохо было бы объединить ее со скриптами, чтобы получать алерты о провалах при создании бэкапа. Первым делом были добавлены декораторы выхода.

Для tar (архивация файлов):

die_if_tar_failed() {
  exitcode=$?
  #1 is ok
  [ $exitcode -eq 0 -o $exitcode -eq 1 ] && return 0
  echo `date +"%Y-%m-%d-%H%M%S"` $1 exitcode $exitcode >>${ERROR_LOG}
  exit 1
}

Для всего остального:

die() {
  exitcode=$?
  echo `date +"%Y-%m-%d-%H%M%S"` $1 exitcode $exitcode >>${ERROR_LOG}
  exit 1
}

В случае провала exitcode ошибки записывается в лог. Наличие записей в логе проверяется агентом мониторинга, который пушит их количество в алертницу. Такую же логику можно использовать и без мониторинга, настроив оповещение письмами — например, небольшой скрипт, который можно добавить в конец скрипта или в крон:

#!/bin/bash
if [ $(wc -l /backup/logs/error.log | awk '{print $1}') -gt 0 ] ; then
mail -s "Backup alert" mail@example.ru < /backup/logs/error.log
fi

Не забудьте обнулить еррорлог после того, как проблема будет устранена:

echo -n > /backup/logs/error.log

Со временем простых алертов нам оказалось мало, и мы начали дополнительно проверять бэкапы в хранилище, подробнее об этом напишем в следующих постах.

О бэкапировании СУБД и репликах


Подробно об этом мы как-то рассказывали у себя в блоге (тут и тут).

Бэкап стримится сразу в хранилище:

/usr/bin/innobackupex --defaults-extra-file=/root/.my.cnf --no-timestamp $DIR/$DT --slave-info --parallel=1 --stream=tar 2>> $LOG | gzip -c | ssh ${SSH} "cat -> ${REMOTE_PATH}/${BACKUP_NAME}" || check_in innobackupex

Бэкап сохраняется локально:

/usr/bin/innobackupex --defaults-extra-file=/root/.my.cnf --slave-info --no-timestamp --stream=tar ./ 2>>$LOG | gzip - > ${BACKUP_NAME}; check_in innobackupex

Или на случай, если у клиента всё хрустит в момент работы gzip, сжимать можно в хранилище:

/usr/bin/innobackupex --defaults-extra-file=/root/.my.cnf --no-timestamp $DIR/$DT --slave-info --parallel=1 --stream=tar 2>> $LOG | ssh ${SSH} "gzip - > ${REMOTE_PATH}/${BACKUP_NAME}" || check_in innobackupex

Если же клиент хочет хранить бэкапы в своем отдельном хранилище или облаке, то это может выглядеть примерно так:

/usr/bin/innobackupex --defaults-extra-file=/root/.my.cnf --no-timestamp $DIR/$DT --slave-info --parallel=1 --stream=tar 2>> $LOG | gzip -9 | s3cmd put - s3://${BACKET}/${BACKUP_NAME} || check_in innobackupex

Аналогично это работает для файловых бэкапов, на примере Битрикса:

tar czhf - /home/bitrix/www/ --exclude=bitrix/{managed,local,stack}_cache --exclude=bitrix/backup --exclude=upload/resize_cache | ssh ${SSH} "cat -> ${REMOTE_PATH}/${BACKUP_NAME}"  \; >>$LOG 2>&1 || die_if_tar_failed files_tar

Xtrabackup пишет удобный и подробный лог во время создания дампа, в случае успеха всегда есть запись в последней строке:

innobackupex: completed OK!

Поэтому для него тоже был написан декоратор типа:

check_in() {
if [ -z "`tail -1 ${LOG} | grep 'completed OK!'`" ]
then
 echo `date +"%Y-%m-%d-%H%M%S"` $1 >> ${ERROR_LOG}
 exit 1
 fi
}

Как это ни удивительно, но самая часто встречающаяся ошибка при создании бэкапа xtrabackup’ом — невозможность авторизоваться в mysql. Можно использовать опции:

innobackupex --user=DBUSER --password=SECRET

Но удобнее пользоваться конфигом:

/root/.my.cnf

Тут есть особенность: если у вас не используется авторизация по паролю, то xtrabackup будет ругаться, что не может подключится. В таком случае в конфиге можно написать:

[client]
user     = root
password =

Другие СУБД бэкапим стандартными средствами. Примеры:

С MongoDB всё просто, если её версия ? 3.2:

/usr/bin/mongodump --authenticationDatabase admin  --username DBUSER --password=SECRET --archive --gzip | ssh ${SSH} "cat -> ${REMOTE_PATH}/${BACKUP_NAME}.tgz" \;

Для PostgreSQL чаще всего мы делаем дамп БД целиком:

su postgres -c pg_dumpall -U postgres | 2>>$LOG | ssh ${SSH} "gzip - > ${REMOTE_PATH}/${BACKUP_NAME}" || die_if_tar_failed 

Бывают случаи, когда нужен бинарный бэкап PostgreSQL:

su postgres -c pg_basebackup -D - -Ft 2>> $LOG | ssh ${SSH} "gzip - > ${REMOTE_PATH}/${BACKUP_NAME}" || die_if_tar_failed

Для бэкапа Redis мы используем такую конструкцию:

echo "[`date`] backup redis 6383 started" >>$LOG 
  CLI="redis-cli -p 6383 -h 127.0.0.1"
if [ -f ~/.redispass ]; then
    CLI="${CLI} -a $(cat ~/.redispass)"
fi
dump=$(echo "CONFIG GET dir" | ${CLI} | grep ^/)/redis.rdb
echo bgsave | ${CLI} >> $LOG
try=10
save_complete=0
sleep 10
while [ $try -gt 0 ] && [ $save_complete -eq 0 ] ; do
  BG=$(echo 'info' | ${CLI} | awk -F: '/bgsave_in_progress/{sub(/\r/, "", $0); print $2}')
  if [[ "${BG}" = "0" ]] ; then
    save_complete=1
    echo "Saving dump is finish" >> $LOG
  else
    echo "Wait save dump" >> $LOG
    try=$((try - 1))
    sleep 60
  fi
done
if [ $save_complete -eq 1 ];then
  tar czhf -  ${DUMP} | ssh ${SSH} "cat -> ${REMOTE_PATH}/${BACKUP_NAME}"  \; >>$LOG 2>&1 || die_if_tar_failed redis_6383_tarring
  else
  die redis_6383_bgsave_timeout
fi 

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

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


  1. erlyvideo
    30.08.2017 17:04

    как вы отслеживаете ситуацию, что ваш бекап укладывает продакшн?


    1. antosha Автор
      30.08.2017 17:19

      Все основные параметры сервера (использование CPU, нагрузка на диски) нами мониторятся, и если во время бэкапа нагрузка чрезмерна — бэкап сразу же останавливается. После этого добавляем nice/ionice в запуск бэкапных скриптов. У некоторых утилит — например, у xtrabackup'a — есть встроенные параметры для регулирования нагрузки во время создания бэкапа. Ну и плюс при настройке бэкапов делаем соответствующие правки в скриптах, исходя из опыта.

      Создавать бэкапы нужно в самое наименее нагруженное время суток — в этом случае аффект работы проекта минимален.

      Если есть возможность — то делать бэкапы лучше не на prod-серверах, конечно, а на реплике БД или резервном сервере, куда синхронизируются файлы с production. Но об этом — в следующих частях :)


      1. Dmitry88
        30.08.2017 19:37

        Единственный вопрос, а что является базой для самой СРК?


        1. antosha Автор
          31.08.2017 11:45

          Вся конфигурация бэкапов описана в плейбуках ансибла. В текущей реализации так.


  1. Dmitry88
    30.08.2017 18:06
    +1

    По-моему надо просто не жадничать и потратиться на нормальную СКР с политиками хранения, с поиском, репортами и т.д, а не создавать монстра из скриптов


    1. dbax
      30.08.2017 18:31

      А можно не тратиться и поставить, например bacula.


      1. antosha Автор
        30.08.2017 18:39

        А bacula мы изначально не стали внедрять, т.к. на наш взгляд это монструозная система с большим количеством лишних для нас функций, на доработку которой под наши нужды потребовалось бы значительное количество времени.


      1. Dmitry88
        30.08.2017 18:44

        как бюджетный вариант вполне, для серьезного продакшна я бы не рекомендовал.


        1. antosha Автор
          30.08.2017 18:49

          Мы поддерживаем проекты самых разных масштабов. Поэтому нам нужен был универсальный инструмент, который можно применить везде.


        1. sohmstyle
          30.08.2017 22:52

          А что бы вы рекомендовали для серьёзного продакшена из бесплатных решений, если Bacula/Bareos не очень подходят.


          1. Dmitry88
            30.08.2017 23:14

            Из бесплатного — ничего (только Bacula/Bareos и есть по сути). Не знаю, продают ли за дешево (отдают ли за так) старые релизы СРК из gartner quadrant, но если смотреть туда и выбирать по своим ресурсам. Дорого — да, но — это стоит потери важных данных


            1. Dmitry88
              31.08.2017 00:09

              Еще дополню, что большинство коммерческих СРК имеют много преимуществ в плане компрессии, дедупликации, распределении данных, репликаций, очень хорошо утилизируют сеть и прочие плюшки.


    1. antosha Автор
      30.08.2017 18:38

      Стоит понимать, что изначально своя система бэкапов началась из совсем простых скриптов — основное назначение которых было простое создание хоть каких-то бэкапов данных.

      А потом они постепенно дорабатывались, исходя из новых требований. Интегрировались в наш мониторинг, в наши процессы, к ним добавлялась автоматизация — и в какой-то момент оказалось, что гораздо удобнее оставить именно их, а не пользоваться какими-то сторонними решениями. Со временем «монстр из скриптов» вырос в полноценный инструмент, который соответствует нашим нуждам и который мы можем расширять новыми модулями по необходимости.


      1. Dmitry88
        30.08.2017 18:47

        Если получился полноценный инструмент, то выкладывайте в мировой доступ или продавайте.
        А так выглядит, как кастомное решение. И это не упрек, если работает, то и хорошо


        1. antosha Автор
          30.08.2017 18:51

          Он будет выложен в открытый доступ :) Ближе к концу года, вероятно.


  1. pansa
    30.08.2017 19:02
    +3

    >С MongoDB всё просто, если её версия ? 3.2: /usr/bin/mongodump…

    Да, всё просто.
    Просто вы так теряете индексы.


  1. timelle
    31.08.2017 04:11

    Было и почище. ISP. Бекапы настроены, всё исправно архивируется. Кроме одного «НО».
    Базы в UTF-8, а настройках указано «авто». Вот это авто писало кириллицу неизвестно в чём. В дампах — UTF-8. Но вместо кириллицы знаки вопроса. Хорошо, что проект не в боевом режиме и база относительно маленькая и данные там однотипные. Поправил руками. А иначе бы капут. Проверяю теперь все бекапы.


    1. antosha Автор
      31.08.2017 04:13

      Да, такое часто случается. Мы все через это прошли, когда-то давно, когда еще пользовались панелями управления)
      Именно поэтому мы делаем бинарные бэкапы MySQL — xtrabackup'ом, а потом при необходимости на бэкапном стенде достаём нужную базу или таблицу, проверяя, что с кодировками всё хорошо.


  1. DesertFoxAst
    31.08.2017 11:33

    Более 2000 машин… СРК нужно. Единый центр управления и мониторинга будет.


    1. antosha Автор
      31.08.2017 11:44

      Мы потихоньку идем к тому, чтобы сделать свою собственную централизованную систему управления бэкапами.


  1. FlashHaos
    01.09.2017 23:39

    «Целый отдел» и «2000 машин и 20 терабайт ежедневно» — очень странно смотрятся друг рядом с другом. Куда тут целый отдел для таких объемов? Не хотелось бы никого ничему учить, но неужели оплата труда квалифицированных инженеров, которые будут лепить велосипеды, выходит дешевле, чем купить нормальный бекапный софт и посадить на его администрирование одного единственного инженера по совместительству?


    1. antosha Автор
      03.09.2017 05:34

      Ввиду нашей специфики работы и количества клиентов/проектов/серверов на поддержке — задачи бэкапного отдела сводятся к решению всевозможных задач клиентов из разряда «тут всё поломалось, надо понять как восстановить, чтобы не потерять изменения, произошедшие с момента последнего бэкапа» и к развитию нашей системы создания бэкапов (дописывания новых модулей для нового ПО, новых правил проверки).
      Благодаря тому, что резервные копии хранятся в удобном для нас формате — мы всегда можем достаточно оперативно достать нужную часть данных.
      Ну и, опять же — применение нашего самописного решения для бэкапов уже не раз себя оправдало. Если бы оно чем-то нас не устраивало — то было бы заменено на что-то другое.