Application: "Bacula Director 5.2/7.4".
Это продолжение инструкции первичной настройки сервера резервного копирования "Bacula", описываемой в отдельной публикации.
Задача: выработать методику резервного копирования по принципу "одно задание - один том"; при этом количество файлов "томов" принципиально не ограничиваем, а удаление неактуальных (устаревших) "томов" будем осуществлять автоматически вызываемым скриптом, ориентирующимся на срок хранения данных в "пуле", задаваемый при конфигурировании параметров задания резервного копирования.
Обращаю внимание на то, что изначально "Bacula" оперирует понятиями из мира ленточных накопителей, имеющих ограниченный размер и требующих периодической ротации.
Предполагается, что запись данных производится в "volume" (том), который когда-то изначально представлял собой кассету с магнитной лентой, а теперь может эмулироваться простым файлом. Условно "тома" объединяются в "pool" (пулы), представляющие собой наборы кассет, предназначавшиеся для хранения определённого набора данных. Задачи резервного копирования привязываются к "пулам", чтобы не ограничиваться характеристиками конкретных "томов". В свою очередь "пулы" привязываются к физическим устройствам хранения вроде "ленточных библиотек" или СХД. Ну а вершиной системы хранения данных является "storage", к которому подключаются физические устройства хранения. Выглядит это примерно так:
Director (центр управления) -> Storage (сервер хранения) -> Device ("стример" или директория ФС) -> Pool (группа "томов") -> Volume (лента или файл).
То есть, ранее принято было считать, что "томов" (кассет ленточных накопителей) ограниченное количество, и они постоянно находятся в процессе перезаписи, когда в рамках "пула" новейшие данные замещают самые старые. Следуя идее хранения данных "как на лентах", при конфигурировании "Bacula" приходится продумывать, сколько "томов" разместить в "пуле", сколько резервных копий пустить в "том", как часто запускать задачи и как долго хранить результирующие данные - причём в дальнейшем приходится строго соблюдать этот график, чтобы не переполнить "пул" сбойными или тестовыми заданиями, данные которых из "тома" вычеркнуть невозможно без полной очистки такового.
При этом, на практике эксплуатации "Bacula" с хранилищами в виде плоской (условно безразмерной) файловой системы вышеописанные исторически сложившиеся ограничения на структуру данных полностью неактуальны и создают ненужные неудобства необходимостью поддерживать прослойку абстракции эмуляции "стримера".
Если отойти от идеологии "tape-like" хранения данных в файловой структуре эмулирующей ленточные накопители, принятой в "Bacula" по умолчанию, и принять идею, что в результате каждого запуска задания может создаваться монолитный завершённый файл резервной копии, который по сути самодостаточен с точки зрения процедур перемещения и восстановления (вкупе с предыдущими, если набор данных инкрементальный или дифференциальный, разумеется), а количество резервных копий не ограничено параметрами "пула", то настройки параметров задания существенно упрощаются.
Кроме упрощения самого подхода к организации хранения данных, их восстановление из монолитных выделенных файлов становиться элементарным даже в случае полного отказа всех компонентов "Bacula" - с помощью утилиты "bextract".
Формируем набор описаний резервируемого ресурса.
Создадим конфигурационный файл описания резервируемого ресурса:
# vi /etc/bacula/client.d/example.net.conf
# # Задаём условное имя набора клиентских параметров и указываем параметры подключения к резервируемому узлу:
Client {
# Задаём произвольное уникальное имя описания параметров подключения к удалённому клиенту
# (желательно включающее FQDN сетевого узла с резервируемым сервисом)
Name = "client-example.net"
# Уникальный FQDN или IP-адрес клиента, по которому осуществляется подключение к удалённому "Bacula-FD"
Address = "example.net"
# Порт на стороне клиента для подключения к нему
FDPort = 9102
# Пароль для подключения к клиенту
Password = "strongPasswordForClient"
# Количество одновременно исполняемых заданий
MaximumConcurrentJobs = 1
# Используемый для хранения "мета-данных" каталог СУБД
Catalog = "cd0.bacula.local"
# Срок хранения в СУБД информации о файлах и статусах отработанных заданий
# (предпочитаю сохранять эти сведения подольше, для "разбора полётов" при необходимости)
FileRetention = 1 year
JobRetention = 1 year
# Разрешаем удалять из СУБД информации о файлах и статусах отработанных заданий по истечению срока их хранения
AutoPrune = yes
}
# # Определяем резервируемую область для последующего применения в задаче:
FileSet {
# Задаём произвольное уникальное имя области резервирования
Name = "file-set-example.net"
Include {
Options {
# Указываем способ сверки целостности передаваемых данных
# signature = MD5
signature = SHA1
# Включаем сжатие файлов на стороне клиента
compression = GZIP
# Явно включаем поддержку компактного сохранения "разрежённых" файлов (с пустыми блоками в содержимом)
sparse = yes
# Явно включаем поддержку Posix ACL
aclsupport = yes
# Собираем данные рекурсивно, включая все дочерние каталоги и файлы
recurse = yes
# Указываем не изменять время последнего доступа к файлу при его резервном копировании
noatime = yes
# Учитывать при резервировании файлы по "жёстким" ссылкам
hardlinks = yes
# При необходимости отключаем ограничение выхода за рамки несущей файловой системы, разрешая выходить в иные примонтированные файловые ресурсы (NFS, SMB, etc)
#onefs = no
}
File = "/etc"
File = "/home"
File = "/root"
File = "/usr/local/etc"
File = "/var/log"
File = "/var/spool/cron"
File = "/var/www"
}
Exclude {
File = ".autofsck"
File = ".journal"
File = ".fsck"
File = "lost+found"
File = "/cdrom"
File = "/dev"
File = "/media"
File = "/proc"
File = "/run"
File = "/selinux"
File = "/sys"
File = "/tmp"
File = "/var/run"
File = "/var/tmp"
}
}
# # Определяем параметры "пула" хранения данных клиента для последующего применения в задаче:
Pool {
# Задаём произвольное уникальное имя "пула" "томов"
Name = "pool-example.net"
# Указываем срок, после которого Bacula может начать очищать тома от данных
# (три месяца хранения - срок достаточный для обнаружения проблемы, на мой взгляд)
VolumeRetention = 93 days
# Явно не ограничиваем максимальное количество "томов" в "пуле"
MaximumVolumes = 0
# Разрешаем использование "тома" только один раз
MaximumVolumeJobs = 1
# Запрещаем Bacula повторное использование "томов" по истечению срока b[ хранения - они должны будут просто удаляться
Recycle = no
RecycleOldestVolume = no
PurgeOldestVolume = no
# Разрешаем Bacula удалять устаревшие записи из СУБД по мере их исполнения
AutoPrune = yes
# Указание обнулять размер файлов при очистке "томов", явно высвобождая при этом место в файловой системе
ActionOnPurge = Truncate
# Указываем тип набора "томов" (обязательный параметр с единственным значением)
PoolType = Backup
# Формируем шаблон для автоматического именования "томов" в "пуле" (файлов резервных копий)
# (без этого параметра не будет работать автоматическая разметка "томов")
LabelFormat = "${Job}.${Year}-${Month:p/2/0/r}-${Day:p/2/0/r}_${Hour:p/2/0/r}:${Minute:p/2/0/r}.${Level}-${JobId}"
}
# # Задаём параметры исполнения непосредственно задания резервного копирования:
Job {
# Уникальное имя задания резервного копирования (желательно включающее FQDN резервируемого сервиса)
# (используется как опорное для именований файлов конфигураций и "томов" резервных копий)
Name = "example.net"
# Включаем: yes (или выключаем: no) задание
Enabled = yes
# Тип задания
Type = Backup
# Уровень резервирования (Full/Incremental/Differential, параметр будет перекрыт указанным в блоке "Schedule")
# Level = Incremental
# Связываем задание с описанием расписания запуска
Schedule = "WeeklyFullDailyInc"
# Связываем задание с описанием клиента
# (связь с параметром "Name" блока "Client")
Client = "client-example.net"
# Связываем задание с описанием области резервирования
FileSet = "file-set-example.net"
# Связываем задание с описанием "пула" хранения данных
Pool = "pool-example.net"
# Указываем на используемое файловое хранилище
Storage = "sd0.bacula.local"
# Применяем приоритет для задания
Priority = 10
# Для слабых или перегруженных серверов ограничиваем скорость выкачивания данных, чтобы не мешать работе основных сервисов (always in bytes per second)
# (доступно только с v7, как серверной, так и клиентской стороны)
# MaximumBandwidth = 10MB/s
# Запуск полного резервирования если обнаружено некорректное завершение предыдущего резервирования
RerunFailedLevels = yes
# Параметры перезапуска заданий завершившихся с ошибкой
# (нет смысла пытаться многократно перезапустить задание - лучше почти сразу проинформировать о проблеме)
RescheduleOnError = Yes
RescheduleInterval = 10 minutes
RescheduleTimes = 2
# Указываем на блок описания обработки уведомлений
Messages = Standard
# Файл описания задания, с помощью которой данные могут быть восстановлены из резервной копии без наличия подключения к "Bacula Catalog"
WriteBootstrap = "/var/lib/bacula/bsr/%n.bsr"
}
Client {
# Задаём произвольное уникальное имя описания параметров подключения к удалённому клиенту
# (желательно включающее FQDN сетевого узла с резервируемым сервисом)
Name = "client-example.net"
# Уникальный FQDN или IP-адрес клиента, по которому осуществляется подключение к удалённому "Bacula-FD"
Address = "example.net"
# Порт на стороне клиента для подключения к нему
FDPort = 9102
# Пароль для подключения к клиенту
Password = "strongPasswordForClient"
# Количество одновременно исполняемых заданий
MaximumConcurrentJobs = 1
# Используемый для хранения "мета-данных" каталог СУБД
Catalog = "cd0.bacula.local"
# Срок хранения в СУБД информации о файлах и статусах отработанных заданий
# (предпочитаю сохранять эти сведения подольше, для "разбора полётов" при необходимости)
FileRetention = 1 year
JobRetention = 1 year
# Разрешаем удалять из СУБД информации о файлах и статусах отработанных заданий по истечению срока их хранения
AutoPrune = yes
}
# # Определяем резервируемую область для последующего применения в задаче:
FileSet {
# Задаём произвольное уникальное имя области резервирования
Name = "file-set-example.net"
Include {
Options {
# Указываем способ сверки целостности передаваемых данных
# signature = MD5
signature = SHA1
# Включаем сжатие файлов на стороне клиента
compression = GZIP
# Явно включаем поддержку компактного сохранения "разрежённых" файлов (с пустыми блоками в содержимом)
sparse = yes
# Явно включаем поддержку Posix ACL
aclsupport = yes
# Собираем данные рекурсивно, включая все дочерние каталоги и файлы
recurse = yes
# Указываем не изменять время последнего доступа к файлу при его резервном копировании
noatime = yes
# Учитывать при резервировании файлы по "жёстким" ссылкам
hardlinks = yes
# При необходимости отключаем ограничение выхода за рамки несущей файловой системы, разрешая выходить в иные примонтированные файловые ресурсы (NFS, SMB, etc)
#onefs = no
}
File = "/etc"
File = "/home"
File = "/root"
File = "/usr/local/etc"
File = "/var/log"
File = "/var/spool/cron"
File = "/var/www"
}
Exclude {
File = ".autofsck"
File = ".journal"
File = ".fsck"
File = "lost+found"
File = "/cdrom"
File = "/dev"
File = "/media"
File = "/proc"
File = "/run"
File = "/selinux"
File = "/sys"
File = "/tmp"
File = "/var/run"
File = "/var/tmp"
}
}
# # Определяем параметры "пула" хранения данных клиента для последующего применения в задаче:
Pool {
# Задаём произвольное уникальное имя "пула" "томов"
Name = "pool-example.net"
# Указываем срок, после которого Bacula может начать очищать тома от данных
# (три месяца хранения - срок достаточный для обнаружения проблемы, на мой взгляд)
VolumeRetention = 93 days
# Явно не ограничиваем максимальное количество "томов" в "пуле"
MaximumVolumes = 0
# Разрешаем использование "тома" только один раз
MaximumVolumeJobs = 1
# Запрещаем Bacula повторное использование "томов" по истечению срока b[ хранения - они должны будут просто удаляться
Recycle = no
RecycleOldestVolume = no
PurgeOldestVolume = no
# Разрешаем Bacula удалять устаревшие записи из СУБД по мере их исполнения
AutoPrune = yes
# Указание обнулять размер файлов при очистке "томов", явно высвобождая при этом место в файловой системе
ActionOnPurge = Truncate
# Указываем тип набора "томов" (обязательный параметр с единственным значением)
PoolType = Backup
# Формируем шаблон для автоматического именования "томов" в "пуле" (файлов резервных копий)
# (без этого параметра не будет работать автоматическая разметка "томов")
LabelFormat = "${Job}.${Year}-${Month:p/2/0/r}-${Day:p/2/0/r}_${Hour:p/2/0/r}:${Minute:p/2/0/r}.${Level}-${JobId}"
}
# # Задаём параметры исполнения непосредственно задания резервного копирования:
Job {
# Уникальное имя задания резервного копирования (желательно включающее FQDN резервируемого сервиса)
# (используется как опорное для именований файлов конфигураций и "томов" резервных копий)
Name = "example.net"
# Включаем: yes (или выключаем: no) задание
Enabled = yes
# Тип задания
Type = Backup
# Уровень резервирования (Full/Incremental/Differential, параметр будет перекрыт указанным в блоке "Schedule")
# Level = Incremental
# Связываем задание с описанием расписания запуска
Schedule = "WeeklyFullDailyInc"
# Связываем задание с описанием клиента
# (связь с параметром "Name" блока "Client")
Client = "client-example.net"
# Связываем задание с описанием области резервирования
FileSet = "file-set-example.net"
# Связываем задание с описанием "пула" хранения данных
Pool = "pool-example.net"
# Указываем на используемое файловое хранилище
Storage = "sd0.bacula.local"
# Применяем приоритет для задания
Priority = 10
# Для слабых или перегруженных серверов ограничиваем скорость выкачивания данных, чтобы не мешать работе основных сервисов (always in bytes per second)
# (доступно только с v7, как серверной, так и клиентской стороны)
# MaximumBandwidth = 10MB/s
# Запуск полного резервирования если обнаружено некорректное завершение предыдущего резервирования
RerunFailedLevels = yes
# Параметры перезапуска заданий завершившихся с ошибкой
# (нет смысла пытаться многократно перезапустить задание - лучше почти сразу проинформировать о проблеме)
RescheduleOnError = Yes
RescheduleInterval = 10 minutes
RescheduleTimes = 2
# Указываем на блок описания обработки уведомлений
Messages = Standard
# Файл описания задания, с помощью которой данные могут быть восстановлены из резервной копии без наличия подключения к "Bacula Catalog"
WriteBootstrap = "/var/lib/bacula/bsr/%n.bsr"
}
Обращаю внимание на важность ограничения количества подключений к целевому клиенту одним единственным, задаваемому опцией "Maximum Concurrent Jobs" в блоке "Client", чтобы избежать вполне реальных конфликтных ситуаций, когда задание запускается дважды и сопутствующие скрипты отрабатываются не в желательном порядке.
Так же объясню, почему я не считаю нужным многократно (параметр "Reschedule Times") пытаться выполнить задание при случаях критических сбоя в процессе или на этапе подключения к сетевому узлу. Дело в том, что сбой доступности ресурса по определению требует внимания администратора, а длительные безуспешные попытки лишь оттягивают возможность исправления проблемы. Кроме того, отсрочка исполнения задания задерживает выстроившуюся за ним очередь других заданий, усугубляя общую ситуацию.
Проверяем корректность конфигурации средствами самого "Bacula" и применяем таковую:
# bacula-dir -c /etc/bacula/bacula-dir.conf -t
# /etc/init.d/bacula-dir reload
# /etc/init.d/bacula-dir reload
Включение в план резервного копирования внешних файловых систем.
Важно иметь в виду, что по умолчанию "Bacula" в процессе выгрузки файлов не переходит в дополнительно примонтированные файловые системы, даже если точка монтирования внутри подлежащего резервному копированию файлового пространства. Самый простой способ заставить "Bacula" забирать файлы из примонтированного участка файловой системы - это явно указать путь к нему дополнительным параметром "File":
....
FileSet {
....
Include {
....
# Основной ресурс
File = "/var/www"
# Примонтированный файловый ресурс
File = "/var/www/bigdata"
}
}
....
FileSet {
....
Include {
....
# Основной ресурс
File = "/var/www"
# Примонтированный файловый ресурс
File = "/var/www/bigdata"
}
}
....
Описание набора файловых ресурсов для "MS Windows".
Пример определения параметров обращения к файловым системам и набора резервируемых ресурсов для "Microsoft Windows":
....
FileSet {
# Произвольное уникальное имя области резервирования
Name = "file-set-example.net"
# Включаем использование механизма "теневого копирования", позволяющего обращаться к открытым и заблокированным файлам
EnableVSS = yes
Include {
Options {
# Указываем способ сверки целостности передаваемых данных
# signature = MD5
signature = SHA1
# Включаем сжатие файлов на стороне клиента
compression = GZIP
# Собираем данные рекурсивно, включая все дочерние каталоги и файлы
recurse = yes
# Указываем не изменять время последнего доступа к файлу при его резервном копировании
noatime = yes
# Учитывать при резервировании файлы по "жёстким" ссылкам
hardlinks = yes
# Указываем игнорировать регистр имён файлов и директорий (специально для Win32)
ignorecase = yes
}
File = "D:/data"
}
Exclude {
File = "*/temp/*"
File = "*/tmp/*"
File = "*.tmp"
File = "*pagefile.sys"
File = "*hiberfil.sys"
}
}
....
FileSet {
# Произвольное уникальное имя области резервирования
Name = "file-set-example.net"
# Включаем использование механизма "теневого копирования", позволяющего обращаться к открытым и заблокированным файлам
EnableVSS = yes
Include {
Options {
# Указываем способ сверки целостности передаваемых данных
# signature = MD5
signature = SHA1
# Включаем сжатие файлов на стороне клиента
compression = GZIP
# Собираем данные рекурсивно, включая все дочерние каталоги и файлы
recurse = yes
# Указываем не изменять время последнего доступа к файлу при его резервном копировании
noatime = yes
# Учитывать при резервировании файлы по "жёстким" ссылкам
hardlinks = yes
# Указываем игнорировать регистр имён файлов и директорий (специально для Win32)
ignorecase = yes
}
File = "D:/data"
}
Exclude {
File = "*/temp/*"
File = "*/tmp/*"
File = "*.tmp"
File = "*pagefile.sys"
File = "*hiberfil.sys"
}
}
....