Ahoj všichni
Jsem správce systému Linux, v roce 2015 jsem se přestěhoval z Ruska do Austrálie na nezávislé profesionální vízum, ale článek nebude o tom, jak nastartovat traktor pro prase. Takových článků už je dost (nicméně, pokud bude zájem, napíšu i o tomto), tak bych rád pohovořil o tom, jak jsem při své práci v Austrálii jako linux-ops inženýr inicioval migraci z jednoho monitorování systému jinému. Konkrétně - Nagios => Icinga2.
Článek je zčásti technický a zčásti o komunikaci s lidmi a problémech spojených s odlišností kultury a pracovních metod.
Značka „code“ bohužel nezvýrazní kód Puppet a yaml, takže jsem musel použít „plaintext“.
Ráno 21. prosince 2016 nebyly žádné známky potíží. Jako obvykle jsem četl Habr od neregistrovaného anonymního uživatele v první půlhodině pracovního dne při pití kávy a narazil jsem .
Protože moje společnost používala Nagios, bez přemýšlení jsem vytvořil tiket v Redmine a poslal odkaz na obecný chat, protože jsem to považoval za důležité. Iniciativa je trestná dokonce i v Austrálii, takže hlavní inženýr mi problém připsal, protože jsem ho objevil.
Screenshot z Redmine
Na našem oddělení je před vyslovením svého názoru zvykem nabídnout alespoň jednu alternativu, i když je výběr zřejmý, proto jsem začal googlováním, jaké monitorovací systémy jsou aktuálně relevantní, jelikož v Rusku na posledním působišti jsem měl svůj vlastní samopsaný systém, velmi primitivní, ale přesto docela fungující a vykonávající všechny úkoly, které mu byly přiděleny. Vládne Python, Petrohradská polytechnika a metro. Ne, metro je na hovno. To je osobní (11 let práce) a zaslouží si samostatný článek, ale ne teď.
Něco málo o pravidlech pro provádění změn v konfiguraci infrastruktury na mém současném místě. Jako princip kódu používáme Puppet, Gitlab a infrastrukturu, takže:
- Žádné ruční změny prostřednictvím SSH ruční změnou jakýchkoli souborů na virtuálních počítačích. Za ty tři roky práce jsem za to mnohokrát dostal ránu, naposledy před týdnem a myslím, že to nebylo naposledy. No, opravdu - opravte jeden řádek v konfiguraci, restartujte službu a podívejte se, zda je problém vyřešen - 10 sekund. Vytvořte novou větev v Gitlabu, zatlačte změny, počkejte, až r10k bude fungovat na Puppetmaster, spusťte Puppet -environment=mybranch a počkejte ještě pár minut, než to všechno bude fungovat – minimálně 5 minut.
- Jakékoli změny se provádějí vytvořením žádosti o sloučení v Gitlabu a musí být schváleny alespoň jedním členem týmu. Velké změny, o kterých rozhodne vedoucí týmu, vyžadují dva nebo tři schválení.
- Všechny změny jsou tak či onak textové (protože manifesty Puppet, skripty a data Hiera jsou text), binární soubory se důrazně nedoporučuje a musí existovat pádný důvod pro schválení takových souborů.
Takže možnosti, které jsem zvažoval:
- Munin - pokud je v infrastruktuře více než 10 serverů, administrace se změní v peklo (od . Neměl jsem žádnou zvláštní chuť si to ověřit, tak jsem ho vzal za slovo).
- Zabbix - Díval jsem se na to už dlouho, v Rusku, ale pak to bylo pro mé úkoly zbytečné. Zde - musel být vyřazen kvůli použití Puppet jako konfiguračního manažera a Gitlabu jako systému pro správu verzí. V té době, pokud jsem pochopil, Zabbix ukládá celou konfiguraci do databáze, a proto nebylo jasné, jak konfiguraci za aktuálních podmínek spravovat a jak sledovat změny.
- Prometheus je to, k čemu nakonec dojdeme, soudě podle nálady na katedře, ale tenkrát jsem ho neovládal a nebyl schopen předvést skutečně fungující vzorek (Proof of Concept), takže jsem musel odmítnout.
- Existovalo také několik dalších možností, které buď vyžadovaly kompletní přepracování systému, nebo byly v plenkách/opuštěny a byly zamítnuty ze stejného důvodu.
Nakonec jsem se rozhodl pro Icinga2 ze tří důvodů:
1 - kompatibilita s Nrpe (klientská služba, která spouští kontroly příkazů z Nagios). To bylo velmi důležité, protože v té době jsme měli 135 (nyní je jich v roce 2019 165) virtuálních strojů s hromadou na zakázku psaných služeb/kontrol a předělat to všechno by byla skutečná bolest.
2 - všechny konfigurační soubory jsou textové, což usnadňuje úpravu této záležitosti, vytváření požadavků na sloučení s možností vidět, co bylo přidáno nebo odstraněno.
3 je živý a rozvíjející se projekt OpenSource. Opravdu milujeme OpenSource a přispíváme k němu všemi možnými způsoby tím, že vytváříme Pull Requests a Issues k řešení problémů.
Takže, jdeme, Icinga2.
První, čemu jsem musel čelit, byla setrvačnost mých kolegů. Všichni jsou zvyklí na Nagios/Naggios (i když ani zde nedokázali dospět ke kompromisu, jak to vyslovit) a rozhraní CheckMK. Rozhraní icinga vypadá úplně jinak (to bylo mínus), ale je možné flexibilně přizpůsobit, co potřebujete vidět, pomocí filtrů pro doslova jakýkoli parametr (to bylo plus, ale hodně jsem o to bojoval).
Filtry
Odhadněte poměr velikosti posuvníku k velikosti posuvného pole.
Za druhé, všichni jsou zvyklí vidět celou infrastrukturu na jednom monitoru, protože CheckMk umožňuje pracovat s několika hostiteli Nagios, ale rozhraní Icinga to neumělo (ve skutečnosti by mohlo, ale o tom níže). Alternativou bylo něco, co se jmenovalo Thruk, ale jeho design způsobil, že všichni v týmu roubili kromě jednoho – toho, kdo to navrhl (ne mě).
Do pece Thruk - jednomyslné rozhodnutí týmu
Po několika dnech brainstormingu jsem navrhl myšlenku monitorování clusteru, kdy je jeden hlavní hostitel v produkční zóně a dva slave – jeden ve vývojovém/testovacím a jeden externí hostitel umístěný u jiného poskytovatele, aby mohli monitorovat naše služby z pohledu klienta nebo vnějšího pozorovatele. Tato konfigurace umožňovala vidět všechny problémy v jednom webovém rozhraní a fungovala docela dobře, ale Puppet... Problém s Puppet byl v tom, že hlavní hostitel nyní musel vědět o všech hostitelích a službách/kontrolách v systému a měl distribuovat je mezi zóny (dev-test, staging-prod, ext), ale odesílání změn přes Icinga API trvá několik sekund, ale kompilace adresáře Puppet všech služeb pro všechny hostitele trvá několik minut. To je mi stále vyčítáno, i když jsem již několikrát vysvětloval, jak vše funguje a proč to celé trvá tak dlouho.
Za třetí, existuje spousta Sněhových vloček – věcí, které vyčnívají z obecného systému, protože mají něco zvláštního, takže se na ně nevztahují obecná pravidla. Bylo to vyřešeno frontálním útokem - pokud jsou alarmy, ale ve skutečnosti je vše v pořádku, musím se ponořit hlouběji a zjistit, proč mě to upozorňuje, i když by nemělo. Nebo naopak – proč Nagios panikaří, ale Icinga ne.
Za čtvrté, Nagios tady přede mnou pracoval tři roky a zpočátku v něj byla větší důvěra než v můj nový hipsterský systém, takže pokaždé, když Icinga vyvolala paniku, nikdo nic neudělal, dokud se Nagios nevzrušil pro stejnou věc. Ale velmi zřídka Icinga generovala skutečné alarmy dříve než Nagios a to považuji za vážný problém, o kterém budu hovořit v části „Závěry“.
Výsledkem bylo, že uvedení do provozu bylo zpožděno o více než 5 měsíců (plánováno na 28. června 2018, ve skutečnosti - 3. prosince 2018), hlavně kvůli „kontrole parity“ - to svinstvo, když je v Nagios několik služeb, které nikdo nezná za posledních pár let jsem o ničem neslyšela, ale PRÁVĚ TEĎ kurva rozdali kritiky bez důvodu a já jsem musel vysvětlit, proč nejsou v mém panelu a musel jsem je přidat do Icinga, takže „kontrola parity je kompletní" (Všechny služby/kontroly v Nagios odpovídají službám/kontroly v Icinga)
Implementace:
První je válka Code vs Data, jako je Puppet Style. Všechna data, úplně všechno, musí být v Hieře a nic jiného. Veškerý kód je v souborech .pp. Proměnné, abstrakce, funkce - vše jde v pp.
Výsledkem je, že máme hromadu virtuálních strojů (165 v době psaní článku) a 68 webových aplikací, které je třeba sledovat z hlediska výkonu a platnosti SSL certifikátů. Ale kvůli historickým hemoroidům jsou informace pro monitorovací aplikace přebírány ze samostatného úložiště gitlab a formát dat se od Puppet 3 nezměnil, což vytváří další složitost v konfiguraci.
Loutkový kód pro aplikace, chraňte své 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
}
}
}
}
}
}
}
}Konfigurační kód pro hostitele a služby také vypadá hrozně:
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',
}
}Na těchto nudlích stále pracuji a vylepšuji je, jak nejlépe umím. Byl to však tento kód, který nám umožnil použít jednoduchou a srozumitelnou syntaxi v Hiera:
Data
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'Všechny kontroly jsou rozděleny do skupin, každá skupina má výchozí nastavení jako kde a jak často tyto kontroly spouštět, jaká upozornění zasílat a komu.
V každé kontrole můžete přepsat libovolnou možnost a to vše se nakonec sčítá s výchozím nastavením všech kontrol jako celku. Proto se v config.pp píše takový nesmysl - sloučí všechna výchozí nastavení se skupinovým nastavením a následně s každou jednotlivou kontrolou.
Další velmi důležitou změnou byla možnost používat funkce v nastavení, například funkce nahrazení portu, adresy a url pro kontrolu 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: trueTo znamená - pokud je v definici hostitele proměnná http_port — použijte, jinak 443. Například webové rozhraní jabberu visí na 9090 a Unifi visí na 7443.
http_vhost znamená ignorovat DNS a převzít tuto adresu.
Pokud je v hostiteli určeno uri, postupujte podle něj, jinak vezměte „/“.
S http_ssl vyšla vtipná historka - tato infekce se nechtěla na požádání odpojit. Dlouho jsem byl zmatený z tohoto řádku, dokud mi nedošlo, že proměnná v definici hostitele je:
http_ssl: falseNahrazeno do výrazu
if(host.vars.http_ssl) { return false } else { return true }как nepravdivý a nakonec se ukáže
if(false) { return false } else { return true }to znamená, že kontrola ssl je vždy aktivní. Vyřešil jsem to nahrazením syntaxe:
http_ssl: noZávěry:
výhody:
- Nyní máme jeden monitorovací systém a ne dva, jako jsme měli posledních 7–8 měsíců, nebo jeden, který je zastaralý a zranitelný.
- Struktura dat hostitelů/služeb (kontrol) je nyní (podle mého názoru) mnohem čitelnější a srozumitelnější. Pro ostatní se ukázalo, že to není tak zřejmé, takže jsem musel umístit pár stránek na místní wiki, abych vysvětlil, jak to všechno funguje a co kde upravit.
- Kontroly je možné flexibilně konfigurovat pomocí proměnných a funkcí, např. pro kontrolu http_regexp lze v nastavení hostitele nastavit požadovaný vzor, návratový kód, url a port.
- Existuje několik dashboardů, pro každý z nich můžete definovat svůj vlastní seznam zobrazených alarmů a to vše spravovat prostřednictvím požadavků Puppet a sloučení.
nevýhody:
- Setrvačnost členů týmu - Nagios pracoval, pracoval a pracoval a tento váš Isinga je neustále zabugovaný a pomalý. Jak tady vidíte historii? Sakra, není to aktualizováno... (Skutečný problém je, že historie alarmů se neaktualizuje automaticky, pouze pomocí F5)
- Setrvačnost systému - když ve webovém rozhraní kliknu na „aktualizovat“ (zkontrolovat nyní) - výsledek provedení závisí na počasí na Marsu, zejména na složitých službách, jejichž spuštění trvá desítky sekund. Takový výsledek je normální.

- Obecně podle půlroční statistiky provozu dvou systémů vedle sebe Nagios vždy fungoval rychleji než Icinga a to mě opravdu štvalo. Zdá se mi, že něco pokazili s časovači a kontrola se dělá každých pět minut, ve skutečnosti se to děje každých 5:30 nebo tak nějak.
- Pokud službu kdykoli restartujete (systemctl restart icinga2) - všechny kontroly, které v té době probíhaly, vygenerují kritický alarm na obrazovce a zvenku to vypadá, jako by všechno spadlo ().
Ale celkově to funguje.
Zdroj: www.habr.com

