Дистрибьютор из Мытищ платил за облачное хранилище четвёртый год и был уверен, что почта у него защищена. Копировался туда каталог с почтой — и всё. В марте 2020-го я развернул этот бэкап на тестовой машине, чтобы показать, что именно защищено. Получилось 43 папки с файлами и ни одной учётной записи.
Бэкап Zimbra OSE без Zextras: холодный и горячий способы
«У нас бэкап настроен» — и что за этим обычно стоит
Фразу эту я слышу почти на каждом первом выезде. Дальше выясняется одно из трёх. Либо копируется виртуальная машина целиком силами гипервизора. Либо ночью улетает архив каталога с почтой. Либо кто-то когда-то настроил задание, и оно с тех пор выполняется — а куда и что кладёт, никто не помнит.
Все три варианта могут быть рабочими. Могут и не быть. Разница между ними не видна до дня, когда бэкап понадобится, и вот тогда узнать её будет дорого.
Начинать разговор придётся с неприятного факта. В бесплатной редакции Zimbra нет встроенного средства резервного копирования. Совсем. Команда, которая делает бэкап, есть только в коммерческой версии; в открытой её на сервере просто нет. Всё, что у вас работает, — это чей-то самописный скрипт или сторонний инструмент, и качество там ровно такое, каким его сделал автор.
Ниже — две схемы, которые я ставлю клиентам сам, с честным разбором того, что каждая из них не сохраняет. Второй пункт важнее первого.
Март 2020, Мытищи: разворачиваем то, что копировалось четыре года
Дистрибьютор бытовой техники, склад и офис в Мытищах, 43 рабочих места. Пришли ко мне по другому поводу — тормозил поиск, — но по ходу аудита я спросил про бэкап и получил уверенное «настроен, в облако».
Что было настроено на самом деле: ночное задание в 02:30, которое архивировало каталог /opt/zimbra/store и заливало архив в объектное хранилище. Работало исправно четвёртый год подряд. Место занимало 620 ГБ в последней копии, счёт за хранение — 4 700 рублей в месяц. Сервер — Zimbra 8.8.15 Open Source Edition на CentOS 7, 32 ГБ памяти, store на массиве из шести дисков.
Я предложил проверить. Взяли тестовую виртуалку, поставили Zimbra той же версии, распаковали архив. Результат:
- 43 каталога с файлами писем — на месте, читаются;
- учётных записей — ноль, каталог не копировался вовсе;
- базы с метаданными — нет, а без неё папки, флаги и структура ящика не восстанавливаются;
- алиасов, списков рассылки, фильтров, настроек домена — нет;
- сертификатов и конфигурации — нет.
То есть в архиве лежала почта как набор файлов, но не почтовая система. Владелец слушал молча и потом сказал фразу, которую я запомнил: «Я четыре года платил за хранение папок». Именно так.
Дальше мы за две недели поставили нормальную схему. Расскажу обе части — и холодную, и горячую, потому что по отдельности каждая неполна.
Из чего вообще состоит Zimbra, и что обязано попасть в копию
Прежде чем копировать, стоит понять, что копируешь. У сервера четыре независимые части, и потеря любой делает остальные бесполезными.
| Часть | Где лежит | Что потеряете без неё |
|---|---|---|
| Тела писем | /opt/zimbra/store | Собственно почту |
| Метаданные | /opt/zimbra/db/data | Папки, флаги, теги, структуру ящиков |
| Каталог учётных записей | /opt/zimbra/data/ldap | Пользователей, пароли, домены, алиасы, списки рассылки |
| Конфигурация и ключи | /opt/zimbra/conf, /opt/zimbra/ssl | Настройки служб и сертификаты |
Индекс (/opt/zimbra/index) в этот список я намеренно не включил: он перестраивается из данных, копировать его можно, но не обязательно — зато он часто занимает десятки гигабайт и раздувает архив.
Главное тут вот что. Тела писем и метаданные должны быть согласованы между собой. База ссылается на конкретные файлы; если файлы сняли в одну минуту, а базу — через двадцать, часть ссылок укажет в пустоту. На живом сервере эта рассинхронизация происходит постоянно и незаметно.
Отсюда растёт разница между двумя схемами. Холодная берёт всё разом в остановленном состоянии и потому согласованна. Горячая работает без простоя, но берёт ящики по одному через интерфейс самого сервера — и там согласованность обеспечивается иначе.
Схема первая: холодная копия с остановкой служб
Идея прямая: останавливаем сервер, снимаем снимок тома, запускаем сервер обратно, а дальше уже спокойно копируем со снимка. Простой измеряется минутами, потому что в остановленном состоянии сервер проводит только время создания снимка.
Скрипт, который у меня работает у нескольких клиентов, в сокращении:
#!/bin/bash
set -euo pipefail
LV=/dev/vg0/zimbra
SNAP=zsnap
MNT=/mnt/zsnap
DEST=/mnt/nas/zimbra/$(date +%Y-%m-%d)
su - zimbra -c '/opt/zimbra/bin/zmcontrol stop'
sleep 20
[ "$(pgrep -u zimbra -c . || true)" = "0" ] || { echo 'процессы живы'; exit 1; }
lvcreate -L 40G -s -n "$SNAP" "$LV"
su - zimbra -c '/opt/zimbra/bin/zmcontrol start'
mkdir -p "$MNT" "$DEST"
mount -o ro "/dev/vg0/$SNAP" "$MNT"
rsync -aHAX --numeric-ids "$MNT/zimbra/" "$DEST/opt-zimbra/"
umount "$MNT"
lvremove -f "/dev/vg0/$SNAP"
Три места, на которых я настаиваю.
Проверка, что процессов действительно не осталось. Штатная остановка возвращает управление раньше, чем всё завершилось. Без этой проверки вы делаете снимок наполовину живой базы и получаете ровно ту рассинхронизацию, ради борьбы с которой всё затевалось.
Ключ --numeric-ids. Копирует числовые идентификаторы владельца, а не имена. При восстановлении на другой машине это сэкономит вам несколько часов разбирательств с правами.
Отдельная выгрузка каталога. Даже при холодной копии я снимаю каталог учётных записей ещё и текстом — он весит копейки, а читается и восстанавливается независимо от всего остального:
su - zimbra -c '/opt/zimbra/libexec/zmslapcat /mnt/nas/zimbra/ldap/'
Снимок тома я беру размером 40 ГБ — этого с многократным запасом хватает на те три часа, пока идёт копирование; если снимок переполнится, он развалится, и копия окажется мусором. На проде в Мытищах простой при таком порядке составил 4 минуты 40 секунд — от остановки до момента, когда службы снова принимали почту. Копирование со снимка на хранилище шло потом ещё 3 часа 10 минут, но уже на работающем сервере.
Официальный скрипт, который тихо клал пустоту
Начинал я не со своего скрипта. Взял тот, что ходит по сети как официальный вариант бэкапа через снимки тома, — зачем изобретать, если есть готовое.
Задание отработало. Ошибок не выдало. Архив появился. Размер архива меня и насторожил: 118 мегабайт вместо ожидаемых сотен гигабайт.
Проверял я это на копии от 12 марта 2020 года. Причина оказалась в одной строке — в подстановке команды через обратные кавычки, которая в этом месте разворачивалась не в путь к смонтированному снимку, а в пустоту. Дальше архиватор честно упаковывал пустой каталог и честно рапортовал об успехе. Ни одного сообщения об ошибке: с точки зрения скрипта всё прошло идеально.
Это и есть самая противная категория проблем с бэкапом. Не «не сработало» — тогда бы заметили. А «сработало, но не с тем». Я после этого случая проверяю не факт выполнения задания, а два числа:
# 1. Размер вчерашней копии против позавчерашней
du -sh /mnt/nas/zimbra/2020-03-1[12]
# 2. Есть ли внутри то, что должно быть
tar -tzf /mnt/nas/zimbra/2020-03-12/opt-zimbra.tar.gz | \
awk -F/ '{print $2"/"$3}' | sort -u | head -20
Отклонение размера больше чем на 15% в любую сторону — повод посмотреть руками. Резкий рост часто означает, что в архив заехало что-то лишнее, резкое падение — что не заехало нужное.
Мой личный вывод из этой истории: чужой скрипт бэкапа — это не готовое решение, а черновик, который надо прочитать построчно. Особенно если он собирает пути из переменных.
Схема вторая: горячий поящичный экспорт через zmmailbox
Холодная копия хороша как основа, но у неё есть свойство: она восстанавливается целиком. Вернуть один ящик за прошлый вторник из неё тяжело. Для этого нужна вторая схема — поящичный экспорт, который работает на живом сервере без простоя.
Сервер сам отдаёт содержимое ящика архивом через собственный интерфейс:
su - zimbra
/opt/zimbra/bin/zmmailbox -z -m sklad@example.ru -t 0 \
getRestURL "//?fmt=tgz" > /mnt/nas/mbox/sklad@example.ru.tgz
Разберу ключи, потому что каждый здесь важен. -z — работать от имени администратора, не спрашивая пароль пользователя. -m — чей ящик выгружаем. -t 0 — снять ограничение по времени операции.
Последний ключ пропускают чаще всего, и это стоит потом дороже всего. Без него на большом ящике команда упирается в таймаут и обрывается вот так:
ERROR: zclient.IO_ERROR (invoke Read timed out)
Причём файл при этом остаётся — обрезанный, но существующий. Задание отчитается об успехе.
Пакетный вариант по всем ящикам:
su - zimbra
zmprov -l gaa > /tmp/accounts.txt
while read -r a; do
/opt/zimbra/bin/zmmailbox -z -m "$a" -t 0 getRestURL "//?fmt=tgz" \
> "/mnt/nas/mbox/$a.tgz" || echo "FAIL $a" >> /mnt/nas/mbox/errors.log
done < /tmp/accounts.txt
Скорость на реальном железе честная, но невысокая: ящик на 3 ГБ выгружается за 4–6 минут, ящик на 28 ГБ — около 50 минут. Это примерно 10 мегабайт в секунду, и цикл идёт последовательно, ящик за ящиком. На 43 ящиках и 620 ГБ полный прогон занял 17 часов 40 минут — в ночной перерыв буднего дня он не помещается никак. Поэтому полный проход я ставлю на вечер пятницы: старт в 18:00, к полудню субботы всё лежит на хранилище. По будням гоняются только самые ходовые ящики, их немного.
Обратная операция — импорт в ящик:
/opt/zimbra/bin/zmmailbox -z -m sklad@example.ru -t 0 \
postRestURL "//?fmt=tgz&resolve=reset" /mnt/nas/mbox/sklad@example.ru.tgz
Параметр resolve=reset означает, что целевой ящик будет полностью очищен перед импортом. Это ровно то, что нужно при восстановлении, и совсем не то, что нужно, если вы хотите долить письма к существующим. Перепутать легко, а последствия необратимы.
Проверьте у себя за две минуты
Проверка отвечает на один вопрос: что именно у вас сейчас копируется. Все команды только читают.
# 1. Редакция сервера — есть ли вообще штатный бэкап
su - zimbra -c 'zmcontrol -v'
ls -l /opt/zimbra/bin/zmbackup 2>/dev/null || echo 'штатного бэкапа нет (OSE)'
# 2. Сколько ящиков должно быть в копии
su - zimbra -c 'zmprov -l gaa' | wc -l
# 3. Что реально лежит в последней копии
ls -1 /path/to/backup/ | head -20
du -sh /path/to/backup/
| Что увидели | Что это значит | Что делать |
|---|---|---|
В версии написано FOSS, файла zmbackup нет | Открытая редакция. Всё, что работает, — чей-то скрипт | Найти этот скрипт и прочитать его |
В копии только каталог store | Скопирована почта, но не почтовая система | Добавить базу, каталог учёток и конфигурацию |
| Число архивов ящиков меньше числа учёток | Часть ящиков не выгружается, и об этом никто не знает | Смотреть журнал ошибок выгрузки |
| Есть архивы размером в единицы килобайт | Выгрузка оборвалась по таймауту — забыт ключ -t 0 | Перевыгрузить с ключом |
| Размер копии не меняется месяцами | Задание выполняется, но кладёт не то | Распаковать и посмотреть содержимое |
Каталог data/ldap в копии отсутствует | Учётные записи и пароли не сохраняются | Добавить выгрузку через zmslapcat |
Третья и четвёртая строки — то, на чём я сам обжёгся у этого клиента. Первую неделю схема работала, а я не сверял размеры. Из 43 архивов шесть весили по 4 килобайта: самые большие ящики обрывались по таймауту, потому что ключ -t 0 я в скрипт поставил, а в ручной прогон для проверки — забыл, и именно ручной прогон считал эталонным.
Честное сравнение: чего не сохраняет каждая схема
Это тот раздел, ради которого писалось всё остальное.
| Критерий | Холодная копия | Горячий экспорт ящиков |
|---|---|---|
| Простой | 4–5 минут за прогон | Нет |
| Время на 620 ГБ | 3 ч 10 мин фоном | 17 ч 40 мин |
| Учётные записи и пароли | Да | Нет |
| Алиасы, списки рассылки, домены | Да | Нет |
| Настройки сервера, сертификаты | Да | Нет |
| Восстановить один ящик | Тяжело | Легко, минуты |
| Восстановить всё разом | Легко | Долго, по одному |
| Работает на другом сервере | С оговорками | Да, на любой Zimbra |
Смотрите на три жирные строки. Горячий экспорт — копия содержимого ящиков и ничего больше. Письма, папки, календари и контакты вы получите, но возвращать их будет некуда: учёток на пустом сервере нет, и создавать их придётся заново — с паролями, алиасами и списками рассылки.
Поэтому у клиента в Мытищах мы поставили обе:
- холодная копия по воскресеньям, простой около 5 минут в 03:00, три последние копии на хранилище — 1,9 ТБ;
- полный горячий поящичный прогон раз в неделю, с вечера пятницы; два последних набора — 820 ГБ, архивы ужимаются примерно на треть от живого store;
- ежесуточный горячий экспорт семи критичных ящиков — закупки, продажи, склад, бухгалтерия, директор и два общих: 54 ГБ за прогон, глубина 14 дней, 756 ГБ на диске;
- выгрузка каталога учётных записей — ежедневно, 34 МБ;
- раз в квартал — восстановление одного случайного ящика на тестовую машину, по регламенту не дольше 40 минут.
Глубину хранения здесь продиктовало не желание, а арифметика тома. Хранилище 4 ТБ, занято под всю схему 3,4 ТБ, свободно около 600 гигабайт. Держать поящичные наборы по всем 43 ящикам за две недели — это ещё почти 6 ТБ, которых нет; поэтому две недели держатся только те семь ящиков, из-за которых обычно и звонят.
Первую проверку прошли 28 марта 2020 года: развернули ящик закупок на 11 ГБ за 34 минуты. Настройка — 16 часов работ. Само хранилище на 4 ТБ стоит в другом помещении и обошлось в 61 тысячу рублей.
Цена альтернативы считается просто. Вся переписка по поставкам, спецификациям и рекламациям живёт в почте, глубина архива — четыре года. Потеря сервера без восстановления остановила бы продажи и снабжение минимум на неделю: 43 человека × 40 часов ≈ 1720 человеко-часов, около 900 тысяч рублей упущенной работы. Претензии по договорам владелец считать отказался.
Что делать, если вы дочитали и поняли, что у вас первый вариант
Короткий порядок действий на ближайшую неделю.
- Найдите скрипт. Не задание в планировщике, а сам файл, и прочитайте его. Половина вопросов снимется на этом шаге.
- Сверьте число архивов с числом учёток. Расхождение почти всегда есть.
- Посмотрите на размеры. Архивы в килобайтах и ровный размер копии месяц за месяцем — два самых частых симптома.
- Добавьте выгрузку каталога учётных записей. Это одна строка и 34 мегабайта, а без неё восстанавливать некуда.
- Разверните одну копию на тестовой машине. Пока этого не сделано, у вас не бэкап, а гипотеза.
И про совет, который часто дают в форумах и который малому бизнесу выходит боком: «просто снимайте снапшот виртуалки, этого достаточно». Снимок гипервизора, сделанный на работающем сервере, фиксирует базу в произвольный момент — с недописанными транзакциями и наполовину обновлёнными ссылками. Иногда такой снимок разворачивается нормально. Иногда база после разворачивания требует восстановления, и вы узнаёте об этом в худший день. Снимок — хорошее дополнение к согласованной копии, но не замена ей.
Хотите понять, что у вас сейчас в копии на самом деле, — выполните три команды из проверки выше и пришлите вывод: версию сервера, число учётных записей и листинг папки с бэкапом. За день отвечу, что именно вы сможете восстановить, чего не сможете, и сколько работы, чтобы закрыть дыру. Если окажется, что всё нормально, — так и напишу, это тоже бывает.
Частые вопросы
Правда ли, что в бесплатной Zimbra совсем нет бэкапа?
Правда. Средство резервного копирования — часть коммерческой редакции; в открытой версии соответствующей команды на сервере просто нет, и проверяется это одной строкой: ls -l /opt/zimbra/bin/zmbackup. Всё, что работает у вас сейчас, — либо самописный скрипт, либо сторонний инструмент, либо снимки гипервизора. Это не значит, что бесплатная версия непригодна: две схемы из статьи закрывают задачу полностью. Это значит, что за качество отвечает тот, кто их настроил, и прочитать этот скрипт стоит до того, как он понадобится.
Зачем нужен ключ -t 0 при выгрузке ящика?
Он снимает ограничение по времени на операцию. Без него выгрузка большого ящика упирается в таймаут и обрывается с ошибкой вида zclient.IO_ERROR (invoke Read timed out). Коварство в том, что файл при этом остаётся на диске — обрезанный, но с виду нормальный, — а скрипт отчитывается об успехе. У клиента в Мытищах из 43 архивов шесть весили по 4 килобайта, и это были ровно шесть самых крупных ящиков, то есть самых ценных. Поэтому ключ ставьте всегда, а после прогона обязательно сверяйте размеры архивов между собой.
Чем resolve=reset отличается от обычного импорта?
Тем, что целевой ящик перед импортом очищается полностью. Для восстановления после аварии это правильное поведение: вы получаете ящик ровно в том состоянии, в каком он был на момент копии, без наслоений. Но если задача другая — например, вернуть письма за один месяц к уже работающему ящику, — этим параметром вы уничтожите всё, что человек накопил после аварии. Перед импортом в живой ящик я всегда сначала делаю его выгрузку, даже если уверен в параметрах. Пять минут страховки против необратимой потери.
Достаточно ли снимков виртуальной машины вместо всего этого?
Как единственная мера — нет. Снимок работающей машины фиксирует базу в произвольной точке: часть транзакций не завершена, часть ссылок на файлы писем обновлена, часть нет. Развернуться такой снимок может нормально, а может потребовать восстановления базы, и предсказать заранее нельзя. Второй момент: снимки обычно живут на том же хранилище, что и сама машина, и умирают вместе с ним. Я использую снимки как быстрый откат на случай неудачного обновления, а рядом держу согласованную копию с остановкой служб. Это разные инструменты для разных бед.
Как часто проверять, что бэкап рабочий?
Автоматически — каждый день: сравнивать размер свежей копии с предыдущей и число архивов ящиков с числом учётных записей. Отклонение больше 15% или недостача архивов — повод посмотреть руками, и это ловится скриптом в пять строк. Руками — раз в квартал: развернуть один случайный ящик на тестовую машину и открыть его. У клиента в Мытищах на этой квартальной проверке за два года дважды всплывали проблемы, о которых иначе узнали бы в аварию. Полноценное учение с разворачиванием сервера с нуля стоит проводить раз в год.
Оставить комментарий