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