Bacula + Zabbix

OS: "Linux Debian 8/9 (Jessie/Stretch)", "Linux Ubuntu 16/18 (Xenial/Bionic) LTS".
Application: "Bacula Director v7", "Zabbix v3.4".

Задача:
 наладить посредством системы мониторинга "Zabbix" отслеживание текущего
 состояния компонентов и задач централизованной системы резервного 
копирования "Bacula", хранения истории отработанных заданий и 
уведомления о сбоях в процессе их исполнения.

Общий принцип действия выработанного решения таков:

1. Каждый час «Zabbix» обращается за списком актуальных задач к «Zabbix Agent»-у на стороне сервера «Bacula», ожидая его в JSON-массиве.
2. Для полученного перечня объектов мониторинга сервером «Zabbix», в соответствии с заготовками в специализированном шаблоне, в соответствии со спецификацией «Low-Level Discovery (LLD)», создаются необходимые элементы и подэлементы.
3. Практически все запросы обрабатываются запускаемыми «Zabbix Agent»-ом самодельными скриптами, извлекающими данные через CLI-интерфейс «Bacula».
Получившееся полностью автоматизированное решение отслеживает состояние системы резервного копирования по следующим позициям:

Наличие сервисов «Bacula Dir», «Bacula SD» & «Bacula DB» (item/trigger, every 30sec);
Статус задач (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:

# aptitude install zabbix-agent bconsole jq
Перед
 всеми дальнейшими работами потребуется применить заранее подготовленный
 мною специализированный шаблон для системы мониторинга:

Очень
 желательно сразу проверить возможность прохождения запроса от сервера 
мониторинга "Zabbix" к запущенному на стороне системы резервного 
копирования "Zabbix Agent"-у:

# sudo -u zabbix -s zabbix_get -s bacula.example.net -k «proc.num[bacula-dir]»
Настраиваем аутентификацию между компонентами.

Прежде
 чем приступать к выстраиванию логики сбора данных, обеспечим к таковым 
ограниченный доступ (только "на чтение", и не ко всем подсистемам) 
агенту мониторинга "Zabbix".

В конфигурации "Bacula Director" 
заведём отдельную точку входа со своими параметрами аутентификации для 
утилиты "bconsole", через которую к "Bacula" будет подключатся "Zabbix 
Agent":

# vi /etc/bacula/bacula-dir.conf

….
# # Блок описания подсистем управления и мониторинга:

Console {
Name = «zabbix-agent.local»
Password = «zabbixConsolePassword»

# Разрешаем исполнение только перечисленных команд
Command ACL = show,list,llist,quit

# Явно разрешаем получать данные о любых задачах и обращаться к любым каталогам хранения метаданных
Job ACL = *all*
Catalog ACL = *all*
}
….

Проверяем корректность конфигурации средствами самого "Bacula" и применяем таковую:

# bacula-dir -c /etc/bacula/bacula-dir.conf -t
# /etc/init.d/bacula-director reload
Мне
 представляется естественным, что клиентский конфигурационный файл 
утилиты "bconsole" должен быть расположен поближе к источнику запросов -
 в директории настроек "Zabbix Agent"-а:

# vi /etc/zabbix/bconsole.conf && chown zabix:zabbix /etc/zabbix/bconsole.conf && chmod o-rwx /etc/zabbix/bconsole.conf

#
# 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":

# bconsole -c /etc/zabbix/bconsole.conf -t
Есть смысл сразу удостовериться, действуют ли ограничения - уж не буду здесь о методике.

Настраиваем "Zabbix Agent".

Учитывая
 то, что процедуры сбора данных и отправки их на сервер мониторинга 
осуществляется клиентом "Zabbix", то конфигурационные файл и скрипты 
расположим в его директории:

# mkdir -p /etc/zabbix/scripts
# mkdir -p /etc/zabbix/zabbix_agents.conf.d

# vi /etc/zabbix/zabbix_agentd.conf.d/bacula.conf

UserParameter=bacula.jobs.discovery, /etc/zabbix/scripts/bacula_discovery.sh
UserParameter=bacula.jobs.check[*], /etc/zabbix/scripts/bacula_check_job.sh $1 $2 $3 $4
Подстраховываясь, закрываем к настройкам и скриптам "Zabbix" доступ посторонним:

# chown -R zabbix:zabbix /etc/zabbix
# chmod o-rwx /etc/zabbix
Для применения изменений в конфигурации "Zabbix Agent" необходимо перезапустить:

# /etc/init.d/zabbix-agent restart
Пишем и тестируем скрипт "Zabbix Auto Discovery (LLD)".

Подготовим
 простейший Bash-скрипт, по запросу подключающийся к "Bacula", 
запрашивающий перечень разрешённых к исполнению задач резервного 
копирования и формирующий соответствующий требования "Zabbix Auto 
Discovery" JSON-массив:

# cd /etc/zabbix/scripts
# vi ./bacula_discovery.sh && chown zabix:zabbix ./bacula_discovery.sh && chmod ug+x ./bacula_discovery.sh

#!/bin/bash
# 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)":

# sudo -u zabbix -s zabbix_get -s bacula.example.net -k «bacula.jobs.discovery»
Пишем и тестируем скрипт получения запрашиваемых параметров.

Создаём
 специализированный Bash-скрипт, принимающий от "Zabbix Agent"-а запросы
 на получение данных по ряду интересующих нас параметров, обращающийся к
 CLI-утилите "Bacula" и нормализующий их перед выдачей:

# cd /etc/zabbix/scripts
# vi ./bacula_check_job.sh && chown zabix:zabbix ./bacula_check_job.sh && chmod ug+x ./bacula_check_job.sh

#!/bin/bash
# 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":

# sudo -u zabbix -s zabbix_get -s bacula.example.net -k «bacula.jobs.check[client0.example.net,jobbytes,F]»
Оптимизация оборота файлов журналов событий.

Наши
 самодельные скрипты пишут в свой файл журнала событий, а кроме того 
конфигурационный файл "Logrotate" для "Zabbix Agent" несовременный - 
заменяем его на свой, всеобъемлющий:

# vi /etc/logrotate.d/zabbix-agent

/var/log/zabbix-agent/*.log {
weekly
rotate 7
compress
delaycompress
missingok
notifempty
copytruncate
su zabbix zabbix
}
Проверяем корректность конфигурации "Logrotate":

# logrotate -d /etc/logrotate.d/zabbix-agent

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *