Pozdrav svima.
Ja sam Linux sistem administrator, preselio sam se iz Rusije u Australiju na nezavisnu stručnu vizu 2015. godine, ali članak neće biti o tome kako pokrenuti traktor za svinju. Ovakvih članaka već ima dovoljno (ipak, ako bude interesovanja, pisaću i o ovome), pa bih želeo da pričam o tome kako sam na svom poslu u Australiji kao linux-ops inžinjer pokrenuo migraciju sa jednog nadzora sistema drugome. Konkretno - Nagios => Icinga2.
Članak je dijelom tehnički, a dijelom o komunikaciji s ljudima i problemima vezanim za razlike u kulturi i metodama rada.
Nažalost, oznaka “code” ne ističe Puppet i yaml kod, pa sam morao koristiti “plaintext”.
Ujutro 21. decembra 2016. godine nije bilo znakova problema. Kao i obično, čitao sam Habr od neregistrovanog anonimnog korisnika u prvih pola sata radnog dana, pijući kafu, i naišao na .
Pošto je moja kompanija koristila Nagios, bez razmišljanja sam napravio tiket u Redmine-u i poslao link na generalni chat, jer sam smatrao da je to važno. Inicijativa je kažnjiva čak i u Australiji, tako da je glavni inženjer zakačio problem na mene od kada sam ga otkrio.
Snimak ekrana sa Redmine-a
U našem odeljenju, pre nego što iznesete svoje mišljenje, uobičajeno je da ponudite bar jednu alternativu, čak i ako je izbor očigledan, pa sam počeo guglajući koji su sistemi za praćenje trenutno relevantni, pošto sam u Rusiji na poslednjem mestu rada imao svoj sistem koji sam pisao, vrlo primitivan, ali je ipak prilično funkcionalan i izvršavao sve zadatke koji su mu bili dodijeljeni. Vladaju Python, Politehnika Sankt Peterburga i metro. Ne, metro je sranje. Ovo je lično (11 godina rada) i vrijedno je posebnog članka, ali ne sada.
Malo o pravilima za promjenu konfiguracije infrastrukture na mom trenutnom mjestu. Koristimo Puppet, Gitlab i Infrastrukturu kao princip koda, tako da:
- Nema ručnih promjena putem SSH-a ručnim mijenjanjem datoteka na virtuelnim mašinama. Tokom tri godine rada, mnogo puta sam dobio kapu za ovo, zadnji put je bio prije nedelju dana i mislim da to nije bio zadnji put. Pa, stvarno - ispravite jednu liniju u konfiguraciji, restartujte servis i vidite da li je problem riješen - 10 sekundi. Kreirajte novu granu u Gitlabu, pritisnite promjene, pričekajte da r10k radi na Puppetmasteru, pokrenite Puppet -environment=mybranch i pričekajte još par minuta dok sve ne proradi - minimalno 5 minuta.
- Sve promjene se vrše kreiranjem zahtjeva za spajanje u Gitlabu i moraju biti odobrene od najmanje jednog člana tima. Za velike promjene koje odluči vođa tima potrebna su dva ili tri odobrenja.
- Sve promjene su tekst na ovaj ili onaj način (pošto su manifesti Puppet, skripte i podaci Hiere tekst), binarne datoteke su vrlo obeshrabrene i mora postojati uvjerljiv razlog za odobravanje takvih datoteka.
Dakle, opcije koje sam razmatrao:
- Munin - ako postoji više od 10 servera u infrastrukturi, administracija se pretvara u pakao (od . Nisam imao posebnu želju da ovo provjeravam, pa sam mu vjerovao na riječ).
- Zabbix - Gledao sam ga dugo, još u Rusiji, ali je tada bio suvišan za moje zadatke. Ovdje - morao je biti odbačen zbog korištenja Puppet-a kao konfiguracijskog menadžera i Gitlaba kao sistema za kontrolu verzija. U to vrijeme, koliko sam shvatio, Zabbix pohranjuje cjelokupnu konfiguraciju u bazu podataka i stoga nije bilo jasno kako upravljati konfiguracijom u trenutnim uslovima i kako pratiti promjene.
- Prometej je ono do čega ćemo na kraju doći, sudeći po raspoloženju u odjelu, ali tada ga nisam savladao i nisam bio u mogućnosti da demonstriram stvarno ispravan uzorak (Proof of Concept), pa sam morao odbiti.
- Bilo je i nekoliko drugih opcija koje su ili zahtijevale potpunu preradu sistema, ili su bile u povoju/napuštene i odbačene iz istog razloga.
Na kraju sam se odlučio za Icinga2 iz tri razloga:
1 - kompatibilnost sa Nrpe (uslugom klijenta koja pokreće provjere naredbi iz Nagiosa). Ovo je bilo jako važno, jer smo u to vrijeme imali 135 (sada ih ima 2019 u 165.) virtuelnih mašina sa gomilom prilagođenih servisa/provjera, a ponavljati sve to bi bila prava muka.
2 - svi konfiguracioni fajlovi su tekstualni, što olakšava uređivanje ove materije, kreiranje zahteva za spajanje sa mogućnošću da vidite šta je dodato ili obrisano.
3 je živi i razvijajući se OpenSource projekat. Jako volimo OpenSource i dajemo mu svaki mogući doprinos kreiranjem Pull zahtjeva i problema za rješavanje problema.
Pa, idemo, Icinga2.
Prvo s čime sam se morao suočiti bila je inercija mojih kolega. Svi su navikli na Nagios/Naggios (iako ni ovdje nisu mogli doći do kompromisa o tome kako se to izgovara) i CheckMK interfejs. Icinga interfejs izgleda potpuno drugačije (ovo je bio minus), ali je moguće fleksibilno prilagoditi ono što vam je potrebno da vidite koristeći filtere za bukvalno bilo koji parametar (ovo je bio plus, ali sam se mnogo borio za to).
Filteri
Procijenite omjer veličine trake za pomicanje i veličine polja za pomicanje.
Drugo, svi su navikli da vide čitavu infrastrukturu na jednom monitoru, jer vam CheckMk omogućava rad sa nekoliko Nagios hostova, ali Icinga interfejs to nije mogao (u stvari, mogao bi, ali o tome u nastavku). Alternativa je bila nešto što se zove Thruk, ali njegov dizajn je učinio da se svi u timu začepe osim jednog — onog koji je to predložio (ne mene).
U Thruk peć - jednoglasna odluka tima
Nakon par dana mozganja, predložio sam ideju praćenja klastera, kada postoji jedan master host u proizvodnoj zoni i dva slave-a - jedan u dev/test i jedan eksterni host koji se nalazi kod drugog provajdera kako bi se pratio naš usluge sa stanovišta klijenta ili posmatrača sa strane. Ova konfiguracija je omogućila da se vide svi problemi u jednom web interfejsu i radila je prilično dobro, ali Puppet... Problem sa Puppet-om je bio u tome što je glavni host sada morao da zna za sve hostove i usluge/provere u sistemu i imao je da ih distribuira između zona (dev-test, staging-prod, ext), ali slanje promjena putem Icinga API-ja traje nekoliko sekundi, ali kompajliranje Puppet direktorija svih servisa za sve hostove traje nekoliko minuta. I dalje mi se to zamera, iako sam već nekoliko puta objasnio kako sve funkcioniše i zašto sve to toliko dugo traje.
Treće, postoji gomila pahuljica - stvari koje se izdvajaju iz opšteg sistema jer imaju nešto posebno, pa za njih ne važe opšta pravila. To je riješeno frontalnim napadom - ako postoje alarmi, a u stvari je sve u redu, onda moram kopati dublje i shvatiti zašto me upozorava, iako ne bi trebalo. Ili obrnuto - zašto Nagios paniči, ali Icinga ne.
Četvrto, Nagios je radio ovdje prije mene tri godine i u početku je bilo više povjerenja u njega nego u moj novonastali hipsterski sistem, tako da svaki put kada je Icinga digao paniku, niko ništa nije uradio dok se Nagios nije uzbudio zbog istog problema. Ali vrlo rijetko je Icinga generirao stvarne alarme ranije od Nagiosa i smatram da je to ozbiljan problem, o čemu ću govoriti u odjeljku “Zaključci”.
Kao rezultat toga, puštanje u rad kasnilo je više od 5 mjeseci (planirano za 28. jun 2018., zapravo - 3. decembar 2018.), uglavnom zbog "provjere pariteta" - tog sranja kada u Nagiosu postoji nekoliko servisa za koje niko ne zna o nisam čuo ništa zadnjih par godina, ali TRENUTNO su jebeno dali kritike bez razloga i morao sam objasniti zašto ih nema na mom panelu i morao sam ih dodati u Icingu tako da "provjera pariteta bude kompletno" (Sve usluge/provjere u Nagiosu odgovaraju uslugama/provjerama u Icingi)
Implementacija:
Prvi je rat koda protiv podataka, kao što je stil lutke. Svi podaci, apsolutno sve, moraju biti u Hieri i ništa više. Sav kod je u .pp fajlovima. Varijable, apstrakcije, funkcije - sve ide u pp.
Kao rezultat toga, imamo gomilu virtuelnih mašina (165 u trenutku pisanja) i 68 web aplikacija koje treba nadgledati radi performansi i valjanosti SSL sertifikata. Ali zbog historijskih hemoroida, informacije za aplikacije za praćenje su preuzete iz zasebnog gitlab repozitorija i format podataka se nije promijenio od Puppet-a 3, što stvara dodatnu složenost u konfiguraciji.
Lutka-kod za aplikacije, zaštitite svoje oči
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
}
}
}
}
}
}
}
}Konfiguracijski kod za hostove i usluge također izgleda užasno:
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',
}
}Još uvijek radim na ovim rezancima i poboljšavam ih najbolje što mogu. Međutim, upravo nam je ovaj kod omogućio da koristimo jednostavnu i razumljivu sintaksu u Hieri:
podaci
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'Sve provjere su podijeljene u grupe, svaka grupa ima zadane postavke kao što su gdje i koliko često da se te provjere pokreću, koje obavijesti poslati i kome.
U svakoj provjeri možete poništiti bilo koju opciju, a sve to na kraju dovodi do zadanih postavki svih provjera u cjelini. Zato je takva glupost napisana u config.pp - spaja sve zadane postavke sa grupnim postavkama i onda sa svakom pojedinačnom provjerom.
Još jedna vrlo važna promjena bila je mogućnost korištenja funkcija u postavkama, na primjer, funkcija zamjene porta, adrese i url-a za provjeru http_regex-a.
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: trueTo znači - ako postoji varijabla u definiciji hosta http_port — koristite ga, inače 443. Na primjer, jabber web interfejs visi na 9090, a Unifi visi na 7443.
http_vhost znači ignorisati DNS i uzeti ovu adresu.
Ako je uri naveden u hostu, slijedite ga, u suprotnom uzmite “/”.
Izašla je smiješna priča sa http_ssl - ova infekcija nije htjela prekinuti vezu na zahtjev. Dugo sam bio zbunjen ovom linijom dok mi nije sinulo da je varijabla u definiciji hosta:
http_ssl: falseZamijenjeno u izraz
if(host.vars.http_ssl) { return false } else { return true }kako lažan i na kraju ispada
if(false) { return false } else { return true }to jest, ssl provjera je uvijek aktivna. Rešio sam to zamenom sintakse:
http_ssl: nonalazi:
Pros:
- Sada imamo jedan sistem za praćenje, a ne dva, kao što smo imali zadnjih 7-8 mjeseci, ili jedan koji je zastario i ranjiv.
- Struktura podataka hostova/usluga(provjerava) je sada (po mom mišljenju) mnogo čitljivija i razumljivija. Za druge se pokazalo da ovo nije toliko očigledno, pa sam morao da postavim par stranica na lokalni wiki da objasnim kako sve to funkcioniše i šta da uređujem gde.
- Moguće je fleksibilno konfigurirati provjere koristeći varijable i funkcije, na primjer, za provjeru http_regexp, željeni uzorak, povratni kod, url i port se mogu postaviti u postavkama hosta.
- Postoji nekoliko nadzornih ploča, za svaku od kojih možete definirati vlastitu listu prikazanih alarma i upravljati svim tim putem Puppet i Merge zahtjeva.
Cons:
- Inercija članova tima - Nagios je radio, radio i radio, a ova tvoja Isinga stalno bugira i spora. Kako možete vidjeti historiju ovdje? Oh, prokletstvo, nije ažurirano... (Pravi problem je što se historija alarma ne ažurira automatski, samo pomoću F5)
- Inercija sistema - kada kliknem na "ažuriraj" (proveri sada) u web interfejsu - rezultat izvršenja zavisi od vremena na Marsu, posebno od složenih servisa koji zahtevaju desetine sekundi da se izvrše. Takav rezultat je normalan.

- Generalno, prema šestomjesečnoj statistici rada dva sistema uporedo, Nagios je uvijek radio brže od Icinge i to me jako nerviralo. Čini mi se da su nešto zabrljali sa tajmerima i provjera se radi svakih pet minuta, u stvari to se dešava svakih 5:30 ili tako nešto.
- Ako ponovo pokrenete uslugu u bilo kojem trenutku (systemctl restart icinga2) - sve provjere koje su bile u toku u to vrijeme će generirati kritičan alarm na ekranu i spolja izgleda kao da je sve palo ().
Ali sve u svemu, funkcionira.
izvor: www.habr.com

