Скрипт для полного и дифференциального бэкапа через LVM-снапшоты
Первый скрипт, показанный ниже, используется мной для создания полного и дифференциального (разницей между последним полным и текущим состоянием - как разновидность инкрементного, где архивируется разница между текущим состоянием и предыдущим, каким бы оно не было - полным или инкрементным) бэкапа сервера с использованием LVM-снапшотов.
Для работы скрипта добавляем записи с ним в crontab рута:
# Полный бэкап 25 22 1,15 * * /root/bin/diffbackup.sh 2>>/var/log/sysbackup.log # Дифференциальный бэкап 25 22 2-14,16-31 * * /root/bin/diffbackup.sh diff 2>>/var/log/sysbackup.log
В данном случае полный бэкап делается 2 раза в месяц - 1 и 15 числа, во все остальные дни в архив попадает только разница между последним полным бэкапом и текущим состоянием.
Для сжатия архивов используется параллельная версия bzip2 - pbzip2, которую необходимо предварительно установить.
Для восстановления раздела из бэкапа после создания файловой системы на новом диске (при необходимости) и монтирования раздела сначала переходим в точку монтирования и распаковываем архив с полным бэкапом, например:
tar xf rootfs_20111015_222612.full.tar.bz2
после чего в том же каталоге распаковываем архив за нужный день таким образом:
tar xf rootfs_20111019_222612.diff.tar.bz2 -g rootfs_20111019_222612.diff.inc
Собственно, сам скрипт:
#!/bin/bash
logfile='/var/log/sysbackup.log' # Файл, куда будут скидываться сообщения хода выполнения скрипта
backupdir='/storage/backup/' # Каталог для складирования архивов
smpdir='/tmp/snap/' # Каталог точки монтирования снапшотов
# Создаем временный каталог и каталог бэкапа
mkdir -p "${smpdir}" "${backupdir}"
# В строке ниже исключаем все LV, которые не нужно обрабатывать - своп, спул Сквида и прочее
ls -1 /dev/sysvg/ | grep -vE 'buildfs|swap|squidcachespace|repo|spyroot|tempvideo' |
while read lv
do
# Создаем временную метку
tstamp="$(date +%Y%m%d_%H%M%S)"
#
pref="${backupdir}${lv}_${tstamp}"
echo "${tstamp}: Processing ${lv}" >> $logfile
# Создаем устройство снапшота размером 5 Гб
/sbin/lvcreate -s -L5G -nbackupsnapshot /dev/sysvg/$lv
# Монтируем устройство снапшота
mount /dev/sysvg/backupsnapshot "${smpdir}"
# Выбираем тип бэкапа
case "${1}" in
diff)
# Делаем дифференциальный бэкап
# Находим последний полный бэкап
lastfull="$(ls -1tr "${backupdir}${lv}"_*.full.inc | tail -n1)"
# Если не нашли - пишем ошибку в лог и выходим
if [ -z "${lastfull}" ]
then
echo "Cant find last full backup for ${lv}" >> "${logfile}"
else
# Создаем новый файл изменений
cp "${lastfull}" "${pref}.diff.inc"
# Делаем дифференциальный бэкап, основываясь на файле изменений последнего полного бэкапа
tar c -C "${smpdir}" . --exclude="*/backups/*.tar.bz2" -g "${pref}.diff.inc" | pbzip2 -9c > "${pref}.diff.tar.bz2"
fi
;;
*)
# Делаем полный бэкап данных, создавая полный файл изменений
# Бэкапим данные, эксклудя бэкапы доменов
tar c -C "${smpdir}" . --exclude="*/backups/*.tar.bz2" -g "${pref}.full.inc" | pbzip2 -9c > "${pref}.full.tar.bz2"
;;
esac
# Демонтируем снапшот
umount "${smpdir}"
# Удаляем устройство снапшота
/sbin/lvremove -f /dev/sysvg/backupsnapshot
echo $(date) $lv done >> $logfile
done
if ! mountpoint -q "${smpdir}" ;
then
rm -rf "${smpdir}"
fi
# Заливаем бэкап на NAS && удаляем архивы на локальном диске
/root/bin/ftpb.sh && rm -f /storage/backup/*.diff.* /storage/backup/*.full.tar.bz2
В последней строке выполняется аплоад полученных архивов на NAS - в моем случае используется Packard Bell NetStore 3500 с аплоадом через FTP. Для этого используется второй скрипт:
#!/bin/bash
nasip='192.168.0.239' # IP NAS'a
auser='adminlogin' # admin-юзер для получения статистики
apass='adminpass' # пароль админ-юзера
ftpbmrk='nas01-gb' # название закладки в lftp для подключения к NAS'у
logfile='/var/log/sysbackup.log'
# Проверяем доступность NAS'a
netcat -w3 -z "${nasip}" 21 || exit 1
# Функция получения количества свободного места на NAS'e в Mb
ndf() {
wget -qO- "http://${auser}:${apass}@${nasip}/status.htm" |
awk '
BEGIN {
fs=0
}
/Free Size/{
fs=1
}
/MB/{
OFMT="%.f"
if (fs=='1') print $1 ; fs=0
}
'
}
echo $(date): Starting upload files to NAS >> "${logfile}"
# Получаем размер бэкапа
bsize="$(du -s /storage/backup/ | cut -f-1)"
echo $(date): Backup size: "${bsize}" Kb >> "${logfile}"
# Преобразуем Килобайты в Мегабайты и делаем запас свободного места на NAS'е
bsize="$((${bsize}/1024+2048))"
# Если свободного места меньше заданного - удаляем один файл в каталоге
while [ "$(ndf)" -lt "${bsize}" ]
do
# Получаем список файлов, сортируем, получаем самый старый файл
rmfile="$(lftp -e 'ls;quit' "${ftpbmrk}" 2>/dev/null |
awk '/_/{print $9}' |
sed -r -e 's/[a-z]+_(.*)/& \1/g' |
sort -nrk2 |
awk '{print $1}' | tail -n1)"
echo $(date): NDF: "$(ndf)" MB >> "${logfile}"
echo $(date): Not enough free space on NAS, removing "${rmfile}" >> "${logfile}"
# Удаляем файл
lftp -e "rm ${rmfile} ; quit" "${ftpbmrk}"
done
echo $(date): BEGIN of fileupload >> "${logfile}"
# Копируем файлы бэкапов на NAS
lftp -e 'lcd /storage/backup/ ; mirror -R . ; quit' "${ftpbmrk}"
echo $(date): END of fileupload >> "${logfile}"
Для аплоада используется FTP-клиент lftp (почему-то NAS с ncftp работать отказался). Для подключения к NAS'у в FTP-клиенте используются закладки для предотвращения отображения пароля пользователя в списке процессов:
# cat .lftp/bookmarks nas01-gb ftp://uploaduser:uploaduserpass@192.168.0.239/in/goro_backup/
Скрипт отслеживает использование диска на NAS, для чего авторизуется на нем через web-интерфейс и получает значение свободного места. Если значение размера текущего бэкапа + 2 Гб (запас на всякий случай для уменьшения фрагментации и т.п.) больше, чем полученное значение свободного места - скрипт удаляет самые старые файлы на NAS'e - до тех пор, пока места не станет достаточно.