Hallo an alle.
Ich bin der Linux-Systemadministrator und bin 2015 mit einem unabhängigen Berufsvisum von Russland nach Australien gezogen. In dem Artikel geht es jedoch nicht darum, wie man einem Ferkel einen Traktor gibt. Solche Artikel sind bereits ausreichend (bei Interesse schreibe ich auch darüber), daher möchte ich darüber sprechen, wie ich bei meiner Arbeit in Australien als Linux-Ops-Ingenieur die Migration von einem System initiiert habe Überwachung zu einem anderen. Insbesondere - Nagios => Icinga2.
Der Artikel ist teilweise technisch und teilweise über die Kommunikation mit Menschen und Probleme im Zusammenhang mit dem Unterschied in Kultur und Arbeitsmethoden.
Leider hebt das "Code" -Tag den Puppet- und Yaml-Code nicht hervor, so dass ich "Klartext" verwenden musste.
Am Morgen des 21. Dezember 2016 wurde nichts krank. Wie üblich las ich Habr in der ersten halben Stunde des Arbeitstages mit einem nicht registrierten Anonymus, nahm Kaffee auf und stieß auf diesen Artikel .
Da Nagios in meiner Firma verwendet wurde, ohne darüber nachzudenken, habe ich ein Ticket in Redmine erstellt und den Link in den allgemeinen Chat geworfen, weil ich es für wichtig hielt. Die Initiative ist sogar in Australien strafbar, daher hat der leitende Ingenieur dieses Problem an mich gehängt, seit ich es entdeckt habe.
In unserer Abteilung ist es üblich, vor der Abgabe unserer Meinung mindestens eine Alternative anzubieten, auch wenn die Wahl offensichtlich ist. Deshalb habe ich zunächst gegoogelt, welche Überwachungssysteme im Allgemeinen derzeit relevant sind, da ich in Russland am letzten Ort, an dem ich gearbeitet habe, mein eigenes persönliches Aufzeichnungssystem hatte. sehr primitiv, aber dennoch ziemlich funktionierend und erfüllt alle ihm zugewiesenen Aufgaben. Python, St. Petersburg Polytechnic und die U-Bahn-Regel. Nein, die U-Bahn ist scheiße. Dies ist persönlich (11 Jahre Arbeit) und verdient einen separaten Artikel, aber nicht jetzt.
Ein wenig über die Regeln für Änderungen an der Infrastrukturkonfiguration an meinem aktuellen Standort. Wir verwenden Puppet, Gitlab und das Prinzip der Infrastruktur als Code, so dass:
- Keine manuellen Änderungen über SSH durch manuelles Ändern von Dateien auf virtuellen Maschinen. Für drei Jahre Arbeit habe ich viele Male einen Hut dafür erhalten, den letzten vor einer Woche, und ich glaube nicht, dass es das letzte Mal war. In der Tat - korrigieren Sie eine Zeile in der Konfiguration, starten Sie den Dienst neu und prüfen Sie, ob das Problem behoben wurde - 10 Sekunden. Erstellen Sie einen neuen Zweig in Gitlab, übertragen Sie die Änderungen, warten Sie, bis r10k auf Puppetmaster funktioniert, führen Sie Puppet --environment = mybranch aus und warten Sie noch ein paar Minuten, bis dies alles funktioniert - mindestens 5 Minuten.
- Alle Änderungen werden durch Erstellen einer Zusammenführungsanforderung in Gitlab vorgenommen. Sie müssen die Genehmigung von mindestens einem Teammitglied einholen. Wesentliche Änderungen am Teamleiter erfordern zwei oder drei Genehmigungen.
- Alle Änderungen sind auf die eine oder andere Weise textuell (da Puppet-Manifeste, Hiera-Skripte und -Daten Text sind), Binärdateien werden dringend empfohlen, und es sind gute Gründe erforderlich, um sie zu genehmigen.
Die Optionen, die ich mir angesehen habe, sind:
- Munin - Wenn mehr als 10 Server in der Infrastruktur vorhanden sind, wird die Verwaltung zur Hölle (aus diesem Artikel . Ich hatte nicht viel Lust, dies zu überprüfen, also habe ich mein Wort dafür genommen).
- Zabbix - hat schon lange in Russland nachgesehen, aber dann war es für meine Aufgaben überflüssig. Hier - musste aufgrund der Verwendung von Puppet als Konfigurationsmanager und Gitlab als Versionskontrollsystem fallengelassen werden. In diesem Moment speichert Zabbix nach meinem Verständnis die gesamte Konfiguration in einer Datenbank. Daher war nicht klar, wie die Konfiguration unter den aktuellen Bedingungen verwaltet und Änderungen verfolgt werden sollen.
- Prometheus ist das, worauf wir am Ende kommen werden, gemessen an der Stimmung in der Abteilung, aber zu diesem Zeitpunkt konnte ich es nicht beherrschen und konnte keine wirklich funktionierende Probe (Proof of Concept) demonstrieren, also musste ich mich weigern.
- Es gab mehrere andere Optionen, die entweder eine vollständige Neugestaltung des Systems erforderten oder noch in den Kinderschuhen steckten / aufgegeben wurden und aus demselben Grund abgelehnt wurden.
Am Ende habe ich mich aus drei Gründen für Icinga2 entschieden:
1 - Kompatibilität mit Nrpe (einem Client-Dienst, der Befehle von Nagios überprüft). Dies war sehr wichtig, da wir zu dieser Zeit 135 (jetzt sind es 165 davon im Jahr 2019) virtuelle Maschinen mit einer Reihe von selbstgeschriebenen Diensten / Schecks hatten und all dies zu wiederholen, wäre eine schreckliche Hämorrhoide.
2 - Alle Konfigurationsdateien sind Textdateien, wodurch es einfach ist, diese Angelegenheit zu bearbeiten, Zusammenführungsanforderungen zu erstellen und zu sehen, was hinzugefügt oder gelöscht wurde.
3 ist ein lebendiges und wachsendes OpenSource-Projekt. Wir lieben OpenSource sehr und leisten einen praktikablen Beitrag dazu, indem wir Pull-Anfragen und -Probleme erstellen, um Probleme zu lösen.
Also lass uns gehen, Icinga2.
Das erste, was ich zu bewältigen hatte, war die Trägheit meiner Kollegen. Jeder ist an Nagios / Najios (obwohl sie auch hier keine Kompromisse bei der Aussprache eingehen konnten) und die CheckMK-Oberfläche gewöhnt. Die Icinga-Oberfläche sieht völlig anders aus (es war ein Minus), aber es ist möglich, das, was Sie sehen müssen, mit Filtern buchstäblich anhand eines beliebigen Parameters flexibel zu konfigurieren (es war ein Plus, aber ich habe insbesondere dafür gekämpft).
Schätzen Sie das Verhältnis der Größe der Bildlaufleiste zur Größe des Bildlauffelds.
Das zweite - jeder ist es gewohnt, die gesamte Infrastruktur auf einem Monitor zu sehen, da Sie mit CheckMk mit mehreren Nagios-Hosts arbeiten können, aber Icinga wusste nicht, wie es geht (tatsächlich, aber mehr dazu weiter unten). Eine Alternative war Thruk, aber ihr Design verursachte Erbrechen bei allen Teammitgliedern, außer bei einem - demjenigen, der es vorgeschlagen hatte (nicht bei mir).
Thruk Firebox - Einstimmige Teamentscheidung Nach ein paar Tagen eines Brainstormings schlug ich die Idee der Clusterüberwachung vor, wenn sich ein Master-Host in der Produktionszone und zwei Untergebene befinden - einer in dev / test und ein externer Host bei einem anderen Anbieter, um unsere Dienste aus der Sicht eines Kunden oder eines Außenstehenden zu überwachen Beobachter. Diese Konfiguration ermöglichte es mir, alle Probleme in einer webbasierten Oberfläche zu sehen, und es funktionierte für mich, aber Puppet ... Das Problem mit Puppet war, dass der Master-Host nun alle Hosts und Dienste / Prüfungen im System kennen und sie auf Zonen verteilen musste (dev-test, staging-prod, ext), aber das Senden von Änderungen über die Icinga-API dauert einige Sekunden, das Kompilieren des Puppet-Verzeichnisses aller Dienste für alle Hosts jedoch einige Minuten. Das wird mir immer noch angelastet, obwohl ich bereits mehrmals erklärt habe, wie alles funktioniert und warum alles so lange dauert.
Drittens - ein Haufen SnowFlakes (Schneeflocken) - Dinge, die aus dem allgemeinen System herausgeschlagen werden, weil sie etwas Besonderes haben, sodass die allgemeinen Regeln für sie nicht gelten. Es wurde durch einen Frontalangriff entschieden - wenn es einen Alarm gibt, aber tatsächlich alles in Ordnung ist, dann müssen Sie hier tiefer graben und verstehen, warum es mich alarmiert, obwohl es nicht sollte. Oder umgekehrt - warum Nagios in Panik gerät, Icinga jedoch nicht.
Viertens: Nagios hat drei Jahre für mich gearbeitet und anfangs gab es mehr Vertrauen in ihn als in mein neues Hipster-System. Jedes Mal, wenn Icinga in Panik geriet, tat niemand etwas, bis Nagios sich über das gleiche Thema aufregte. Aber sehr selten gab Icinga früher als Nagios echte Alarme aus, und ich halte dies für einen ernsthaften Pfosten, den ich im Abschnitt "Schlussfolgerungen" erörtern werde.
Infolgedessen verzögerte sich die Inbetriebnahme um mehr als 5 Monate (geplant am 28. Juni 2018, tatsächlich - 3. Dezember 2018), hauptsächlich wegen der „Paritätsprüfung“ - diesem Mist, wenn es in Nagios mehrere Dienste gibt, über die niemand spricht Ich habe in den letzten Jahren nichts gehört, aber JETZT haben sie verdammt ohne Grund Kritik abgegeben und ich musste erklären, warum sie nicht in meinem Panel waren und sie zu Icinga hinzufügen, um "Paritätsprüfung ist abgeschlossen" (Alle Dienste / Prüfungen) in Nagios entsprechen Dienstleistungen / Schecks in Icinga)
Implementierung:
Der erste ist der Code vs Data War wie Puppet Style. Alle Daten, hier alles im Allgemeinen, sollten in Hiera sein und sonst nichts. Der gesamte Code befindet sich in PP-Dateien. Variablen, Abstraktionen, Funktionen - alles geht in pp.
Infolgedessen verfügen wir über eine Reihe virtueller Maschinen (165 zum Zeitpunkt des Schreibens) und 68 Webanwendungen, die auf den Zustand und die Gültigkeit von SSL-Zertifikaten überwacht werden müssen. Aufgrund historischer Hämorrhoiden werden Informationen für Überwachungsanwendungen aus einem separaten Gitlab-Repository entnommen, und das Datenformat hat sich seit Puppet 3 nicht geändert, was zu zusätzlichen Konfigurationsschwierigkeiten führt.
Puppet-Code für Anwendungen, Vorsichtdefine 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 } } } } } } } }
Der Host- und Service-Konfigurationscode sieht ebenfalls schrecklich aus:
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', } }
Ich arbeite immer noch an dieser Nudel und verbessere sie, wann immer es möglich ist. Es war jedoch dieser Code, der es ermöglichte, eine einfache und klare Syntax in Hiera zu verwenden:
Daten 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'
Alle Überprüfungen sind in Gruppen unterteilt. Jede Gruppe verfügt über Standardeinstellungen, z. B. wo und wie oft diese Überprüfungen ausgeführt werden sollen, welche Benachrichtigungen an wen gesendet werden sollen.
Bei jeder Prüfung können Sie jede Option überschreiben, und all dies summiert sich schließlich zu den Standardeinstellungen aller Prüfungen als Ganzes. Daher werden solche Nudeln in config.pp geschrieben - alle Standardeinstellungen werden mit den Einstellungen der Gruppen und dann mit jeder einzelnen Prüfung zusammengeführt.
Eine sehr wichtige Änderung war auch die Möglichkeit, Funktionen in den Einstellungen zu verwenden, z. B. die Funktion zum Ändern des Ports, der Adresse und der URL, um http_regex zu überprüfen.
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
Dies bedeutet, dass - wenn die Hostdefinition eine http_port- Variable enthält - diese verwendet wird, andernfalls 443. Die Jabber-Weboberfläche hängt beispielsweise an 9090 und Unifi an 7443.
http_vhost bedeutet, DNS zu ignorieren und diese Adresse zu übernehmen.
Wenn uri im Host angegeben ist, gehen Sie mit, andernfalls nehmen Sie "/".
Mit http_ssl kam eine lustige Geschichte heraus - diese Infektion wollte nicht bei Bedarf die Verbindung trennen. Ich bin lange Zeit dumm über diese Zeile gestolpert, bis mir klar wurde, dass die Variable in der Host-Definition:
http_ssl: false
Ersatz in Ausdruck
if(host.vars.http_ssl) { return false } else { return true }
als falsch und am Ende stellt sich heraus
if(false) { return false } else { return true }
Das heißt, die SSL-Prüfung ist immer aktiv. Es wurde durch Ersetzen der Syntax entschieden:
http_ssl: no
Schlussfolgerungen :
Vorteile:
- Wir haben jetzt ein Überwachungssystem und nicht zwei, wie in den letzten 7 bis 8 Monaten, oder eines, das veraltet und anfällig ist.
- Die Datenstruktur von Hosts / Services (Checks) ist jetzt (meiner Meinung nach) viel lesbarer und verständlicher. Für andere war dies nicht so offensichtlich, deshalb musste ich ein paar Seiten im lokalen Wiki schneiden, um zu erklären, wie es funktioniert und wo es bearbeitet werden kann.
- Es ist möglich, Überprüfungen mithilfe von Variablen und Funktionen flexibel zu konfigurieren, z. B. um http_regexp zu überprüfen. Das gewünschte Muster, der Rückkehrcode, die URL und der Port können in den Hosteinstellungen festgelegt werden.
- Es gibt mehrere Dashboards, für die Sie jeweils eine eigene Liste der angezeigten Alarme definieren und all dies über Puppet- und Merge-Anforderungen verwalten können.
Nachteile:
- Trägheit der Teammitglieder - Nagios hat gearbeitet, gearbeitet und gearbeitet, und dies ist eine ständige Störung und Verlangsamung Ihrer Isinga. Und wie kann ich die Geschichte sehen? Und verdammt noch mal, es wird nicht aktualisiert ... (Das eigentliche Problem ist, dass der Alarmverlauf nicht automatisch aktualisiert wird, nur durch F5)
- Die Trägheit des Systems - wenn ich in der Weboberfläche auf "Jetzt prüfen" klicke - hängt das Ergebnis der Ausführung vom Wetter auf dem Mars ab, insbesondere von komplexen Diensten, deren Ausführung mehrere zehn Sekunden dauert. Ein ähnliches Ergebnis ist normal.

- Im Allgemeinen arbeitete Nagios laut der Halbjahresstatistik der beiden nebeneinander arbeitenden Systeme immer schneller als Icinga, was mich sehr ärgerte. Es scheint mir, dass es etwas gibt, das mit Timern getäuscht wird, und eine fünfminütige Überprüfung der Tatsache findet alle 5:30 oder so etwas statt.
- Wenn Sie den Dienst jederzeit neu starten (systemctl restart icinga2), lösen alle zu diesem Zeitpunkt laufenden Überprüfungen einen kritischen Alarm <beendet durch Signal 15> auf dem Bildschirm aus und von der Seite sieht es so aus, als wäre alles gefallen ( bestätigter Fehler) )
Aber im Allgemeinen - es funktioniert.