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