> Вводная часть (со ссылками на все статьи)
Сложные системы (распределённые/крупные/со сложной логикой/сложной системой данных) – как живой организм: подвижный, изменчивый и самостоятельный. Всё это требует постоянного контроля со стороны разработчиков/администраторов/DevOps-инженеров.
К этому выводу я пришёл, когда система несколько раз «загибалась» в ходе её разработки, настройки сервера и эксплуатации. Это натолкнуло меня на мысль, что мониторинг должен осуществляться не только на этапе производственной эксплуатации, но и на этапе разработки.
Обо всём по порядку…
Когда я пришёл к выводу о необходимости мониторинга за проектом (как минимум серверной части) я решил, что идеальным вариантом для этого будет схема: «коллектор данных > TSDB > веб-клиент для отображения данных».
На текущий момент очень много статей посвящённых настройке Graphite в качестве TSDB, я же выбрал более современное и legacy-free решение на базе InfluxDB. Об InfluxDB уже писалось на Хабре в блоге компании Selectel. Не хочу копировать чужой текст, единственное могу сказать, что часть информации уже не соответствует действительности, но основа всё так же верна – система производительна, гибка, доступна для работы для разных языков и поддерживает разные протоколы других TSDB и агентов. Graphite же меня отпугнул наличием нескольких связанных друг с другом демонов, написанных на Python (излишняя сложность и дополнительные компоненты).
Выбор веб-клиента для отображения данных был сделан давно (видел его в действии давненько и всегда хотел использовать в своём проекте). Вот несколько скриншотов с моего проекта:
Особенностями grafana являются:
И ссылка на готовые dashboard'ы — есть чудесная возможность посмотреть как другие формируют графики и подчерпнуть что-то для себя (полезное и/или красивое). Я подчерпнул :)
Клиент, идущий в комплекте с самим InfluxDB – Chronograf пока не настолько хорош в части функционала.
Основным источником отображения данных являются time-series данные из InfluxDB, а в неё они попадают из 2-х источников: демона collectd и java-библиотеки «com.github.davidb:metrics-influxdb».
Collectd — демон, написанный на C, способный передавать данные своему сетевому аналогу, протокол которого InfluxDB и может эмулировать. «Из коробки» он может собирать достаточно большое количество метрик по серверному окружению и по части сервисов, возможность расширения достигается посредством модулей, написанных на python или Java.
Функционал collectd по сбору информации о функционировании сервера (физического и виртуального) меня вполне устраивает, однако дополнительные настройки требуемые для сбора данных со сторонних сервисов (в моём случае Elsticsearch, MongoDB и Apache Storm) достаточно нетривиальны и не всегда функциональны в полном объёме (например для Elsticsearch не корректно собирается информация по скорости выполнения запроса при наличии нескольких shard на разных репликах). Скорее всего следует посмотреть в сторону родного клиент InfluxDB — Telegraf.
Указанная библиотека фактически является адаптером для широко известной java-библиотеки метрик Metrics. Поддерживает протокол версии 0.9 для InfluxDB и позволяет передавать необходимую информацию в полном объёме.
Инициализация осуществляется примерно так:
В дальнейшем используется обычный Metrics API, что позволяет повысить прозрачность того что и как делается в моём ПО в любой момент времени.
Указанная система позволила не только следить за работой системы в рабочем режиме, но и отслеживать изменения, вызванные внесенными изменениями – как увеличилось потребление памяти, скорость обработки, объем данных и т.д. Сейчас, в принципе, страничка с общим дашбоардом является закладкой на кухонном ноутбуке и утренний завтрак всегда сопровождается просмотром событий за последние 9 часов.
Спасибо за внимание!
Сложные системы (распределённые/крупные/со сложной логикой/сложной системой данных) – как живой организм: подвижный, изменчивый и самостоятельный. Всё это требует постоянного контроля со стороны разработчиков/администраторов/DevOps-инженеров.
К этому выводу я пришёл, когда система несколько раз «загибалась» в ходе её разработки, настройки сервера и эксплуатации. Это натолкнуло меня на мысль, что мониторинг должен осуществляться не только на этапе производственной эксплуатации, но и на этапе разработки.
Обо всём по порядку…
Когда я пришёл к выводу о необходимости мониторинга за проектом (как минимум серверной части) я решил, что идеальным вариантом для этого будет схема: «коллектор данных > TSDB > веб-клиент для отображения данных».
Выбор TSDB
На текущий момент очень много статей посвящённых настройке Graphite в качестве TSDB, я же выбрал более современное и legacy-free решение на базе InfluxDB. Об InfluxDB уже писалось на Хабре в блоге компании Selectel. Не хочу копировать чужой текст, единственное могу сказать, что часть информации уже не соответствует действительности, но основа всё так же верна – система производительна, гибка, доступна для работы для разных языков и поддерживает разные протоколы других TSDB и агентов. Graphite же меня отпугнул наличием нескольких связанных друг с другом демонов, написанных на Python (излишняя сложность и дополнительные компоненты).
Puppet-скрипт для установки и настройки InfluxDB
class storyline_infra::influxdb () {
include stdlib
$params = lookup({"name" => "storyline_infra.influxdb",
"merge" => {"strategy" => "deep"}})
$port_http = $params['port_http']
$port_rpc = $params['port_rpc']
$pid_file = $params['pid_file']
$init_script = $params['init_script']
$dir_data = $params['dir_data']
$dir_logs = $params['dir_logs']
$enabled_auth = $params['enabled_auth']
$enabled_startup = $params['enabled_startup']
$enabled_running = $params['enabled_running']
$version = $params['version']
$dist_name = $facts['os']['name']
user { 'influxdb':
ensure => "present",
managehome => true,
}
exec { "influxdb-mkdir":
command => "/bin/mkdir -p /data/db && /bin/mkdir -p /data/logs",
cwd => "/",
unless => '/usr/bin/test -d /data/db -a -d /data/logs',
} ->
# working dir
file { $dir_logs:
ensure => "directory",
recurse => "true",
owner => "influxdb",
group=> "influxdb",
require => Exec['influxdb-mkdir'],
}
file { $dir_data:
ensure => "directory",
recurse => "true",
owner => "influxdb",
group=> "influxdb",
require => Exec['influxdb-mkdir'],
}
# see by "gpg --verify keyfile"
apt::key { 'influxdb-key':
id => '05CE15085FC09D18E99EFB22684A14CF2582E0C5',
source => 'https://repos.influxdata.com/influxdb.key',
} ->
# echo "deb https://repos.influxdata.com/${DISTRIB_ID,,} ${DISTRIB_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/influxdb.list
apt::source { 'influxdb-repo':
comment => 'influxdb repo',
location => "https://repos.influxdata.com/${downcase($dist_name)}",
release => "${facts['os']['distro']['codename']}",
repos => 'stable',
include => {
'deb' => true,
},
} ->
package { 'influxdb':
ensure => $version,
} ->
file { "/etc/influxdb/influxdb.conf":
replace => true,
content => epp('storyline_infra/influxdb.epp'),
owner => "influxdb",
group=> "influxdb",
notify => Service['influxdb'],
}->
file { $init_script:
replace => true,
content => epp('storyline_infra/influxdb_startup.epp'),
mode=>"ug=rwx,o=r",
notify => Service['influxdb'],
}->
service { 'influxdb':
ensure => $enabled_running,
enable => $enabled_startup,
start => "${init_script} start",
stop => "${init_script} stop",
status => "${init_script} status",
restart => "${init_script} restart",
hasrestart => true,
hasstatus => true,
}
if $enabled_startup != true {
exec { "disable_influxdb":
command => "/bin/systemctl disable influxdb",
cwd => "/",
}
}
logrotate::rule { 'influxdb':
path => "${dir_logs}/*.log",
rotate => 10,
missingok => true,
copytruncate => true,
dateext => true,
size => '10M',
rotate_every => 'day',
}
}
Grafana
Выбор веб-клиента для отображения данных был сделан давно (видел его в действии давненько и всегда хотел использовать в своём проекте). Вот несколько скриншотов с моего проекта:
Особенностями grafana являются:
- Приятный внешний вид
- Динамичное обновление всех данных
- Визуальный конструктор
- Подключение большого количества типов источников данных (Graphite, InfluxDB, Prometheus, Elasticsearch…)
- Множество способов аутентификации
- Возможность отправки Alert`ов ( Slack, PagerDuty, VictorOps, OpsGenie...)
- Большое количество plugin`ов для расширения функционала
И ссылка на готовые dashboard'ы — есть чудесная возможность посмотреть как другие формируют графики и подчерпнуть что-то для себя (полезное и/или красивое). Я подчерпнул :)
Клиент, идущий в комплекте с самим InfluxDB – Chronograf пока не настолько хорош в части функционала.
Puppet-скрипт для установки и настройки Grafana
class storyline_infra::grafana () {
include stdlib
$params = lookup({"name" => "storyline_infra.grafana",
"merge" => {"strategy" => "deep"}})
$port = $params['port']
$pid_file = $params['pid_file']
$init_script = $params['init_script']
$dir_data = $params['dir_data']
$dir_logs = $params['dir_logs']
$enabled_startup = $params['enabled_startup']
$enabled_running = $params['enabled_running']
$version = $params['version']
user { 'grafana':
ensure => "present",
managehome => true,
}
exec { "grafana-mkdir":
command => "/bin/mkdir -p /data/db && /bin/mkdir -p /data/logs",
cwd => "/",
unless => '/usr/bin/test -d /data/db -a -d /data/logs',
} ->
# working dir
file { $dir_logs:
ensure => "directory",
recurse => "true",
owner => "grafana",
group=> "grafana",
require => Exec['grafana-mkdir'],
}
file { $dir_data:
ensure => "directory",
recurse => "true",
owner => "grafana",
group=> "grafana",
require => Exec['grafana-mkdir'],
}
# see by "gpg --verify keyfile"
apt::key { 'grafana-key':
id => '418A7F2FB0E1E6E7EABF6FE8C2E73424D59097AB',
source => 'https://packagecloud.io/gpg.key',
} ->
# deb https://packagecloud.io/grafana/stable/debian/ jessie main
apt::source { 'grafana-repo':
comment => 'grafana repo',
location => "https://packagecloud.io/grafana/stable/debian/",
release => "jessie",
repos => 'main',
include => {
'deb' => true,
},
} ->
package { 'grafana':
ensure => 'present',
}
file { '/etc/init.d/grafana-server':
ensure => 'absent',
} ->
file { '/etc/grafana':
ensure => "directory",
} ->
file { "/etc/grafana/grafana.ini":
replace => true,
content => epp('storyline_infra/grafana.epp'),
owner => "grafana",
group=> "grafana",
notify => Service['grafana'],
} ->
file { $init_script:
replace => true,
content => epp('storyline_infra/grafana_startup.epp'),
mode=>"ug=rwx,o=r",
notify => Service['grafana'],
}->
service { 'grafana':
ensure => $enabled_running,
enable => $enabled_startup,
start => "${init_script} start",
stop => "${init_script} stop",
status => "${init_script} status",
restart => "${init_script} restart",
hasrestart => true,
hasstatus => true,
}
if $enabled_startup != true {
exec { "disable_grafana":
command => "/bin/systemctl disable grafana",
cwd => "/",
}
}
}
О сборе данных
Основным источником отображения данных являются time-series данные из InfluxDB, а в неё они попадают из 2-х источников: демона collectd и java-библиотеки «com.github.davidb:metrics-influxdb».
Collectd
Collectd — демон, написанный на C, способный передавать данные своему сетевому аналогу, протокол которого InfluxDB и может эмулировать. «Из коробки» он может собирать достаточно большое количество метрик по серверному окружению и по части сервисов, возможность расширения достигается посредством модулей, написанных на python или Java.
Функционал collectd по сбору информации о функционировании сервера (физического и виртуального) меня вполне устраивает, однако дополнительные настройки требуемые для сбора данных со сторонних сервисов (в моём случае Elsticsearch, MongoDB и Apache Storm) достаточно нетривиальны и не всегда функциональны в полном объёме (например для Elsticsearch не корректно собирается информация по скорости выполнения запроса при наличии нескольких shard на разных репликах). Скорее всего следует посмотреть в сторону родного клиент InfluxDB — Telegraf.
Puppet-скрипт для установки и настройки Collectd
class storyline_infra::collectd () {
include stdlib
$params = lookup({"name" => "storyline_infra.collectd",
"merge" => {"strategy" => "deep"}})
$server_port = $params['server_port']
$server_address = $params['server_address']
$pid_file = $params['pid_file']
$init_script = $params['init_script']
$dir_data = $params['dir_data']
$dir_logs = $params['dir_logs']
$enabled_startup = $params['enabled_startup']
$enabled_running = $params['enabled_running']
$version = $params['version']
# mongo db
$enabled_mongodb = $params['enabled_mongodb']
$mongodb_user = $params['mongodb_user']
$mongodb_password = $params['mongodb_password']
# storm db
$enabled_storm = $params['enabled_storm']
$storm_ui_url = $params['storm_ui_url']
# elasticsearch
$enabled_elasticsearch = $params['enabled_elasticsearch']
$elasticsearch_host = $params['elasticsearch_host']
$elasticsearch_port = $params['elasticsearch_port']
$elasticsearch_cluster = $params['elasticsearch_cluster']
exec { "collectd-mkdir":
command => "/bin/mkdir -p /data/db && /bin/mkdir -p /data/logs",
cwd => "/",
unless => '/usr/bin/test -d /data/db -a -d /data/logs',
} ->
# working dir
file { $dir_logs:
ensure => "directory",
recurse => "true",
require => Exec['collectd-mkdir'],
}
file { $dir_data:
ensure => "directory",
recurse => "true",
require => Exec['collectd-mkdir'],
}
package { 'collectd':
# ensure => $version,
ensure => "present",
} ->
file { "/etc/collectd/collectd.conf":
replace => true,
content => epp('storyline_infra/collectd.epp'),
notify => Service['collectd'],
}->
file { $init_script:
replace => true,
content => epp('storyline_infra/collectd_startup.epp'),
mode=>"ug=rwx,o=r",
notify => Service['collectd'],
}->
service { 'collectd':
ensure => $enabled_running,
enable => $enabled_startup,
start => "${init_script} start",
stop => "${init_script} stop",
status => "${init_script} status",
restart => "${init_script} restart",
hasrestart => true,
hasstatus => true,
}
if $enabled_startup != true {
exec { "disable_collectd":
command => "/bin/systemctl disable collectd & /bin/systemctl disable collectd.service",
cwd => "/",
}
}
if $enabled_mongodb {
package { 'python-pip':
ensure => "present",
} ->
exec { "install-pymongo":
command => "/usr/bin/python -m pip install pymongo",
cwd => "/",
unless => '/usr/bin/python -m pip show pymongo',
} ->
file { "/usr/share/collectd/mongodb":
ensure => "directory",
}->
file { "/usr/share/collectd/mongodb.py":
replace => true,
content => epp('storyline_infra/collectd_mongodb_py.epp'),
}->
file { "/usr/share/collectd/mongodb/types.db":
replace => true,
content => epp('storyline_infra/collectd_mongodb_types_db.epp'),
}->
file { "/etc/collectd/collectd.conf.d/mongodb.conf":
replace => true,
content => epp('storyline_infra/collectd_mongodb_conf.epp'),
notify => Service['collectd'],
}
} # if $enabled_mongodb {
# https://github.com/srotya/storm-collectd
if $enabled_storm {
file { "/usr/share/collectd/java/storm-collectd.jar":
replace => true,
ensure => file,
source => "puppet:///modules/storyline_infra/storm-collectd.jar",
}->
file { "/etc/collectd/collectd.conf.d/storm.conf":
replace => true,
content => epp('storyline_infra/collectd_storm_conf.epp'),
notify => Service['collectd'],
}
} # if $enabled_mongodb {
# https://github.com/signalfx/integrations/tree/master/collectd-elasticsearch
# https://github.com/signalfx/collectd-elasticsearch
if $enabled_elasticsearch {
file { "/usr/share/collectd/elasticsearch.py":
replace => true,
content => epp('storyline_infra/collectd_elasticsearch_py.epp'),
}->
file { "/etc/collectd/collectd.conf.d/elasticsearch.conf":
replace => true,
content => epp('storyline_infra/collectd_elasticsearch_conf.epp'),
notify => Service['collectd'],
}
} # if $enabled_mongodb {
}
com.github.davidb:metrics-influxdb
Указанная библиотека фактически является адаптером для широко известной java-библиотеки метрик Metrics. Поддерживает протокол версии 0.9 для InfluxDB и позволяет передавать необходимую информацию в полном объёме.
Инициализация осуществляется примерно так:
if (metricsConfiguration.enabled) {
String hostName = InetAddress.getLocalHost().getCanonicalHostName();
final ScheduledReporter reporterInfluxDB = InfluxdbReporter.forRegistry(metricRegistry)
.protocol(new HttpInfluxdbProtocol("http", metricsConfiguration.influxdbHost,
metricsConfiguration.influxdbPort, metricsConfiguration.influxdbUser,
metricsConfiguration.influxdbPassword, metricsConfiguration.influxdbDB))
// rate + dim conversions
.convertRatesTo(TimeUnit.SECONDS).convertDurationsTo(TimeUnit.MILLISECONDS)
// filter
.filter(MetricFilter.ALL)
// don't skip
.skipIdleMetrics(false)
// hostname tag
.tag("host", hostName)
// !!! converter
// al metrics must be of form: "processed_links.site_ru .crawling" -> "crawling
// source=site_ru, param=processed_links value=0.1"
.transformer(new CategoriesMetricMeasurementTransformer("param", "source"))
.build();
reporterInfluxDB.start(metricsConfiguration.reportingPeriod, TimeUnit.SECONDS);
}
В дальнейшем используется обычный Metrics API, что позволяет повысить прозрачность того что и как делается в моём ПО в любой момент времени.
Указанная система позволила не только следить за работой системы в рабочем режиме, но и отслеживать изменения, вызванные внесенными изменениями – как увеличилось потребление памяти, скорость обработки, объем данных и т.д. Сейчас, в принципе, страничка с общим дашбоардом является закладкой на кухонном ноутбуке и утренний завтрак всегда сопровождается просмотром событий за последние 9 часов.
Спасибо за внимание!
Комментарии (6)
rudenkovk
30.08.2017 11:41Почему collectd, а не нативный для стека influx telegraf?
fedor_malyshkin Автор
30.08.2017 14:47Ранее был опыт с работы с ним. Однако его поддержка новых продуктов не на высоте, хотя стабильность и низкие требования к ресурсам нареканий не вызывает. По тексту я уже писал, что к Telegraf придётся вернуться.
rudenkovk
30.08.2017 15:08+1Сорян, не заметил :)
они достаточно бодро пишут плагины. Правда на паре проектов я держу свои ветки, где делаем свои датчики сбрасывающий информации. Это куда удобнее чем писать на С для collectd. Или запускать питоновские скрипты, через обертку collectd.
Sovigod
InfluxDB для метрик отличное решение. Но если вам нужно метрик больше чем за пару последних недель — вам обязательно нужно использовать Continuous Queries для уплотнения данных. Тогда графики даже за несколько лет будут моментально отображаться.
fedor_malyshkin Автор
Полностью согласен. Continuous Queries вместе с Retention Policy даёт возможность хранить данные за почти произвольный период. Однако нужно решить как долго хранить и что. Для незнакомых с CQ и RP: Разные метрики нужно downsampl’ить по разному – где-то подойдёт среднее, где-то максимум. Я сейчас просто удаляю данные старше 2-х недель без downsamplin’га.