Migratsioon Nagioselt Icinga2-le Austraalias

Tere kõigile.

Olen Linux-süsteemi administraator, kolisin 2015. aastal Venemaalt Austraaliasse sõltumatu ametialase viisa alusel, kuid artikkel ei räägi sellest, kuidas siga traktoriga tutvustada. Selliseid artikleid on juba piisavalt (kui see huvi pakub, võin ka sellest kirjutada), seega tahaksin rääkida sellest, kuidas oma tööl Austraalias Linux-ops-insenerina olin ma algataja migratsioonis ühelt jälgimissüsteemilt teisele. Konkreetsemalt — Nagios => Icinga2.

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

Kahjuks ei rõhuta silt «code» Puppet ja YAML koodi, seega pidin kasutama «plaintext».

21. detsember 2016 hommikul ei tõotanud miski hullu. Tavalise praktikana lugesin Habr'it anonüümselt tööpäeva esimesed pool tundi, nautides kohvi, kui märkasin seda artiklit.

Kuna minu ettevõttes kasutati just Nagios't, lõin ma mõtteid mõlgutades Redmine'is piletit ja jagasin linki ühises vestluses, kuna pidasin seda oluliseks. Algatus on Austraaliaski karistatav, nii et peainsener määras selle probleemi mulle, kuna avastasin selle.

Ekraanipilt Redmine'istMigratsioon Nagioselt Icinga2-le Austraalias

Meie osakonnas on enne oma arvamuse esitamisele üleminekut tavaline pakkuda vähemalt ühte alternatiivi, isegi kui valik on ilmselge, seega alustasin ma selle otsimisega, millised jälgimissüsteemid on hetkel aktuaalsed, kuna Venemaal eelmisel töökohal oli mul enda loodud süsteem, mis oli väga primitiivne, kuid siiski töötas korralikult ja täitis kõik oma ülesandeid. Python, Peterburi Polütehnika ja metroo on ülihead. Ei, metroo — on jama. See on isiklik (11 aastat töökogemust) ja väärib eraldi artiklit, kuid mitte praegu.

Veidi infrastruktuuri konfiguratsiooni muutmise reeglitest minu praeguses kohas. Me kasutame Puppet'i, Gitlabi ja Infrastructure as Code'i põhimõtteid, nii et:

  • Mitte ei ole käsitsi muudatusi SSH kaudu virtuaalmasinate failide käsitsi muutmise teel. Kolme aasta jooksul olen selle eest mitu korda pead saanud, viimane kord nädal tagasi ja ma ei usu, et see oli viimane kord. Tõeliselt — ühe lõigu parandamine konfiguratsioonis, teenuse taaskäivitamine ja vaatamine, kas probleem lahendatud — 10 sekundit. Uue haru loomine Gitlabis, muudatuste saatmine, ootamine, kuni r10k töötab Puppetmasteris, Puppet käivitamine —environment=mybranch ja veel paar minutit oodata, kuni see kõik töötab — vähemalt 5 minutit.
  • Kõik muudatused tehakse Merge Requesti loomise teel Gitlabis ja vajalik on vähemalt ühe meeskonnaliikme heakskiit. Olulised muudatused, mille otsustab tiimijuht, nõuavad kahte või kolme heakskiitu.
  • Kõik muudatused on mingil viisil tekstilised (kuna Puppet manifestid, skriptid ja Hiera andmed on tekst), binaarfailide kasutamine ei ole soovitatav ja selliste failide heakskiitmiseks on vaja olulisi põhjusi.

Nii et variandid, mida ma kaalusin:

  • Munin — kui infrastruktuuris on rohkem kui 10 serverit, muutub haldamine põrguks (välja arvatud selle artikli. Mul ei olnud erilist soovi seda kontrollida, nii et ma usaldasin seda öeldut).
  • Zabbix — olen seda juba kaua jälginud, veel Venemaal, kuid siis oli see minu ülesannete jaoks ülemäärane. Siin pidin selle kõrvale heitma, kuna kasutasin Puppetit konfiguratsiooni haldurina ja Gitlabit versioonihaldussüsteemina. Sel hetkel, niipalju kui ma aru sain — Zabbix salvestab kogu konfiguratsiooni andmebaasi, mistõttu ei olnud selgelt arusaadav, kuidas praegustes tingimustes konfiguratsiooni hallata ja muudatusi jälgida.
  • Prometheus — see, kuhu me lõpuks jõuame, arvestades osakonna meeleolu, kuid tol hetkel ei suutnud ma seda edasi viia ja ei suutnud esitada tegelikult funktsioneerivat näidisprototüüpi (Proof of Concept), nii et pidin sellest loobuma.
  • Lisaks oli veel mõned muud variandid, mis kas nõudsid süsteemi täielikku ümberkujundamist või olid alles lapsekingades / hüljatud ja seetõttu jäid need kõrvale.

Lõpuks valisin Icinga2 kolme põhjuse tõttu:

1 — ühilduvus Nrpe'ga (klienditeenus, mis käivitab kontrolle Nagioselt saadud käskude põhjal). See oli väga oluline, sest meil oli sel hetkel 135 (praegu 2019. aastaks 165) virtuaalmasinat koos hulga isekirjutatud teenuste/kontrollidega ja kõike seda ümber teha oleks olnud äärmiselt tüütu.
2 — kõik konfiguratsioonifailid on tekstilised, mis võimaldab seda kergesti redigeerida, luua ühinemisettepanekuid ning näha, mis on lisatud või eemaldatud.
3 — see on elav ja arenev avatud lähtekoodiga projekt. Me armastame avatud lähtekoodi ja teeme oma panuse selle arendamisse, luues Pull Request'e ja Issues probleeme lahendada.

Nii et, alustame Icinga2-ga.

Esimene probleem, millega silmitsi seisin, oli kolleegide passiivsus. Kõik olid harjunud Nagiose/Nadjiuse (kuigi isegi siin ei suudetud leppida kokku, kuidas seda hääldada) ja CheckMK kasutajaliidesega. Icinga kasutajaliides näeb välja totaalselt erinev (see oli miinus), kuid seal on võimalus paindlikult seadistada, mida on vaja näha, kasutades filtreid sõna otseses mõttes iga parameetri järgi (see oli pluss, kuid ma pidin selle nimel tõsiselt vaidlema).

FiltridMigratsioon Nagioselt Icinga2-le Austraalias

Hinnake kerimisriba suuruse suhet kerimisala suurusega.

Teiseks — kõik olid harjunud nägema kogu infrastruktuuri ühel ekraanil, sest CheckMK võimaldab töötada mitme Nagiose hostiga, kuid Icinga liides ei osanud seda teha (tegelikult oskas, kuid sellest allpool). Alternatiiviks oli asi nimega Thruk, kuid selle disain tekitas kõigi meeskonnaliikmete seas okserefleksi, välja arvatud ühe — selle, kes selle välja pakkus (mitte mina).

Thruk kõrvale — meeskonna ühisotsus.Migratsioon Nagioselt Icinga2-le Austraalias

Mõne päeva mõttetöö järel pakkusin välja idee klusterseire kohta, kus on üks meistriserver tootmispiirkonnas ja kaks alluvat — üks dev/test ja üks väline server, mis asub teise teenusepakkuja juures, et jälgida meie teenuseid kliendi või välise vaatlejana. Selline konfiguratsioon võimaldas näha kõiki probleeme ühes veebiliideses ja töötas üsna hästi, kuid Puppet… Puppetiga oli probleem selles, et meistriserver pidi nüüd teadma kõigist serveritest ja teenustest/süsteemidest ja jagama neid piirkondade vahel (dev-test, staging-prod, ext), kuid muudatuste edastamine Icinga API kaudu kestab paar sekundit, samas kui Puppet'i kõigi teenuste katalooge koondamine kõigi serverite jaoks — paar minutit. Seda heidetakse mulle siiani ette, kuigi olen juba mitu korda selgitanud, kuidas kõik töötab ja miks see nii kaua aega võtab.

Kolmas — hulk SnowFlakes (lumepallikesi) — asju, mis ei sobi tavapärasesse süsteemi, kuna neil on midagi erilist, mistõttu üldreeglid neile ei kehti. Probleem lahendati otsese rünnaku kaudu — kui on hoiatused, kuid tegelikult on kõik korras, siis siin tuleb sügavamalt kaevata ja välja selgitada, miks see mind hoiatab, kuigi ei peaks. Või vastupidi — miks Nagios paanikat tekitab, aga Icinga mitte.

Neljas — Nagios töötas siin enne mind kolm aastat ja temasse usaldati algselt rohkem kui minu moodsasse hipster-süsteemi, nii et iga kord, kui Icinga tõstis paanikat — ei teinud keegi midagi, kuni Nagios ei ärritunud sama küsimuse puhul. Kuid väga harva andis Icinga tõsiseid hoiatuseid enne kui Nagios, ja ma pean seda tõsiseks defektiks, millest räägin jaotises "Järeldused".

Kokkuvõttes venis kasutuselevõtt rohkem kui 5 kuud (oli planeeritud 28. juuniks 2018, kuupäevaks 3. detsember 2018), peamiselt „pariteedi kontrolli“ tõttu — see jama, kus on mitu teenust Nagioses, millest keegi midagi ei teadnud viimase paari aasta jooksul, kuid JUST SEL MOMENTIL andsid nad, kurat, crit ilma igasuguse põhjuseta ja mul tuli seletada, miks neid minu paneelil pole ja tuli need Icingasse lisada, et „pariteedi kontroll on lõpule viidud“ (Kõik teenused/süsteemid Nagioses vastavad teenustele/süsteemidele Icingas).

Rakendamine:
Esimene on Code vs Data sõda, Puppet Style'i mõttes. Kõik andmed, tõepoolest kõik, peavad olema Hiera's ja mitte kuidagi teisiti. Kõik kood on .pp failides. Muutujad, abstraktsioonid, funktsioonid - kõik läheb pp-ks.
Lõpptulemusena on meil hulk virtuaalseid masinaid (165 artikli kirjutamise hetkel) ja 68 veebirakendust, mida tuleb jälgida töövõime ja SSL-sertifikaatide kehtivuse osas. Kuid ajaloolise segaduse tõttu saadakse teave rakenduste jälgimiseks eraldi gitlab-repositooriumist ja andmeformaati ei ole muudatused alates Puppet 3, mis tekitab lisaprobleeme konfiguratsioonis.

Rakenduste Puppet-kood, hoidke oma silmi.

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 tüm varsayılan gruplar için bildirim ekler (sistemler) + int/pm_docker_apps.eyaml'da tanımlanan grup

    $data = merge($webhost_defaults, $apps_accessible_from, $app_data)

    $site_domain = $app_data['site_domain']

    $regexp = pick($app_data['check_regex'], 'html')        # Kontrol için bir regex seç

    $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 | {        # Varsa iki veya daha fazla alan adı olan bir uygulamayı ayırın
      $vhost_name = {'http_vhost' > $vhost}

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

      $web_ipaddress = is_array($vdata['web_ipaddress']) ? {  # IP adresini bir dizi yap, değilse, çünkü askizzy'nin 2 ip'si var ve bu bir dizidir
        true  > $vdata['web_ipaddress'],
        false > [$vdata['web_ipaddress']],
      }

      $access_from_zones = [$zone] + $apps_access_list[$data['accessible_from']] # Varsayılan alanı (uygulamanın tanımlandığı yer) ve varsa ek alanları birleştir
      $web_ipaddress.each | String $ip_address | {            # Her IP için (birden fazlamız varsa)
        $suffix = length($web_ipaddress) ? {                  # Birden fazla varsa, bu ana makine adına IP'yi ekleyin, kaynakları çoğaltmaktan kaçınmak için
          1       > '',
          default > "_${ip_address}"
        }
        $octets = split($ip_address, '.')
        $ip_tag = "${octets[2]}.${octets[3]}" # Sadece son octeti kullanmak, nginx-vip 203.15.70.94 ile ext. ip 49.255.194.94 arasında çakışmaya neden olur

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

          $nginx_vip_name = "${zone_prefix}_nginx-vip-${ip_tag}" # Dışa aitse, prefix 'ext_' olur (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
              }
            }
          }
        }
      }
    }
  }
}

Hostide 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
      } # Assign 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 töötan endiselt selle jao kallal ja parandan seda nii palju kui võimalik. Just selline kood võimaldas kasutada Hiera jaoks lihtsat ja arusaadavat 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 rühmadesse, igal grupil on vaikeseaded, kus ja kui sageli neid kontrollimisi käivitada, milliseid teateid saata ja kellele.

Igas kontrollis saab üle kirjutada iga suvandi, ja see kõik liitub lõpuks veel ka kõigi kontrollide vaikeseadetega. Seetõttu on config.pp-s selline jao, kus toimub kõikide vaikeseadete ja rühmade seadete ning seejärel iga individuaalse kontrolli sulam.

Samuti oli väga oluline muudatus see, et seadetes sai hakata kasutama funktsioone, näiteks, pordi, aadressi ja url'i asendamise funktsioon http_regex kontrollimisel.

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, jabber veebiliides on 9090 peal, aga Unifi on 7443 peal.
http_vhost tähendab DNS-i ignoreerimist ja selle aadressi kasutamist.
Kui hostis on määratud uri - siis minna mööda seda, muidu võtta "\/".

Http_ssl'i osas tekkis naljakas lugu - see näiliselt ei soovinud nõudmisel välja lülituda. Ma mõtlesin sellele reale kaua aega, kuni sain aru, et muutuja on hosti määratlemises:

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 }

nii et ssl-i kontroll on alati aktiivne. See lahendati süntaksivahetusega:

http_ssl: no

Järeldused:

Plussid:

  • Nüüd on meil üks jälgimissüsteem, mitte kaks, nagu see oli viimased 7-8 kuud, või üks, aegunud ja haavatav.
  • Hostide / teenuste (kontrollide) andmestruktuur on nüüd (minu arvates) oluliselt arusaadavam ja loetavam. Teiste jaoks ei olnud see nii ilmne, nii et pidin loksuma paar lehte kohalikku vikisse, et selgitada, kuidas see kõik töötab ja mida kus muuta.
  • On olemas võimalus kontrollide paindlikuks seadistamiseks muutujate ja funktsioonide abil, näiteks http_regexp kontrolli jaoks, otsitav muster, tagastuscode, url ja port saab seadistada hosti seadetes.
  • On mitmeid paneele (dashboards), igaühe jaoks saab määrata oma kuvamisteate nimekirja ning hallata seda kõike läbi Puppet ja merge requests.

Miinused:

  • Meeskonna inertsi probleem — Nagios töötas, töötas ja töötas, aga sinu Icinga hangub pidevalt ja kiirus on kehv. Kuidas siin ajalugu vaadata? Ah, kurat, see ei uuene ju… (Tegelik probleem — alarmide ajalugu ei uuene automaatselt, ainult F5-ga)
  • Süsteemi inertsi probleem — kui ma klikin veebiliideses „uuenda” (check now) — täitmise tulemus sõltub Marsi ilmast, eriti keerukatel teenustel, mis vajavad täitmiseks kümneid sekundeid. Selline tulemus on normaalne. Migratsioon Nagioselt Icinga2-le Austraalias
  • Kokkuvõttes, kuue kuu statistika kahest süsteemist kõrvuti — Nagios töötas alati kiiremini kui Icinga ja see ajas mind tõeliselt närvi. Minu arvates on seal midagi häiritud ajastitega, ja kontrollimine iga viie minuti järel toimub tegelikult iga 5:30 või midagi sellist.
  • Kui teenust igal ajal taaskäivitada (systemctl restart icinga2) — kõik kontrollid, mis sel hetkel olid täitmisel, annavad ekraanile häire critical ja see näeb välja nagu kõik oleks kokku kukkunud (kinnitatud viga).

Aga kokkuvõttes — see töötab.

Allikas: habr.com

Osta usaldusväärne veebimajutus DDoS-kaitsega veebisaitidele, VPS VDS serverid 🔥 Osta usaldusväärne veebimajutus DDoS-kaitsega veebisaitidele, VPS VDS serverid - ProHoster