Hola a todos.
Soy administrador de sistemas Linux, me mudé de Rusia a Australia con una visa profesional independiente en 2015, pero el artículo no tratará sobre cómo poner en marcha un tractor para un cerdo. Ya hay suficientes artículos de este tipo (sin embargo, si hay interés, también escribiré sobre esto), por lo que me gustaría hablar sobre cómo, en mi trabajo en Australia como ingeniero de operaciones de Linux, inicié la migración desde un sistema de monitoreo. a otro. Específicamente - Nagios => Icinga2.
El artículo es en parte técnico y en parte sobre la comunicación con las personas y los problemas asociados con las diferencias en cultura y métodos de trabajo.
Desafortunadamente, la etiqueta "código" no resalta el código Puppet y yaml, por lo que tuve que usar "texto sin formato".
No había señales de problemas en la mañana del 21 de diciembre de 2016. Como de costumbre, estaba leyendo Habr por parte de un usuario anónimo no registrado en la primera media hora de la jornada laboral, mientras tomaba café, y me encontré .
Como mi empresa usaba Nagios, sin pensarlo dos veces creé un ticket en Redmine y envié el enlace al chat general, porque lo consideraba importante. La iniciativa es punible incluso en Australia, por lo que el ingeniero principal me atribuyó el problema desde que lo descubrí.
Captura de pantalla de Redmine
En nuestro departamento, antes de expresar su opinión, es costumbre ofrecer al menos una alternativa, incluso si la elección es obvia, así que comencé a buscar en Google qué tipo de sistemas de monitoreo son relevantes actualmente, ya que en Rusia, en mi último lugar de trabajo, Tenía mi propio sistema escrito por mí mismo, muy primitivo, pero sin embargo bastante funcional y realizando todas las tareas que se le asignaban. Python, Politécnico de San Petersburgo y la regla del metro. No, el metro apesta. Esto es personal (11 años de trabajo) y digno de un artículo aparte, pero no ahora.
Un poco sobre las reglas para realizar cambios en la configuración de la infraestructura en mi lugar actual. Usamos Puppet, Gitlab y la Infraestructura como principio de Código, por lo que:
- No se realizan cambios manuales a través de SSH cambiando manualmente ningún archivo en las máquinas virtuales. A lo largo de los tres años de trabajo, me golpearon con un sombrero muchas veces por esto, la última vez fue hace una semana y no creo que sea la última. Bueno, de verdad: corrija una línea en la configuración, reinicie el servicio y vea si el problema se resuelve: 10 segundos. Cree una nueva rama en Gitlab, envíe los cambios, espere a que r10k funcione en Puppetmaster, ejecute Puppet -environment=mybranch y espere un par de minutos más hasta que todo funcione: 5 minutos como mínimo.
- Cualquier cambio se realiza mediante la creación de una Solicitud de combinación en Gitlab y debe ser aprobado por al menos un miembro del equipo. Los cambios importantes decididos por el líder del equipo requieren dos o tres aprobaciones.
- Todos los cambios son texto de una forma u otra (ya que los manifiestos de Puppet, los scripts y los datos de Hiera son texto), se desaconsejan los archivos binarios y debe haber una razón convincente para aprobar dichos archivos.
Entonces, las opciones que consideré:
- Munin: si hay más de 10 servidores en la infraestructura, la administración se convierte en un infierno (de . No tenía ningún deseo particular de comprobar esto, así que tomé su palabra).
- Zabbix: lo había estado mirando durante mucho tiempo, en Rusia, pero luego resultó redundante para mis tareas. Aquí tuvo que ser descartado debido al uso de Puppet como administrador de configuración y Gitlab como sistema de control de versiones. En ese momento, hasta donde tenía entendido, Zabbix almacena toda la configuración en la base de datos y, por lo tanto, no estaba claro cómo administrar la configuración en las condiciones actuales y cómo rastrear los cambios.
- Prometheus es a lo que llegaremos al final, a juzgar por el estado de ánimo en el departamento, pero en ese momento no lo dominaba y no pude demostrar una muestra realmente funcional (Prueba de concepto), así que tuve que negarme.
- También hubo varias otras opciones que requirieron una reelaboración completa del sistema o estaban en su infancia/abandonadas y fueron rechazadas por la misma razón.
Al final, me decidí por Icinga2 por tres razones:
1 - compatibilidad con Nrpe (un servicio de cliente que ejecuta comprobaciones de comandos desde Nagios). Esto fue muy importante, porque en ese momento teníamos 135 (ahora hay 2019 en 165) máquinas virtuales con un montón de servicios/verificaciones personalizadas, y rehacerlo todo habría sido una verdadera molestia.
2: todos los archivos de configuración son de texto, lo que facilita la edición de este asunto, la creación de solicitudes de combinación con la capacidad de ver lo que se ha agregado o eliminado.
3 es un proyecto OpenSource vivo y en desarrollo. Amamos mucho OpenSource y hacemos todas las contribuciones posibles creando solicitudes de extracción y problemas para resolver problemas.
Entonces, vamos, Icinga2.
Lo primero que tuve que afrontar fue la inercia de mis compañeros. Todo el mundo está acostumbrado a Nagios/Naggios (aunque incluso aquí no pudieron llegar a un acuerdo sobre cómo pronunciarlo) y a la interfaz CheckMK. La interfaz de Icinga se ve completamente diferente (esto fue un inconveniente), pero es posible personalizar de manera flexible lo que necesita ver usando filtros para literalmente cualquier parámetro (esto fue una ventaja, pero luché mucho por ello).
Filtros
Calcule la relación entre el tamaño de la barra de desplazamiento y el tamaño del campo de desplazamiento.
En segundo lugar, todos están acostumbrados a ver toda la infraestructura en un monitor, porque CheckMk le permite trabajar con varios hosts de Nagios, pero la interfaz Icinga no pudo hacer esto (de hecho, podría, pero más sobre eso a continuación). La alternativa era algo llamado Thruk, pero su diseño hizo que todos en el equipo se atragantaran excepto uno: el que lo sugirió (no yo).
En el horno Thruk: la decisión unánime del equipo
Después de un par de días de lluvia de ideas, propuse la idea de monitorear el clúster, cuando hay un host maestro en la zona de producción y dos esclavos, uno en desarrollo/prueba y un host externo ubicado en otro proveedor para monitorear nuestro servicios desde el punto de vista de un cliente o de un observador externo. Esta configuración hizo posible ver todos los problemas en una interfaz web y funcionó bastante bien, pero Puppet... El problema con Puppet era que el host maestro ahora tenía que conocer todos los hosts y servicios/verificaciones en el sistema y tenía para distribuirlos entre zonas (dev-test, staging-prod, ext), pero enviar cambios a través de la API de Icinga lleva un par de segundos, pero compilar el directorio Puppet de todos los servicios para todos los hosts lleva un par de minutos. Todavía me culpan de esto, aunque ya he explicado varias veces cómo funciona todo y por qué lleva tanto tiempo.
En tercer lugar, hay un montón de SnowFlakes, cosas que se destacan del sistema general porque tienen algo especial, por lo que las reglas generales no se aplican a ellos. Se resolvió mediante un ataque frontal: si hay alarmas, pero en realidad todo está en orden, entonces necesito profundizar más y descubrir por qué me alerta, aunque no debería ser así. O viceversa: por qué Nagios entra en pánico, pero Icinga no.
Cuarto, Nagios trabajó aquí antes que yo durante tres años e inicialmente había más confianza en él que en mi novedoso sistema hipster, por lo que cada vez que Icinga provocaba pánico, nadie hacía nada hasta que Nagios se entusiasmaba con el mismo tema. Pero muy rara vez Icinga generó verdaderas alarmas antes que Nagios y considero que esto es un problema grave, del que hablaré en la sección “Conclusiones”.
Como resultado, la puesta en servicio se retrasó más de 5 meses (prevista para el 28 de junio de 2018, de hecho, el 3 de diciembre de 2018), principalmente debido al "control de paridad", esa mierda cuando hay varios servicios en Nagios que nadie conoce. No he escuchado nada en los últimos años, pero AHORA MISMO dieron críticos sin motivo y tuve que explicar por qué no estaban en mi panel y tuve que agregarlos a Icinga para que "el control de paridad sea completo" (Todos los servicios/controles en Nagios corresponden a los servicios/controles en Icinga)
Implementación:
La primera es la guerra Código vs Datos, como Puppet Style. Todos los datos, absolutamente todo, debe estar en Hiera y nada más. Todo el código está en archivos .pp. Variables, abstracciones, funciones: todo va en las págs.
Como resultado, tenemos un montón de máquinas virtuales (165 en el momento de escribir este artículo) y 68 aplicaciones web que deben ser monitoreadas para determinar el rendimiento y la validez de los certificados SSL. Pero debido a las hemorroides históricas, la información para monitorear las aplicaciones se toma de un repositorio de gitlab separado y el formato de los datos no ha cambiado desde Puppet 3, lo que crea una complejidad adicional en la configuración.
Código títere para aplicaciones, protege tus ojos
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
}
}
}
}
}
}
}
}El código de configuración para hosts y servicios también parece terrible:
monitoreo/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',
}
}Todavía estoy trabajando en estos fideos y mejorándolos lo mejor que puedo. Sin embargo, fue este código el que nos permitió utilizar una sintaxis simple y comprensible en Hiera:
Datos
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'Todas las comprobaciones se dividen en grupos, cada grupo tiene configuraciones predeterminadas, como dónde y con qué frecuencia ejecutar estas comprobaciones, qué notificaciones enviar y a quién.
En cada verificación, puede anular cualquier opción y, en última instancia, todo esto se suma a la configuración predeterminada de todas las comprobaciones en su conjunto. Es por eso que estas tonterías están escritas en config.pp: fusiona todas las configuraciones predeterminadas con la configuración del grupo y luego con cada verificación individual.
Otro cambio muy importante fue la posibilidad de utilizar funciones en la configuración, por ejemplo, la función de reemplazar el puerto, la dirección y la URL para verificar 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: trueEsto significa: si hay una variable en la definición del host puerto_http — úselo; de lo contrario, 443. Por ejemplo, la interfaz web de Jabber se bloquea en 9090 y Unifi se bloquea en 7443.
http_vhost significa ignorar DNS y tomar esta dirección.
Si el uri está especificado en el host, sígalo; de lo contrario, utilice "/".
Surgió una historia divertida con http_ssl: esta infección no quería desconectarse a pedido. Estuve confundido acerca de esta línea durante mucho tiempo hasta que me di cuenta de que la variable en la definición del host es:
http_ssl: falseSustituido en expresión
if(host.vars.http_ssl) { return false } else { return true }cómo false y al final resulta
if(false) { return false } else { return true }es decir, la verificación ssl siempre está activa. Lo resolví reemplazando la sintaxis:
http_ssl: noHallazgos:
Pros:
- Ahora tenemos un sistema de seguimiento, y no dos, como hemos tenido durante los últimos siete u ocho meses, o uno que está desactualizado y es vulnerable.
- La estructura de datos de hosts/servicios (verificaciones) ahora es (en mi opinión) mucho más legible y comprensible. Para otros, esto resultó no ser tan obvio, así que tuve que publicar un par de páginas en la wiki local para explicar cómo funciona todo y qué editar y dónde.
- Es posible configurar comprobaciones de forma flexible utilizando variables y funciones, por ejemplo, para comprobar http_regexp, el patrón deseado, el código de retorno, la URL y el puerto se pueden configurar en la configuración del host.
- Hay varios paneles, para cada uno de los cuales puede definir su propia lista de alarmas mostradas y administrar todo esto a través de Puppet y solicitudes de combinación.
Contras:
- Inercia de los miembros del equipo: Nagios trabajó, trabajó y trabajó, y este Isinga tuyo tiene constantemente errores y es lento. ¿Cómo puedes ver la historia aquí? Oh, maldita sea, no está actualizado... (El verdadero problema es que el historial de alarmas no se actualiza automáticamente, sólo con F5)
- La inercia del sistema, cuando hago clic en "actualizar" (verificar ahora) en la interfaz web, el resultado de la ejecución depende del clima en Marte, especialmente en servicios complejos que requieren decenas de segundos para ejecutarse. Este resultado es normal.

- En general, según las estadísticas semestrales de funcionamiento de los dos sistemas uno al lado del otro, Nagios siempre funcionó más rápido que Icinga y esto realmente me molestó. Me parece que se equivocaron en algo con los cronómetros y el chequeo se hace cada cinco minutos, de hecho pasa cada 5:30 o algo así.
- Si reinicia el servicio en cualquier momento (systemctl restart icinga2), todas las comprobaciones que estaban en curso en ese momento generarán una alarma crítica. en la pantalla y desde fuera parece como si todo se hubiera caído ().
Pero en general funciona.
Fuente: habr.com

