Migratsioon Nagioselt Icinga2-le Austraalias

Tere kõigile.

Olen Linuxi süsteemiadministraator, kes kolis 2015. aastal Venemaalt Austraaliasse sõltumatul ametialasel viisal. Kuid artikkel ei räägi sellest, kuidas sigaloomale traktor saada. Selliseid artikleid on juba piisavalt (kui see huvitab, võin sellest ka kirjutada), seega soovin jagada, kuidas oma töös Austraalias Linuxi opsiinsenerina algatasin migratsiooni ühest monitooringusüsteemist teise. Nimelt: Nagios => Icinga2.

Artikkel on osaliselt tehniline ja osaliselt räägib inimestevahelisest suhtlemisest ning kultuuriliste ja töömeetodite erinevustest tulenevatest probleemidest.

Kahjuks ei esitle silt «code» Puppet'i ja yaml'i koodi, seega tuli kasutada «plaintext».

21. detsember 2016 hommikul ei valmistanud miski mulle muret. Lugesin nagu tavaliselt Habr'i registreerimata anonüümsena tööpäeva esimesed pool tundi, nautides kohvi ja sattusin peale. selle artikli.

Kuna minu ettevõttes kasutati Nagios't, lõin kiiresti pilet Redmine'is ja jagasin lingi ühises vestluses, kuna pidasin seda oluliseks. Algatus on Austraalias isegi karistatav, seega määras juhtiv insener selle probleemi minu kanda, kuna avastasin selle.

Ekraanipilt Redmine'istMigratsioon Nagioselt Icinga2-le Austraalias

Meie osakonnas on tavaks esitada enne oma arvamuse väljaütlemist vähemalt üks alternatiiv, isegi kui valik on ilmselge. Seetõttu alustasin otsingut, millised jälgimissüsteemid on hetkel aktuaalsed. Venemaal oli minu eelnevas töökohas oma isiklik jälgimissüsteem, mis oli väga primitiviivne, kuid töötas siiski korralikult ja täitis kõik oma ülesanded. Python, Peterburi Tehnikaülikool ja metroo on ägedad. Ei, metroo on jama. See on isiklik kogemus (11 aastat tööd) ja väärib eraldi artiklit, aga mitte praegu.

Veidi reeglitest infrastruktuuri konfiguratsiooni muutmiseks minu praeguses kohas. Meil kasutatakse Puppetit, Gitlab'i ja põhimõtet Infrastructure as Code, seega:

  • Mitte ei ole lubatud käsitsi muudatusi SSH kaudu, muutes faile virtuaalmasinates. Kolme aasta jooksul olen selle tõttu palju karistada saanud, viimane kord oli nädal tagasi ja ei arva, et see on viimane kord. Ausalt öeldes — ühe rea parandamine konfiguratsioonis, teenuse taaskäivitamine ja vaatamine, kas probleem on lahendatud — 10 sekundit. Uue haru loomine Gitlabis, muudatuste pushimine, oodates, kuni r10k töötab Puppetmasteril, Puppet käivitamine —environment=mybranch ja veel paar minutit oodata, kuni kõik see töötab — vähemalt 5 minutit.
  • Mis tahes muudatused tehakse Merge Requesti loomise kaudu Gitlabis ning nõutakse, et vähemalt ühe meeskonnaliikme heakskiit oleks saadud. Tõsised muudatused, mille otsustab tiimijuht, nõuavad kahte või kolme heakskiitu.
  • Kõik muudatused on mingil moel tekstipõhised (kuna Puppet'i manifestid, skriptid ja Hiera andmed on tekst), binaarfailide kasutamine ei ole soovitatav ning nende heakskiitmiseks on vaja kaalukat põhjust.

Nii et valikud, mida ma kaalusin:

  • Munin — kui infrastruktuuris on rohkem kui 10 serverit, muutub haldamine tõeliseks põrguks (kui selle artikli. Mul ei olnud erilist soovi seda kontrollida, seega usaldasin sõna.
  • Zabbix — olen seda juba ammu jälginud, veel Venemaal, kuid siis oli see minu vajaduste jaoks liiga keeruline. Siin pidin loobuma, kuna kasutasin Puppet'i konfiguratsiooni juhtimise tööriistana ja GitLab'i versioonihaldussüsteemina. Sel hetkel, kui ma õigesti arusaama, salvestab Zabbix kogu konfiguratsiooni andmebaasi, mistõttu oli ebaselge, kuidas konfiguratsiooni antud tingimustes hallata ja muudatusi jälgida.
  • Prometheus — see, mille poole me lõpuks suundume, arvestades osakonnas valitsevat meeleolu, kuid sel hetkel ei suutnud ma seda tõhusalt rakendada ja ei suutnud näidata tõeliselt töötavat näidist (Proof of Concept), seega pidin loobuma.
  • Võttes arvesse, et mõned teised variandid nõudsid süsteemi täielikku ümbertegemist või olid lapsekingades / hüljatud, tõrjusin need samuti.

Lõpuks peatusin Icinga2-l kolmel põhjusel:

1 — ühilduvus Nrpe'ga (klientteenus, mis viib läbi kontrollimisi käsu kaudu Nagioselt). See oli väga oluline, kuna meil oli sel ajal 135 (praegu 2019. aastal 165) virtuaalmasinat, millega oli palju isetehtud teenuseid / kontrollimisi, ja kõike seda ümber teha oleks olnud äärmiselt keeruline.
2 — kõik konfiguratsioonifailid on tekstivormingus, mis võimaldab neid hõlpsalt redigeerida ning luua merge-päringuid, kus on võimalik näha, mis on lisatud või kustutatud.
3 — see on elav ja arenev OpenSource projekt. Me armastame väga OpenSource'i ja panustame sellesse, luues Pull Requests ja Issues probleemide lahendamiseks.

Nii et alustame, Icinga2.

Esimene probleem, millega pidin silmitsi seisma, oli kolleegide inertsus. Kõik olid harjunud Nagios'/Nadji'se (kuigi isegi siin ei suudetud kompromissi saavutada, kuidas seda hääldada) ja CheckMK liidesega. Icinga liides näeb välja täielikult erinev (see oli miinus), kuid pakub võimalust paindlikult seadistada, mida soovite näha, kasutades filtreid peaaegu igas parameetris (see oli plusspunkt, kuid see nõudis palju vastasseisu).

FiltridMigratsioon Nagioselt Icinga2-le Austraalias

Hinnake kerimisriba suuruse suhet kerimisala suurusega.

Teiseks — kõik olid harjunud nägema kogu infrastruktuuri ühel monitoril, kuna CheckMk võimaldab töötada mitme Nagios-hostiga, kuid Icinga liides ei osanud seda (tegelikult oskas, aga sellest räägime hiljem). Alternatiiviks oli asi nimega Thruk, kuid tema disain tekitas igasuguseid oksendustunneid kogu meeskonnas, välja arvatud ühes — selle ettepanija (mitte mina).

Thruk läheb prügikasti — meeskonna üksmeelne otsusMigratsioon Nagioselt Icinga2-le Austraalias

Paar päeva mõttevahetust hiljem esitasin idee klastrimonitoringu kohta, kus on üks põhihost tootmisalal ja kaks alamhosti — üks dev/testis ja üks väline host, mis asub teise teenusepakkuja juures, et jälgida meie teenuseid kliendi või välise vaatlejana. See konfigureerimine lubas näha kõiki probleeme ühes veebiliideses ja toimis päris hästi, kuid Puppet... Probleem Puppetiga oli see, et põhihost pidi nüüd teadma kõigist hostidest ja teenustest/ kontrollidest süsteemis ning jagama need ära alade vahel (dev-test, staging-prod, ext), kuid muudatuste saatmine Icinga API kaudu kestab paar sekundit, samas kui kõikide hostide jaoks Puppet katalogi kompileerimine teenustes võtab paar minutit. Seda on mulle dodmimmutatud, kuigi olen juba korduvalt selgitanud, kuidas kõik töötab ja miks see kõik nii kaua aega võtab.

Kolmas — hulk SnowFlakes'ist (lumihelbedest) — asjad, mis ei sobi süsteemi, sest nendes on midagi erilist, mistõttu üldised reeglid nende kohta ei kehti. Probleemilahendamine toimus otse rünnaku kaudu — kui on häireid, aga tegelikult on kõik korras, tuleb süveneda ja uurida, miks see mul alarmeerib, kuigi ei peaks. Või vastupidi — miks Nagios paanitseb, aga Icinga ei tee seda.

Neljas — Nagios töötas siin kolm aastat enne mind ja tema usaldusväärsus oli algselt suurem kui minu moodsa hipsterliku süsteemi oma, nii et iga kord, kui Icinga pidas paanikat — ei tehtud midagi, kuni Nagios ei hakanud sama mure üle ärevust tundma. Kuid väga harva andis Icinga reaalseid häireid varem kui Nagios, ja ma pean seda tõsiseks puuduseks, millest räägin jaotises „Järeldused“.

Kokkuvõttes venis kasutuselevõtt rohkem kui 5 kuud (plaanitud 28. juuniks 2018, tegelikult 3. detsembriks 2018), peamiselt seoses „parity check'iga“ — sellega, et Nagioses on mitu teenust, millest keegi ei ole viimasel ajal midagi kuulnud, kuid JUST SELLEL HETKEL nad, jumal küll, andsid kriitilise teate ilma igasuguse põhjuseta ning pidin selgitama, miks neid ei ole minu paneelis ja pidin need Icingasse lisama, et „parity check on lõpule viidud“ (kõik teenused/ettevõtted Nagioses vastavad teenustele/ettevõtetele Icingas).

Rakendamine:
Esiteks — sõda Code vs Data, nagu Puppet'i stiil. Kõik andmed, tõeliselt kõik, peavad olema Hieras ja mitte mujal. Kood peab olema .pp failides. Muutujad, abstraktsioonid, funktsioonid — kõik on .pp-s.
Tulemuseks on see, et meil on hunnik virtuaalseid masinaid (165 artikli kirjutamise hetkel) ja 68 veebirakendust, mida tuleb jälgida töökindluse ning SSL-sertifikaatide kehtivuse osas. Kuid ajalise mure tõttu tuleb rakenduste jälgimise teave eraldi gitlab-repositooriumist ja andmevorming ei ole muutunud alates Puppet 3-st, mis tekitab konfigureerimisel lisaraskusi.

Puppet-kood rakenduste jaoks, hoidke silmad lahti.

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'], {} )) } # lisab defauktse rühma (süsteemid) teadetega + kõik rühmad, mis on määratletud 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')        # vali regex kontrollimiseks

    $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 | {        # Jagab rakenduse domeenide järgi, kui on kaks või enam
      $vhost_name = {'http_vhost' => $vhost}

      $vars = $data['vars'] + $vhost_name + $check_regex + $check_url

      $web_ipaddress = is_array($vdata['web_ipaddress']) ? {  # Muudab IP-aadressi massiiviks, kui see ei ole, sest askizzy-l on 2 ip-d ja see on massiiv
        true  => $vdata['web_ipaddress'],
        false => [$vdata['web_ipaddress']],
      }

      $access_from_zones = [$zone] + $apps_access_list[$data['accessible_from']] # Ühendab vaikesendi, kus rakendus on määratletud ja lisazoonid, kui need eksisteerivad
      $web_ipaddress.each | String $ip_address | {            # Iga IP jaoks (kui meil on mitu)
        $suffix = length($web_ipaddress) ? {                  # Kui meil on rohkem kui üks - lisame IP sellele nimele, et vältida ressursside dubleerimist
          1       => '',
          default => "_${ip_address}"
        }
        $octets = split($ip_address, '.')
        $ip_tag = "${octets[2]}.${octets[3]}" # Kasutades ainult viimast okteti põhjustab kokkupõrke nginx-vip 203.15.70.94 ja ext. ip 49.255.194.94 vahel

        $access_from_zones.each | $zone_prefix |{
          $zone_target = $targets[$zone_prefix]

          $nginx_vip_name = "${zone_prefix}_nginx-vip-${ip_tag}" # Kui see on host ext - eesliide muutub '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
              }
            }
          }
        }
      }
    }
  }
}

Serverite ja teenuste konfiguratsioonikood näeb samuti kohutav välja:

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

}

Ma jätkan siiani selle koodi täiustamist, et muuta see võimalikult arusaadavaks. Just selline kood võimaldas Hiera's kasutada lihtsat ja selget süntaksit:

Andmed

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'

Kõik kontrollid on jagatud gruppidesse, millel igal ühel on vaikeseaded, kuidas ja kui sageli neid kontrolle käivitada, milliseid teateid saata ja kellele.

Iga kontrolli korral on võimalik üle määrata igasuguseid seadeid, ja see kõik liitub ka nende kõigi vaikeseadetega. Seetõttu on config.pp-s selline keeruline struktuur — seal toimub vaikeseadete, grupiseadete ja iga individuaalse kontrolli seadete ühendamine.

Samuti on väga oluline uuendus, et seadetesse on võimalik lisada funktsioone, näiteks porta, aadressi ja URL-i asendamise funktsioon http_regex kontrollimiseks.

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

See tähendab — kui hosti määratlemisel on muutuja http_port — kasutada seda, muidu 443. Näiteks web-liides jabber töötab 9090-l, ja Unifi — 7443-l.
http_vhost tähendab DNS-i ignoreerimist ja selle aadressi kasutamist.
Kui hostis on määratud uri — siis mine selle kaudu, vastasel juhul kasuta «/».

http_ssl-i ümber on tekkinud naljakas lugu — see jube asi ei tahtnud mingil moel nõude peale välja lülituda. Ma vahtisin seda rida pikka aega, kuni lõpuks taipasin, et muutuja asub hosti määratlemise kohta:

http_ssl: false

Sisestatakse väljendisse

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

kuidas false ja lõpuks saadakse

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

see tähendab, et ssl-i kontroll on alati aktiivne. Probleem lahendati süntaksi muutmisega:

http_ssl: no

Järeldused:

Plussid:

  • Meil on nüüd üks jälgimissüsteem, mitte kaks, nagu viimased 7-8 kuud, või üks, aegunud ja haavatav.
  • Hostide / teenuste (kontrollide) andmestruktuur on nüüd (minu arvates) palju loetavam ja arusaadavam. Teiste jaoks ei osutunud see nii ilmselgeks, nii et pidin kohalikku wikis paar lehekülge üles ehitama, et selgitada, kuidas see kõik töötab ja mida kus muuta.
  • On võimalus paindlikult seadistada kontrollimist muutuja ja funktsioonide abil, näiteks http_regexp kontrollimiseks, otsitav muster, tagastuskoht, url ja port saab hosti seadistustes määrata.
  • On mitmeid paneele (dashboards), mille jaoks saab määrata oma kuvamisalarmide loendi ja hallata seda kõike Puppet'i ja ühinemisettepanekute kaudu.

Miinused:

  • Meeskonnaliikmete inertsus — Nagios töötas, töötas ja töötas, samas kui sinu Icinga pidevalt jupsib ja hangub. Kuidas siit ajalugu vaadata? Ah, kurat, see ei uuene... (Tegelikkuses probleem — alarmide ajalugu ei uuene automaatselt, vaid ainult klahviga F5.)
  • Süsteemi inertsus — kui ma klikin veebiliideses „uuenda” (check now), siis tulemuse täitmine sõltub Marsi ilmast, eriti keerulistel teenustel, mis nõuavad täitmiseks kümneid sekundeid. Selline tulemus on normaalne. Migratsioon Nagioselt Icinga2-le Austraalias
  • Kokkuvõttes, kui vaadata poolaasta statistikat, on Nagios alati töötanud kiiremini kui Icinga ja see häirib mind väga. Tundub, et seal on midagi ajastamisega segi aetud ja kontrollimine iga viie minuti järel toimub tegelikult 5:30 või midagi sellist.
  • Teen, kui teenust igal ajal taaskäivitada (systemctl restart icinga2) — kõik kontrollid, mis sel hetkel käimas on, annavad ekraanile hoiatuse critical ja see näeb välja nagu kõik oleks kokku kukkunud (kinnitatud viga).

Aga üldiselt — see töötab.

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster