Главная » Хабрахабр » [Из песочницы] Миграция с Nagios на Icinga2 в Австралии

[Из песочницы] Миграция с Nagios на Icinga2 в Австралии

Всем привет.

Таких статей уже и так достаточно (тем не менее, если будет интерес — напишу и про это), так что я хотел бы рассказать о том, как на своей работе в Австралии в должности linux-ops-инженера я был инициатором миграции с одной системы мониторинга на другую. Я — сисадмин linux, переехал из России в Австралию по независимой профессиональной визе в 2015 году, но статья будет не о том, как поросёнку завести трактор. Конкретно — Nagios => Icinga2.

Статья частично техническая и частично — про общение с людьми и проблемы, связанные с разницей в культуре и методах работы.

К сожалению тэг "code" не подсвечивает код Puppet и yaml, так что пришлось использовать "plaintext".

Я, как обычно, читал Хабр незарегистрированным анонимусом в первые полчаса рабочего дня, поглощая кофе и наткнулся на эту статью. Ничто не предвещало беды утром 21 декабря 2016 года.

Инициатива наказуема даже в Австралии, так что ведущий инженер повесил эту проблему на меня, раз уж я её обнаружил. Поскольку в моей компании как раз использовался Nagios, я, недолго думая, создал тикет в Redmine и скинул ссылку в общий чат, поскольку счёл это важным.

Скрин из Редмайн

Python, Питерский Политех и метро рулят. У нас в отделе перед изложением своего мнения принято предложить как минимум одну альтернативу, даже если выбор очевиден, так что я начал с гуглирования какие вообще системы мониторинга на данный момент актуальны, поскольку в России на последнем месте работы у меня была своя собственная самописная система, очень примитивная, но тем не менее вполне себе рабочая и выполняющая все возложенные на неё задачи. Это личное (11 лет работы) и достойно отдельной статьи, но не сейчас. Нет, метро — отстой.

У нас используется Puppet, Gitlab и принцип Infrastructure as a Code, так что: Немного о правилах внесения изменений в конфигурацию инфраструктуры на моём текущем месте.

  • Никаких ручных изменений через SSH путём ручного изменения каких-либо файлов на виртуальных машинах. За три года работы я за это получал по шапке много раз, последний — неделю назад и не думаю, что это был последний раз. Ну в самом деле — поправить одну строку в конфиге, перезапустить службу и посмотреть решилась ли проблема — 10 секунд. Создать новую ветку в Gitlab, запушить изменения, дождаться, пока r10k отработает на Puppetmaster, запустить Puppet --environment=mybranch и ещё пару минут подождать пока всё это отработает — 5 минут минимум.
  • Любые изменения делаются путём создания Merge Request в Gitlab и необходимо получение одобрения минимум от одного члена команды. Серьёзные изменения по решению тим-лида требуют два или три одобрения.
  • Все изменения так или иначе являются текстовыми (поскольку манифесты Puppet, скрипты и данные Hiera — это текст), бинарные файлы крайне не рекомендуются и для одобрения таких файлов нужны веские причины.

Итак, варианты, которые я рассмотрел:

  • Munin — если в инфраструктуре больше 10 серверов, администрирование превращается в ад (из этой статьи. У меня не было особого желания проверять это, так что я поверил на слово).
  • Zabbix — давно уже присматривался, ещё в России, но тогда он был избыточен для моих задач. Здесь — пришлось отбросить по причине использования Puppet в качестве менеджера конфигурации и Gitlab в качестве системы контроля версий. На тот момент, насколько я понял — Zabbix хранит всю конфигурацию в базе данных, в связи с чем было непонятно как управлять конфигурацией в текущих условиях и как отслеживать изменения.
  • Prometheus — то, к чему мы придём в итоге, судя по настроению в отделе, но на тот момент я не осилил это и не смог продемонстрировать реально работающий образец (Proof of Concept), так что пришлось отказаться.
  • Ещё было несколько других вариантов, которые либо требовали полной переработки системы, либо были в зачаточном состоянии / заброшены и по этой же причине были отвергнуты.

В итоге я остановился на Icinga2 по трём причинам:

Это было очень важно, потому что у нас на тот момент было 135 (сейчас на 2019 их 165) виртуальных машин с кучей самописных сервисов/проверок и переделывать это всё было бы лютым геморроем.
2 — все конфигурационные файлы текстовые, что позволяет легко править это дело, создавать merge requests с возможность видеть что добавлено или удалено.
3 — это живой и развивающийся OpenSource проект. 1 — совместимость с Nrpe (клиентской службой, которая запускает проверки по командам от Nagios). У нас очень любят OpenSource и делают посильный вклад в него путём создания Pull Requests и Issues для решения проблем.

Итак, поехали, Icinga2.

Все привыкли к Нагиосу/Наджиосу (хотя даже здесь не смогли придти к компромису как это произносить) и интерфейсу CheckMK. Первое, с чем пришлось столкнуться — инертность коллег. У icinga интерфейс выглядит капитально иначе (это был минус), но есть возможность гибко настраивать то, что нужно видеть с помощью фильтров буквально по любому параметру (это был плюс, но я за него знатно повоевал).

Фильтры

Оцените отношение размера скролл-бара к размеру поля для прокрутки.

Альтернативой была вещь под названием Thruk, но её дизайн вызывал рвотные позывы у всех членов команды, кроме одного — того, кто это предложил (не я). Второе — все привыкли видеть всю инфраструктуру на одном мониторе, потому что CheckMk позволяет работать с несколькими хостами Nagios, но интерфейс Icinga так не умел (на самом деле умел, но об этом ниже).

В топку Thruk - единогласное решение команды

Такая конфигурация позволяла видеть все проблемы в одном web-интерфейсе и вполне себе работала, но Puppet… Проблема с Puppet заключалась в том, что мастер-хост теперь должен был знать о всех хостах и службах/проверках в системе и должен был распределять их между зонами (dev-test, staging-prod, ext), но отправка изменений через Icinga API занимает пару секунд, а вот компиляция каталога Puppet всех сервисов для всех хостов — пару минут. Спустя пару дней мозгового шторма я предложил идею кластерного мониторинга, когда есть один мастер-хост в production-зоне и два подчинённых — один в dev/test и один внешний хост, расположенный у другого провайдера с целью мониторить наши сервисы с точки зрения клиента или постороннего наблюдателя. Это мне до сих пор вменяют в вину, хотя я уже несколько раз объяснял как всё работает и почему это всё так долго.

Решалось путём лобовой атаки — если есть тревоги, но по факту всё в порядке, значит тут надо копать глубже и разбираться почему оно у меня алёртит, хотя не должно. Третье — куча SnowFlakes (снежинок) — вещей, которые выбиваются из общей системы, потому что в них есть что-то особенное, поэтому общие правила к ним неприменимы. Или наоборот — почему Нагиос паникует, а Icinga — нет.

Но очень редко Icinga выдавала реальные тревоги раньше, чем Nagios и я считаю это серьёзным косяком, о котором расскажу в разделе "Выводы". Четвертое — Нагиос работал тут до меня три года и к нему доверия было изначально больше, чем к моей новомодной хипстерской системе, так что каждый раз, когда Icinga поднимала панику — никто ничего не делал, пока Нагиос не возбуждался по тому же вопросу.

В итоге, ввод в эксплуатацию затянулся больше, чем на 5 месяцев (планировалось 28 июня 2018, по факту — 3 декабря 2018), в основном из-за "parity check" — той хрени, когда есть несколько сервисов в Нагиос, о которых никто ничего не слышал последние пару лет, но ИМЕННО СЕЙЧАС они, блин, выдали crit без какой-либо причины и мне пришлось объясняться почему их нет на моей панели и пришлось добавить их в Icinga, чтобы "parity check is complete" (Все службы/проверки в Нагиос соответствуют службам/проверкам в Icinga)

Все данные, прямо вот вообще все, должны быть в Hiera и никак иначе. Внедрение:
Первое — война Code vs Data, типа Puppet Style. Переменные, абстракции, фунции — всё идёт в pp.
В итоге — у нас есть куча виртуальных машин (165 на момент написания статьи) и 68 web-приложений, которые надо мониторить на предмет работоспособности и на действительности SSL-сертификатов. Весь код — в файлах .pp. Но из-за исторического геморроя информация для мониторинга приложений берётся из отдельного gitlab-репозитория и формат данных не менялся со времён Puppet 3, что создаёт дополнительные сложности в конфигурациии.

Puppet-code для приложений, берегите глаза

define profiles::services::monitoring::docker_apps( Hash $app_list, Hash $apps_accessible_from, Hash $apps_access_list, Hash $webhost_defaults, Hash $webcheck_defaults, Hash $service_overrides, Hash $targets, Hash $app_checks, )
{
#### APPS #### $zone = $name $app_list.each | String $app_name, Hash $app_data | )) } # adds notifications for default group (systems) + any group defined in int/pm_docker_apps.eyaml $data = merge($webhost_defaults, $apps_accessible_from, $app_data) $site_domain = $app_data['site_domain'] $regexp = pick($app_data['check_regex'], 'html') # Pick a regex to check $check_url = $app_data['check_url'] ? { undef => { 'http_uri' => '/' }, default => { 'http_uri' => $app_data['check_url'] } } $check_regex = $regexp ?{ 'absent' => {}, default => {'http_expect_body_regex' => $regexp} } $site_domain.each | String $vhost, Hash $vdata | { # Split an app by domains if there are two or more $vhost_name = {'http_vhost' => $vhost} $vars = $data['vars'] + $vhost_name + $check_regex + $check_url $web_ipaddress = is_array($vdata['web_ipaddress']) ? { # Make IP-address an array if it's not, because askizzy has 2 ips and it's an array true => $vdata['web_ipaddress'], false => [$vdata['web_ipaddress']], } $access_from_zones = [$zone] + $apps_access_list[$data['accessible_from']] # Merge default zone (where the app is defined) and extra zones if they exist $web_ipaddress.each | String $ip_address | { # For each IP (if we have multiple) $suffix = length($web_ipaddress) ? { # If we have more than one - add IP as a suffix to this hostname to avoid duplicating resources 1 => '', default => "_${ip_address}" } $octets = split($ip_address, '\.') $ip_tag = "${octets[2]}.${octets[3]}" # Using last octet only causes a collision between nginx-vip 203.15.70.94 and ext. ip 49.255.194.94 $access_from_zones.each | $zone_prefix |{ $zone_target = $targets[$zone_prefix] $nginx_vip_name = "${zone_prefix}_nginx-vip-${ip_tag}" # If it's a host for ext - prefix becomes 'ext_' (ext_nginx-vip...) $nginx_host_vip = { $nginx_vip_name => { ensure => present, target => $zone_target, address => $ip_address, check_command => 'hostalive', groups => ['nginx_vip',], } } $ssl_vars = $app_checks['ssl'] $regex_vars = $app_checks['http'] + $vars + $webcheck_defaults[$zone] + $notify_group if !defined( Profiles::Services::Monitoring::Host[$nginx_vip_name] ) { ensure_resources('profiles::services::monitoring::host', $nginx_host_vip) } if !defined( Icinga2::Object::Service["${nginx_vip_name}_ssl"] ) { icinga2::object::service {"${nginx_vip_name}_ssl": ensure => $data['ensure'], assign => ["host.name == $nginx_vip_name",], groups => ['webchecks',], check_command => 'ssl', check_interval => $service_overrides['ssl']['check_interval'], target => $targets['services'], apply => true, vars => $ssl_vars } } if $regexp != 'absent'{ if !defined(Icinga2::Object::Service["${vhost}${$suffix} regex"]){ icinga2::object::service {"${vhost}${$suffix} regex": ensure => $data['ensure'], assign => ["match(*_nginx-vip-${ip_tag}, host.name)",], groups => ['webchecks',], check_command => 'http', check_interval => $service_overrides['regex']['check_interval'], target => $targets['services'], enable_flapping => true, apply => true, vars => $regex_vars } } } } } } }
}

Код конфигурации хостов и сервисов тоже выглядит ужасно:

monitoring/config.pp


class profiles::services::monitoring::config( Array $default_config, Array $hostgroups, Hash $hosts = {}, Hash $host_defaults, Hash $services, Hash $service_defaults, Hash $service_overrides, Hash $webcheck_defaults, Hash $servicegroups, String $servicegroup_target, Hash $user_defaults, Hash $users, Hash $oncall, Hash $usergroup_defaults, Hash $usergroups, Hash $notifications, Hash $notification_defaults, Hash $notification_commands, Hash $timeperiods, Hash $webhost_defaults, Hash $apps_access_list, Hash $check_commands, Hash $hosts_api = {}, Hash $targets = {}, Hash $host_api_defaults = {},
)
{ # Profiles::Services::Monitoring::Hostgroup <<| |>> # will be enabled when we move to icinga completely
#### APPS #### case $location { 'int', 'ext': { $apps_by_zone = {} } 'pm': { $int_apps = hiera('int_docker_apps') $int_app_defaults = hiera('int_docker_app_common') $st_apps = hiera('staging_docker_apps') $srs_apps = hiera('pm_docker_apps_srs') $pm_apps = hiera('pm_docker_apps') + $st_apps + $srs_apps $pm_app_defaults = hiera('pm_docker_app_common') $apps_by_zone = { 'int' => $int_apps, 'pm' => $pm_apps, } $app_access_by_zone = { 'int' => {'accessible_from' => $int_app_defaults['accessible_from']}, 'pm' => {'accessible_from' => $pm_app_defaults['accessible_from']}, } } default: { fail('Please ensure the node has $location fact set (int, pm, ext)') } } file { '/etc/icinga2/conf.d/': ensure => directory, recurse => true, purge => true, owner => 'icinga', group => 'icinga', mode => '0750', notify => Service['icinga2'], } $default_config.each | String $file_name |{ file {"/etc/icinga2/conf.d/${file_name}": ensure => present, source => "puppet:///modules/profiles/services/monitoring/default_config/${file_name}", owner => 'icinga', group => 'icinga', mode => '0640', } } $app_checks = { 'ssl' => $services['webchecks']['checks']['ssl']['vars'], 'http' => $services['webchecks']['checks']['http_regexp']['vars'] } $apps_by_zone.each | String $zone, Hash $app_list | { profiles::services::monitoring::docker_apps{$zone: app_list => $app_list, apps_accessible_from => $app_access_by_zone[$zone], apps_access_list => $apps_access_list, webhost_defaults => $webhost_defaults, webcheck_defaults => $webcheck_defaults, service_overrides => $service_overrides, targets => $targets, app_checks => $app_checks, } } #### HOSTS #### # Profiles::Services::Monitoring::Host <<| |>> # This is for spaceship invasion when it's ready. $hosts_has_large_disks = query_nodes('mountpoints.*.size_bytes >= 1099511627776') $hosts.each | String $hostgroup, Hash $list_of_hosts_with_settings | { # Splitting site lists by hostgroups - docker_host/gluster_host/etc $list_of_hosts_in_group = $list_of_hosts_with_settings['hosts'] $hostgroup_settings = $list_of_hosts_with_settings['settings'] $merged_hostgroup_settings = deep_merge($host_defaults, $list_of_hosts_with_settings['settings']) $list_of_hosts_in_group.each | String $host_name, Hash $host_settings |{ # Splitting grouplists by hosts # Is this host in the array $hosts_has_large_disks ? If so set host.vars.has_large_disks if ( $hosts_has_large_disks.reduce(false) | $found, $value| { ( $value =~ "^${host_name}" ) or $found } ) { $vars_has_large_disks = { 'has_large_disks' => true } } else { $vars_has_large_disks = {} } $host_data = deep_merge($merged_hostgroup_settings, $host_settings) $hostgroup_settings_vars = pick($hostgroup_settings['vars'], {}) $host_settings_vars = pick($host_settings['vars'], {}) $host_notify_group = delete_undef_values($host_defaults['vars']['notify_group'] + $hostgroup_settings_vars['notify_group'] + $host_settings_vars['notify_group']) $host_data_vars = delete_undef_values(deep_merge($host_data['vars'] , {'notify_group' => $host_notify_group}, $vars_has_large_disks)) # Merging vars separately $hostgroups = delete_undef_values([$hostgroup] + $host_data['groups']) profiles::services::monitoring::host{$host_name: ensure => $host_data['ensure'], display_name => $host_data['display_name'], address => $host_data['address'], groups => $hostgroups, target => $host_data['target'], check_command => $host_data['check_command'], check_interval => $host_data['check_interval'], max_check_attempts => $host_data['max_check_attempts'], vars => $host_data_vars, template => $host_data['template'], } } } if !empty($hosts_api){ # All hosts managed by API $hosts_api.each | String $zone, Hash $hosts_api_zone | { # Split api hosts by zones $hosts_api_zone.each | String $hostgroup, Hash $list_of_hosts_with_settings | { # Splitting site lists by hostgroups - docker_host/gluster_host/etc $list_of_hosts_in_group = $list_of_hosts_with_settings['hosts'] $hostgroup_settings = $list_of_hosts_with_settings['settings'] $merged_hostgroup_settings = deep_merge($host_api_defaults, $list_of_hosts_with_settings['settings']) $list_of_hosts_in_group.each | String $host_name, Hash $host_settings |{ # Splitting grouplists by hosts # Is this host in the array $hosts_has_large_disks ? If so set host.vars.has_large_disks if ( $hosts_has_large_disks.reduce(false) | $found, $value| { ( $value =~ "^${host_name}" ) or $found } ) { $vars_has_large_disks = { 'has_large_disks' => true } } else { $vars_has_large_disks = {} } $host_data = deep_merge($merged_hostgroup_settings, $host_settings) $hostgroup_settings_vars = pick($hostgroup_settings['vars'], {}) $host_settings_vars = pick($host_settings['vars'], {}) $host_api_notify_group = delete_undef_values($host_defaults['vars']['notify_group'] + $hostgroup_settings_vars['notify_group'] + $host_settings_vars['notify_group']) $host_data_vars = delete_undef_values(deep_merge($host_data['vars'] , {'notify_group' => $host_api_notify_group}, $vars_has_large_disks)) $hostgroups = delete_undef_values([$hostgroup] + $host_data['groups']) if defined(Profiles::Services::Monitoring::Host[$host_name]){ $hostname = "${host_name}_from_${zone}" } else { $hostname = $host_name } profiles::services::monitoring::host{$hostname: ensure => $host_data['ensure'], display_name => $host_data['display_name'], address => $host_data['address'], groups => $hostgroups, target => "${host_data['target_base']}/${zone}/hosts.conf", check_command => $host_data['check_command'], check_interval => $host_data['check_interval'], max_check_attempts => $host_data['max_check_attempts'], vars => $host_data_vars, template => $host_data['template'], } } } } } #### END OF HOSTS #### #### SERVICES #### $services.each | String $service_group, Hash $s_list |{ # Service_group and list of services in that group $service_list = $s_list['checks'] # List of actual checks, separately from SG settings $service_list.each | String $service_name, Hash $data |{ $merged_defaults = merge($service_defaults, $s_list['settings']) # global service defaults + service group defaults $merged_data = merge($merged_defaults, $data) $settings_vars = pick($s_list['settings']['vars'], {}) $this_service_vars = pick($data['vars'], {}) $all_service_vars = delete_undef_values($service_defaults['vars'] + $settings_vars + $this_service_vars) # If we override default check_timeout, but not nrpe_timeout, make nrpe_timeout the same as check_timeout if ( $merged_data['check_timeout'] and ! $this_service_vars['nrpe_timeout'] ) { # NB: Icinga will convert 1m to 60 automatically! $nrpe = { 'nrpe_timeout' => $merged_data['check_timeout'] } } else { $nrpe = {} } # By default we use nrpe and all commands are run via nrpe. So vars.nrpe_command = $service_name is a default value # If it's server-side Icinga command - we don't need 'nrpe_command' # but there is no harm to have that var and the code is shorter if $merged_data['check_command'] == 'nrpe'{ $check_command = $merged_data['vars']['nrpe_command'] ? { undef => { 'nrpe_command' => $service_name }, default => { 'nrpe_command' => $merged_data['vars']['nrpe_command'] } } }else{ $check_command = {} } # Assembling $vars from Global Default service settings, servicegroup settings, this particular check settings and let's not forget nrpe settings. if $all_service_vars['graphite_template'] { $graphite_template = {'check_command' => $all_service_vars['graphite_template']} }else{ $graphite_template = {'check_command' => $service_name} } $service_notify = [] + pick($settings_vars['notify_group'], []) + pick($this_service_vars['notify_group'], []) # pick is required everywhere, otherwise becomes "The value '' cannot be converted to Numeric" $service_notify_group = $service_notify ? { [] => $service_defaults['vars']['notify_group'], default => $service_notify } # Assing default group (systems) if no other groups are defined $vars = $all_service_vars + $nrpe + $check_command + $graphite_template + {'notify_group' => $service_notify_group} # This needs to be merged separately, because merging it as part of MERGED_DATA overwrites arrays instead of merging them, so we lose some "assign" and "ignore" values $assign = delete_undef_values($service_defaults['assign'] + $s_list['settings']['assign'] + $data['assign']) $ignore = delete_undef_values($service_defaults['ignore'] + $s_list['settings']['ignore'] + $data['ignore']) icinga2::object::service {$service_name: ensure => $merged_data['ensure'], apply => $merged_data['apply'], enable_flapping => $merged_data['enable_flapping'], assign => $assign, ignore => $ignore, groups => [$service_group], check_command => $merged_data['check_command'], check_interval => $merged_data['check_interval'], check_timeout => $merged_data['check_timeout'], check_period => $merged_data['check_period'], display_name => $merged_data['display_name'], event_command => $merged_data['event_command'], retry_interval => $merged_data['retry_interval'], max_check_attempts => $merged_data['max_check_attempts'], target => $merged_data['target'], vars => $vars, template => $merged_data['template'], } } }
#### END OF SERVICES #### #### OTHER BORING STUFF #### $servicegroups.each | $servicegroup, $description |{ icinga2::object::servicegroup{ $servicegroup: target => $servicegroup_target, display_name => $description } } $hostgroups.each| String $hostgroup |{ profiles::services::monitoring::hostgroup { $hostgroup:} } $notifications.each | String $name, Hash $settings |{ $assign = pick($notification_defaults['assign'], []) + $settings['assign'] $ignore = pick($notification_defaults['ignore'], []) + $settings['ignore'] $merged_settings = $settings + $notification_defaults icinga2::object::notification{$name: target => $merged_settings['target'], apply => $merged_settings['apply'], apply_target => $merged_settings['apply_target'], command => $merged_settings['command'], interval => $merged_settings['interval'], states => $merged_settings['states'], types => $merged_settings['types'], assign => delete_undef_values($assign), ignore => delete_undef_values($ignore), user_groups => $merged_settings['user_groups'], period => $merged_settings['period'], vars => $merged_settings['vars'], } } # Merging notification settings for users with other settings $users_oncall = deep_merge($users, $oncall) # Magic. Do not touch. create_resources('icinga2::object::user', $users_oncall, $user_defaults) create_resources('icinga2::object::usergroup', $usergroups, $usergroup_defaults) create_resources('icinga2::object::timeperiod',$timeperiods) create_resources('icinga2::object::checkcommand', $check_commands) create_resources('icinga2::object::notificationcommand', $notification_commands) profiles::services::sudoers { 'icinga_runs_ping_l2': ensure => present, sudoersd_template => 'profiles/os/redhat/centos7/sudoers/icinga.erb', } }

Однако именно такой код позволил использовать простой и понятный синтаксис в Hiera: Я до сих пор работаю над этой лапшой и улучшаю её по мере возможности.

Данные

profiles::services::monitoring::config::services: perf_checks: settings: check_interval: '2m' assign: - 'host.vars.type == linux' checks: procs: {} load: {} memory: {} disk: check_interval: '5m' vars: notification_period: '24x7' disk_iops: vars: notifications: - 'silent' cpu: vars: notifications: - 'silent' dns_fqdn: check_interval: '15m' ignore: - 'xenserver in host.groups' vars: notifications: - 'silent' iftraffic_nrpe: vars: notifications: - 'silent' logging: settings: assign: - 'logserver in host.groups' checks: rsyslog: {} nginx_limit_req_other: {} nginx_limit_req_s2s: {} nginx_limit_req_s2x: {} nginx_limit_req_srs: {} logstash: {} logstash_api: vars: notifications: - 'silent'

Все проверки разбиты на группы, у каждой группы есть дефолтные настройки вида где и как часто запускать эти проверки, какие уведомления отправлять и кому.

Поэтому в config.pp и написана такая лапша — там идёт слияние всех дефолтных настроек с настройками групп и потом ещё с каждой индивидуальной проверкой. В каждой проверке можно переопределить любую опцию и всё это в итоге складывается ещё и с дефолтными настройками всех проверок в целом.

Так же очень важным изменением стала возможность использовать функции в настройках, к примеру, функция подмены порта, адреса и url для проверки http_regex.

http_regexp: assign: - 'host.vars.http_regex' - 'static_sites in host.groups' check_command: 'http' check_interval: '1m' retry_interval: '20s' max_check_attempts: 6 http_port: '{{ if(host.vars.http_port) { return host.vars.http_port } else { return 443 } }}' vars: notification_period: 'host.vars.notification_period' http_vhost: '{{ if(host.vars.http_vhost) { return host.vars.http_vhost } else { return host.name } }}' http_ssl: '{{ if(host.vars.http_ssl) { return false } else { return true } }}' http_expect_body_regex: 'host.vars.http_regex' http_uri: '{{ if(host.vars.http_uri) { return host.vars.http_uri } else { return "/" } }}' http_onredirect: 'follow' http_warn_time: 8 http_critical_time: 15 http_timeout: 30 http_sni: true

Например, web-интерфейс jabber висит на 9090, а Unifi — на 7443.
http_vhost означает игнорировать DNS и брать этот адрес.
Если в хосте указан uri — то идти по нему, иначе брать "/". Это означает — если в определении хоста есть переменная http_port — использовать её, иначе 443.

Я долго тупил в эту строку, пока до меня не дошло, что в переменная в определении хоста: С http_ssl вышла забавная история — эта зараза никак не хотела отключаться по требованию.

http_ssl: false

Подставляется в выражение

if(host.vars.http_ssl) { return false } else { return true }

как false и в итоге получается

if(false) { return false } else { return true }

Решилось заменой синтаксиса: то есть проверка ssl получается всегда активна.

http_ssl: no

Выводы:

Плюсы:

  • У нас теперь одна система мониторинга, а не две, как было последние 7-8 месяцев, или одна, устаревшая и уязвимая.
  • Структура данных хостов / служб(проверок)теперь (на мой взгляд) намного более читаема и понятна. Для других это оказалось не так очевидно, так что пришлось запилить пару страниц в местной вики для разъяснения как оно всё работает и что где править.
  • Есть возможность гибкой настройки проверок с помощью переменных и функций, например для проверки http_regexp искомый паттерн, код возврата, url и порт можно задавать в настройках хоста. [здесь ссылка на мануал по функциям]
  • Есть несколько панелей(dashboards), для каждой из готорых можно определить свой список отображаемых тревог и управлять всем этим через Puppet и merge requests.

Минусы:

  • Инертность членов команды — Нагиос работал, работал и работал, а эта твоя Исинга постоянно глючит и тормозит. А как тут посмотреть историю? А, блин, она же не обновляется… (Реальная проблема — история тревог не обновляется автоматом, только по F5)
  • Инертность системы — когда я кликаю в web-интерфейсе на "обновить" (check now) — результат выполнения зависит от погоды на Марсе, особенно на сложных сервисах, которые требуют десятки секуда для выполнения. Подобный результат — нормальное дело.
  • В целом по полугодовой статистике работы двух систем бок о бок Нагиос всегда отрабатывал быстрей, чем Icinga и это очень меня раздражало. Как мне кажется, там что-то намутили с таймерами и проверка раз в пять минут по факту идёт раз в 5:30 или что-то в этом духе.
  • Если перезапустить сервис в любой момент времени (systemctl restart icinga2) — все проверки, которые на тот момент были в процессе выполнения, выдадут тревогу critical <terminated by signal 15> на экран (подтверждённый баг).

Но в целом — оно работает.


Оставить комментарий

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

*

x

Ещё Hi-Tech Интересное!

Импортозамещение на практике. Часть 3. Операционные системы

В предыдущих публикациях были рассмотрены варианты замещения развернутых систем на «отечественные», и конкретно гипервизоры «отечественного производства». Продолжаем наш цикл статей про импортозамещение. Теперь настала очередь поговорить об «отечественных» операционных системах, входящих в реестр Минкомсвязи на настоящий момент. 0. Отправная точка ...

Последний IRM — апгрейдим Siebel до IP17+

В этом посте вы не найдете брызг радости или намека на легкость бытия. Ну всё, шутки в сторону — поговорим о вечном. Начиная с 2013 года, Oracle проводит кампанию по принципиальной модернизации CRM-системы. Потому что он для тех, кто боролся ...