Задача
Мы используем 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.
}
}
}
}
}
fact {"host_templates_iis_base":
fact_name => "host_templates",
value => ['Web Cluster','OS - Windows Server 2008 WMI - IIS Server'],
ensure => present,
}
#Устанавливаем разделитель в зависимости от операционной системы
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