Настройка резервного копирования уверенно занимает одно из важнейших мест в деятельности администратора. В зависимости от задач резервного копирования, типов приложений и вида данных резервное копирование может осуществляться с помощью различных инструментов, таких как rsync, duplicity, rdiff-backup, bacula и других, коих существует огромное множество.
Помимо осуществления самого процесса резервного копирования, который бы отвечал потребностям организации, существует ряд проблем, которые неизбежно возникают при осуществлении резервного копирования, одна из которых — увеличение нагрузки на дисковую подсистему, что может приводить к деградации производительности приложений.
Решение данной задачи не является простым — часто администратор вынужден идти на компромиссы, которые ведут к тому, что продолжительность процедуры увеличивается или периодичность резервного копирования уменьшается с ежедневного до еженедельного. Данные компромиссы неизбежны и являются вынужденной реакцией на существующие технические ограничения.
И, тем не менее, основной ��опрос остается открытым. Как же осуществлять резервное копирование таким образом, чтобы основные приложения получали приемлемое качество обслуживания? Операционные системы семейства UNIX предоставляют штатный механизм управления приоритетами ввода-вывода для приложений, который называется ionice, кроме того, конкретные реализации UNIX предоставляют свои механизмы, которые позволяют наложить дополнительные ограничения. К примеру, в случае GNU/Linux существует механизм cgroups, который позволяет ограничить полосу пропускания (для физически подключенных устройств) и установить относительный приоритет для группы процессов.
Тем не менее, в некоторых случаях таких решений недостаточно и необходимо ориентироваться на фактическое "самочувствие" системных процессов, которое отражают такие параметры системы как Load Average или %IOWait. В этом случае на помощь может прийти подход, который я успешно применяю уже достаточно продолжительное время при осуществлении резервного копирования данных с LVM2 с помощью dd.
Описание задачи
Имеется сервер GNU/Linux, на котором настроено хранилище, использующее LVM2 и для данного сервера каждую ночь осуществляется процедура резервного копирования тома, которая выполняется с помощью создания снимка раздела и запуска dd + gzip:
ionice -c3 dd if=/dev/vg/volume-snap bs=1M | gzip --fast | ncftpput ...При осуществлении резервного копирования хочется выполнить его максимально быстро, но опытным путем замечено, что при повышение %IOWait до 30%, качество обслуживание дисковой системой приложений становится неприемлемым, поэтому необходимо держать его ниже данного уровня. Требуется реализовать ограничительный механизм, который бы обеспечивал обработку в предельно допустимых значениях %IOWait.
Поиск решения
Изначально для решения был применен подход с ionice -с3, но он не давал стабильного результата. Механизмы, основанные на cpipe и cgroups (throttling) были отброшены как не дающие возможности копировать данные быстро, если %IOWait в норме. В итоге было выбрано решение, основанное на мониторинге %IOWait и приостановке/возобновлении процесса dd с помощью сигналов SIGSTOP, SIGCONT совместно с сервисом статистики sar.
Решение
Схематично решение выглядит следующим образом:
- Запрашиваем статистику в течение N секунд и получаем среднее значение %IOWait;
- Определяем действие:
a. Если значение %IOWait < 30, то возобновляем процесс (SIGCONT);
b. Если значение %IOWait > 30, останавливаем процесс (SIGSTOP), увеличиваем счетчик; - Если процесс остановлен дольше чем N x K, возобновляем процесс и останавливаем его снова через 2 секунды
Скорее всего п.3 вызывает вопросы. Зачем такое странное действие? Дело в том, что в рамках резервного копирования осуществляется передача данных по FTP на удаленный сервер и если процесс копирования остановлен достаточно продолжительное время, то мы можем потерять соединение по таймауту. Для того, чтобы этого не произошло, мы выполняем принудительное возобновление и остановку процесса копирования даже в том случае, если находимся в "красной" зоне.
К��д решения приведен ниже.
#!/bin/bash
INTERVAL=10
CNTR=0
while :
do
CUR_LA=`LANG=C sar 1 $INTERVAL | grep Average | awk '{print $6}' | perl -pe 'if ($_ > 30) { print "HIGH "} else {print "LOW "}'`
echo $CUR_LA
MARKER=`echo $CUR_LA | awk '{print $1}'`
if [ "$MARKER" = "LOW" ]
then
CNTR=0
pkill dd -x --signal CONT
continue
else
let "CNTR=$CNTR+1"
pkill dd -x --signal STOP
fi
if [ "$CNTR" = "5" ]
then
echo "CNTR = $CNTR - CONT / 2 sec / STOP to avoid socket timeouts"
CNTR=0
pkill dd -x --signal CONT
sleep 2
pkill dd -x --signal STOP
fi
doneДанное решение успешно решило проблему с перегрузкой IO на сервере, при этом не ограничивает скорость жестко, и уже несколько месяцев служит верой и правдой, в то время, как решения, основанные на предназначенных для этого механизмах, не дали положительного результата. Стоит отметить, что значение параметра, получаемое sar может быть легко заменено на Load Average и иные параметры, которые коррелируют с деградацией сервиса. Данный скрипт вполне подходит и для задач, в которых применяется не LVM2 + dd, а, к примеру, Rsync или другие инструменты резервного копирования.
C помощью cgroups возможно таким же образом реализовать не остановку, а ограничение полосы, если речь идет о копировании данных с физического блочного устройства.
PS: Скрипт приведен без редактуры в оригинальном виде.
