Pull to refresh
140.57
ITSumma
Эксперты в производительности

Резервное копирование не «для галочки». Часть первая: мониторинг, бэкапы баз данных и реплики

Reading time 6 min
Views 22K
Создание скриптов резервного копирования всегда представляется простой, нудной и очень обычной задачей. Напиши скрипт, поставь его в крон, проверь, что он сработал — казалось бы все, да? Но это только верхушка айсберга, а под водой скрывается огромное количество проблем. Все помнят недавную проблему на 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, файлов сайта и больших объемах, о проверках бэкапов (мы обязательно проверяем их на работоспособность). А позже выложим свое решение в опенсорс.
Tags:
Hubs:
+23
Comments 22
Comments Comments 22

Articles

Information

Website
www.itsumma.ru
Registered
Founded
Employees
101–200 employees
Location
Россия
Representative
ITSumma