Настройка резервного копирования и восстановления Zimbra OSE целиком и отдельными ящиками, не используя Zextras +11



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 $ArchPath ]; then
			#Создание каталога хранения долговрочных архивов
			echo "Создание каталога хранения долгосроных архивов..."
			mkdir -p $ArchPath
			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 $Path -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
	else
		#Создание резервных копий каждого ящика из списка
		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
	fi
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
		else
			#Создание резервной копии ящика $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
		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, вот тут.

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

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (2):

  1. flameflower74
    /#19738620

    Скрипты парсер скушал или забыли приложить листинг?

    • Snow_Bars
      /#19738630

      Скрипты под спойлерами. Копипастишь — и в бой.