Olá a todos.
Sou administrador de sistema Linux, mudei da Rússia para a Austrália com um visto profissional independente em 2015, mas o artigo não será sobre como ligar um trator para um porco. Já existem artigos suficientes (no entanto, se houver interesse, escreverei sobre isso também), então gostaria de falar sobre como, em meu trabalho na Austrália como engenheiro de operações Linux, iniciei a migração de um monitoramento de sistema para outro. Especificamente - Nagios => Icinga2.
O artigo é em parte técnico e em parte sobre a comunicação com as pessoas e os problemas associados às diferenças de cultura e métodos de trabalho.
Infelizmente, a tag “code” não destaca o código Puppet e yaml, então tive que usar “texto simples”.
Não houve sinais de problemas na manhã de 21 de dezembro de 2016. Como sempre, eu estava lendo Habr de um usuário anônimo não registrado na primeira meia hora da jornada de trabalho, enquanto tomava café, e me deparei .
Como minha empresa utilizava o Nagios, sem pensar duas vezes, criei um ticket no Redmine e enviei o link para o chat geral, pois considerei importante. A iniciativa é punível até mesmo na Austrália, então o engenheiro-chefe atribuiu o problema a mim desde que o descobri.
Captura de tela do Redmine
No nosso departamento, antes de expressar a sua opinião, é habitual oferecer pelo menos uma alternativa, mesmo que a escolha seja óbvia, por isso comecei por pesquisar no Google que tipo de sistemas de monitorização são atualmente relevantes, já que na Rússia, no meu último local de trabalho, eu tinha meu próprio sistema escrito por mim mesmo, muito primitivo, mas ainda assim bastante funcional e executando todas as tarefas que lhe eram atribuídas. Python, Politécnico de São Petersburgo e a regra do metrô. Não, o metrô é uma merda. Isto é pessoal (11 anos de trabalho) e digno de um artigo separado, mas não agora.
Um pouco sobre as regras para fazer alterações na configuração da infraestrutura da minha localização atual. Usamos o Puppet, o Gitlab e a Infraestrutura como princípio de código, então:
- Nenhuma alteração manual via SSH, alterando manualmente quaisquer arquivos nas máquinas virtuais. Ao longo dos três anos de trabalho, fui atingido muitas vezes por isso, a última vez foi há uma semana e não creio que tenha sido a última. Bem, sério - corrija uma linha na configuração, reinicie o serviço e veja se o problema foi resolvido - 10 segundos. Crie um novo branch no Gitlab, envie as alterações, espere o r10k funcionar no Puppetmaster, execute Puppet -environment=mybranch e espere mais alguns minutos até que tudo funcione - 5 minutos no mínimo.
- Quaisquer alterações são feitas criando uma solicitação de mesclagem no Gitlab e devem ser aprovadas por pelo menos um membro da equipe. Mudanças importantes decididas pelo líder da equipe exigem duas ou três aprovações.
- Todas as alterações são texto de uma forma ou de outra (já que manifestos do Puppet, scripts e dados do Hiera são texto), binários são altamente desencorajados e é necessário haver um motivo convincente para aprovar tais arquivos.
Então, as opções que considerei:
- Munin - se houver mais de 10 servidores na infraestrutura, a administração vira um inferno (de . Eu não tinha nenhum desejo particular de verificar isso, então acreditei na palavra dele).
- Zabbix - Eu já estava olhando para ele há muito tempo, na Rússia, mas era redundante para minhas tarefas. Aqui - teve que ser descartado devido ao uso do Puppet como gerenciador de configuração e do Gitlab como sistema de controle de versão. Naquela época, pelo que entendi, o Zabbix armazenava toda a configuração no banco de dados e, portanto, não estava claro como gerenciar a configuração nas condições atuais e como rastrear as alterações.
- Prometheus é o que chegaremos no final, a julgar pelo clima do departamento, mas naquela época eu não o dominava e não consegui demonstrar uma amostra realmente funcional (Prova de Conceito), então tive que recusar.
- Havia também várias outras opções que exigiam uma reformulação completa do sistema ou estavam em sua infância/abandonadas e foram rejeitadas pelo mesmo motivo.
No final, optei pelo Icinga2 por três motivos:
1 - compatibilidade com Nrpe (um serviço cliente que executa verificações de comando do Nagios). Isso foi muito importante, porque naquela época tínhamos 135 (agora são 2019 em 165) máquinas virtuais com vários serviços/verificações personalizados, e refazer tudo teria sido uma verdadeira dor de cabeça.
2 - todos os arquivos de configuração são de texto, o que facilita a edição deste assunto, criação de solicitações de mesclagem com possibilidade de ver o que foi adicionado ou excluído.
3 é um projeto OpenSource vivo e em desenvolvimento. Amamos muito o OpenSource e fazemos todas as contribuições possíveis criando Pull Requests e Issues para resolver problemas.
Então vamos lá, Icinga2.
A primeira coisa que tive que enfrentar foi a inércia dos meus colegas. Todo mundo está acostumado com Nagios/Naggios (embora mesmo aqui eles não tenham conseguido chegar a um acordo sobre como pronunciá-lo) e com a interface CheckMK. A interface do icinga parece completamente diferente (isso foi um sinal de menos), mas é possível personalizar com flexibilidade o que você precisa ver usando filtros para literalmente qualquer parâmetro (isso foi um ponto positivo, mas lutei muito por isso).
Filtros
Estime a proporção entre o tamanho da barra de rolagem e o tamanho do campo de rolagem.
Em segundo lugar, todos estão acostumados a ver toda a infraestrutura em um monitor, porque CheckMk permite trabalhar com vários hosts Nagios, mas a interface Icinga não conseguiu fazer isso (na verdade, poderia, mas mais sobre isso abaixo). A alternativa era algo chamado Thruk, mas seu design fez com que todos na equipe engasgassem, exceto um – aquele que sugeriu (não eu).
Na fornalha Thruk - a decisão unânime da equipe
Após alguns dias de brainstorming, propus a ideia de monitoramento de cluster, quando há um host mestre na zona de produção e dois escravos - um em dev/teste e um host externo localizado em outro provedor para monitorar nosso serviços do ponto de vista de um cliente ou de um observador externo. Esta configuração tornou possível ver todos os problemas em uma interface web e funcionou muito bem, mas o Puppet... O problema com o Puppet era que o host mestre agora precisava saber sobre todos os hosts e serviços/verificações no sistema e tinha para distribuí-los entre zonas (dev-test, staging-prod, ext), mas o envio de alterações por meio da API Icinga leva alguns segundos, mas a compilação do diretório Puppet de todos os serviços para todos os hosts leva alguns minutos. A culpa ainda é minha, embora já tenha explicado várias vezes como tudo funciona e por que demora tanto.
Terceiro, há vários SnowFlakes - coisas que se destacam do sistema geral porque têm algo especial, portanto as regras gerais não se aplicam a eles. Foi resolvido com um ataque frontal - se há alarmes, mas na verdade tudo está em ordem, então preciso me aprofundar e descobrir por que isso me alerta, embora não devesse. Ou vice-versa - por que Nagios entra em pânico, mas Icinga não.
Quarto, Nagios trabalhou aqui antes de mim por três anos e inicialmente havia mais confiança nele do que em meu sistema moderno e moderno, então toda vez que Icinga entrava em pânico, ninguém fazia nada até que Nagios ficasse entusiasmado com o mesmo assunto. Mas muito raramente o Icinga gerava alarmes reais antes do Nagios e considero isso um problema sério, sobre o qual falarei na seção “Conclusões”.
Como resultado, o comissionamento foi adiado por mais de 5 meses (previsto para 28 de junho de 2018, na verdade - 3 de dezembro de 2018), principalmente devido à “verificação de paridade” - aquela porcaria quando existem vários serviços no Nagios que ninguém conhece sobre não ouvi nada nos últimos anos, mas AGORA MESMO eles deram críticas sem motivo e eu tive que explicar por que eles não estavam no meu painel e tive que adicioná-los ao Icinga para que "a verificação de paridade fosse completo" (Todos os serviços/cheques no Nagios correspondem aos serviços/cheques no Icinga)
Implementação:
A primeira é a guerra Código vs Dados, como o Puppet Style. Todos os dados, absolutamente tudo, devem estar em Hiera e nada mais. Todo o código está em arquivos .pp. Variáveis, abstrações, funções – tudo vai nas pp.
Como resultado, temos um monte de máquinas virtuais (165 no momento em que este artigo foi escrito) e 68 aplicativos da web que precisam ser monitorados quanto ao desempenho e à validade dos certificados SSL. Mas devido a hemorróidas históricas, as informações para monitorar aplicativos são retiradas de um repositório gitlab separado e o formato dos dados não mudou desde o Puppet 3, o que cria complexidade adicional na configuração.
Código fantoche para aplicativos, proteja seus olhos
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
}
}
}
}
}
}
}
}O código de configuração para hosts e serviços também parece terrível:
monitoramento/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',
}
}Ainda estou trabalhando nesse macarrão e melhorando-o da melhor maneira que posso. Porém, foi esse código que nos permitiu usar uma sintaxe simples e compreensível no Hiera:
Dados
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 as verificações são divididas em grupos, cada grupo possui configurações padrão como onde e com que frequência executar essas verificações, quais notificações enviar e para quem.
Em cada verificação, você pode substituir qualquer opção e, em última análise, tudo isso resulta nas configurações padrão de todas as verificações como um todo. É por isso que esse absurdo está escrito no config.pp - ele mescla todas as configurações padrão com as configurações do grupo e depois com cada verificação individual.
Outra mudança muito importante foi a possibilidade de utilizar funções nas configurações, por exemplo, a função de substituir porta, endereço e 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: trueIsto significa - se houver uma variável na definição do host http_port - use-o, caso contrário, 443. Por exemplo, a interface da web do jabber trava em 9090 e o Unifi trava em 7443.
http_vhost significa ignorar o DNS e usar este endereço.
Se o uri for especificado no host, siga-o, caso contrário, use “/”.
Uma história engraçada surgiu com http_ssl - essa infecção não queria ser desconectada sob demanda. Fiquei confuso sobre essa linha por um longo tempo, até que me dei conta de que a variável na definição do host é:
http_ssl: falseSubstituído na expressão
if(host.vars.http_ssl) { return false } else { return true }como falso e no final acontece
if(false) { return false } else { return true }isto é, a verificação SSL está sempre ativa. Eu resolvi isso substituindo a sintaxe:
http_ssl: noDescobertas:
Prós:
- Temos agora um sistema de monitorização, e não dois, como tivemos nos últimos 7 a 8 meses, ou um que está desatualizado e vulnerável.
- A estrutura de dados hosts/services(checks) é agora (na minha opinião) muito mais legível e compreensível. Para outros, isso acabou não sendo tão óbvio, então tive que postar algumas páginas no wiki local para explicar como tudo funciona e o que editar e onde.
- É possível configurar verificações de forma flexível usando variáveis e funções, por exemplo, para verificar http_regexp, o padrão desejado, código de retorno, url e porta podem ser definidos nas configurações do host.
- Existem vários dashboards, para cada um dos quais você pode definir sua própria lista de alarmes exibidos e gerenciar tudo isso através de solicitações Puppet e merge.
Contras:
- Inércia dos membros da equipe - Nagios trabalhou, trabalhou e trabalhou, e esse seu Isinga está constantemente cheio de bugs e lento. Como você pode ver a história aqui? Ah, droga, não está atualizado... (O verdadeiro problema é que o histórico de alarmes não é atualizado automaticamente, apenas por F5)
- A inércia do sistema - quando clico em “atualizar” (confira agora) na interface web - o resultado da execução depende do clima em Marte, principalmente em serviços complexos que requerem dezenas de segundos para serem executados. Este resultado é normal.

- Em geral, de acordo com as estatísticas semestrais de operação dos dois sistemas lado a lado, o Nagios sempre funcionou mais rápido que o Icinga e isso me incomodou muito. Parece-me que bagunçaram alguma coisa com os cronômetros e a verificação é feita a cada cinco minutos, na verdade acontece a cada 5h30 ou algo parecido.
- Se você reiniciar o serviço a qualquer momento (systemctl restart icinga2) - todas as verificações que estavam em andamento naquele momento gerarão um alarme crítico na tela e por fora parece que tudo caiu ().
Mas no geral, funciona.
Fonte: habr.com

