OS: "Linux Debian 8/9 (Jessie/Stretch)", "Linux Ubuntu 16/18 (Xenial/Bionic) LTS". Application: "Bacula Director v7", "Zabbix v3.4". Задача: наладить посредством системы мониторинга "Zabbix" отслеживание текущего состояния компонентов и задач централизованной системы резервного копирования "Bacula", хранения истории отработанных заданий и уведомления о сбоях в процессе их исполнения. Общий принцип действия выработанного решения таков:
2. Для полученного перечня объектов мониторинга сервером «Zabbix», в соответствии с заготовками в специализированном шаблоне, в соответствии со спецификацией «Low-Level Discovery (LLD)», создаются необходимые элементы и подэлементы.
3. Практически все запросы обрабатываются запускаемыми «Zabbix Agent»-ом самодельными скриптами, извлекающими данные через CLI-интерфейс «Bacula».
Получившееся полностью автоматизированное решение отслеживает состояние системы резервного копирования по следующим позициям:
Статус задач (item, every 5min);
Длительность исполнения задач (item/graph, every 2hour);
Объём загруженных при исполнении задач данных (item/graph, every 2hour);
Количество загруженных при исполнении задач файлов (item/graph, every 2hour);
Уведомление о неудачном завершении задач (trigger);
Уведомление об активности задач в данный момент (trigger);
Уведомление о длительном отсутствии данных о статусе задач (trigger, 6hour);
Уведомление о длительном перерыве в исполнении задач (trigger, 15 day).
Предварительная подготовка.
На
стороне сервера резервного копирования должен быть установлен "Zabbix
Agent" и CLI-интерфейс для "Bacula", а также утилита проверки синтаксиса
JSON:
Перед всеми дальнейшими работами потребуется применить заранее подготовленный мною специализированный шаблон для системы мониторинга:
Размер файла: 44.83 KB
Очень желательно сразу проверить возможность прохождения запроса от сервера мониторинга "Zabbix" к запущенному на стороне системы резервного копирования "Zabbix Agent"-у:
Настраиваем аутентификацию между компонентами.
Прежде
чем приступать к выстраиванию логики сбора данных, обеспечим к таковым
ограниченный доступ (только "на чтение", и не ко всем подсистемам)
агенту мониторинга "Zabbix".
В конфигурации "Bacula Director"
заведём отдельную точку входа со своими параметрами аутентификации для
утилиты "bconsole", через которую к "Bacula" будет подключатся "Zabbix
Agent":
# # Блок описания подсистем управления и мониторинга:
Console {
Name = «zabbix-agent.local»
Password = «zabbixConsolePassword»
# Разрешаем исполнение только перечисленных команд
Command ACL = show,list,llist,quit
# Явно разрешаем получать данные о любых задачах и обращаться к любым каталогам хранения метаданных
Job ACL = *all*
Catalog ACL = *all*
}
….
Проверяем корректность конфигурации средствами самого "Bacula" и применяем таковую:
# /etc/init.d/bacula-director reload
Мне представляется естественным, что клиентский конфигурационный файл утилиты "bconsole" должен быть расположен поближе к источнику запросов - в директории настроек "Zabbix Agent"-а:
# Bacula Console Configuration File, intended for Zabbix-Agent
#
# Указываем на сервис «Director Daemon», к которому нужно подключится:
Director {
Name = bacula.example.net
Address = bacula.example.net
DIRport = 9101
# (несмотря на то, что в действительности аутентификация осуществляется в контексте блока «Console», в блоке описания «Director» тоже требуется указание параметра «Password» — он не должен ничему соответствовать, просто необходимо присутствие его определения)
Password = «XXXXXXXXXXX»
}
# Параметры аутентификации клиента при подключении к «Director Daemon»:
Console {
Name = zabbix-agent.local
Password = «zabbixConsolePassword»
Director = bacula.example.net
}
Проверяем корректность конфигурации средствами самого CLI-агента "Bacula":
Есть смысл сразу удостовериться, действуют ли ограничения - уж не буду здесь о методике.
Настраиваем "Zabbix Agent".
Учитывая
то, что процедуры сбора данных и отправки их на сервер мониторинга
осуществляется клиентом "Zabbix", то конфигурационные файл и скрипты
расположим в его директории:
# mkdir -p /etc/zabbix/zabbix_agents.conf.d
UserParameter=bacula.jobs.check[*], /etc/zabbix/scripts/bacula_check_job.sh $1 $2 $3 $4
Подстраховываясь, закрываем к настройкам и скриптам "Zabbix" доступ посторонним:
# chmod o-rwx /etc/zabbix
Для применения изменений в конфигурации "Zabbix Agent" необходимо перезапустить:
Пишем и тестируем скрипт "Zabbix Auto Discovery (LLD)".
Подготовим
простейший Bash-скрипт, по запросу подключающийся к "Bacula",
запрашивающий перечень разрешённых к исполнению задач резервного
копирования и формирующий соответствующий требования "Zabbix Auto
Discovery" JSON-массив:
# vi ./bacula_discovery.sh && chown zabix:zabbix ./bacula_discovery.sh && chmod ug+x ./bacula_discovery.sh
# usage: ./bacula_discovery.sh
# Задаём переменные рабочего окружения
BCON=»bconsole -c /etc/zabbix/bconsole.conf»
LOG=»/var/log/zabbix-agent/zabbix-bacula-error.log»
DATE=$(date +»%Y-%m-%d.%H:%M:%S»)
# Проверяем наличие ожидаемых утилит
[ -x «$(command -v bconsole)» ] && [ -x «$(command -v jq)» ] || { echo «${DATE}: Не обнаружены необходимые для работы утилиты. Процедура создания списка активных заданий резервного копирования прервана.» >> ${LOG}; exit 1; }
# Формируем список имён активных заданий «Bacula»
JOBS=$(echo -e «show job\n.\nquit» | ${BCON} | grep -i -E «^job:.*enabled[ \t]*=[ \t]*1» | awk ‘{print $2}’ | grep -i -E «^name[ \t]*=[ \t]*.*$» | awk -F «=» ‘{print $2}’)
# Если список имён заданий не пуст, то формируем JSON для «Zabbix Low-Level Discovery (LLD)»
if [ ! -z «${JOBS}» ] ; then
# Задаём начало JSON
JSONZLLD=»{\»data\»:[»
FIRST=1
# Перебираем имена заданий
for JOB in ${JOBS} ; do
# Пропускаем обработку некоторых задач (заготовку для «восстановления», например)
[ $(echo ${JOB} | grep «restore») ] && continue
# Проставляем разделитель между элементами JSON
if [ ${FIRST} == 0 ] ; then
JSONZLLD=${JSONZLLD}»,»
fi
FIRST=0
# Вводим имя задания в качестве элмента JSON
JSONZLLD=${JSONZLLD}»{\»{#JOB}\»:\»${JOB}\»}»
done
# Завершаем JSON
JSONZLLD=${JSONZLLD}»]}»
# Проверяем синтаксическую корректность JSON
if jq -e . 1>/dev/null 2>&1 <<< «${JSONZLLD}» ; then
# Отдаём JSON на STDOUT
echo ${JSONZLLD}
fi
fi
exit ${?}
Пример получаемого в ответ на запрос JSON-файла для "Zabbix LLD", с обнаруженными активными задачами:
«data»:[
{«{#JOB}»:»client0.example.net»},
{«{#JOB}»:»clientX.example.net»}
] }
Есть смысл сразу проверить корректность срабатывания скрипта "Auto Discovery (LLD)":
Пишем и тестируем скрипт получения запрашиваемых параметров.
Создаём
специализированный Bash-скрипт, принимающий от "Zabbix Agent"-а запросы
на получение данных по ряду интересующих нас параметров, обращающийся к
CLI-утилите "Bacula" и нормализующий их перед выдачей:
# vi ./bacula_check_job.sh && chown zabix:zabbix ./bacula_check_job.sh && chmod ug+x ./bacula_check_job.sh
# usage: ./bacula_check_job.sh «jobName» «jobParam» {jobLevel}
# Задаём переменные рабочего окружения
BCON=»bconsole -c /etc/zabbix/bconsole.conf»
LOG=»/var/log/zabbix-agent/zabbix-bacula-error.log»
DATE=$(date +»%Y-%m-%d.%H:%M:%S»)
# Проверяем наличие ожидаемых утилит
[ -x «$(command -v bconsole)» ] || { echo «${DATE}: Не обнаружены необходимые для работы утилиты. Процедура запроса статуса задания резервного копирования прервана.» >> ${LOG}; exit 1; }
# Проверяем корректность вводимых данных и выводим подсказку в журнал событий при необходимости
#
JINAME=»${1}»
JIPARAM=»${2}»
JILEVEL=»${3}»
#
[ ! «${JINAME}» ] && { echo «${DATE}: Запрос: \»$(basename $0) $@\». Не указано имя задания резервного копирования. Процедура запроса статуса задания резервного копирования прервана.» >> ${LOG}; exit 1; }
#
[ ! «${JIPARAM}» ] && { echo «${DATE}: Запрос: \»$(basename $0) $@\». Не указан запрашиваемый параметр задания резервного копирования. Процедура запроса статуса задания резервного копирования прервана.» >> ${LOG}; exit 1; }
#
[ «${JILEVEL}» ] && [[ «${JILEVEL}» != «F» && «${JILEVEL}» != «D» && «${JILEVEL}» != «I» ]] && { echo «${DATE}: Запрос: \»$(basename $0) $@\». Некорректно указан уровень задания резервного копирования. Процедура запроса статуса задания резервного копирования прервана.» >> ${LOG}; exit 1; }
# Запрашиваем упрощённую статистику задания, опираясь на его имя
JOBLINE=$(echo -e «list job=${JINAME}\n.\nquit» | ${BCON})
# Первым отрабатываем самые простые запросы о статусах текущего (последнего в списке) задания
if [ «${JIPARAM}» == «jobstatus» ] ; then
ANSWER=$(echo -e «${JOBLINE}» | grep ‘^|’ | tail -n1 | awk -F «|» ‘{print $9}’ | sed -e ‘s/[ ]*//g’)
elif [ «${JIPARAM}» == «lastexecution» ] ; then
STARTTIME=$(echo -e «${JOBLINE}» | grep ‘^|’ | tail -n1 | awk -F «|» ‘{print $4}’ | xargs -I{} date -d «{}» +%s)
let «ANSWER = $(date +%s) — STARTTIME»
# (для дальнейшей выгрузки статистики по отработанным задачам необходимо указание их уровня)
elif [ ! -z «${JILEVEL}» ] ; then
# Для получения статистики выясняем идентификатор последнего успешно завершённого задания
JOBID=$(echo -e «list job=${JINAME}\n.\nquit» | ${BCON} | grep ‘^|’ | grep -i -E «.*\|[ \t]*B[ \t]*\|[ \t]*${JILEVEL}[ \t]*\|.*\|[ \t]*T[ \t]*\|[ \t]*$» | tail -n1 | awk -F «|» ‘{print $2}’ | sed -e ‘s/[ \t]*//g’ | sed -e ‘s/,//g’)
if [ ! -z «${JOBID}» ] ; then
# Запрашиваем расширенную статистику задания, опираясь на его идентификатор
JOBLONG=$(echo -e «llist jobid=${JOBID}\n.\nquit» | ${BCON})
# Формируем запросы и обрабатываем ответы с вычленением значений запрашиваемых параметров
if [ ! -z «${JOBLONG}» ] ; then
case «${JIPARAM}» in
«jobbytes»)
ANSWER=$(echo -e «${JOBLONG}» | grep -i -E «^[ \t]*jobbytes:» | awk -F «:» ‘{print $2}’ | sed -e ‘s/[ \t]*//g’ | sed -e ‘s/,//g’)
;;
«jobfiles»)
ANSWER=$(echo -e «${JOBLONG}» | grep -i -E «^[ \t]*jobfiles:» | awk -F «:» ‘{print $2}’ | sed -e ‘s/[ \t]*//g’ | sed -e ‘s/,//g’)
;;
«duration»)
STARTTIME=$(echo -e «${JOBLONG}» | grep -i -E «^[ \t]*starttime:» | awk -F «starttime:» ‘{print $2}’ | xargs -I{} date -d «{}» +%s)
ENDTIME=$(echo -e «${JOBLONG}» | grep -i -E «^[ \t]*endtime:» | awk -F «endtime:» ‘{print $2}’ | xargs -I{} date -d «{}» +%s)
let «ANSWER = ENDTIME — STARTTIME»
;;
*) echo «${DATE}: Запрос: \»$(basename $0) $@\». Некорректно указан запрашиваемый параметр задания резервного копирования. Процедура запроса статуса задания резервного копирования прервана.» >> ${LOG}; exit 1; ;;
esac
else
echo «${DATE}: Запрос: \»$(basename $0) $@\». Не получен ответ на запрос идентификатора и расширенного описания задания от подсистемы резервного копирования. Процедура запроса статуса задания резервного копирования прервана.» >> ${LOG}; exit 1;
fi
fi
else
echo «${DATE}: Запрос: \»$(basename $0) $@\». Не указан уровень задания резервного копирования. Процедура запроса статуса задания резервного копирования прервана.» >> ${LOG}; exit 1;
fi
# Отдаём значение запрашиваемого параметра (или ничего не делаем если запрашиваемый параметр отсутствует)
[ ! -z «${ANSWER}» ] && echo ${ANSWER}
exit ${?}
Аналогично предыдущим этапам проверяем корректность прохождения запросов к "Bacula" от сервера мониторинга "Zabbix":
Оптимизация оборота файлов журналов событий.
Наши
самодельные скрипты пишут в свой файл журнала событий, а кроме того
конфигурационный файл "Logrotate" для "Zabbix Agent" несовременный -
заменяем его на свой, всеобъемлющий:
weekly
rotate 7
compress
delaycompress
missingok
notifempty
copytruncate
su zabbix zabbix
}
Проверяем корректность конфигурации "Logrotate":