Hallo.
Ich bin Linux-Systemadministrator und bin 2015 mit einem unabhängigen Berufsvisum von Russland nach Australien gezogen, aber in dem Artikel geht es nicht darum, wie man einen Traktor für ein Schwein startet. Es gibt bereits genügend solcher Artikel (wenn jedoch Interesse besteht, werde ich auch darüber schreiben), daher möchte ich darüber sprechen, wie ich bei meiner Arbeit in Australien als Linux-Ops-Ingenieur die Migration von einer Systemüberwachung initiiert habe zum anderen. Konkret - Nagios => Icinga2.
Der Artikel ist teils technisch, teils geht es um die Kommunikation mit Menschen und um Probleme im Zusammenhang mit Unterschieden in Kultur und Arbeitsmethoden.
Leider hebt das Tag „code“ Puppet- und Yaml-Code nicht hervor, sodass ich „plaintext“ verwenden musste.
Am Morgen des 21. Dezember 2016 gab es keine Anzeichen von Problemen. Wie immer habe ich in der ersten halben Stunde des Arbeitstages beim Kaffeetrinken Habr von einem nicht registrierten anonymen Benutzer gelesen und bin darauf gestoßen .
Da mein Unternehmen Nagios nutzte, habe ich ohne lange nachzudenken ein Ticket in Redmine erstellt und den Link zum allgemeinen Chat gesendet, weil ich es für wichtig hielt. Die Initiative ist selbst in Australien strafbar, daher hat der leitende Ingenieur mir das Problem in die Schuhe geschoben, seit ich es entdeckt habe.
Screenshot von Redmine
In unserer Abteilung ist es üblich, vor einer Meinungsäußerung mindestens eine Alternative anzubieten, auch wenn die Wahl offensichtlich ist. Deshalb habe ich zunächst gegoogelt, welche Art von Überwachungssystemen derzeit relevant sind, da ich in Russland an meinem letzten Arbeitsplatz war Ich hatte mein eigenes, selbst geschriebenes System, sehr primitiv, aber dennoch recht funktionsfähig und erfüllte alle ihm zugewiesenen Aufgaben. Python, das St. Petersburger Polytechnikum und die U-Bahn-Regel. Nein, die U-Bahn ist scheiße. Das ist persönlich (11 Jahre Arbeit) und verdient einen eigenen 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 Infrastructure as a Code-Prinzip, also:
- Keine manuellen Änderungen über SSH durch manuelles Ändern von Dateien auf den virtuellen Maschinen. Im Laufe meiner dreijährigen Arbeit wurde ich dafür oft mit einem Hut belegt, das letzte Mal war vor einer Woche und ich glaube nicht, dass das das letzte Mal war. Nun, wirklich – korrigieren Sie eine Zeile in der Konfiguration, starten Sie den Dienst neu und prüfen Sie, ob das Problem gelöst ist – 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 alles funktioniert – mindestens 5 Minuten.
- Alle Änderungen werden durch Erstellen einer Merge-Anfrage in Gitlab vorgenommen und müssen von mindestens einem Teammitglied genehmigt werden. Größere Änderungen, die vom Teamleiter beschlossen werden, erfordern zwei oder drei Genehmigungen.
- Bei allen Änderungen handelt es sich auf die eine oder andere Weise um Text (da es sich bei Puppet-Manifesten, Skripten und Hiera-Daten um Text handelt). Von Binärdateien wird dringend abgeraten und es muss einen zwingenden Grund für die Genehmigung solcher Dateien geben.
Also, die Optionen, die ich in Betracht gezogen habe:
- Munin – wenn es mehr als 10 Server in der Infrastruktur gibt, wird die Verwaltung zur Hölle (von . Ich hatte keinen besonderen Wunsch, dies zu überprüfen, also habe ich mich auf sein Wort verlassen.
- Zabbix – ich hatte es mir schon lange in Russland angesehen, aber dann war es für meine Aufgaben überflüssig. Hier musste aufgrund der Verwendung von Puppet als Konfigurationsmanager und Gitlab als Versionskontrollsystem verworfen werden. Zu diesem Zeitpunkt speicherte Zabbix meines Wissens die gesamte Konfiguration in der Datenbank und daher war nicht klar, wie die Konfiguration unter den aktuellen Bedingungen verwaltet und Änderungen verfolgt werden sollten.
- Zu Prometheus werden wir am Ende kommen, gemessen an der Stimmung in der Abteilung, aber zu diesem Zeitpunkt beherrschte ich es nicht und konnte kein wirklich funktionierendes Beispiel (Proof of Concept) vorweisen, also musste ich ablehnen.
- Es gab auch mehrere andere Optionen, die entweder eine komplette Überarbeitung des Systems erforderten oder noch in den Kinderschuhen steckten bzw. aufgegeben wurden und aus dem gleichen Grund abgelehnt wurden.
Am Ende habe ich mich aus drei Gründen für Icinga2 entschieden:
1 – Kompatibilität mit Nrpe (ein Client-Dienst, der Befehlsprüfungen von Nagios ausführt). Das war sehr wichtig, denn zu diesem Zeitpunkt hatten wir 135 (jetzt sind es 2019 im Jahr 165) virtuelle Maschinen mit einer Reihe individuell geschriebener Dienste/Prüfungen, und es wäre eine echte Mühe gewesen, alles zu wiederholen.
2 – Alle Konfigurationsdateien sind Textdateien, was es einfach macht, diese Angelegenheit zu bearbeiten, Zusammenführungsanfragen zu erstellen und zu sehen, was hinzugefügt oder gelöscht wurde.
3 ist ein lebendiges und sich entwickelndes OpenSource-Projekt. Wir lieben OpenSource sehr und leisten jeden möglichen Beitrag dazu, indem wir Pull Requests und Issues erstellen, um Probleme zu lösen.
Also, los geht's, Icinga2.
Das erste, womit ich mich auseinandersetzen musste, war die Trägheit meiner Kollegen. Jeder ist an Nagios/Naggios (obwohl man sich auch hier bei der Aussprache nicht einigen konnte) und die CheckMK-Schnittstelle gewöhnt. Die Icinga-Oberfläche sieht völlig anders aus (das war ein Minus), aber es ist möglich, mithilfe von Filtern für buchstäblich jeden Parameter flexibel anzupassen, was Sie sehen müssen (das war ein Plus, aber ich habe viel dafür gekämpft).
Filter
Schätzen Sie das Verhältnis der Größe der Bildlaufleiste zur Größe des Bildlauffelds.
Zweitens ist jeder es gewohnt, die gesamte Infrastruktur auf einem Monitor zu sehen, denn mit CheckMk kann man mit mehreren Nagios-Hosts arbeiten, die Icinga-Schnittstelle konnte dies jedoch nicht (tatsächlich könnte sie das, aber dazu weiter unten mehr). Die Alternative war etwas namens Thruk, aber sein Design brachte alle im Team zum Würgen, bis auf einen – den, der es vorgeschlagen hatte (nicht mich).
Ab in den Thruk-Ofen – die einstimmige Entscheidung des Teams
Nach ein paar Tagen des Brainstormings schlug ich die Idee der Clusterüberwachung vor, bei der es einen Master-Host in der Produktionszone und zwei Slaves gibt – einen in Entwicklung/Test und einen externen Host bei einem anderen Anbieter, um unsere zu überwachen Dienstleistungen aus der Sicht eines Kunden oder eines externen Beobachters. Diese Konfiguration ermöglichte es, alle Probleme in einer Weboberfläche zu sehen und funktionierte recht gut, aber Puppet ... Das Problem mit Puppet bestand darin, dass der Master-Host nun über alle Hosts und Dienste/Prüfungen im System Bescheid wissen musste und dies auch tat um sie zwischen Zonen zu verteilen (dev-test, staging-prod, ext), aber das Senden von Änderungen über die Icinga-API dauert ein paar Sekunden, aber das Kompilieren des Puppet-Verzeichnisses aller Dienste für alle Hosts dauert ein paar Minuten. Das wird mir immer noch vorgeworfen, obwohl ich schon mehrfach erklärt habe, wie alles funktioniert und warum das alles so lange dauert.
Drittens gibt es eine Reihe von SnowFlakes – Dinge, die sich vom allgemeinen System abheben, weil sie etwas Besonderes haben, sodass die allgemeinen Regeln für sie nicht gelten. Das Problem wurde durch einen Frontalangriff gelöst. Wenn es Alarme gibt, aber tatsächlich alles in Ordnung ist, muss ich tiefer graben und herausfinden, warum es mich alarmiert, obwohl es nicht so sein sollte. Oder umgekehrt – warum Nagios in Panik gerät, Icinga jedoch nicht.
Viertens arbeitete Nagios drei Jahre vor mir hier und das Vertrauen zu ihm war anfangs größer als zu meinem neumodischen Hipster-System, sodass jedes Mal, wenn Icinga Panik auslöste, niemand etwas unternahm, bis Nagios sich für das gleiche Problem begeisterte. Aber sehr selten hat Icinga früher als Nagios echte Alarme ausgelöst, und ich halte dies für ein ernstes Problem, auf das ich im Abschnitt „Schlussfolgerungen“ eingehen werde.
Infolgedessen verzögerte sich die Inbetriebnahme um mehr als 5 Monate (eigentlich geplant für den 28. Juni 2018 – den 3. Dezember 2018), hauptsächlich aufgrund der „Paritätsprüfung“ – diesem Mist, wenn es in Nagios mehrere Dienste gibt, die niemand kennt Ich habe in den letzten paar Jahren ungefähr nichts gehört, aber IM JETZT haben sie verdammt noch mal grundlos Kritik geäußert und ich musste erklären, warum sie nicht in meinem Panel waren und musste sie zu Icinga hinzufügen, damit „Paritätsprüfung“ ist vollständig“ (Alle Dienste/Prüfungen in Nagios entsprechen Diensten/Prüfungen in Icinga)
Implementierung:
Der erste ist der Code-gegen-Daten-Krieg, wie zum Beispiel Puppet Style. Alle Daten, absolut alles, müssen in Hiera sein und nichts anderes. 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 Leistung und Gültigkeit von SSL-Zertifikaten überwacht werden müssen. Aber 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ätzlicher Komplexität in der Konfiguration führt.
Puppet-Code für Anwendungen, schützen Sie Ihre Augen
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
}
}
}
}
}
}
}
}Auch der Konfigurationscode für Hosts und Dienste sieht 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 diesen Nudeln und verbessere sie so gut ich kann. Es war jedoch dieser Code, der es uns ermöglichte, in Hiera eine einfache und verständliche Syntax 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 Prüfungen sind in Gruppen unterteilt. Jede Gruppe verfügt über Standardeinstellungen, z. B. wo und wie oft diese Prüfungen durchgeführt werden sollen, welche Benachrichtigungen gesendet werden sollen und an wen.
In jeder Prüfung können Sie jede Option überschreiben, und all dies ergibt letztendlich die Standardeinstellungen aller Prüfungen als Ganzes. Deshalb steht in der config.pp so ein Unsinn – da werden alle Standardeinstellungen mit den Gruppeneinstellungen und dann mit jeder einzelnen Prüfung zusammengeführt.
Eine weitere sehr wichtige Änderung war die Möglichkeit, Funktionen in den Einstellungen zu verwenden, beispielsweise die Funktion zum Ersetzen von Port, Adresse und 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: trueDies bedeutet, dass in der Host-Definition eine Variable vorhanden ist http_port – verwenden Sie es, andernfalls 443. Beispielsweise hängt die Jabber-Weboberfläche bei 9090 und Unifi bei 7443.
http_vhost bedeutet, DNS zu ignorieren und diese Adresse zu übernehmen.
Wenn die URL im Host angegeben ist, folgen Sie dieser, andernfalls verwenden Sie „/“.
Mit http_ssl kam eine lustige Geschichte heraus – diese Infektion wollte die Verbindung bei Bedarf nicht trennen. Ich war lange verwirrt über diese Zeile, bis mir klar wurde, dass die Variable in der Host-Definition lautet:
http_ssl: falseIn Ausdruck ersetzt
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. Ich habe es gelöst, indem ich die Syntax ersetzt habe:
http_ssl: noBefund:
Profis:
- Wir haben jetzt ein Überwachungssystem und nicht zwei, wie wir es in den letzten sieben bis acht Monaten hatten, oder eines, das veraltet und anfällig ist.
- Die Datenstruktur hosts/services(checks) ist jetzt (meiner Meinung nach) viel besser lesbar und verständlich. Für andere stellte sich heraus, dass dies nicht so offensichtlich war, also musste ich ein paar Seiten im lokalen Wiki veröffentlichen, um zu erklären, wie das alles funktioniert und was man wo bearbeiten kann.
- Es ist möglich, Prüfungen mithilfe von Variablen und Funktionen flexibel zu konfigurieren, um beispielsweise http_regexp zu prüfen. Das gewünschte Muster, der Rückgabecode, die URL und der Port können in den Hosteinstellungen festgelegt werden.
- Es gibt mehrere Dashboards, für jedes davon können Sie Ihre eigene Liste der angezeigten Alarme definieren und all dies über Puppet- und Merge-Anfragen verwalten.
Nachteile:
- Trägheit der Teammitglieder – Nagios hat gearbeitet, gearbeitet und gearbeitet, und Ihr Isinga ist ständig fehlerhaft und langsam. Wie kann man hier die Geschichte sehen? Oh, verdammt, es wird nicht aktualisiert... (Das eigentliche Problem ist, dass der Alarmverlauf nicht automatisch aktualisiert wird, sondern nur durch F5)
- Die Trägheit des Systems – wenn ich im Webinterface auf „Aktualisieren“ (jetzt prüfen) klicke – hängt das Ausführungsergebnis vom Wetter auf dem Mars ab, insbesondere bei komplexen Diensten, deren Ausführung mehrere zehn Sekunden dauert. Ein solches Ergebnis ist normal.

- Im Allgemeinen arbeitete Nagios laut der sechsmonatigen Statistik des Betriebs der beiden Systeme nebeneinander immer schneller als Icinga, und das hat mich wirklich geärgert. Mir kommt es so vor, als hätten sie etwas mit den Timern vermasselt, und die Überprüfung erfolgt alle fünf Minuten, tatsächlich geschieht sie alle 5:30 Uhr oder so ähnlich.
- Wenn Sie den Dienst zu irgendeinem Zeitpunkt neu starten (systemctl restart icinga2), generieren alle zu diesem Zeitpunkt laufenden Prüfungen einen kritischen Alarm auf dem Bildschirm und von außen sieht es so aus, als ob alles heruntergefallen wäre ().
Aber im Großen und Ganzen funktioniert es.
Source: habr.com

