Migration de Nagios vers Icinga2 en Australie

Bonjour à tous.


Je suis l'administrateur système Linux, j'ai déménagé de Russie en Australie avec un visa professionnel indépendant en 2015, mais l'article ne portera pas sur la façon d'obtenir un porcelet sur un tracteur. De tels articles sont déjà suffisants (néanmoins, s'il y a un intérêt, j'écrirai à ce sujet également), donc je voudrais parler de la façon dont, dans mon travail en Australie en tant qu'ingénieur Linux, j'ai initié la migration à partir d'un système surveillance à l'autre. Plus précisément - Nagios => Icinga2.


L'article est en partie technique et en partie sur la communication avec les gens et les problèmes liés à la différence de culture et de méthodes de travail.


Malheureusement, la balise "code" ne met pas en évidence le code Puppet et yaml, j'ai donc dû utiliser du "texte en clair".


Rien de mauvais augure le matin du 21 décembre 2016. Comme d'habitude, j'ai lu Habr avec un anonymus non enregistré dans la première demi-heure de la journée de travail, absorbant le café et suis tombé sur cet article .


Depuis que Nagios a été utilisé dans mon entreprise, sans y réfléchir à deux fois, j'ai créé un ticket dans Redmine et jeté le lien dans le chat général, parce que je pensais que c'était important. L'initiative est punissable même en Australie, donc l'ingénieur en chef m'a accroché ce problème depuis que je l'ai découvert.


Écran de Redmine

Dans notre département, avant d'exprimer notre avis, il est de coutume de proposer au moins une alternative, même si le choix est évident, j'ai donc commencé par googler quels systèmes de monitoring en général sont actuellement pertinents, car en Russie au dernier endroit où j'ai travaillé, j'avais mon propre système d'enregistrement personnel, très primitif, mais néanmoins assez fonctionnel et remplissant toutes les tâches qui lui sont assignées. Python, Polytechnique de Saint-Pétersbourg et la règle du métro. Non, le métro craint. C'est personnel (11 ans de travail) et digne d'un article séparé, mais pas maintenant.


Un peu sur les règles pour apporter des modifications à la configuration de l'infrastructure à mon emplacement actuel. Nous utilisons Puppet, Gitlab et le principe de l'infrastructure en tant que code, de sorte que:


  • Aucune modification manuelle via SSH en modifiant manuellement les fichiers sur les machines virtuelles. Pendant trois ans de travail, j'ai reçu un chapeau pour cela plusieurs fois, la dernière il y a une semaine et je ne pense pas que ce soit la dernière fois. Eh bien, en fait - corrigez une ligne dans la configuration, redémarrez le service et voyez si le problème a été résolu - 10 secondes. Créez une nouvelle branche dans Gitlab, appuyez sur les modifications, attendez que r10k fonctionne sur Puppetmaster, exécutez Puppet --environment = mybranch et attendez quelques minutes de plus jusqu'à ce que tout fonctionne - 5 minutes minimum.
  • Toutes les modifications sont apportées en créant une demande de fusion dans Gitlab et vous devez obtenir l'approbation d'au moins un membre de l'équipe. Les changements majeurs apportés au chef d'équipe nécessitent deux ou trois approbations.
  • Toutes les modifications sont textuelles d'une manière ou d'une autre (puisque les manifestes Puppet, les scripts et les données Hiera sont du texte), les binaires sont fortement déconseillés et de bonnes raisons sont nécessaires pour les approuver.

Ainsi, les options que j'ai examinées sont:


  • Munin - s'il y a plus de 10 serveurs dans l'infrastructure, l'administration se transforme en enfer (à partir de cet article . Je n'avais pas beaucoup envie de vérifier cela, alors j'ai pris ma parole).
  • Zabbix - a longtemps regardé, de retour en Russie, mais c'était redondant pour mes tâches. Ici - a dû être abandonné en raison de l'utilisation de Puppet comme gestionnaire de configuration et de Gitlab comme système de contrôle de version. À ce moment, si je comprends bien, Zabbix stocke la configuration entière dans une base de données, et donc il n'était pas clair comment gérer la configuration dans les conditions actuelles et comment suivre les changements.
  • Prométhée est ce à quoi nous arriverons à la fin, à en juger par l'humeur du département, mais à l'époque je ne pouvais pas le maîtriser et je ne pouvais pas démontrer un échantillon vraiment fonctionnel (Proof of Concept), donc j'ai dû refuser.
  • Il y avait plusieurs autres options qui nécessitaient une refonte complète du système, ou étaient à leurs balbutiements / abandonnées et pour la même raison ont été rejetées.

Au final, j'ai opté pour Icinga2 pour trois raisons:


1 - compatibilité avec Nrpe (un service client qui exécute des vérifications sur les commandes de Nagios). C'était très important, car à cette époque, nous avions 135 (maintenant il y en a 165 en 2019) des machines virtuelles avec un tas de services / contrôles auto-écrits et refaire tout cela serait une terrible hémorroïdes.
2 - tous les fichiers de configuration sont du texte, ce qui facilite la modification de ce sujet, la création de demandes de fusion avec la possibilité de voir ce qui a été ajouté ou supprimé.
3 est un projet OpenSource vivant et en pleine croissance. Nous aimons beaucoup OpenSource et y apportons une contribution possible en créant des demandes et des problèmes d'extraction pour résoudre les problèmes.


Alors allons-y, Icinga2.


La première chose à laquelle j'ai dû faire face a été l'inertie de mes collègues. Tout le monde est habitué à Nagios / Najios (bien que même ici, ils ne pouvaient pas faire de compromis sur la façon de prononcer cela) et à l'interface CheckMK. L'interface icinga est complètement différente (c'était un inconvénient), mais il est possible de configurer de manière flexible ce que vous devez voir avec les filtres littéralement par n'importe quel paramètre (c'était un plus, mais je me suis battu pour cela notamment).


Filtres

Estimez le rapport entre la taille de la barre de défilement et la taille du champ de défilement.


Deuxièmement, tout le monde a l'habitude de voir toute l'infrastructure sur un seul moniteur, car CheckMk vous permet de travailler avec plusieurs hôtes Nagios, mais Icinga ne savait pas comment le faire (en fait, il l'a fait, mais plus à ce sujet ci-dessous). Une alternative était une chose appelée Thruk, mais sa conception a fait vomir tous les membres de l'équipe, sauf un - celui qui l'a proposé (pas moi).


Thruk Firebox - Décision unanime de l'équipe

Après quelques jours d'une tempête de cerveaux, j'ai proposé l'idée de la surveillance du cluster, quand il y a un hôte maître dans la zone de production et deux subordonnés - un en dev / test et un hôte externe situé chez un autre fournisseur afin de surveiller nos services du point de vue d'un client ou d'un étranger observateur. Cette configuration m'a permis de voir tous les problèmes dans une seule interface Web et cela a fonctionné pour moi, mais Puppet ... Le problème avec Puppet était que l'hôte maître devait maintenant connaître tous les hôtes et services / vérifications du système et devait les répartir entre les zones (dev-test, staging-prod, ext), mais l'envoi des modifications via l'API Icinga prend quelques secondes, mais la compilation du répertoire Puppet de tous les services pour tous les hôtes prend quelques minutes. C'est toujours de ma faute, même si j'ai déjà expliqué à plusieurs reprises comment tout fonctionne et pourquoi tout cela prend autant de temps.


Troisièmement - un tas de SnowFlakes (flocons de neige) - des choses qui sont éliminées du système général, car elles ont quelque chose de spécial, donc les règles générales ne s'appliquent pas à elles. Cela a été décidé par une attaque frontale - s'il y a une alarme, mais en fait tout est en ordre, alors ici, vous devez creuser plus profondément et comprendre pourquoi cela m'alerte, même si cela ne devrait pas. Ou vice versa - pourquoi Nagios panique, mais pas Icinga.


Quatrièmement - Nagios a travaillé ici pour moi pendant trois ans et au début, il y avait plus de confiance en lui qu'en mon nouveau système hipster, donc à chaque fois Icinga soulevait la panique - personne n'a rien fait jusqu'à ce que Nagios soit enthousiasmé par le même problème. Mais très rarement Icinga a émis de véritables alarmes avant Nagios et je considère cela comme un montant sérieux, dont je parlerai dans la section "Conclusions".


En conséquence, la mise en service a été retardée de plus de 5 mois (prévu le 28 juin 2018 en fait - 3 décembre 2018), principalement à cause du «contrôle de parité» - cette merde quand il y a plusieurs services à Nagios dont personne ne parle Je n'ai rien entendu au cours des deux dernières années, mais MAINTENANT, ils ont foutrement émis des critiques sans raison et j'ai dû expliquer pourquoi ils n'étaient pas sur mon panel et ont dû les ajouter à Icinga pour "le contrôle de parité est terminé" (Tous les services / contrôles à Nagios correspondent aux services / contrôles à Icinga)


Réalisation:
Le premier est la guerre Code vs Data, comme Puppet Style. Toutes les données, ici tout en général, devraient être dans Hiera et rien d'autre. Tout le code est dans des fichiers .pp. Variables, abstractions, fonctions - tout se passe en pp.
En conséquence, nous avons un tas de machines virtuelles (165 au moment de la rédaction) et 68 applications Web qui doivent être surveillées pour l'intégrité et la validité des certificats SSL. Mais en raison d'hémorroïdes historiques, les informations pour la surveillance des applications proviennent d'un référentiel gitlab distinct et le format des données n'a pas changé depuis Puppet 3, ce qui crée des difficultés de configuration supplémentaires.


Puppet-code pour les applications, faites attention
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 | { $notify_group = { 'notify_group' => ($webcheck_defaults[$zone]['notify_group'] + pick($app_data['notify_group'], {} )) } # 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 } } } } } } } } 

Le code de configuration de l'hôte et du service est également horrible:


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', } } 

Je travaille toujours sur cette nouille et je l'améliore autant que possible. Cependant, c'est ce code qui a permis d'utiliser une syntaxe simple et claire dans Hiera:


Les données
 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' 

Tous les contrôles sont divisés en groupes, chaque groupe a des paramètres par défaut tels que où et à quelle fréquence exécuter ces contrôles, quelles notifications envoyer et à qui.


Dans chaque vérification, vous pouvez remplacer n'importe quelle option, et tout cela finit par s'ajouter aux paramètres par défaut de toutes les vérifications dans leur ensemble. Par conséquent, ces nouilles sont écrites dans config.pp - il y a une fusion de tous les paramètres par défaut avec les paramètres des groupes, puis avec chaque vérification individuelle.


En outre, un changement très important a été la possibilité d'utiliser des fonctions dans les paramètres, par exemple, la fonction de changer le port, l'adresse et l'URL pour vérifier 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 

Cela signifie - s'il y a une variable http_port dans la définition d' hôte - utilisez-la, sinon 443. Par exemple, l'interface Web jabber se bloque à 9090 et Unifi - à 7443.
http_vhost signifie ignorer DNS et prendre cette adresse.
Si uri est spécifié dans l'hôte, suivez-le, sinon prenez "/".


Une histoire amusante est sortie avec http_ssl - cette infection ne voulait pas se déconnecter à la demande. J'ai bêtement trébuché sur cette ligne pendant longtemps, jusqu'à ce qu'il me vienne à l'esprit qu'il y avait une variable dans la définition d'hôte:


 http_ssl: false 

Remplace l'expression


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

comme faux et à la fin il s'avère


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

c'est-à-dire que la vérification ssl est toujours active. Il a été décidé en remplaçant la syntaxe:


 http_ssl: no 

Conclusions :


Avantages:


  • Nous avons maintenant un système de surveillance, et non deux, comme ce fut le cas au cours des 7 à 8 derniers mois, ou un, dépassé et vulnérable.
  • La structure des données des hôtes / services (contrôles) est désormais (à mon avis) beaucoup plus lisible et compréhensible. Pour d'autres, ce n'était pas si évident, j'ai donc dû couper quelques pages sur le wiki local pour expliquer comment cela fonctionne et où le modifier.
  • Il est possible de configurer de manière flexible les vérifications à l'aide de variables et de fonctions, par exemple, pour vérifier http_regexp, le modèle souhaité, le code retour, l'url et le port peuvent être définis dans les paramètres de l'hôte.
  • Il existe plusieurs tableaux de bord, pour chacun desquels vous pouvez définir votre propre liste d'alarmes affichées et gérer tout cela via des demandes de marionnettes et de fusion.

Inconvénients:


  • Inertie des membres de l'équipe - Nagios a travaillé, travaillé et travaillé, et cela votre Isinga bogue et ralentit constamment. Et comment puis-je voir l'histoire? Et, bon sang, il n'est pas mis à jour ... (Le vrai problème est que l'historique des alarmes n'est pas mis à jour automatiquement, uniquement par F5)
  • L'inertie du système - lorsque je clique sur «vérifier maintenant» dans l'interface Web - le résultat de l'exécution dépend de la météo sur Mars, en particulier des services complexes qui prennent des dizaines de secondes à compléter. Un résultat similaire est une chose normale.
  • En général, selon les statistiques semestrielles des deux systèmes fonctionnant côte à côte, Nagios fonctionnait toujours plus vite que Icinga et cela m'a vraiment énervé. Il me semble qu'il y a quelque chose dupé par des minuteries et un contrôle de cinq minutes sur le fait va tous les 5h30 ou quelque chose comme ça.
  • Si vous redémarrez le service à tout moment (systemctl restart icinga2) - toutes les vérifications qui étaient en cours à ce moment déclencheront une alarme critique <terminée par le signal 15> sur l'écran et de côté, il semblera que tout soit tombé ( bug confirmé )

Mais en général - cela fonctionne.

Source: https://habr.com/ru/post/fr444060/


All Articles