Application: LDAP-server "389-DS v1.3", "Bacula v5.2/7.4/9.0".
Задача: наладить резервное копирование настроек и "баз данных" LDAP-инстансов полностью, с последующей выгрузкой в централизованное хранилище под управлением "Bacula".
Программное обеспечение SLDAP (Standalone LDAP), предназначающееся для обслуживания "баз данных" LDAP (Lightweight Directory Access Protocol), родилось почти одновременно с "интернетом" (в 1992-м году в Мичиганском университете выпускается пререлиз, работающий со спецификацией LDAP-протокола ещё не утверждённой в RFC) и похоже, что его инструментарий и методы инициализации не сильно с тех пор развились - оно было простым и осталось таковым настолько, что даже начинает выглядеть корявым на фоне современных подходов к реализации интерфейсов и средств управления.
Обзор резервируемых ресурсов.
Если говорить о LDAP-сервере "389-DS (Directory Server)" разработки "RedHat", то параметры первого этапа запуска инстансов (их может быть несколько) сосредоточены в файлах конфигурации, именованных по принципу "dirsrv-INSTANCE_NAME", по умолчанию располагающихся в немного неподходящей для этого директории "/etc/default". Там всего несколько переменных окружения, указывающих на месторасположение исполняемых и конфигурационных файлов запускаемого сервиса, таких как:
# cat /etc/default/dirsrv-ldap.example.net
....
SERVERBIN_DIR=/usr/sbin ; export SERVERBIN_DIR
CONFIG_DIR=/etc/dirsrv/slapd-ldap.example.net ; export CONFIG_DIR
....
SERVERBIN_DIR=/usr/sbin ; export SERVERBIN_DIR
CONFIG_DIR=/etc/dirsrv/slapd-ldap.example.net ; export CONFIG_DIR
....
Запускающийся сервис LDAP-сервера "389-DS" руководствуется обширным набором параметров из конфигурационных LDIF-файлов, загружаемых из соответствующей инстансу директории, задаваемой переменной окружения "CONFIG_DIR". Данные "каталогов" как таковых обычно размещаются в директории "/var/lib/dirsrv/slapd-INSTANCE_NAME/db" (определяется параметром "nsslapd-directory").
С учётом крайней простоты реализации методик обработки и хранения данных LDAP-сервером "389-DS" для полноценного резервного копирования достаточно выгрузки конфигурационных файлов из двух мест (оба в структуре директории "/etc") и набора файлов "базы данных" как таковых. Но, разумеется, простое копирование файлов несёт в себе риск получения консистентного набора данных в резервной копии - а потому для таких операций повсеместно применяются вспомогательные инструменты.
Общие рассуждения о неприменимости готовых решений.
Если копнуть глубже, то в дистрибутивном наборе LDAP "389-DS" обнаружатся два скрипта, предназначающихся специально для резервного копирования - один на Bash (/usr/sbin/db2bak), а второй на Perl (/usr/sbin/db2bak-online). Причём Bash-скрипт может выгружать резервную копию только с полным выключением LDAP-сервиса, а вот Perl-скрипт может это делать и "на лету".
Однако в использовании этих двух готовых решений есть ряд неудобств - как часто бывает в "этих ваших линуксах". Во первых, Bash-скрипт "db2bak" представляет собой лишь простейшую обёртку для единственной команды "ns-slapd db2archive", которую мы и там можем отдать самостоятельно, избегнув применения лишней прослойки. Во вторых, Perl-скрипт "db2bak-online" требует предоставления ему в открытом виде логина и пароля для подключения к LDAP-сервису с полным доступом к иерархии данных резервируемого "каталога", и не предоставляет чёткой обратной связи о процессе резервного копирования (!), сразу после отдачи команды сообщая о её успешном завершении, вне зависимости от реального состояния дел (!), совершая все дальнейшие процедуры в фоновом режиме и выводя текущие статусные сообщения в журнал ошибок (!) - иначе говоря, вообще невозможно без дополнительных проверок узнать, сделана ли утилитой "db2bak-online" резервная копия.
Итого: применение встроенных средств резервного копирования LDAP "389-DS" нецелесообразно и даже небезопасно.
Выше упомянутая комбинация команд "ns-slapd db2archive" делает резервную копию посредством простого копирования всех используемых LDAP-инстансом файлов "баз данных". Если LDAP-сервис эксплуатируется в режиме частой записи, такой подход может быть небезопасным, так как при копировании файлов не учитывается возможное изменение уже скопированных до него. Ранее для подстраховки я применял кратковременное отключение LDAP-сервера на момент копирования, но на практике это приводит к проблемам (например, в одной из конфигураций на старте сервис иногда "падал" из-за ошибки в системной библиотеке SSL-шифрования), в следствии чего я пришёл к тому, что до тех пор, пока объёмы "базы данных" исчисляются десятками и сотнями мегабайт, вполне допустимо делать полную выгрузку данных в формате LDIF, который удобен тем, что при необходимости для устранения проблем неконсистентности в нём легко разобраться самыми простейшими инструментами работы с текстовыми файлами.
Скрипт резервного копирования "баз данных" LDAP.
LDAP-инстанс может поддерживать работу нескольких "баз данных". Для минимизации возни с настройкой параметров сбора данных, был написан простейший bash-скрипт, собирающий в кучку архивов всё, что нужно для полноценной резервной копии сервиса:
# vi /usr/local/bin/389-backup.sh
#!/bin/bash
# Проверяем наличие обязательного входящего аргумента
[ ! "${1}" ] && { echo "Usage: ${0} /dir/to/backup" ; echo "The directory for backup is not specified. Operation aborted." ; exit 1; }
# Подготавливаем директорию для резервной копии
BDIR=${1}
mkdir -p "${BDIR}"
chown -R dirsrv:dirsrv "${BDIR}"
# Вычленяем и перебираем наименования всех настроенных LDAP-инстансов
for DIR in `find /etc/dirsrv/ -mindepth 1 -maxdepth 1 -type d -name 'slapd-*' | xargs -n 1 basename` ; do
# Вычленяем имя LDAP-инстанса
INST="$(echo ${DIR} | sed -e 's/slapd-//g')"
# Вычленяем и перебираем все "базы данных" в LDAP-инстансе
for NSDB in `find /var/lib/dirsrv/${DIR}/db/ -mindepth 1 -maxdepth 1 -type d | xargs -n 1 basename` ; do
# Выгружаем данные в формате LDIF и сразу их сжимаем
ns-slapd db2ldif -m -D /etc/dirsrv/${DIR} -n ${NSDB} -a "${BDIR}/${INST}-${NSDB}.ldif"
gzip "${BDIR}/${INST}-${NSDB}.ldif"
done
done
# Дополняем комплект резервной копии архивом конфигурационных файлов
tar -czf "${BDIR}/etc-dirsrv.tar.gz" /etc/dirsrv 2>/dev/null
exit ${?}
# Проверяем наличие обязательного входящего аргумента
[ ! "${1}" ] && { echo "Usage: ${0} /dir/to/backup" ; echo "The directory for backup is not specified. Operation aborted." ; exit 1; }
# Подготавливаем директорию для резервной копии
BDIR=${1}
mkdir -p "${BDIR}"
chown -R dirsrv:dirsrv "${BDIR}"
# Вычленяем и перебираем наименования всех настроенных LDAP-инстансов
for DIR in `find /etc/dirsrv/ -mindepth 1 -maxdepth 1 -type d -name 'slapd-*' | xargs -n 1 basename` ; do
# Вычленяем имя LDAP-инстанса
INST="$(echo ${DIR} | sed -e 's/slapd-//g')"
# Вычленяем и перебираем все "базы данных" в LDAP-инстансе
for NSDB in `find /var/lib/dirsrv/${DIR}/db/ -mindepth 1 -maxdepth 1 -type d | xargs -n 1 basename` ; do
# Выгружаем данные в формате LDIF и сразу их сжимаем
ns-slapd db2ldif -m -D /etc/dirsrv/${DIR} -n ${NSDB} -a "${BDIR}/${INST}-${NSDB}.ldif"
gzip "${BDIR}/${INST}-${NSDB}.ldif"
done
done
# Дополняем комплект резервной копии архивом конфигурационных файлов
tar -czf "${BDIR}/etc-dirsrv.tar.gz" /etc/dirsrv 2>/dev/null
exit ${?}
Просто для вырабатывания хорошей привычки закрываем скрипт от посторонних:
# chown root:root /usr/local/bin/389-backup.sh
# chmod ug+x /usr/local/bin/389-backup.sh
# chmod o-rwx /usr/local/bin/389-backup.sh
# chmod ug+x /usr/local/bin/389-backup.sh
# chmod o-rwx /usr/local/bin/389-backup.sh
При запуске резервного копирования достаточно указать, в какое место файловой системы положить резервную копию:
# /usr/local/bin/389-backup.sh /var/backups/test-slapd
Настройка резервного копирования посредством "Bacula".
Дополним описание настроек "Bacula", касающиеся резервного копирования клиентского LDAP-сервиса:
# vi /etc/bacula/client.d/ldap.example.net.conf
....
File Set {
Name = "file-set-ldap.example.net"
....
# Директории конфигураций и резервных копий сервиса LDAP
File = "/etc/default/"
File = "/etc/dirsrv/"
File = "/var/backups/bacula-slapd/"
....
}
....
Job {
Name = "ldap.example.net"
Type = Backup
....
# Запуск выгрузки резервных копий БД всех LDAP-инстансов "389-DS":
# (Debian: основной конфигурационный файл "/etc/default/dirsrv-ldap.example.net")
Run Script {
Runs When = Before
Fail Job On Error = No
Command = "rm -rf /var/backups/bacula-slapd"
Command = "mkdir -p /var/backups/bacula-slapd"
Command = "chown -R dirsrv:dirsrv /var/backups/bacula-slapd"
Command = "chmod -R go-rwx /var/backups/bacula-slapd"
Command = "/bin/bash -c '/usr/local/bin/389-backup.sh /var/backups/bacula-slapd 1>/var/log/bacula-slapd.log 2>&1'"
}
#
Run Script {
Runs When = After
Runs On Failure = yes
Command = "rm -rf /var/backups/bacula-slapd"
}
....
}
File Set {
Name = "file-set-ldap.example.net"
....
# Директории конфигураций и резервных копий сервиса LDAP
File = "/etc/default/"
File = "/etc/dirsrv/"
File = "/var/backups/bacula-slapd/"
....
}
....
Job {
Name = "ldap.example.net"
Type = Backup
....
# Запуск выгрузки резервных копий БД всех LDAP-инстансов "389-DS":
# (Debian: основной конфигурационный файл "/etc/default/dirsrv-ldap.example.net")
Run Script {
Runs When = Before
Fail Job On Error = No
Command = "rm -rf /var/backups/bacula-slapd"
Command = "mkdir -p /var/backups/bacula-slapd"
Command = "chown -R dirsrv:dirsrv /var/backups/bacula-slapd"
Command = "chmod -R go-rwx /var/backups/bacula-slapd"
Command = "/bin/bash -c '/usr/local/bin/389-backup.sh /var/backups/bacula-slapd 1>/var/log/bacula-slapd.log 2>&1'"
}
#
Run Script {
Runs When = After
Runs On Failure = yes
Command = "rm -rf /var/backups/bacula-slapd"
}
....
}
Не забываем проверять корректность конфигурации в целом средствами самого Bacula:
# bacula-dir -c /etc/bacula/bacula-dir.conf -t
О процедуре восстановления из "бэкапа".
Выше для выгрузки данных из LDAP мы применили утилиту "db2ldif". Для загрузки данных, сохранённых в формате LDIF, обратно в LDAP, предназначена похожая утилита "ldif2db". Принцип восстановления тривиален, вычитывается из прилагаемой к утилите документации и рассматривать его здесь нет смысла.