Австралийн Нагиосоос Айсинга2 руу нүүсэн

Бүгдээрээ сайн байцгаана уу.

Би Линукс системийн администратор, би 2015 онд Оросоос Австрали руу бие даасан мэргэжлийн визээр нүүсэн боловч нийтлэл нь гахайн тракторыг хэрхэн эхлүүлэх тухай биш юм. Ийм нийтлэл хангалттай байгаа (гэхдээ сонирхол байвал би бас энэ тухай бичих болно) би Австралид linux-ops инженерээр ажиллаж байхдаа нэг системийн хяналтаас шилжих ажлыг хэрхэн эхлүүлсэн талаар ярихыг хүсч байна. нөгөө рүү. Тодруулбал - Нагиос => Icinga2.

Энэхүү нийтлэл нь зарим талаараа техникийн шинж чанартай, зарим талаараа хүмүүстэй харилцах, соёл, ажлын арга барилын ялгаатай холбоотой асуудлуудтай холбоотой юм.

Харамсалтай нь, "code" шошго нь Хүүхэлдэйн болон yaml кодыг онцолдоггүй тул би "plaintext" ашиглах шаардлагатай болсон.

21 оны 2016 дугаар сарын XNUMX-ний өглөө ямар нэгэн хүндрэлийн шинж тэмдэг илрээгүй. Ердийнх шигээ би ажлын өдрийн эхний хагаст нэрээ нууцалсан хэрэглэгчийн Habr-ыг уншиж байгаад кофе ууж байгаад тааралдлаа. энэ нийтлэл.

Манай компани Нагиосыг ашигладаг байсан тул хоёр ч удаа бодолгүйгээр Redmine-д тасалбар үүсгэж, линкийг нь чухал гэж үзсэн тул ерөнхий чат руу илгээсэн. Энэ санаачилгыг Австралид хүртэл шийтгэдэг тул ахлах инженер намайг олж мэдсэнээс хойш энэ асуудлыг надад тавьсан.

Redmine-ээс авсан дэлгэцийн агшинАвстралийн Нагиосоос Айсинга2 руу нүүсэн

Манай хэлтэст санал бодлоо илэрхийлэхээсээ өмнө сонголт нь тодорхой байсан ч гэсэн ядаж нэг хувилбар санал болгодог заншилтай тул би Орост сүүлийн ажлын байрандаа ямар төрлийн хяналтын системүүд хамааралтай болохыг Google-ээс хайж эхэлсэн. Би өөрөө бичсэн системтэй, маш анхдагч, гэхдээ маш сайн ажиллаж, түүнд өгсөн бүх ажлыг гүйцэтгэдэг. Питон, Санкт-Петербургийн Политехник, метроны дүрэм. Үгүй ээ, метро муу байна. Энэ бол хувийн (11 жилийн ажил) бөгөөд тусдаа өгүүлэл бичих нь зүйтэй, гэхдээ одоо биш.

Миний одоогийн байгаа газрын дэд бүтцийн тохиргоонд өөрчлөлт оруулах дүрмийн талаар бага зэрэг. Бид Puppet, Gitlab болон Infrastructure-ийг код зарчим болгон ашигладаг тул:

  • Виртуал машин дээрх аливаа файлыг гараар өөрчлөх замаар SSH-ээр дамжуулан гараар өөрчлөх боломжгүй. Гурван жил ажиллахдаа би ийм малгайнд олон удаа өртсөн, хамгийн сүүлд долоо хоногийн өмнө байсан бөгөөд энэ нь сүүлийн удаа биш гэж би бодож байна. За, үнэхээр - тохиргооны нэг мөрийг засч, үйлчилгээг дахин эхлүүлээд асуудал шийдэгдсэн эсэхийг хараарай - 10 секунд. Gitlab-д шинэ салбар үүсгэж, өөрчлөлтүүдийг дарж, Puppetmaster дээр r10k ажиллахыг хүлээж, Puppet -environment=mybranch-г ажиллуулаад бүх зүйл ажиллах хүртэл дахиад хэдэн минут хүлээнэ үү - хамгийн багадаа 5 минут.
  • Аливаа өөрчлөлтийг Gitlab дээр нэгтгэх хүсэлтийг үүсгэснээр хийгддэг бөгөөд хамгийн багадаа нэг багийн гишүүнээр зөвшөөрөгдсөн байх ёстой. Багийн ахлагчийн шийдвэрлэсэн томоохон өөрчлөлтөд хоёроос гурван зөвшөөрөл шаардлагатай.
  • Бүх өөрчлөлтүүд нь нэг талаараа текст юм (хүүхэлдэйн манифестууд, скриптүүд болон Hiera өгөгдөл нь текст учраас), хоёртын файлууд нь маш их урам зориггүй байдаг тул ийм файлуудыг зөвшөөрөх зайлшгүй шалтгаан байх шаардлагатай.

Тиймээс, миний авч үзсэн сонголтууд:

  • Мунин - хэрэв дэд бүтцэд 10-аас дээш сервер байгаа бол удирдлага нь там болж хувирдаг. Энэ нийтлэлийг үзнэ үү. Би үүнийг шалгах ямар ч хүсэлгүй байсан тул би түүний үгийг хүлээж авсан).
  • Заббикс - Би үүнийг Орост буцаж ирээд удаан хугацаанд харж байсан боловч дараа нь энэ нь миний даалгаварт илүүц байсан. Энд - Хүүхэлдэйг тохиргооны менежер, Gitlab-ийг хувилбарын хяналтын систем болгон ашигласан тул хаях шаардлагатай болсон. Тэр үед миний ойлгосноор Zabbix бүх тохиргоог мэдээллийн санд хадгалдаг тул одоогийн нөхцөлд тохиргоог хэрхэн удирдах, өөрчлөлтийг хэрхэн хянах нь тодорхойгүй байсан.
  • Прометей бол тэнхим дэх сэтгэл санааны байдлаас харахад эцэст нь бидний хүрэх зүйл юм, гэхдээ тэр үед би үүнийг эзэмшээгүй бөгөөд үнэхээр ажиллаж байгаа жишээг (Үзэл баримтлалын нотолгоо) харуулж чадаагүй тул би татгалзахаас өөр аргагүй болсон.
  • Системийг бүрэн дахин боловсруулах шаардлагатай, эсвэл анхан шатандаа байсан / орхигдсон, ижил шалтгаанаар татгалзсан хэд хэдэн сонголт байсан.

Эцэст нь би Icinga2 дээр гурван шалтгаанаар суурьшсан.

1 - Nrpe-тэй нийцтэй байх (Nagios-аас командын шалгалтыг ажиллуулдаг үйлчлүүлэгчийн үйлчилгээ). Энэ нь маш чухал байсан, учир нь тэр үед бид 135 (одоо 2019 онд 165 байгаа) виртуал машинтай, захиалгаар бичсэн үйлчилгээ/чекүүдтэй байсан бөгөөд бүгдийг дахин хийх нь үнэхээр зовлон байх байсан.
2 - бүх тохиргооны файлууд нь текст бөгөөд энэ асуудлыг засварлахад хялбар болгож, нэмсэн эсвэл устгасан зүйлийг харах боломжтой нэгтгэх хүсэлтийг бий болгодог.
3 нь амьд, хөгжиж буй OpenSource төсөл юм. Бид OpenSource-г маш их хайрладаг бөгөөд асуудлыг шийдвэрлэхийн тулд татах хүсэлт болон асуудлуудыг бий болгосноор түүнд бүх боломжит хувь нэмэр оруулдаг.

За, явцгаая, Icinga2.

Миний хамгийн түрүүнд тулгарсан зүйл бол хамт ажиллагсдынхаа инерци байв. Нагиос/Наггиос (хэдийгээр энд ч гэсэн тэд үүнийг хэрхэн дуудах талаар тохиролцож чадаагүй) болон CheckMK интерфейст бүгд дассан. Мөсний интерфэйс нь огт өөр харагдаж байна (энэ нь хасах байсан), гэхдээ та ямар ч параметрийн шүүлтүүр ашиглан үзэх шаардлагатай зүйлээ уян хатан байдлаар өөрчлөх боломжтой (энэ нь нэмэлт байсан, гэхдээ би үүний төлөө маш их тэмцсэн).

ШүүлтүүрАвстралийн Нагиосоос Айсинга2 руу нүүсэн

Гүйлгэх мөрний хэмжээг гүйлгэх талбарын хэмжээтэй харьцуулсан харьцааг тооцоол.

Хоёрдугаарт, хүн бүр бүхэл бүтэн дэд бүтцийг нэг монитор дээр үзэхэд дассан байдаг, учир нь CheckMk нь хэд хэдэн Nagios хостуудтай ажиллах боломжийг олгодог боловч Icinga интерфэйс нь үүнийг хийж чадахгүй (үнэндээ үүнийг хийх боломжтой, гэхдээ доороос илүү дэлгэрэнгүй). Альтернатив хувилбар нь Thruk хэмээх зүйл байсан ч түүний загвар нь санал болгосон хүнээс (би биш) бусад багийн гишүүдийг гайхшруулсан.

Thruk зууханд - багийн санал нэгтэй шийдвэрАвстралийн Нагиосоос Айсинга2 руу нүүсэн

Хэдэн өдрийн турш оюуны довтолгооны дараа би үйлдвэрлэлийн бүсэд нэг мастер хост, хоёр боол - нэг нь хөгжүүлэлт/туршилтанд, нөгөө нь өөр үйлчилгээ үзүүлэгч дээр байрлах нэг гадаад хост байгаа үед кластерын мониторинг хийх санааг санал болгов. үйлчлүүлэгч эсвэл хөндлөнгийн ажиглагчийн байр сууринаас үйлчилгээ. Энэхүү тохиргоо нь бүх асуудлыг нэг вэб интерфэйсээс харах боломжтой болгосон бөгөөд нэлээд сайн ажилласан боловч Хүүхэлдэй... Хүүхэлдэйтэй холбоотой асуудал бол мастер хост нь систем дэх бүх хостууд болон үйлчилгээ/шалгалтуудын талаар мэдэх шаардлагатай болсон. тэдгээрийг бүс хооронд хуваарилах (dev-test, staging-prod, ext) гэхдээ Icinga API-ээр дамжуулан өөрчлөлтийг илгээхэд хэдэн секунд шаардагдах боловч бүх хостуудад зориулсан бүх үйлчилгээний Хүүхэлдэйн лавлахыг эмхэтгэх нь хэдэн минут болно. Бүх зүйл хэрхэн ажилладаг, яагаад ийм удаан хугацаа шаардагддаг талаар хэд хэдэн удаа тайлбарласан ч энэ нь намайг буруутгасаар байна.

Гуравдугаарт, олон тооны Цасан ширхгүүд байдаг - ерөнхий системээс онцгой зүйл байдаг тул ерөнхий дүрэм журам нь тэдэнд хамаарахгүй. Үүнийг урд талын дайралтаар шийдсэн - хэрвээ түгшүүртэй байсан ч үнэн хэрэгтээ бүх зүйл эмх цэгцтэй байгаа бол би илүү гүнзгий ухаж, яагаад сэрэмжлүүлж байгааг ойлгох хэрэгтэй. Эсвэл эсрэгээрээ - Нагиос яагаад сандарч байгаа ч Исинга тэгдэггүй.

Дөрөвдүгээрт, Нагиос надаас өмнө энд гурван жил ажилласан бөгөөд анхнаасаа түүнд миний шинээр бий болсон hipster системээс илүү их итгэл төрж байсан тул Исинга сандрах тоолонд Нагиос ижил асуудалд сэтгэл догдлох хүртэл хэн ч юу ч хийгээгүй. Гэхдээ Icinga нь Нагиосоос өмнө жинхэнэ дохиолол үүсгэдэг байсан нь маш ховор бөгөөд үүнийг би "Дүгнэлт" хэсэгт ярих болно.

Үүний үр дүнд ашиглалтад оруулах хугацаа 5 сар гаруй хугацаагаар хойшилсон (28 оны 2018-р сарын 3-нд төлөвлөсөн, үнэндээ - 2018 оны XNUMX-р сарын XNUMX) гол төлөв "паритет шалгах" -аас болж Нагиос хотод хэн ч мэдэхгүй хэд хэдэн үйлчилгээ байдаг. Сүүлийн хэдэн жилийн турш тэд юу ч сонсоогүй, гэхдээ ЯГ ОДОО тэд ямар ч шалтгаангүйгээр шүүмжилсэн бөгөөд би тэднийг яагаад миний самбарт байхгүй байгааг тайлбарлаж, Icinga-д нэмэх шаардлагатай болсон тул "паритет шалгах" бүрэн" (Нагиос дахь бүх үйлчилгээ/шалгалтууд нь Icinga дахь үйлчилгээ/чекүүдтэй тохирч байна)

Хэрэгжилт:
Эхнийх нь Puppet Style гэх мэт Code vs Data war юм. Бүх өгөгдөл, туйлын бүх зүйл Hiera-д байх ёстой, өөр юу ч биш. Бүх код .pp файлд байна. Хувьсагч, хийсвэрлэл, функцууд - бүх зүйл х.
Үүний үр дүнд бид олон тооны виртуал машинууд (бичих үед 165), SSL сертификатын гүйцэтгэл, хүчинтэй эсэхийг хянах шаардлагатай 68 вэб програмтай болсон. Гэхдээ түүхэн hemorrhoids-ийн улмаас програмуудыг хянах мэдээллийг тусдаа gitlab репозитороос авдаг бөгөөд Puppet 3-аас хойш мэдээллийн формат өөрчлөгдөөгүй бөгөөд энэ нь тохиргоонд нэмэлт төвөгтэй байдлыг бий болгодог.

Хэрэглээний хүүхэлдэйн код, нүдээ хамгаалаарай

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'], {} )) } # adds notifications for default group (systems) + any group defined in 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')        # Pick a regex to check

    $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 | {        # Split an app by domains if there are two or more
      $vhost_name = {'http_vhost' => $vhost}

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

      $web_ipaddress = is_array($vdata['web_ipaddress']) ? {  # Make IP-address an array if it's not, because askizzy has 2 ips and it's an array
        true  => $vdata['web_ipaddress'],
        false => [$vdata['web_ipaddress']],
      }

      $access_from_zones = [$zone] + $apps_access_list[$data['accessible_from']] # Merge default zone (where the app is defined) and extra zones if they exist
      $web_ipaddress.each | String $ip_address | {            # For each IP (if we have multiple)
        $suffix = length($web_ipaddress) ? {                  # If we have more than one - add IP as a suffix to this hostname to avoid duplicating resources
          1       => '',
          default => "_${ip_address}"
        }
        $octets = split($ip_address, '.')
        $ip_tag = "${octets[2]}.${octets[3]}" # Using last octet only causes a collision between nginx-vip 203.15.70.94 and 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}" # If it's a host for ext - prefix becomes '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
              }
            }
          }
        }
      }
    }
  }
}

Хост болон үйлчилгээний тохиргооны код нь бас аймшигтай харагдаж байна:

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

}

Би эдгээр гоймон дээр ажиллаж, чадах чинээгээрээ сайжруулж байна. Гэсэн хэдий ч энэ код нь Hiera-д энгийн бөгөөд ойлгомжтой синтакс ашиглах боломжийг бидэнд олгосон юм.

мэдээ

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'

Бүх чекийг бүлгүүдэд хуваадаг бөгөөд бүлэг бүр эдгээр шалгалтыг хаана, хэр олон удаа хийх, ямар мэдэгдэл, хэнд илгээх зэрэг үндсэн тохиргоотой.

Шалгалт бүрт та дурын сонголтыг хүчингүй болгох боломжтой бөгөөд энэ бүхэн эцэст нь бүх шалгалтын үндсэн тохиргоонд нэмэгддэг. Тийм ч учраас config.pp дээр ийм утгагүй зүйл бичигдсэн байдаг - энэ нь бүх үндсэн тохиргоог бүлгийн тохиргоотой нэгтгэж, дараа нь тус бүрийг шалгадаг.

Өөр нэг чухал өөрчлөлт бол тохиргоон дахь функцуудыг ашиглах, жишээлбэл, порт, хаяг, url-ийг солих функцийг 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

Энэ нь хэрэв хостын тодорхойлолтод хувьсагч байгаа бол гэсэн үг юм http_port — үүнийг ашигла, үгүй ​​бол 443. Жишээ нь, jabber вэб интерфэйс 9090, Unifi 7443 дээр өлгөөтэй байна.
http_vhost DNS-ийг үл тоомсорлож, энэ хаягийг авна гэсэн үг.
Хэрэв хост дээр uri-г зааж өгсөн бол түүнийг дагаж, үгүй ​​бол "/" гэж авна уу.

http_ssl-тэй инээдтэй түүх гарч ирэв - энэ халдвар нь хүсэлтээр салахыг хүсээгүй. Хостын тодорхойлолт дахь хувьсагч нь:

http_ssl: false

Илэрхийлэлд орлуулсан

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

хэрхэн хуурамч тэгээд эцэст нь энэ нь тодорхой болно

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

өөрөөр хэлбэл ssl шалгах үргэлж идэвхтэй байдаг. Би синтаксийг орлуулах замаар үүнийг шийдсэн:

http_ssl: no

үр дүн нь:

Нөхцөл:

  • Сүүлийн 7-8 сарын турш хэрэгжүүлсэн шигээ хоёр биш, нэг хяналтын системтэй, эсвэл хуучирсан, эмзэг системтэй болсон.
  • Хост/үйлчилгээний(шалгах) өгөгдлийн бүтэц одоо (миний бодлоор) илүү уншигдахуйц, ойлгомжтой болсон. Бусдын хувьд энэ нь тийм ч тодорхой биш байсан тул би энэ бүхэн хэрхэн ажилладаг, хаана юу засахаа тайлбарлахын тулд орон нутгийн вики дээр хоёр хуудас нийтлэх шаардлагатай болсон.
  • Хувьсагч болон функцуудыг ашиглан чекийг уян хатан тохируулах боломжтой, жишээлбэл http_regexp-г шалгахын тулд хостын тохиргоонд хүссэн загвар, буцах код, url болон портыг тохируулж болно.
  • Хэд хэдэн хяналтын самбар байдаг бөгөөд тус бүрдээ та өөрийн үзүүлсэн дохиоллын жагсаалтыг тодорхойлж, Хүүхэлдэйн болон нэгтгэх хүсэлтээр дамжуулан энэ бүгдийг удирдах боломжтой.

Нөхцөл байдал:

  • Багийн гишүүдийн инерци - Нагиос ажиллаж, ажиллаж, ажилласан, таны энэ Исинга байнга алдаатай, удаан байдаг. Эндээс түүхийг хэрхэн харж болох вэ? Өө, хараал ид, энэ нь шинэчлэгдээгүй байна ... (Бодит асуудал бол сэрүүлгийн түүх автоматаар шинэчлэгдээгүй, зөвхөн F5-ээр шинэчлэгддэг)
  • Системийн инерци - би вэб интерфэйс дэх "шинэчлэх" (одоо шалгах) дээр дарахад гүйцэтгэлийн үр дүн нь Ангараг гарагийн цаг агаар, ялангуяа гүйцэтгэхэд хэдэн арван секунд шаардагдах нарийн төвөгтэй үйлчилгээнээс хамаарна. Ийм үр дүн нь хэвийн үзэгдэл юм. Австралийн Нагиосоос Айсинга2 руу нүүсэн
  • Ерөнхийдөө хоёр системийн зэрэгцэн ажилласан зургаан сарын статистик мэдээллээс үзэхэд Нагиос үргэлж Исингагаас илүү хурдан ажилладаг байсан нь намайг үнэхээр бухимдуулсан. Тэд таймертай ямар нэг зүйл будилуулсан юм шиг санагдаж байна, шалгалтыг таван минут тутамд хийдэг, үнэндээ 5:30 эсвэл үүнтэй төстэй зүйл тохиолддог.
  • Хэрэв та үйлчилгээг хүссэн үедээ дахин эхлүүлбэл (systemctl restart icinga2) - тухайн үед хийгдэж байсан бүх шалгалтууд чухал дохиолол үүсгэх болно. дэлгэцэн дээр болон гаднаас харахад бүх зүйл унасан мэт харагдаж байна (батлагдсан алдаа).

Гэхдээ ерөнхийдөө энэ нь ажилладаг.

Эх сурвалж: www.habr.com

DDoS хамгаалалт, VPS VDS сервер бүхий сайтуудад найдвартай хостинг худалдаж аваарай 🔥 DDoS хамгаалалттай, VPS VDS сервертэй найдвартай вэбсайт хостинг худалдаж аваарай | ProHoster