Puppet + Opsview: автоматический мониторинг на основе шаблонов

от автора

Задача

Мы используем Opsview для мониторинга и Puppet для управления конфигурациями. В Opsview есть шаблоны (Host Templates), которые позволяют определить определенный список проверок (Service Checks) для определенного типа хостов. Например для хоста с шаблоном IIS будут проверяться всевозможные параметры IIS данного хоста, к примеру количество текущих подключений или например средняя скорость подключения.
Возникла задача автоматически назначать шаблон на хост, в зависимости от того, какие классы назначены в манифесте. Всё это, как всегда,  для удовлетворения потребности автоматизации и лени. Итоговая цель — назначил хосту класс, вернулся через минут 15, а он уже с уствновленным IIS, с настроенными сайтами (как вариант уже с деплойнутым контентом), все они мониторятся и по этим данным строятся графики, а также алерты дают знать если что-то случилось.

Сложности

Основная сложность здесь, как обычно, в том что этого никто не сделал этого для меня. Не существует модуля «Мониторинг IIS в один клик» для моей инфраструктуры. Практически сложность заключается в том, как сообщить модулю который управляет конфигурацией Opsview что в другом модуле создали сайт, передать параметры URI которые нужно мониторить, а также имена шаблонов хоста (в данном случае это будет как минимум шаблон IIS). Мои попытки и пробы включали следующее:

Глобальные переменные

Не сработало, потому что у puppet scoping (как по-русски?) работает таким образом, что нельзя сделать append ко глобальной переменной, можно только сделать копию, а нам ведь нужно именно аккумулировать имена шаблонов, так как у нас может их быть бесчисленное множество.

Шаблоны Ruby

Также есть вариант реализации глобальных переменных через шаблоны (это когда в скобках < % пишем ruby код %>, который присваивает соответствующие значения, но это считается багом и может быть исправлено в следующих Версиях puppet (убрана фишка с возможностью установки значений переменных в других scope).

Plussignment

Бегло посмотрел возможности назначения параметров ресурса при помощи "~>" — вроде как значения склеивались, но все опять сводилось либо к глобальным переменным или к тому, что описывать весть хост который мы мониторим приходилось в каждом месте где мы хотим добавить шаблон к этому хосту, а это не совсем возможно, так как мы не всегда можем знать исходные параметры данного ресурса, а они обязательны — например имя хоста, которое формируется в основном классе opsview::node, и так далее).

Доработать Провайдер

Думал доделать провайдер управления Opsview, что может быть и стало верным решением при правильной реализации (здесь опять вероятные сложности с необходимостью декларации всегоо ресурса в каждом месте) и на ум пришло вот что (как всегда «слишком просто» для многих).

Реализация

Посколько среда гетерогенная (по-русски — «зоопарк»), нужно думать в масштабе, как минимум, Windows и Linux.

Windows

Оказывается если установить переменную окружения на требуемой ноде в виде FACTER_host_temates=IIS Server;Windows Server WMI;Basic Network то при следующем прогоне puppet run это превратится в факт, который можно будет использовать в любом манифесте как глобальную переменную, а это-то нам и надо! Я не стал писать отдельный провайдер, а быстренько создал новый тип, который использует прекрасный модуль windows_env. Все предельно просто, нужно только заметить что имя ресурса должно быть уникальным, поэтому приходится использовать дополнительный параметр (по умолчанию имя переменной среды берется из названия ресурса). В объявлении ресурса fact имеем возможность использовать имя ресурса для передачи имени переменной окружения. В качестве разделителя используется стандартное для Windows ";"

Linux

Второй частью задачи являлось выполнить тоже самое, но под Linux. Я знаю что управление переменными окружения в nix прямолинейно, и ничего сложного в оборачивании bash команд в ресурс puppet нет, я все же надеялся что кто-то сделал уже доброе дело и изобретать колесо не придется, но не тут то было… Пришлось сделать небольшие «костыли». Делаю я это создавая файл в дефолтном профиле, где экспортируется переменная окружения со всеми значениями (через двоеточие). Все это было разложено по правильным операционным системам и были добавлены проверки чтобы лишний раз ничего не запускалось.

В итоге получился такой вот манифест

define fact ($fact_name=$name, $value, $ensure = present) {        case $::osfamily {     'windows': {       windows_env { "${fact_name}:${value}":       variable  => "FACTER_${fact_name}",       ensure    => $ensure,       mergemode => insert,       value     => $value,       notify    => Service['puppet'], #Restart service after environment update       }        }      'RedHat': {       $splitvalue = join($value,":")        file { "/etc/profile.d/FACTER_${fact_name}.sh":         ensure => present,                 content => "export FACTER_${fact_name}=\"${$splitvalue}\"",         mode => 775,       }        exec { "FACTER_${fact_name}=${splitvalue}":                 command => "/bin/bash -c \"source /etc/profile\"",         unless  => "/bin/echo \${FACTER_${fact_name}} | /bin/grep -q \"${splitvalue}\"",         subscribe => File["/etc/profile.d/FACTER_${fact_name}.sh"],               }     }     default: {       case $::operatingsystem {         'Gentoo': {          #No Gentoo in production.         }       }     }   } } 

Использование ресурса в классе packages::iis

fact {"host_templates_iis_base":     fact_name => "host_templates",     value => ['Web Cluster','OS - Windows Server 2008 WMI - IIS Server'],     ensure => present, } 

Основной класс opsview::node

#Устанавливаем разделитель в зависимости от операционной системы  if $::kernel == "windows" { 	$delimeter=";" } else { 	$delimeter=":" }  #Обращаемся к факту который содержит список шаблонов if $::host_templates  { 	#Разбираем строку на элементы массива, если она содержит что-то 	$host_templates = split($::host_templates,$delimeter) } else { 	$host_templates =[]			 } #Здесь массив $host_templates уже либо null либо содержит список шаблонов.  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #Передаем информацию о хосте основному ресурсу opsview_monitored @@opsview_monitored { "$::hostname": 	ip            => $ip, 	hosttemplates => $host_templates,	 	hostattributes => $hostattributes, 	hostgroup     => $hostgroup, 	enable_snmp => $enable_snmp, 	snmp_community => $snmp_community, 	snmp_port => $snmp_port, 	notification_interval => $notification_interval, 	servicechecks => $servicechecks, 	icon_name => $icon_name, 	keywords => $keywords, 	reload_opsview => "1", 	require => [Class['packages::opsview']],  } 

Заключение

Задача выполнена, данная реализация позволяет накапливать имена шаблонов в массиве, и применять их.
Недостатки: факты становятся доступны только на следующий запуск puppet, что при правильной обработке их (или их отсутствия) в основном классе opsview::node не является большой проблемой, т.к. данные параметры добавляются с низкой периодичностью (если вообще добавляются).
Достоинства: факты можно использовать не только для шаблонов opsview, но и для многих других задач, в том числе для добавления атрибутов, ключевых слов и любых других потенциально-кумулятивных параметров puppet. Факты Puppet можно устанавливать не только вручную, но и через код, что позволяет использовать их в качестве основы для многих интересных вещей.

Как всегда, данное решение хоть оно и полностью рабочее, не претендует на место самого «правильного», и если у кого-то есть какие-либо мысли по этому поводу — милости прошу в комменты. Часто приходится решать проблемы, решения для которых пока просто не существует, так недавно пришлось писать собственный провайдер для управления реестром Windows, так-как существующий puppetlabs/registry не хотел работать с ключами содержащими символ "\" в любых вариантах, с экранированием или без.
Если кому-то данный пост был интересен, буду рад еще поделиться опытом управления конфигурациями.

ссылка на оригинал статьи http://habrahabr.ru/post/203184/