مرحبا.
أنا مسؤول نظام لينوكس ، انتقلت من روسيا إلى أستراليا بتأشيرة مهنية مستقلة في عام 2015 ، لكن المقال لن يكون حول كيفية بدء تشغيل جرار لخنزير. يوجد بالفعل ما يكفي من هذه المقالات (مع ذلك ، إذا كان هناك اهتمام ، سأكتب عنها) ، لذلك أود أن أتحدث عن كيف بدأت ، في عملي في أستراليا كمهندس في لينكس ، الترحيل من نظام واحد مراقبة لآخر. على وجه التحديد - Nagios => Icinga2.
المقال تقني جزئيًا وجزئيًا عن التواصل مع الناس والمشاكل المرتبطة بالاختلاف في الثقافة وأساليب العمل.
لسوء الحظ ، لا تبرز علامة "الشفرة" رمز Puppet و yaml ، لذلك اضطررت إلى استخدام "نص عادي".
لم ينذر أي شيء بالمتاعب في صباح يوم 21 ديسمبر / كانون الأول 2016. كالعادة ، قرأت هبر من قبل مجهول غير مسجل في النصف ساعة الأولى من يوم العمل ، أشرب القهوة وتعثرت. .
منذ أن تم استخدام Nagios للتو في شركتي ، دون التفكير مرتين ، أنشأت تذكرة في Redmine وألقيت بالرابط في الدردشة العامة ، لأنني اعتبرته أمرًا مهمًا. هذه المبادرة يعاقب عليها حتى في أستراليا ، لذلك علق المهندس الرئيسي هذه المشكلة علي ، منذ أن اكتشفتها.
لقطة شاشة من Redmine
في قسمنا ، قبل التعبير عن رأينا ، من المعتاد تقديم بديل واحد على الأقل ، حتى لو كان الخيار واضحًا ، لذلك بدأت بالبحث على Google عن أنظمة المراقبة ذات الصلة حاليًا ، لأنه في روسيا في مكان عملي الأخير كان لدي نظام مكتوب ذاتيًا ، بدائي للغاية ، لكنه مع ذلك نظام فعال ويؤدي جميع المهام الموكلة إليه. Python وجامعة سانت بطرسبرغ للفنون التطبيقية وحكم المترو. لا ، مترو الأنفاق سيء. هذا شخصي (11 عامًا من العمل) ويستحق مقالة منفصلة ، لكن ليس الآن.
القليل عن قواعد إجراء تغييرات على تكوين البنية التحتية في موقعي الحالي. نحن نستخدم Puppet و Gitlab والبنية التحتية ككود ، لذلك:
- لا توجد تغييرات يدوية عبر SSH عن طريق تغيير أي ملفات يدويًا على الأجهزة الافتراضية. لثلاث سنوات من العمل ، تلقيت سقفًا لهذا مرات عديدة ، آخرها كان قبل أسبوع ولا أعتقد أنها كانت المرة الأخيرة. حسنًا ، في الواقع - إصلاح سطر واحد في التكوين ، وإعادة تشغيل الخدمة ومعرفة ما إذا كان قد تم حل المشكلة - 10 ثوانٍ. أنشئ فرعًا جديدًا في Gitlab ، وادفع التغييرات ، وانتظر حتى تعمل r10k على Puppetmaster ، وقم بتشغيل Puppet --environment = mybranch وانتظر بضع دقائق أخرى حتى يعمل كل شيء - 5 دقائق كحد أدنى.
- يتم إجراء أي تغييرات من خلال إنشاء طلب دمج في Gitlab ويجب أن يوافق عليها عضو واحد على الأقل في الفريق. تتطلب التغييرات الرئيسية وفقًا لتقدير قائد الفريق موافقات أو ثلاثة موافقات.
- جميع التغييرات نصية بطريقة أو بأخرى (لأن بيانات الدمى والنصوص وبيانات Hiera هي نصوص) ، لا يُنصح بشدة بالثنائيات وهناك حاجة إلى أسباب وجيهة للموافقة على هذه الملفات.
لذا فإن الخيارات التي فكرت فيها هي:
- Munin - إذا كان هناك أكثر من 10 خوادم في البنية التحتية ، فإن الإدارة تتحول إلى جحيم (من . لم تكن لدي رغبة كبيرة في التحقق من ذلك ، لذلك أخذت كلامي من أجل ذلك).
- Zabbix - لقد كنت أبحث عن كثب لفترة طويلة ، في روسيا ، ولكن بعد ذلك كانت زائدة عن الحاجة لمهامي. هنا - يجب التخلص منها بسبب استخدام Puppet كمدير تكوين و Gitlab كنظام تحكم في الإصدار. في ذلك الوقت ، بقدر ما فهمت ، يخزن Zabbix جميع التكوينات في قاعدة البيانات ، وبالتالي لم يكن من الواضح كيفية إدارة التكوين في الظروف الحالية وكيفية تتبع التغييرات.
- بروميثيوس هو ما سننتهي إليه ، بناءً على الحالة المزاجية في القسم ، لكن في ذلك الوقت لم أتقن ذلك ولم أستطع إظهار عينة عملية حقًا (إثبات المفهوم) ، لذلك اضطررت إلى الرفض.
- كان هناك أيضًا العديد من الخيارات الأخرى التي تتطلب إما إصلاحًا كاملاً للنظام ، أو كانت في مهدها / تم التخلي عنها وتم رفضها لنفس السبب.
في النهاية ، استقرت على Icinga2 لثلاثة أسباب:
1 - التوافق مع Nrpe (خدمة عميل تقوم بتشغيل عمليات التحقق من الأوامر من Nagios). كان هذا مهمًا للغاية ، لأنه في ذلك الوقت كان لدينا 135 جهازًا افتراضيًا (يوجد الآن 2019 منهم في عام 165) مع مجموعة من الخدمات / الشيكات المكتوبة ذاتيًا ، وإعادة كل هذا سيكون بمثابة بواسير شديدة.
2 - جميع ملفات التكوين عبارة عن نصوص مما يسهل تعديل هذه الحالة وإنشاء طلبات دمج مع إمكانية رؤية ما تم إضافته أو إزالته.
3 هو مشروع مفتوح المصدر مباشر ومتطور. نحن مغرمون جدًا بالمصادر المفتوحة ونقدم مساهمة مجدية لها من خلال إنشاء طلبات السحب والمشكلات لحل المشكلات.
لنبدأ ، Icinga2.
كان أول شيء كان عليّ مواجهته هو خمول زملائي. الجميع معتادون على Nagios / Nagios (على الرغم من أنهم حتى هنا لا يمكنهم الاتفاق على كيفية نطقها) وواجهة CheckMK. في icinga ، تبدو الواجهة مختلفة تمامًا (كان هذا ناقصًا) ، ولكن من الممكن تكوين ما تحتاج إلى رؤيته بمرونة باستخدام المرشحات لأي معلمة حرفيًا (كانت هذه ميزة إضافية ، لكنني ناضلت من أجلها بشكل ملحوظ).
مرشحات
تقدير نسبة حجم شريط التمرير إلى حجم حقل التمرير.
ثانيًا ، اعتاد الجميع على رؤية البنية التحتية بالكامل على شاشة واحدة ، لأن CheckMk يسمح لك بالعمل مع العديد من مضيفي Nagios ، لكن واجهة Icinga لم تعرف كيف (لقد فعلت ذلك بالفعل ، ولكن المزيد عن ذلك أدناه). كان البديل شيئًا يسمى Thruk ، لكن تصميمه جعل كل فرد في الفريق يتقيأ باستثناء الشخص الذي اقترحه (وليس أنا).
في فرن ثروك - قرار بالإجماع من الفريق
بعد يومين من العصف الذهني ، اقترحت فكرة مراقبة الكتلة ، عندما يكون هناك مضيف رئيسي واحد في منطقة الإنتاج واثنين من العبيد - أحدهما قيد التطوير / الاختبار ومضيف خارجي آخر موجود في مزود آخر من أجل مراقبة الخدمات من وجهة نظر العميل أو المراقب الخارجي. سمح لي هذا التكوين برؤية جميع المشكلات في واجهة ويب واحدة وعمل بشكل جيد ، ولكن Puppet ... كانت المشكلة في Puppet أن المضيف الرئيسي الآن يجب أن يعرف جميع المضيفين والخدمات / عمليات التحقق في النظام وكان عليه أن قم بتوزيعها بين المناطق (dev-test ، staging-prod ، ext) ، لكن دفع التغييرات عبر Icinga API يستغرق بضع ثوانٍ ، لكن تجميع دليل Puppet لجميع الخدمات لجميع المضيفين يستغرق بضع دقائق. لا يزال هذا يلومني ، على الرغم من أنني قد شرحت عدة مرات كيف يعمل كل شيء ولماذا يستغرق وقتًا طويلاً.
ثالثًا - مجموعة من SnowFlakes (رقاقات الثلج) - أشياء خرجت من النظام العام ، لأن لها شيئًا خاصًا ، لذلك لا تنطبق عليها القواعد العامة. تم حلها عن طريق هجوم أمامي - إذا كانت هناك مخاوف ، ولكن في الواقع كل شيء على ما يرام ، فنحن بحاجة إلى التعمق أكثر ومعرفة سبب تنبيهني ، على الرغم من أنه لا ينبغي. أو العكس - لماذا يشعر Nagios بالذعر ، لكن Icinga لا يفعل ذلك.
رابعًا ، عمل Nagios هنا لمدة ثلاث سنوات قبلي ، وفي البداية كان هناك ثقة به أكثر مما كانت عليه في نظام الهيبستر الجديد الخاص بي ، لذلك في كل مرة أثار فيها إيسينجا ذعرًا ، لم يفعل أحد أي شيء حتى كان Nagios متحمسًا بشأن نفس المشكلة. لكن نادرًا ما أعطى إيسينغا إنذارات حقيقية قبل Nagios وأنا أعتبر هذه مشكلة خطيرة ، والتي سأتحدث عنها في قسم الاستنتاجات.
نتيجة لذلك ، تأخر بدء التشغيل لأكثر من 5 أشهر (تم التخطيط له في 28 يونيو 2018 ، في الواقع - 3 ديسمبر 2018) ، ويرجع ذلك أساسًا إلى "فحص التكافؤ" - هذا هراء عندما يكون هناك العديد من الخدمات في Nagios ، التي لم يسمع عنها أحد شيئًا في العامين الماضيين ، ولكنهم الآن ينتقدون بدون سبب ، وكان علي أن أوضح سبب عدم وجودهم في لوحتي واضطررت إلى إضافتهم إلى Icinga إلى "اكتمل التحقق من التكافؤ "(جميع الخدمات / الشيكات في Nagios تتوافق مع الخدمات / الشيكات في Icinga)
تطبيق:
الأول هو حرب Code vs Data ، مثل Puppet Style. يجب أن تكون جميع البيانات ، هنا بشكل عام ، كل شيء ، في هييرا ولا شيء آخر. جميع الأكواد موجودة في ملفات .pp. المتغيرات والتجريدات والوظائف - كل شيء يدخل في الصفحات.
نتيجة لذلك ، لدينا مجموعة من الأجهزة الافتراضية (165 في وقت كتابة هذا التقرير) و 68 تطبيقًا على الويب يجب مراقبتها للتأكد من صحة شهادات SSL وصلاحيتها. ولكن بسبب البواسير التاريخية ، يتم أخذ معلومات مراقبة التطبيق من مستودع gitlab منفصل ولم يتغير تنسيق البيانات منذ Puppet 3 ، مما يخلق صعوبات تكوين إضافية.
رمز الدمى للتطبيقات ، ابق عينيك مقشرتين
define profiles::services::monitoring::docker_apps(
Hash $app_list,
Hash $apps_accessible_from,
Hash $apps_access_list,
Hash $webhost_defaults,
Hash $webcheck_defaults,
Hash $service_overrides,
Hash $targets,
Hash $app_checks,
)
{
#### APPS ####
$zone = $name
$app_list.each | String $app_name, Hash $app_data |
{
$notify_group = { 'notify_group' => ($webcheck_defaults[$zone]['notify_group'] + pick($app_data['notify_group'], {} )) } # adds notifications for default group (systems) + any group defined in int/pm_docker_apps.eyaml
$data = merge($webhost_defaults, $apps_accessible_from, $app_data)
$site_domain = $app_data['site_domain']
$regexp = pick($app_data['check_regex'], 'html') # Pick a regex to check
$check_url = $app_data['check_url'] ? {
undef => { 'http_uri' => '/' },
default => { 'http_uri' => $app_data['check_url'] }
}
$check_regex = $regexp ?{
'absent' => {},
default => {'http_expect_body_regex' => $regexp}
}
$site_domain.each | String $vhost, Hash $vdata | { # Split an app by domains if there are two or more
$vhost_name = {'http_vhost' => $vhost}
$vars = $data['vars'] + $vhost_name + $check_regex + $check_url
$web_ipaddress = is_array($vdata['web_ipaddress']) ? { # Make IP-address an array if it's not, because askizzy has 2 ips and it's an array
true => $vdata['web_ipaddress'],
false => [$vdata['web_ipaddress']],
}
$access_from_zones = [$zone] + $apps_access_list[$data['accessible_from']] # Merge default zone (where the app is defined) and extra zones if they exist
$web_ipaddress.each | String $ip_address | { # For each IP (if we have multiple)
$suffix = length($web_ipaddress) ? { # If we have more than one - add IP as a suffix to this hostname to avoid duplicating resources
1 => '',
default => "_${ip_address}"
}
$octets = split($ip_address, '.')
$ip_tag = "${octets[2]}.${octets[3]}" # Using last octet only causes a collision between nginx-vip 203.15.70.94 and ext. ip 49.255.194.94
$access_from_zones.each | $zone_prefix |{
$zone_target = $targets[$zone_prefix]
$nginx_vip_name = "${zone_prefix}_nginx-vip-${ip_tag}" # If it's a host for ext - prefix becomes 'ext_' (ext_nginx-vip...)
$nginx_host_vip = {
$nginx_vip_name => {
ensure => present,
target => $zone_target,
address => $ip_address,
check_command => 'hostalive',
groups => ['nginx_vip',],
}
}
$ssl_vars = $app_checks['ssl']
$regex_vars = $app_checks['http'] + $vars + $webcheck_defaults[$zone] + $notify_group
if !defined( Profiles::Services::Monitoring::Host[$nginx_vip_name] ) {
ensure_resources('profiles::services::monitoring::host', $nginx_host_vip)
}
if !defined( Icinga2::Object::Service["${nginx_vip_name}_ssl"] ) {
icinga2::object::service {"${nginx_vip_name}_ssl":
ensure => $data['ensure'],
assign => ["host.name == $nginx_vip_name",],
groups => ['webchecks',],
check_command => 'ssl',
check_interval => $service_overrides['ssl']['check_interval'],
target => $targets['services'],
apply => true,
vars => $ssl_vars
}
}
if $regexp != 'absent'{
if !defined(Icinga2::Object::Service["${vhost}${$suffix} regex"]){
icinga2::object::service {"${vhost}${$suffix} regex":
ensure => $data['ensure'],
assign => ["match(*_nginx-vip-${ip_tag}, host.name)",],
groups => ['webchecks',],
check_command => 'http',
check_interval => $service_overrides['regex']['check_interval'],
target => $targets['services'],
enable_flapping => true,
apply => true,
vars => $regex_vars
}
}
}
}
}
}
}
}يبدو رمز تكوين المضيفين والخدمات رهيباً أيضًا:
المراقبة / config.pp
class profiles::services::monitoring::config(
Array $default_config,
Array $hostgroups,
Hash $hosts = {},
Hash $host_defaults,
Hash $services,
Hash $service_defaults,
Hash $service_overrides,
Hash $webcheck_defaults,
Hash $servicegroups,
String $servicegroup_target,
Hash $user_defaults,
Hash $users,
Hash $oncall,
Hash $usergroup_defaults,
Hash $usergroups,
Hash $notifications,
Hash $notification_defaults,
Hash $notification_commands,
Hash $timeperiods,
Hash $webhost_defaults,
Hash $apps_access_list,
Hash $check_commands,
Hash $hosts_api = {},
Hash $targets = {},
Hash $host_api_defaults = {},
)
{
# Profiles::Services::Monitoring::Hostgroup <<| |>> # will be enabled when we move to icinga completely
#### APPS ####
case $location {
'int', 'ext': {
$apps_by_zone = {}
}
'pm': {
$int_apps = hiera('int_docker_apps')
$int_app_defaults = hiera('int_docker_app_common')
$st_apps = hiera('staging_docker_apps')
$srs_apps = hiera('pm_docker_apps_srs')
$pm_apps = hiera('pm_docker_apps') + $st_apps + $srs_apps
$pm_app_defaults = hiera('pm_docker_app_common')
$apps_by_zone = {
'int' => $int_apps,
'pm' => $pm_apps,
}
$app_access_by_zone = {
'int' => {'accessible_from' => $int_app_defaults['accessible_from']},
'pm' => {'accessible_from' => $pm_app_defaults['accessible_from']},
}
}
default: {
fail('Please ensure the node has $location fact set (int, pm, ext)')
}
}
file { '/etc/icinga2/conf.d/':
ensure => directory,
recurse => true,
purge => true,
owner => 'icinga',
group => 'icinga',
mode => '0750',
notify => Service['icinga2'],
}
$default_config.each | String $file_name |{
file {"/etc/icinga2/conf.d/${file_name}":
ensure => present,
source => "puppet:///modules/profiles/services/monitoring/default_config/${file_name}",
owner => 'icinga',
group => 'icinga',
mode => '0640',
}
}
$app_checks = {
'ssl' => $services['webchecks']['checks']['ssl']['vars'],
'http' => $services['webchecks']['checks']['http_regexp']['vars']
}
$apps_by_zone.each | String $zone, Hash $app_list | {
profiles::services::monitoring::docker_apps{$zone:
app_list => $app_list,
apps_accessible_from => $app_access_by_zone[$zone],
apps_access_list => $apps_access_list,
webhost_defaults => $webhost_defaults,
webcheck_defaults => $webcheck_defaults,
service_overrides => $service_overrides,
targets => $targets,
app_checks => $app_checks,
}
}
#### HOSTS ####
# Profiles::Services::Monitoring::Host <<| |>> # This is for spaceship invasion when it's ready.
$hosts_has_large_disks = query_nodes('mountpoints.*.size_bytes >= 1099511627776')
$hosts.each | String $hostgroup, Hash $list_of_hosts_with_settings | { # Splitting site lists by hostgroups - docker_host/gluster_host/etc
$list_of_hosts_in_group = $list_of_hosts_with_settings['hosts']
$hostgroup_settings = $list_of_hosts_with_settings['settings']
$merged_hostgroup_settings = deep_merge($host_defaults, $list_of_hosts_with_settings['settings'])
$list_of_hosts_in_group.each | String $host_name, Hash $host_settings |{ # Splitting grouplists by hosts
# Is this host in the array $hosts_has_large_disks ? If so set host.vars.has_large_disks
if ( $hosts_has_large_disks.reduce(false) | $found, $value| { ( $value =~ "^${host_name}" ) or $found } ) {
$vars_has_large_disks = { 'has_large_disks' => true }
} else {
$vars_has_large_disks = {}
}
$host_data = deep_merge($merged_hostgroup_settings, $host_settings)
$hostgroup_settings_vars = pick($hostgroup_settings['vars'], {})
$host_settings_vars = pick($host_settings['vars'], {})
$host_notify_group = delete_undef_values($host_defaults['vars']['notify_group'] + $hostgroup_settings_vars['notify_group'] + $host_settings_vars['notify_group'])
$host_data_vars = delete_undef_values(deep_merge($host_data['vars'] , {'notify_group' => $host_notify_group}, $vars_has_large_disks)) # Merging vars separately
$hostgroups = delete_undef_values([$hostgroup] + $host_data['groups'])
profiles::services::monitoring::host{$host_name:
ensure => $host_data['ensure'],
display_name => $host_data['display_name'],
address => $host_data['address'],
groups => $hostgroups,
target => $host_data['target'],
check_command => $host_data['check_command'],
check_interval => $host_data['check_interval'],
max_check_attempts => $host_data['max_check_attempts'],
vars => $host_data_vars,
template => $host_data['template'],
}
}
}
if !empty($hosts_api){ # All hosts managed by API
$hosts_api.each | String $zone, Hash $hosts_api_zone | { # Split api hosts by zones
$hosts_api_zone.each | String $hostgroup, Hash $list_of_hosts_with_settings | { # Splitting site lists by hostgroups - docker_host/gluster_host/etc
$list_of_hosts_in_group = $list_of_hosts_with_settings['hosts']
$hostgroup_settings = $list_of_hosts_with_settings['settings']
$merged_hostgroup_settings = deep_merge($host_api_defaults, $list_of_hosts_with_settings['settings'])
$list_of_hosts_in_group.each | String $host_name, Hash $host_settings |{ # Splitting grouplists by hosts
# Is this host in the array $hosts_has_large_disks ? If so set host.vars.has_large_disks
if ( $hosts_has_large_disks.reduce(false) | $found, $value| { ( $value =~ "^${host_name}" ) or $found } ) {
$vars_has_large_disks = { 'has_large_disks' => true }
} else {
$vars_has_large_disks = {}
}
$host_data = deep_merge($merged_hostgroup_settings, $host_settings)
$hostgroup_settings_vars = pick($hostgroup_settings['vars'], {})
$host_settings_vars = pick($host_settings['vars'], {})
$host_api_notify_group = delete_undef_values($host_defaults['vars']['notify_group'] + $hostgroup_settings_vars['notify_group'] + $host_settings_vars['notify_group'])
$host_data_vars = delete_undef_values(deep_merge($host_data['vars'] , {'notify_group' => $host_api_notify_group}, $vars_has_large_disks))
$hostgroups = delete_undef_values([$hostgroup] + $host_data['groups'])
if defined(Profiles::Services::Monitoring::Host[$host_name]){
$hostname = "${host_name}_from_${zone}"
}
else
{
$hostname = $host_name
}
profiles::services::monitoring::host{$hostname:
ensure => $host_data['ensure'],
display_name => $host_data['display_name'],
address => $host_data['address'],
groups => $hostgroups,
target => "${host_data['target_base']}/${zone}/hosts.conf",
check_command => $host_data['check_command'],
check_interval => $host_data['check_interval'],
max_check_attempts => $host_data['max_check_attempts'],
vars => $host_data_vars,
template => $host_data['template'],
}
}
}
}
}
#### END OF HOSTS ####
#### SERVICES ####
$services.each | String $service_group, Hash $s_list |{ # Service_group and list of services in that group
$service_list = $s_list['checks'] # List of actual checks, separately from SG settings
$service_list.each | String $service_name, Hash $data |{
$merged_defaults = merge($service_defaults, $s_list['settings']) # global service defaults + service group defaults
$merged_data = merge($merged_defaults, $data)
$settings_vars = pick($s_list['settings']['vars'], {})
$this_service_vars = pick($data['vars'], {})
$all_service_vars = delete_undef_values($service_defaults['vars'] + $settings_vars + $this_service_vars)
# If we override default check_timeout, but not nrpe_timeout, make nrpe_timeout the same as check_timeout
if ( $merged_data['check_timeout'] and ! $this_service_vars['nrpe_timeout'] ) {
# NB: Icinga will convert 1m to 60 automatically!
$nrpe = { 'nrpe_timeout' => $merged_data['check_timeout'] }
} else {
$nrpe = {}
}
# By default we use nrpe and all commands are run via nrpe. So vars.nrpe_command = $service_name is a default value
# If it's server-side Icinga command - we don't need 'nrpe_command'
# but there is no harm to have that var and the code is shorter
if $merged_data['check_command'] == 'nrpe'{
$check_command = $merged_data['vars']['nrpe_command'] ? {
undef => { 'nrpe_command' => $service_name },
default => { 'nrpe_command' => $merged_data['vars']['nrpe_command'] }
}
}else{
$check_command = {}
}
# Assembling $vars from Global Default service settings, servicegroup settings, this particular check settings and let's not forget nrpe settings.
if $all_service_vars['graphite_template'] {
$graphite_template = {'check_command' => $all_service_vars['graphite_template']}
}else{
$graphite_template = {'check_command' => $service_name}
}
$service_notify = [] + pick($settings_vars['notify_group'], []) + pick($this_service_vars['notify_group'], []) # pick is required everywhere, otherwise becomes "The value '' cannot be converted to Numeric"
$service_notify_group = $service_notify ? {
[] => $service_defaults['vars']['notify_group'],
default => $service_notify
} # Assing default group (systems) if no other groups are defined
$vars = $all_service_vars + $nrpe + $check_command + $graphite_template + {'notify_group' => $service_notify_group}
# This needs to be merged separately, because merging it as part of MERGED_DATA overwrites arrays instead of merging them, so we lose some "assign" and "ignore" values
$assign = delete_undef_values($service_defaults['assign'] + $s_list['settings']['assign'] + $data['assign'])
$ignore = delete_undef_values($service_defaults['ignore'] + $s_list['settings']['ignore'] + $data['ignore'])
icinga2::object::service {$service_name:
ensure => $merged_data['ensure'],
apply => $merged_data['apply'],
enable_flapping => $merged_data['enable_flapping'],
assign => $assign,
ignore => $ignore,
groups => [$service_group],
check_command => $merged_data['check_command'],
check_interval => $merged_data['check_interval'],
check_timeout => $merged_data['check_timeout'],
check_period => $merged_data['check_period'],
display_name => $merged_data['display_name'],
event_command => $merged_data['event_command'],
retry_interval => $merged_data['retry_interval'],
max_check_attempts => $merged_data['max_check_attempts'],
target => $merged_data['target'],
vars => $vars,
template => $merged_data['template'],
}
}
}
#### END OF SERVICES ####
#### OTHER BORING STUFF ####
$servicegroups.each | $servicegroup, $description |{
icinga2::object::servicegroup{ $servicegroup:
target => $servicegroup_target,
display_name => $description
}
}
$hostgroups.each| String $hostgroup |{
profiles::services::monitoring::hostgroup { $hostgroup:}
}
$notifications.each | String $name, Hash $settings |{
$assign = pick($notification_defaults['assign'], []) + $settings['assign']
$ignore = pick($notification_defaults['ignore'], []) + $settings['ignore']
$merged_settings = $settings + $notification_defaults
icinga2::object::notification{$name:
target => $merged_settings['target'],
apply => $merged_settings['apply'],
apply_target => $merged_settings['apply_target'],
command => $merged_settings['command'],
interval => $merged_settings['interval'],
states => $merged_settings['states'],
types => $merged_settings['types'],
assign => delete_undef_values($assign),
ignore => delete_undef_values($ignore),
user_groups => $merged_settings['user_groups'],
period => $merged_settings['period'],
vars => $merged_settings['vars'],
}
}
# Merging notification settings for users with other settings
$users_oncall = deep_merge($users, $oncall)
# Magic. Do not touch.
create_resources('icinga2::object::user', $users_oncall, $user_defaults)
create_resources('icinga2::object::usergroup', $usergroups, $usergroup_defaults)
create_resources('icinga2::object::timeperiod',$timeperiods)
create_resources('icinga2::object::checkcommand', $check_commands)
create_resources('icinga2::object::notificationcommand', $notification_commands)
profiles::services::sudoers { 'icinga_runs_ping_l2':
ensure => present,
sudoersd_template => 'profiles/os/redhat/centos7/sudoers/icinga.erb',
}
}ما زلت أعمل على هذه المعكرونة وتحسينها قدر الإمكان. ومع ذلك ، كان هذا الرمز هو الذي جعل من الممكن استخدام بناء جملة بسيط ومفهوم في Hiera:
معطيات
profiles::services::monitoring::config::services:
perf_checks:
settings:
check_interval: '2m'
assign:
- 'host.vars.type == linux'
checks:
procs: {}
load: {}
memory: {}
disk:
check_interval: '5m'
vars:
notification_period: '24x7'
disk_iops:
vars:
notifications:
- 'silent'
cpu:
vars:
notifications:
- 'silent'
dns_fqdn:
check_interval: '15m'
ignore:
- 'xenserver in host.groups'
vars:
notifications:
- 'silent'
iftraffic_nrpe:
vars:
notifications:
- 'silent'
logging:
settings:
assign:
- 'logserver in host.groups'
checks:
rsyslog: {}
nginx_limit_req_other: {}
nginx_limit_req_s2s: {}
nginx_limit_req_s2x: {}
nginx_limit_req_srs: {}
logstash: {}
logstash_api:
vars:
notifications:
- 'silent'يتم تقسيم جميع عمليات التحقق إلى مجموعات ، ولكل مجموعة إعدادات عرض افتراضية حيث وكم مرة يتم تشغيل هذه الفحوصات والإشعارات التي يجب إرسالها ولمن.
في كل فحص ، يمكنك تجاوز أي خيار ، وكل هذا يتضافر في النهاية مع الإعدادات الافتراضية لجميع الشيكات بشكل عام. لذلك ، تتم كتابة هذه المعكرونة في config.pp - هناك دمج لجميع الإعدادات الافتراضية مع إعدادات المجموعة ثم مع كل فحص فردي.
أيضًا ، كان التغيير المهم للغاية هو القدرة على استخدام الوظائف في الإعدادات ، على سبيل المثال ، وظيفة تغيير المنفذ والعنوان وعنوان url للتحقق من http_regex.
http_regexp:
assign:
- 'host.vars.http_regex'
- 'static_sites in host.groups'
check_command: 'http'
check_interval: '1m'
retry_interval: '20s'
max_check_attempts: 6
http_port: '{{ if(host.vars.http_port) { return host.vars.http_port } else { return 443 } }}'
vars:
notification_period: 'host.vars.notification_period'
http_vhost: '{{ if(host.vars.http_vhost) { return host.vars.http_vhost } else { return host.name } }}'
http_ssl: '{{ if(host.vars.http_ssl) { return false } else { return true } }}'
http_expect_body_regex: 'host.vars.http_regex'
http_uri: '{{ if(host.vars.http_uri) { return host.vars.http_uri } else { return "/" } }}'
http_onredirect: 'follow'
http_warn_time: 8
http_critical_time: 15
http_timeout: 30
http_sni: trueهذا يعني - إذا كان هناك متغير في تعريف المضيف http_port - استخدمه ، وإلا 443. على سبيل المثال ، واجهة الويب jabber معلقة على 9090 ، و Unifi - على 7443.
http_vhost يعني تجاهل DNS واتخاذ هذا العنوان.
إذا تم تحديد uri في المضيف ، فاتبعه ، وإلا فاخذ "/".
ظهرت قصة مضحكة مع http_ssl - لم ترغب هذه العدوى في إيقافها عند الطلب. لقد كنت غبيًا في هذا السطر لفترة طويلة ، حتى اتضح لي أنه في متغير في تعريف المضيف:
http_ssl: falseاستبدال في التعبير
if(host.vars.http_ssl) { return false } else { return true }كيف زائف وفي النهاية اتضح
if(false) { return false } else { return true }وهذا يعني أن فحص SSL نشط دائمًا. تم حلها عن طريق تغيير بناء الجملة:
http_ssl: noالنتائج:
الايجابيات:
- لدينا الآن نظام مراقبة واحد ، وليس نظامين ، كما كان في الأشهر السبعة أو الثمانية الماضية ، أو نظام واحد ، عفا عليه الزمن وعرضة للخطر.
- أصبح هيكل بيانات المضيفين / الخدمات (يتحقق) الآن (في رأيي) أكثر قابلية للقراءة والفهم. بالنسبة للآخرين ، اتضح أنه ليس واضحًا جدًا ، لذلك اضطررت إلى تقديم صفحتين على موقع wiki المحلي لشرح كيفية عمل كل شيء وما الذي يجب تحريره في المكان.
- من الممكن تكوين الشيكات بمرونة باستخدام المتغيرات والوظائف ، على سبيل المثال ، للتحقق من http_regexp ، يمكن تعيين النمط المطلوب ورمز الإرجاع وعنوان url والمنفذ في إعدادات المضيف.
- هناك العديد من اللوحات (لوحات المعلومات) ، لكل منها يمكنك تحديد قائمة الإنذارات المعروضة الخاصة بك وإدارة كل ذلك من خلال طلبات الدمى والدمج.
سلبيات:
- القصور الذاتي لأعضاء الفريق - لقد عمل Nagios وعمل وعمل ، وهذه Isinga لك هي عربات التي تجرها الدواب وبطيئة باستمرار. كيف يمكنك رؤية التاريخ؟ أوه ، اللعنة ، لا يتم تحديثه ... (المشكلة الحقيقية هي أن سجل التنبيه لا يتم تحديثه تلقائيًا ، فقط بواسطة F5)
- قصور النظام - عند النقر فوق "تحديث" (تحقق الآن) في واجهة الويب - تعتمد نتيجة التنفيذ على الطقس على كوكب المريخ ، خاصة على الخدمات المعقدة التي تستغرق عشرات الثواني لإكمالها. هذا النوع من النتائج طبيعي.

- بشكل عام ، وفقًا للإحصاءات نصف السنوية لعمل نظامين جنبًا إلى جنب ، كان Nagios يعمل دائمًا بشكل أسرع من Icinga ، وقد أزعجني هذا كثيرًا. يبدو لي أن شيئًا ما تم العبث به مع أجهزة ضبط الوقت وأن الشيك كل خمس دقائق في الواقع يذهب مرة واحدة في الساعة 5:30 أو شيء من هذا القبيل.
- إذا أعدت تشغيل الخدمة في أي وقت (إعادة تشغيل systemctl icinga2) ، فإن جميع عمليات التحقق التي كانت قيد التقدم في ذلك الوقت ستولد إنذارًا بالغ الأهمية على الشاشة ومن الجانب يبدو أن كل شيء قد سقط ().
لكن بشكل عام ، إنها تعمل.
المصدر: www.habr.com

