image

1. С чего начать


С чего начинается резервное копирование? Планирование. При резервировании любой системы необходимо составить план резервного копирования: что именно, как часто, как долго хранить, хватит ли свободного пространства? Из ответов на эти вопросы вытекает ответ на главный вопрос – чем бэкапить?

Если свободного пространства много – можно хранить целиком виртуальную машину. Делать бэкапы тем же Veeam по расписанию, и не думать о сложностях. Но как по мне, так это расточительство, я привык делать все максимально сжато и, по возможности, экономично. Veeam у меня, конечно же, развернут, но им я делаю резервные копии лишь тех систем, которые либо невозможно, либо проблематично и очень долго разворачивать из резервных копий.

Про скрипты средств управления виртуальной средой просто многозначительно промолчу.

У Zimbra есть инструмент zmmailbox. И, при ближайшем рассмотрении его функционала, я понял, что его мне будет более, чем достаточно. Он умеет резервировать и восстанавливать ящики даже на живой системе. И мне понравилась возможность делать бэкапы критичных почтовых ящиков отдельно от бэкапа всей системы. Таким образом простр��нство, занимаемое резервными копиями, будет ограничено размером заархивированных почтовых ящиков, помноженным на количество дней «глубины бэкапа», а не объемом всей системы, помноженной на то же количество дней. К тому же с бэкапа всей системы, в случае с Zimbra, крайне сложно восстановиться. Гораздо проще скопировать виртуальную машину с помощью Veeam или средств управления виртуальной средой (Hyper-V, ESXI, вписать нужное) сразу после настройки системы, и положить «на полочку», чтобы в критичный момент можно было быстро развернуть почти ничего не весящую ВМ и залить в нее бэкапы ящиков. По-моему, это наименее затратный во всех отношениях сценарий.

2. Исходные данные:


ОС сервера: CentOS 7

По поводу ОС
На самом деле разница между CentOS7 и любой другой системой будет заключаться исключительно в командах серверу на установку пакетов, и, возможно, расположении некоторых файлов. Работа ведется в основном с командлетами Zimbra, так что отличия настройки будет минимальны.

Домен Zimbra: zimbramail.home.local
Имя пользователя и пароль для доступа к расшаренной папке: ZimbraBackUp / 123123
Путь к расшаренной папке: \\BackUpServer1\ZM\
Путь монтирования шары на хосте Zimbra: /mnt/ZM/

3. Настройка


Итак, начнем!

Бэкапы будем писать на сервер под управлением Windows, в расшаренную папку. При желании можно сливать резервные копии куда угодно, но у меня все настроено так, что большинство бэкапов пишутся именно на BackUpServer1.home.local. Так что:

1) Создаем в домене пользователя для доступа к р��сшаренной папке на этом сервере, чтобы можно было смонтировать ее на сервере Zimbramail.home.local. Имя пользователя ZimbraBackUp, пароль, условно, 123123.

2) На сервере BackUpServer1 создаем в хранилище директорию \ZM\, и расшариваем ее, давая права на изменение для пользователя ZimbraBackUp. Путь к шаре будет таким: \\BackUpServer1\ZM\

3) Монтируем шару к серверу Zimbramail.home.local. Чтобы папка монтировалась автоматически, нужно поправить файл /etc/fstab, добавив в него строку:

//BackUpServer/ZM /mnt/ZM cifs user,uid=500,rw,suid,username=ZimbraBackUp,password=123123 0 0

Но сначала надо проверить, работает ли монтирование:

$ mount -t cifs //BackUpServer/ZM /mnt/ZM -o user=ZimbraBackUp,password=123123

Часто возникает ошибка примерно такого вида:

mount: wrong fs type, bad option, bad superblock on //BackUpServer1/ZM/,
missing codepage or helper program, or other error
(for several filesystems (e.g. nfs, cifs) you might
need a /sbin/mount.<type> helper program)
In some cases useful info is found in syslog - try
dmesg | tail or so.

Исправить можно установкой cifs-utils:

$ yum install cifs-utils

После настройки рекомендую перезагрузить сервер.

4) Создаем 3 файла-скрипта: FullBackUp.sh HandBackUp.sh Restore.sh

Первый файл будет использоваться для автоматического создания резервных копий по расписанию, второй для возможности резервного копирования отдельных ящиков, или просто ручного запуска «а вдруг что». И третий скрипт – восстановление одного или сразу всех ящиков. Я постарался максимально прокомментировать работу скриптов и прописал логирование.

Содержание файла FullBackUp.sh:
#!/bin/bash
#Куда положить бэкап
Path="/mnt/ZM/BackUps"
#Куда положить архив бэкапа
ArchPath="/mnt/ZM/Archive"
#Куда положить месячный бэкап
MPath="/mnt/ZM/Mounthly"
#Название домена Zimbra
ZDomain="zimbramail.home.local"
#Список ящиков
MBoxes="/mnt/ZM/MBoxesList"
#Текущая дата
CDate=$(date +%d-%m-%Y)
#Запоминаем день месяца
MDay=$(date +%d)
#Куда писать логи
log="/mnt/ZM/BackUpLog.txt"
echo -en "BackUp ALL MailBoxes started in $(date +%T)\n" >> $log
#Проверка не существования каталога для резервного копирования
if [ ! -d $Path ]; then
	#Создание каталога хранения резервных копий
	echo "Создание каталога хранения резервных копий..."
	mkdir -p $Path
	if [ $? -eq 0 ]; then
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "BackUp dirctory was created in $(date +%T)\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
		echo
		echo -en "BackUp dirctory was NOT created in $(date +%T)\n" >> $log
	fi
else
echo "Каталог хранения резервных копий существует, проверка существования каталога на сегодняшнее число..."
fi
#Првоерка не существования каталога на сегодняшнюю дату
if [ ! -d $Path/$CDate ]; then
	echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
	echo
	#Создание каталога хранения резервных копий на сегодняшнее число
	echo "Создание каталога хранения резервных копий на сегодняшнее число..."
	mkdir -p $Path/$CDate
	if [ $? -eq 0 ]; then
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "BackUp CDate dirctory was created in $(date +%T)\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
		echo
		echo -en "BackUp CDate dirctory was NOT created in $(date +%T)\n" >> $log
	fi
else
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
	echo
	echo "Каталог хранения резервных копий существует, запись списка ящиков в файл..."
fi
#Запись списка ящиков в файл
/opt/zimbra/bin/zmprov -l gaa $ZDomain > $MBoxes
#Вывод результата записи списка
if [ $? -eq 0 ]; then
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
	echo
	echo -en "Mail Boxes list created in $(date +%T)\n" >> $log
else
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[FAIL]"
	echo
	echo -en "Mail Box list is NOT created! in $(date +%T)\n" >> $log
	exit
fi
#создание резервных копий каждого ящика из списка
for MailBox in $( cat $MBoxes); do
	echo "Создание резервной копии ящика $MailBox..."
	/opt/zimbra/bin/zmmailbox -z -m $MailBox getRestUrl "//?fmt=tgz" > $Path/$CDate/$MailBox
	if [ $? -eq 0 ]; then
		#Вывод результата создания резервной копии для каждого ящика
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "Mail Box $MailBox BackUp created in $(date +%T)\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[FAIL]"
		echo
		echo -en "Mail Box $MailBox BackUp is NOT created! in $(date +%T)\n" >> $log
	fi
done
#Очищаем файл со списком ящиков
echo "Очистка файла со споском ящиков..."
echo -n > $MBoxes
if [ $? -eq 0 ]; then
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
	echo
	echo -en "File $MBoxes clear\n" >> $log
else
	echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
	echo
	echo -en "File $MBoxes can NOT be cleared\n" >> $log
fi
#Создание архива и работа с архивами
#Проверка не существования кат��лога для архивирвоания
if [ ! -d $ArchPath ]; then
	#Создание каталога хранения архивов
	echo "Создание каталога хранения архивов..."
	mkdir -p $ArchPath
	if [ $? -eq 0 ]; then
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "Archive dirctory was created in $(date +%T)\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
		echo
		echo -en "Archive dirctory was NOT created in $(date +%T)\n" >> $log
	fi
else
echo "Каталог хранения архивов существует, архививрование..."
fi
tar -czf $ArchPath/$CDate.tar $Path/$CDate
if [ $? -eq 0 ]; then
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
	echo
	echo -en "Archive created in $(date +%T)\n" >> $log
	#Каждое первое число месяца копирование архива на долгосрочное хранение
	if [ "$MDay" = 1 ]; then
		#Проверка существования каталога хранения долгосрочных архивов
		if [ ! -d $MPath ]; then
			#Создание каталога хранения долговрочных архивов
			echo "Создание каталога хранения долгосроных архивов..."
			mkdir -p $MPath
			if [ $? -eq 0 ]; then
				echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
				echo
				echo -en "Long saving dirctory was created in $(date +%T)\n" >> $log
			else
				echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
				echo
				echo -en "Long saving dirctory was NOT created in $(date +%T)\n" >> $log
			fi
		else
		#Каталог существует
		echo "Каталог долгосрочного хранения архивов существует, копирование архива на долгосрочное хранение..."
		fi
		cp $ArchPath/$CDate.tgz $MPath/$CDate
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
			echo
			echo -en "Archive is copied in $(date +%T)\n" >> $log
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
			echo
			echo -en "Archive is NOT copied in $(date +%T)\n" >> $log
		fi		
		echo "Удаление старых резервных копий (старше 1 недели)..."
		#Удаление каталогов, содержащих резервные копии, старше недели
		find $Path -atime +7 | xargs rm -d
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
			echo
			echo -en "Old BackUps files was deleted\n" >> $log
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
			echo
			echo -en "Old BackUps files is NOT deleted in $(date +%T)\n" >> $log
		fi
		#Удаление старых архивов бэкапов (старше 2 месяцев)
		echo "Удаление старых архивов резервных копий (старше 2 месяцев)..."
		find $ArchPath -atime +61 | xargs rm -f
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
			echo
			echo -en "Old BackUps archives was deleted\n" >> $log
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
			echo
			echo -en "Old BackUps archives is NOT deleted in $(date +%T)\n" >> $log
		fi
	fi
else
	echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
	echo
	echo -en "Archive is NOT created in $(date +%T)\n" >> $log
fi
echo "BackUp job finished in $(date +%T)"
#запись в лог-файл времени окончания резервного копирования
echo -en "BackUp job finished in $(date +%T) $(date +%T)\n" >> $log
echo -en "_____________________________________________\n" >> $log

Содержание файла HandBackUp.sh:
#!/bin/bash
#Куда положить бэкап
Path="/mnt/ZM/BackUps"
#Куда положить архив бэкапа
ArchPath="/mnt/ZM/Archive"
#Название домена Zimbra
ZDomain="zimbramail.home.local"
#Список ящиков
MBoxes="/mnt/ZM/MBoxesList"
#Текущая дата
CDate=$(date +%d-%m-%Y)
#Куда писать логи
log="/mnt/ZM/BackUpLog.txt"
read -p "Введите имя почтового ящика (без указания домена), или ALL для резервного копирования всех почтовых ящиков: " A
if [[ "$A" = "ALL" || "$A" = "all" ]]; then
	echo -en "BackUp started in $(date +%T)\n" >> $log
	#Запись списка ящиков в файл
	echo "Запись списка ящиков в файл..."
	/opt/zimbra/bin/zmprov -l gaa $ZDomain > $MBoxes
	if [ $? -eq 0 ]; then
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "Mail Boxes list created in $(date +%T)\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[FAIL]"
		echo
		echo -en "Mail Box list is NOT created! in $(date +%T)\n" >> $log
		exit
	fi
	
	if ! [ -d $Path/$СDate ]; then
		#Создание каталога резервного копирования
		mkdir -p $Path/$CDate/
		echo -en "BackUp directory created in $(date +%T)\n" >> $log
	fi
	#Создание резервных копий каждого ящика из списка
	echo "Создание резервных копий каждого ящика из списка"
	for MailBox in $( cat $MBoxes); do
		echo "Создание резервной копии ящика $MailBox..."
		/opt/zimbra/bin/zmmailbox -z -m $MailBox getRestUrl "//?fmt=tgz" > $Path/$CDate/$MailBox
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
			echo
			echo -en "Mail Box $MailBox BackUp created in $(date +%T)\n" >> $log
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[FAIL]"
			echo
			echo -en "Mail Box $MailBox BackUp is NOT created! in $(date +%T)\n" >> $log
		fi
	done
else
	MailBox="$A@$ZDomain"
	#Проверка существования запрошенного ящика
	echo "Проверка существования запрошенного ящика..."
	Result=$(/opt/zimbra/bin/zmprov getMailboxInfo $MailBox)
	if [ $? -eq 0 ]; then
		#Запрошенный ящик существует
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo "Запрошенный ящик $MailBox существует, резервное копирование..."
		echo -en "Required Mail Box $MailBox exist $(date +%T)\n" >> $log
		#Проверка существования каталога для резервного копирования
		if ! [ -d $Path/$СDate ]; then
			#Создание резервной копии ящика
			mkdir -p $Path/$CDate/
			echo -en "BackUp directory created in $(date +%T)\n" >> $log
		fi
		#Создание резервной копии ящика $MailBox
		/opt/zimbra/bin/zmmailbox -z -m $MailBox getRestUrl "//?fmt=tgz" > $Path/$CDate/$MailBox
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
			echo
			echo -en "Mail Box $MailBox BackUp created in $(date +%T)\n" >> $log
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[FAIL]"
			echo
			echo -en "Mail Box $MailBox BackUp is NOT created! in $(date +%T)\n" >> $log
		fi
	else
		#Запрошенный ящик не существовует - выход
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[FAIL]"
		echo
		echo "Запрошенный ящик $MailBox не существует. Завершение работы скрипта"
		echo -en "Required Mail Box $MailBox is not exist\n" >> $log
		exit
	fi
fi
read -p "Хотите запустить архивацию резервной копии ящика $MailBox? [N]: " F
if [[ "$F" = "Y" || "$F" = "y" ]]; then
	#Проверка не существования каталога для архивирвоания
	if [ ! -d $ArchPath ]; then
		#Создание каталога хранения резервных копий
		echo "Создание каталога хранения архивов..."
		mkdir -p $ArchPath
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
			echo
			echo -en "Archive dirctory was created in $(date +%T)\n" >> $log
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
			echo
			echo -en "Archive dirctory was NOT created in $(date +%T)\n" >> $log
		fi
	else
	echo "Каталог хранения архивов существует, архививрование..."
	fi
	#Создание архива резервной копии
	echo "Архивирование резервной копии..."
	if [[ "$A" = "ALL" || "$A" = "all" ]]; then
		tar -czf $ArchPath/$CDate.tar $Path/$CDate
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
			echo
			echo -en "Archive created in $(date +%T)\n" >> $log	
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
			echo
			echo -en "Archive is NOT created in $(date +%T)\n" >> $log
		fi
	else
		tar -czf $ArchPath/$MailBox.tar $Path/$CDate/$MailBox
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
			echo
			echo -en "Archive created in $(date +%T)\n" >> $log
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
				echo
			echo -en "Archive is NOT created in $(date +%T)\n" >> $log
		fi
	fi
else
echo "Архив создан не будет"
echo -en "User decline archive creating\n" >> $log
fi
#Очищаем файл со списком ящиков
echo "Очистка файла со споском ящиков..."
echo -n > $MBoxes
if [ $? -eq 0 ]; then
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
	echo
else
	echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
	echo
fi
echo "BackUp job finished in $(date +%T) $(date +%T)"
#запись в лог-файл времени окончания резервного копирования
echo -en "BackUp job finished in $(date +%T) $(date +%T)\n" >> $log
echo -en "_____________________________________________\n" >> $log

Содержание файла Restore.sh:
#!/bin/bash
#Где лежат бэкапы
Path="/mnt/ZM/BackUps"
#Название домена Zimbra
ZDomain="zimbramail.home.local"
#Список ящиков
MBoxes="/mnt/ZM/MBoxesList"
#Куда писать логи
log="/mnt/ZM/RestoreLog.txt"
read -p "Дата резервной копии, которую необходимо восстановить в формате 02-09-2001: " Date
if ! [ -d $Path/$Date ]; then
echo "Нет резервных копий на указанную дату."
echo -en "No BackUp file at $Date $(date +%T)\n" > $log
exit
fi
read -p "Введите имя почтового ящика (без указания домена), или ALL для восстановления всех почтовых ящиков на указанную дату: " A
if [[ "$A" = "ALL" || "$A" = "all" ]]; then
	echo -en "Restore started in $(date +%T)\n" >> $log
	#Запись списка ящиков в файл
	ls "$Path/$Date" > $MBoxes
	for MailBox in $( cat $MBoxes); do
		#Проверка существования ящика
		echo "Проверка существования ящика $MailBox"
		Result=$(/opt/zimbra/bin/zmprov getMailboxInfo $MailBox)
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [OK]"
			echo
			echo -en "Start restore job for $MailBox $(date +%T)\n" >> $log
			echo "Ящик $MailBox существует. Восстановление..."
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
			echo
			echo -en "Mail Box $MailBox does not exist, creating Mail Box $MailBox $(date +%T)\n" >> $log
			echo "Ящик $MailBox не существует. Создание ящика $MailBox..."
			#Проверка создания ящика
			Result=$(/opt/zimbra/bin/zmprov ca $MailBox 12345678 displayName "$MailBox")
			if [ $? -eq 0 ]; then
				echo -n "$(tput hpa $(tput cols))$(tput cub 6) [OK]"
				echo
				echo -en "Mail Box $MailBox is created, starting restore $(date +%T)\n" >> $log
				echo "Ящик $MailBox создан успешно. Восстановление..."
			else
				echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
				echo
				echo -en "Can NOT create Mail Box $MailBox ! $(date +%T)\n" >> $log
				echo "Ящик $MailBox создать не удалось."
			fi
		fi
		#Восстановление ящика
		Result=$(/opt/zimbra/bin/zmmailbox -z -m $MailBox postRestURL "//?fmt=tgz&resolve=replace" $Path/$Date/$MailBox)
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
			echo
			echo -en "Ящик $MailBox восстановлен в $(date +%T)\n" >> $log
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[FAIL]"
			echo
			echo -en "Ящик $MailBox НЕ восстановлен! $(date +%T)\n" >> $log
		fi	
	done
else
	#Проверка существования запрошенной резервной копии
	MailBox="$A@$ZDomain"
	if [ -a $Path/$Date/$MailBox ]; then
		#Проверка существования ящика
		echo "Проверка существования ящика $MailBox..."
		Result=$(/opt/zimbra/bin/zmprov getMailboxInfo $MailBox)
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [OK]"
			echo
			echo -en "Start restore job for $MailBox $(date +%T)\n" >> $log
			echo "Ящик $MailBox существует. Восстановление..."
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
			echo
			echo -en "Mail Box $MailBox does not exist $(date +%T)\n" >> $log
			echo "Ящик $MailBox не существует."
			read -p "Хотите создать почтовый ящик с указанным именем и восстановить в него резервную копию? [N]: " B
			if [[ "$B" = "Y" || "$B" = "y" ]]; then
				echo "Создание почтового ящика $MailBox..."
				Result=$(/opt/zimbra/bin/zmprov ca $MailBox 12345678 displayName "$MailBox")
				#Проверка создания ящика
				if [ $? -eq 0 ]; then
					echo -n "$(tput hpa $(tput cols))$(tput cub 6) [OK]"
					echo
					echo -en "Mail Box $MailBox is created, starting restore $(date +%T)\n" >> $log
					echo "Ящик $MailBox создан успешно. Восстановление..."
				else
					echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
					echo
					echo -en "Can NOT create Mail Box $MailBox ! $(date +%T)\n" >> $log
					echo "Ящик $MailBox создать не удалось. Завершение работы скрипта..."
					exit
				fi
			else
				#Ящик не будет создан, нечего восстанавливать. Выход
				echo "Ящик не будет создан. Завершение работы скрипта"
				exit
			fi
		fi
		#Восстановление ящика
		Result=$(/opt/zimbra/bin/zmmailbox -z -m $MailBox postRestURL "//?fmt=tgz&resolve=replace" $Path/$Date/$MailBox)
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
			echo
			echo -en "Ящик $MailBox восстановлен в $(date +%T)\n" >> $log
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[FAIL]"
			echo
			echo -en "Ящик $MailBox НЕ восстановлен! $(date +%T)\n" >> $log
		fi	
	else
		#Запрошенной резервной копии не существует
		echo "Запрошенной резервной копии не существует. Завершение работы скрипта"
		echo -en "Required BackUp file is not exist\n" >> $log
		exit
	fi
fi	
#Очищаем файл со списком ящиков
echo "Очистка файла со споском ящиков..."
echo -n > $MBoxes
if [ $? -eq 0 ]; then
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
	echo
else
	echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
	echo
fi
echo "BackUp job finished in $(date +%T) $(date +%T)"
#запись в лог-файл времени окончания резервного копирования
echo -en "Restore job complete in $(date +%T)\n" >> $log
echo -en "____________________________________\n" >> $log

Тут есть одна тонкость. Если создавать и редактировать файлы скриптов для bash под Windows, то при попытке запустить скрипт будет выпадать ошибка: /bin/sh^M: bad interpreter: No such file or directory, которая заключается в том, что редакторы, работающие под Windows, добавляют в конце строки символ возврата каретки «CR\LF», который редакторы в Linux не отображают. Но символ этот там есть, и никуда не делся. Чтобы избавиться от ошибки и удалить лишние символы делаем следующее:

$ cat your-script.sh | tr -d '\r' > corrected-your-script.sh

Ну или можно воспользоваться утилитой dos2unix, но ее еще надо установить:

$ yum install dos2unix
$ dos2unix your-script.sh

5) Делаем скрипты исполняемыми:

$ chmod 0740 /opt/zimbra/BkUpRestScripts/FullBackUp.sh
$ chmod 0740 /opt/zimbra/BkUpRestScripts/HandBackUp.sh
$ chmod 0740 /opt/zimbra/BkUpRestScripts/Restore.sh

6) Рекомендую руками запустить скрипты и проверить, работают ли они, и как они работают.

7) Создать задачу в CRON для ежедневного резервного копирования почтовых ящиков:

10 0 * * * root /opt/zimbra/ BkUpRestScripts/FullBackUp.sh

8) Наслаждаться

4. Работа скриптов


На случай, если комментарии в самих скриптах не помогли.

1) Скрипт FullBaclUp.sh:
В начале определяются переменные, какая за что отвечает – все прокомментировано.

Далее следует проверка отсутствия каталога для резервного копирования, если он не существует – создается. Делается вывод в лог-файл и на экран (если запущен руками) о успехе или не успехе создания каталога.

Проверка отсутствия каталога с сегодняшней датой. Если его нет — создается, так же вывод на экран и в лог результата создания каталога.

Запись в файл всех существующих почтовых ящиков Zimbra. Он нужен для последовательного резерварования каждого ящика. Вывод на экран результата выполнения команды.

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

Очистка файла со списком ящиков. Так как с этим файлом работают все 3 скрипта, то целесообразно чистить его после каждого прогона, чтобы не было неожиданностей. Можно чистить его и перед началом работы скрипта, но мне как-то привычнее не хранить лишних данных после выполнения работы, а делать одно и то же до и после работы скрипта – легкая степень шизофрении.

Далее блок по созданию сжатого архива.

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

Архивирование.

Проверка «а не первое ли сегодня число?». Я предпочитаю долгосрочно хранить бэкапы за 1е числа всех месяцев, мне этого для работы достаточно. Можно и по неделям хранить, но зачастую это избыточно. А вот сверху поставлено условие хранения бэкапов за весь период работы почтового сервера, так что лежать им там веки вечные. Если число – первое, то полученный архив копируется в отдельный каталог “ Mounthly ”. И чтобы его туда скопировать – нужно проверить, нет ли такого каталога, и если нет – создать его, о чем сообщать на экран и в лог-файл.

Далее осуществляется чистка хранилища – удаляются все резервные копии старше 2х недель, а также архивы, старше 61 дня. Результат удаления выводится на экран и в лог-файл.

После чего скрипт пишет время завершения работы в лог-файл и на экран, и завершает свою работу.

2) Скрипт HandBackUp.sh:
В нач��ле так же определяются переменные, так же прокомментированные для чего они нужны.

Далее пользователю предлагается ввести имя почтового ящика, который необходимо бэкапить, причем без указания домена Zimbra. (там написано в приглашении на ввод), или же выбрать бэкап всех почтовых ящиков, написав ALL или all.

Если выбрано резервное копирование всех ящиков, то скрипт работает почти так же, как и первый, приведенный выше, за исключением того, что после создания бэкапов каждого ящика, он спросит – а надо ли их архивировать?

Если был написан конкретный почтовый ящик, то осуществляется проверка его существования в Zimbra. Если ящик с указанным именем существует – то запускается процесс резервного копирования этого ящика, с выводом на экран и в лог-файл соответствующей информации. Если указанного ящика не существует – об этом будет сообщено пользователю, и скрипт завершит свою работу.

После завершения резервного копирования ящика будет предложено заархивировать копию. Если пользователь соглашается – идет проверка отсутствия каталога для складирования архива и далее по списку, как и в первом скрипте.

После завершения архивирования происходит очистка файла со списком ящиков. На всякий случай.

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

3) Скрипт Restore.sh:
Как и в предыдущих файлах, сначала – определение переменных с комментариями.

Приглашение пользователю выбрать дату резервной копии, которую нужно восстановить. Если указанной даты в бэкапах нет – скрипт завершается, сообщив об этом.

Если бэкапы на указанную дату существуют – приглашение пользователю ввести имя ящика, который нужно восстановить. Чтобы восстановить все ящики нужно написать ALL или all.

Если выбран параметр ALL, скрипт создает список файлов в папке бэкапа в файл, оповещает о результате создания.

Далее идет пошаговое восстановление каждого ящика. Сначала проверка существования ящика в системе, в случае отсутствия он создается, после чего восстанавливается. О каждой операции выводится информация в лог-файл и на экран.

Если выбран конкретный ящик – почти то же самое. Проверка существования файла с запрошенным именем. Вывод на экран и в лог-файл результата.

Проверка существования ящика в системе. Вы��од на экран и в лог-файл результата.
Приглашение пользователю согласиться с созданием ящика в случае отсутствия такового в системе. Вывод на экран и в лог-файл результата.

Создание ящика в случае согласия. Вывод на экран и в лог-файл результата.
Восстановление ящика. Вывод на экран и в лог-файл результата.

Очистка файла со списком ящиков на восстановление.

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

5. По поводу восстановления


Если немного модифицировать данные скрипты, то они вполне подойдут и для перемещения почты с сервера на сервер, просто создав на новом месте ящики со всем содержимым. Так же можно и восстановить работу системы «с нуля», когда по каким-то причинам проще заново поднять сервер Zimbra, чем пытаться оживить старый. Как было написано выше, можно сохранить образ настроенной виртуальной машины, развернув его при необходимости, и залив в него все данные. Тот же алгоритм действий при переезде с одного сервера Zimbra на другой. И не нужно никаких платных утилит типа Zextras.

6. Заключение


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

7. P.S.:


Это вторая статья из серии «как я «Zimbra» внедрял». Первая, про внедрение, LDAP-авторизацию и автоматическое создание ящиков для пользователей AD, вот тут.
Третья статья об автоматическом формировании и обновлении списков рассылки на основе групп AD в Zimbra OSE вот тут.

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