Migrimi nga Nagios në Icinga2 në Australi

Përshëndetje të gjithëve.

Unë jam një administrator linux, transferuar nga Rusia në Australi me një vizë profesionale të pavarur në vitin 2015, por ky artikull nuk do të jetë për mënyrën se si një derra mund të blejë një traktor. Ka mjaft artikuj të tillë (ndonjëherë, nëse ka interes, do të shkruaj edhe për këtë), kështu që do të doja të flisja për mënyrën se si, në punën time në Australi si inxhiner linux-ops, isha iniciatori i migrimit nga një sistem monitorimi në një tjetër. Konkretisht - Nagios => Icinga2.

Artikulli është pjesërisht teknik dhe pjesërisht - mbi ndërveprimin me njerëzit dhe problemet e lidhura me diferencat kulturore dhe metodat e punës.

Fatkeqësisht, tiku «code» nuk ndriçon kodin Puppet dhe yaml, kështu që duhet të përdor «plaintext».

Asgjë nuk parashikonte ndodhi mëngjesin e 21 dhjetorit 2016. Unë, ashtu si zakonisht, isha duke lexuar Habr si një anonim i pa regjistruar në dy orët e para të ditës së punës, duke e pirë kafen dhe u ndala te këtë artikull.

Duke qenë se në kompaninë time përdorej Nagios, nuk mendova gjatë dhe krijova një tiket në Redmine dhe shpërndaova lidhjen në bisedën e përbashkët, pasi e konsiderova të rëndësishme. Iniciativa është ndëshkuese edhe në Australi, kështu që inxhinieri kryesor e ngarkoi këtë problem mbi mua, duke qenë se unë e zbulova atë.

Skena nga RedmineMigrimi nga Nagios në Icinga2 në Australi

Në departamentin tonë, para se të shprehim mendimin tonë, është zakon t'u ofrojmë të paktën një alternativë, edhe nëse zgjedhja është e dukshme, prandaj fillova me googlimin e cilave sisteme monitorimi janë të aktualizuara aktualisht, pasi në vendin tim të fundit të punës në Rusi kisha sistemin tim të shkruar vetë, shumë primitiv, por megjithatë plotësisht funksional dhe duke përmbushur të gjitha detyrat që i ishin ngarkuar. Python, Politehnika e Shën Pjetërburgut dhe metroja janë të shkëlqyera. Jo, metroja është e keqe. Kjo është personale (11 vjet punë) dhe meriton një artikull të veçantë, por jo tani.

Pak rreth rregullave të ndryshimeve në konfigurimin e infrastrukturës në vendin tim të tanishëm të punës. Ne përdorim Puppet, Gitlab dhe parimin e Infrastrukturës si Kod, kështu që:

  • AsnjĂ« ndryshim manual pĂ«rmes SSH duke ndryshuar dorazi ndonjĂ« skedar nĂ« makinĂ«n virtuale. GjatĂ« tre viteve tĂ« punĂ«s kam marrĂ« ndĂ«shkime pĂ«r kĂ«tĂ« shumĂ« herĂ«, e fundit - njĂ« javĂ« mĂ« parĂ« dhe nuk mendoj se ishte hera e fundit. MirĂ«, nĂ« tĂ« vĂ«rtetĂ« - tĂ« rregullosh njĂ« rresht nĂ« konfigurim, tĂ« rinisĂ«sh shĂ«rbimin dhe tĂ« shohĂ«sh nĂ«se u zgjidh problemi - 10 sekonda. TĂ« krijosh njĂ« degĂ« tĂ« re nĂ« Gitlab, tĂ« shtosh ndryshimet, tĂ« presĂ«sh qĂ« r10k tĂ« funksionojĂ« nĂ« Puppetmaster, tĂ« nisim Puppet -environment=mybranch dhe disa minuta tĂ« tjera tĂ« presĂ«sh derisa gjithçka tĂ« funksionojĂ« - tĂ« paktĂ«n 5 minuta.
  • TĂ« gjitha ndryshimet bĂ«hen pĂ«rmes krijimit tĂ« njĂ« Merge Request nĂ« Gitlab dhe nevojitet miratimi i paktĂ«n nga njĂ« anĂ«tar i ekipit. Ndryshime tĂ« mĂ«dha sipas vendimit tĂ« liderit tĂ« ekipit kĂ«rkojnĂ« dy ose tre miratime.
  • TĂ« gjitha ndryshimet janĂ« nĂ« formĂ« teksti (pasi manifestat e Puppet, skriptet dhe tĂ« dhĂ«nat Hiera janĂ« tekste), skedarĂ«t binarĂ« janĂ« shumĂ« tĂ« papĂ«lqyer dhe pĂ«r miratimin e kĂ«tyre skedarĂ«ve kĂ«rkohen arsye tĂ« forta.

Pra, opsionet që shqyrtova ishin:

  • Munin - nĂ«se infrastruktura ka mĂ« shumĂ« se 10 servera, administrimi shndĂ«rrohet nĂ« njĂ« çmenduri (nga kĂ«tĂ« artikull. Nuk kisha dĂ«shirĂ« tĂ« vepronte kĂ«tĂ«, prandaj e pranuam si fjalĂ«).
  • Zabbix - kam pasur pĂ«r njĂ« kohĂ« nĂ« vĂ«mendje, edhe nĂ« Rusi, por atĂ«herĂ« ishte i tepruar pĂ«r nevojat e mia. KĂ«tu - duhej ta pĂ«rjashtoja pĂ«r shkak tĂ« pĂ«rdorimit tĂ« Puppet si menaxher konfigurimi dhe Gitlab si sistem kontrolli versioni. NĂ« atĂ« kohĂ«, sa kuptova - Zabbix ruan tĂ« gjitha konfigurimet nĂ« njĂ« bazĂ« tĂ« dhĂ«nash, pĂ«r tĂ« cilĂ«n nuk ishte e qartĂ« si tĂ« menaxhohej konfigurimi nĂ« kushtet aktuale dhe si tĂ« pĂ«rndiqeshin ndryshimet.
  • Prometheus - ajo nĂ« tĂ« cilĂ«n ne do tĂ« arrijmĂ« nĂ« fund, sipas humorit nĂ« departament, por nĂ« atĂ« kohĂ« nuk isha nĂ« gjendje ta tregoja njĂ« mostĂ«r funksionale (Prova e Konceptit), kĂ«shtu qĂ« duhej tĂ« heqja dorĂ«.
  • Ishte edhe disa opsione tĂ« tjera, qĂ« kĂ«rkonin njĂ« rivlerĂ«sim tĂ« plotĂ« tĂ« sistemit, ose ishin nĂ« faza fillestare / tĂ« braktisura dhe pĂ«r kĂ«tĂ« arsye u pĂ«rjashtuan.

Përfundimisht, u ndala në Icinga2 për tri arsye:

1 - kompatibilitet me Nrpe (shërbimi klient që ekzekuton kontrollin me komandat nga Nagios). Kjo ishte shumë e rëndësishme, sepse në atë kohë kishim 135 (tani në 2019, janë 165) makina virtuale me një shumëllojshmëri shërbimesh/kontrollesh të shkruara vetë dhe riparimi i të gjitha këtyre do të ishte një dhimbje e madhe.
2 - të gjitha skedarët e konfigurimit janë tekst, gjë që lejon editimin e lehtë të kësaj, krijimin e kërkesave për bashkimin me mundësinë e shikimit çfarë është shtuar ose hequr.
3 - është një projekt OpenSource aktiv dhe në zhvillim. Ne e duam shumë OpenSource dhe kontribuojmë në të përmes krijimit të Kërkesave të Bashkimit dhe Problemesh për zgjidhjen e çështjeve.

Pra, le të fillojmë, Icinga2.

E para, qĂ« çfarĂ« kam hasur — inertĂ«sia e kolegĂ«ve. TĂ« gjithĂ« ishin mĂ«suar me Nagios/Nagios (ndonĂ«se kĂ«tu nuk arritĂ«m tĂ« gjenim njĂ« kompromis mbi si ta shqiptojmĂ«) dhe ndĂ«rfaqen e CheckMK. Me icinga ndĂ«rfaqja duket krejt ndryshe (kjo ishte njĂ« disavantazh), por ka mundĂ«sinĂ« pĂ«r tĂ« pĂ«rshtatur me fleksibilitet atĂ« qĂ« duhet tĂ« shikohet pĂ«rmes filtrave nĂ« çdo parametrim (kjo ishte njĂ« avantazh, por pĂ«r tĂ« luftuar pĂ«r tĂ«, kam pasur shumĂ« punĂ«).

FiltratMigrimi nga Nagios në Icinga2 në Australi

Vlerësoni raportin e madhësisë së shiritit të skrollit me madhësinë e fushës për skrollim.

E dyta — tĂ« gjithĂ« ishin mĂ«suar tĂ« shihnin gjithĂ« infrastrukturĂ«n nĂ« njĂ« monitor, sepse CheckMk lejon tĂ« punosh me disa hoste Nagios, por ndĂ«rfaqja Icinga nuk e bĂ«nte kĂ«tĂ« (nĂ« fakt e bĂ«nte, por pĂ«r kĂ«tĂ« do flasim mĂ« poshtĂ«). NjĂ« alternativĂ« ishte diçka e quajtur Thruk, por dizajni i saj shkaktonte qĂ« tĂ« gjithĂ« anĂ«tarĂ«t e ekipit tĂ« pĂ«rjetonin tĂ« vjella, pĂ«rveç njĂ«rit — atij qĂ« e propozoi (nuk isha unĂ«).

FatkeqĂ«sisht Thruk — ishte njĂ« vendim unanim i ekipit.Migrimi nga Nagios nĂ« Icinga2 nĂ« Australi

Pas disa ditĂ«sh brainstorming-u, kam propozuar idenĂ« e monitorimit tĂ« grupeve, ku ka njĂ« host master nĂ« zonĂ«n e prodhimit dhe dy nĂ«nshĂ«rbime — njĂ« nĂ« dev/test dhe njĂ« host tĂ« jashtĂ«m, i vendosur tek njĂ« ofrues tjetĂ«r, me qĂ«llim qĂ« tĂ« monitorojmĂ« shĂ«rbimet tona nga kĂ«ndvĂ«shtrimi i klientit ose njĂ« vĂ«zhgues tĂ« jashtĂ«m. Kjo konfigurim lejonte tĂ« shihnim tĂ« gjitha problemet nĂ« njĂ« ndĂ«rfaqe web dhe funksiononte mjaft mirĂ«, por Puppet
 Problemi me Puppet ishte se hosti master tani duhej tĂ« dinte pĂ«r tĂ« gjithĂ« hostet dhe shĂ«rbimet/kontrollimet nĂ« sistem dhe duhej t'i shpĂ«rndante ato mes zonave (dev-test, staging-prod, ext), por dĂ«rgimi i ndryshimeve pĂ«rmes Icinga API merr disa sekonda, ndĂ«rsa kompilimi i katalogut Puppet pĂ«r tĂ« gjitha shĂ«rbimet pĂ«r tĂ« gjitha hostet merr disa minuta. Kjo ende mĂ« Ă«shtĂ« vĂ«nĂ« nĂ« faj, ndonĂ«se kam shpjeguar disa herĂ« si funksionon dhe pse Ă«shtĂ« kaq e gjatĂ«.

E treta — shumĂ« SnowFlakes (flokĂ« bore) — gjĂ«ra qĂ« duken jashtĂ« sistemit tĂ« zakonshĂ«m, sepse kanĂ« diçka tĂ« veçantĂ«, prandaj rregullat e zakonshme nuk zbatohen pĂ«r to. Kjo u zgjidh pĂ«rmes njĂ« sulmi frontal — nĂ«se ka alarm, por nĂ« fakt gjithçka Ă«shtĂ« nĂ« rregull, atĂ«herĂ« kĂ«tu duhet tĂ« shqyrtojmĂ« thellĂ« dhe tĂ« kuptojmĂ« pse mĂ« alarmon, ndonĂ«se nuk duhet. Ose e kundĂ«rta — pse Nagios panikohet, ndĂ«rsa Icinga jo.

E katĂ«rt — Nagios ka punuar kĂ«tu pĂ«r tre vjet para meje dhe kishte mĂ« shumĂ« besim fillimisht sesa sistemi im modern hipster, prandaj çdo herĂ« qĂ« Icinga ngjiste panik — askush nuk bĂ«nte asgjĂ«, derisa Nagios nuk ngrihej pĂ«r tĂ« njĂ«jtin çështje. Por shumĂ« rrallĂ« Icinga jepte alarmet reale mĂ« parĂ« se Nagios dhe unĂ« e konsideroj kĂ«tĂ« njĂ« gabim tĂ« rĂ«ndĂ«sishĂ«m, pĂ«r tĂ« cilin do tĂ« flas nĂ« seksionin "PĂ«rfundimet".

NĂ« fund, futja nĂ« funksionim u zgjat mĂ« shumĂ« se 5 muaj (planifikuar pĂ«r 28 qershor 2018, nĂ« fakt — 3 dhjetor 2018), kryesisht pĂ«r shkak tĂ« "parity check" — ajo gjĂ«, ku ka disa shĂ«rbime nĂ« Nagios, pĂ«r tĂ« cilat askush nuk kishte dĂ«gjuar pĂ«r dy vitet e fundit, por PIKËRISHT TANI ata, dreqi, dolĂ«n crit pa asnjĂ« arsye dhe unĂ« duhej tĂ« shpjegoja pse nuk ishin nĂ« panelin tim dhe duhej t'i shtoja ata nĂ« Icinga, qĂ« "parity check Ă«shtĂ« pĂ«rfunduar" (TĂ« gjitha shĂ«rbimet/kontrollimet nĂ« Nagios pĂ«rputhen me shĂ«rbimet/kontrollimet nĂ« Icinga).

Zbatimi:
E para — lufta Code vs Data, si nĂ« stilin Puppet. TĂ« gjitha tĂ« dhĂ«nat, pikĂ«risht tĂ« gjitha, duhet tĂ« jenĂ« nĂ« Hiera dhe ndryshe asnjĂ«herĂ«. I gjithĂ« kodi — nĂ« skedarĂ« .pp. Variablat, abstraksionet, funksionet — gjithçka nĂ« pp.
NĂ« fund — kemi njĂ« shumĂ«llojshmĂ«ri makinash virtuale (165 nĂ« momentin e shkruarjes sĂ« kĂ«tij artikulli) dhe 68 aplikacione web, qĂ« duhet tĂ« monitorohen pĂ«r funksionalitetin dhe pĂ«r vĂ«rtetĂ«sinĂ« e certifikateve SSL. Por pĂ«r shkak tĂ« njĂ« histori tĂ« vĂ«shtirĂ«, informacioni pĂ«r monitorimin e aplikacioneve merret nga njĂ« repozitor gitlab tĂ« veçantĂ« dhe formati i tĂ« dhĂ«nave nuk Ă«shtĂ« ndryshuar qĂ« nga Puppet 3, gjĂ« qĂ« krijon vĂ«shtirĂ«si shtesĂ« nĂ« konfigurim.

Kodi Puppet për aplikacionet, ruani sytë tuaj.

defino profile::shërbimeve::monitorimi::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,
  )
{
#### APLIKACIONET ####
  $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'], {} )) } # shton njoftime për grupin e paracaktuar (sistemet) + çdo grup të përcaktuar në 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')        # Zgjidhni një regex për të kontrolluar

    $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 | {        # Ndarja e një aplikacioni sipas domenesh nëse ka dy ose më shumë
      $vhost_name = {'http_vhost' => $vhost}

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

      $web_ipaddress = is_array($vdata['web_ipaddress']) ? {  # Bëni IP-adresën një array nëse nuk është, sepse askizzy ka 2 ip dhe është një array
        true  => $vdata['web_ipaddress'],
        false => [$vdata['web_ipaddress']],
      }

      $access_from_zones = [$zone] + $apps_access_list[$data['accessible_from']] # Bashkoni zonën standarde (ku është përcaktuar aplikacioni) dhe zonat shtesë nëse ekzistojnë
      $web_ipaddress.each | String $ip_address | {            # Për çdo IP (nëse kemi shumë)
        $suffix = length($web_ipaddress) ? {                  # Nëse kemi më shumë se një - shtoni IP-në si një prapashtesë në këtë emër host-i për të shmangur shumëzimin e burimeve
          1       => '',
          default => "_${ip_address}"
        }
        $octets = split($ip_address, '.')
        $ip_tag = "${octets[2]}.${octets[3]}" # Përdorimi i oktetit të fundit krijon një kolizion mes nginx-vip 203.15.70.94 dhe ext. ip 49.255.194.94

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

          $nginx_vip_name = "${zone_prefix}_nginx-vip-${ip_tag}" # Nëse është një host për ext - prapashtesa bëhet '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
              }
            }
          }
        }
      }
    }
  }
}

Kodi i konfigurimit të hosteve dhe shërbimeve gjithashtu duket jashtëzakonisht i papërshtatshëm:

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

}

Unë vazhdoj të punoj mbi këtë kod dhe e përmirësoj sa më shumë që të jetë e mundur. Megjithatë, ky kod lejon përdorimin e një sintakse të thjeshtë dhe të qartë në Hiera:

Të dhënat

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'

Të gjitha kontrollimet janë të ndara në grupe, dhe çdo grup ka cilësimet e tij të paracaktuara për vendin dhe frekuencën e ekzekutimit të këtyre kontrollimeve, si dhe për cilat njoftime të dërgohen dhe kujt.

NĂ« çdo kontrollim Ă«shtĂ« e mundur tĂ« ripĂ«rcaktohet çfarĂ«do opsioni dhe gjithçka pĂ«rfundon duke u kombinuar me cilĂ«simet e paracaktuara tĂ« tĂ« gjithĂ« kontrollimeve nĂ« tĂ«rĂ«si. Prandaj, nĂ« config.pp Ă«shtĂ« shkruar njĂ« formĂ« e tillĂ« — aty bĂ«het bashkimi i tĂ« gjitha cilĂ«simeve tĂ« paracaktuara me cilĂ«simet e grupeve dhe pastaj me çdo kontrollim individual.

Një ndryshim shumë i rëndësishëm ishte mundësia për të përdorur funksione në cilësime, për shembull, funksioni për zëvendësimin e portit, adresës dhe URL-së për kontrollimin e http_regex.

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

Kjo do tĂ« thotĂ« — nĂ«se nĂ« definicionin e hostit ka njĂ« variabĂ«l http_port — ta pĂ«rdorĂ«sh atĂ«, pĂ«rndryshe 443. PĂ«r shembull, ndĂ«rfaqja web e jabber Ă«shtĂ« mbi 9090, ndĂ«rsa Unifi Ă«shtĂ« mbi 7443.
http_vhost do të thotë të injorosh DNS dhe të marrësh këtë adresë.
NĂ«se nĂ« host Ă«shtĂ« e specifikuar uri — atĂ«herĂ« tĂ« ndiqesh atĂ«, pĂ«rndryshe tĂ« marrĂ«sh «/».

Me http_ssl pasi qĂ« ngjarjat janĂ« disi qesharake — kjo ishte njĂ« problem qĂ« nuk donte tĂ« çaktivizohej sipas kĂ«rkesĂ«s. Kaluar njĂ« kohĂ« tĂ« gjatĂ« duke u ndalur pĂ«r kĂ«tĂ« rresht, kisha kuptuar se ndryshimi nĂ« definicionin e hostit:

http_ssl: false

Zëvendësohej në shprehjen

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

si false dhe në fund del

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

pra kontrollimi ssl është gjithmonë aktiv. E zgjidhëm duke ndryshuar sintaksën:

http_ssl: no

Përfundimet:

Avantazhet:

  • Tani kemi njĂ« sistem tĂ« vetĂ«m monitorimi, dhe jo dy, si ishte pĂ«r 7-8 muajt e fundit, apo njĂ« tĂ« vjetruar dhe tĂ« brishtĂ«.
  • Struktura e tĂ« dhĂ«nave tĂ« hosteve / shĂ«rbimeve (kontrollimeve) tani (nĂ« mendimin tim) Ă«shtĂ« shumĂ« mĂ« e lexueshme dhe e kuptueshme. PĂ«r tĂ« tjerĂ«t, kjo nuk ishte aq e qartĂ«, kĂ«shtu qĂ« isha i detyruar tĂ« krijoj disa faqe nĂ« wikin lokal pĂ«r tĂ« sqaruar si funksionon gjithçka dhe çfarĂ« duhet edituar.
  • Ka mundĂ«si pĂ«r konfigurimin fleksibĂ«l tĂ« kontrollimeve duke pĂ«rdorur variabla dhe funksione, pĂ«r shembull pĂ«r kontrollimin http_regexp, modeli i kĂ«rkuar, kodi i rikthimit, URL dhe porti mund tĂ« definohen nĂ« cilĂ«simet e hostit.
  • Ka disa panele (dashboards), pĂ«r secilĂ«n prej tĂ« cilĂ«ve mund tĂ« pĂ«rcaktohet lista e alarmave tĂ« shfaqura dhe tĂ« menaxhohet gjithçka pĂ«rmes Puppet dhe merge requests.

Disavantazhet:

  • Inercia e anĂ«tarĂ«ve tĂ« ekipit — NagiOS punonte, punonte dhe punonte, ndĂ«rsa ajo e Icinga vazhdimisht dĂ«shtonte dhe ngadalĂ«sohej. Si mund ta shoh histori? Ah, tĂ« drejtat, ajo nuk pĂ«rditĂ«sohet
 (Problemi real — historia e alarmave nuk pĂ«rditĂ«sohet automatikisht, vetĂ«m pĂ«rmes F5)
  • Inercia e sistemit — kur klikoja nĂ« ndĂ«rfaqen web mbi «refresh» (kontrollo tani) — rezultati i ekzekutimit varet nga moti nĂ« Mars, sidomos pĂ«r shĂ«rbimet e ndĂ«rlikuara qĂ« kĂ«rkojnĂ« disa sekonda pĂ«r ekzekutimin. NjĂ« rezultat i tillĂ« Ă«shtĂ« diçka normale. Migrimi nga Nagios nĂ« Icinga2 nĂ« Australi
  • Duke parĂ« statistikat pĂ«r gjashtĂ« muaj tĂ« dy sistemeve paralelisht, NagiOS gjithmonĂ« e realizoi mĂ« shpejt sesa Icinga dhe kjo mĂ« shqetĂ«sonte shumĂ«. Sikur tĂ« ishte njĂ« problem me timerat dhe kontrolli çdo pesĂ« minuta Ă«shtĂ« nĂ« fakt çdo 5:30 ose diçka e tillĂ«.
  • NĂ«se e rinis shĂ«rbimin nĂ« çdo kohĂ« (systemctl restart icinga2) — tĂ« gjitha kontrollimet qĂ« ishin nĂ« proces nĂ« atĂ« moment do tĂ« raportojnĂ« alarmin kritike <terminuar nga sinjali 15> nĂ« ekran dhe nga jashtĂ« duket si tĂ« kishte rĂ«nĂ« gjithçka (bug i konfirmuar).

Por nĂ« pĂ«rgjithĂ«si — po funksionon.

Burimi: habr.com

Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster