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

в 17:46, , рубрики: opsview, puppet, windows, автоматизация, инфраструктура, системное администрирование, управление конфигурациями, метки: , , , , ,

Задача

Мы используем 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 не хотел работать с ключами содержащими символ "" в любых вариантах, с экранированием или без.
Если кому-то данный пост был интересен, буду рад еще поделиться опытом управления конфигурациями.

Автор: kireevco

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js