ExpvarMon — консольный мониторинг сервисов на Go

от автора

Для Go-программ существует удобнейший стандартный пакадж expvar, позволяющий одной строчкой подключить вывод дебаг информации в JSON-формате. И чтобы максимально быстро и наглядно мониторить текущее состояние, была написана консольная программа Expvarmon, требующая минимум конфигурации для вывода метрик и дебаг-информации для ваших Go-сервисов.

Функции:

  • single- и multi-services режимы
  • мониторинг локальных и удаленных программ
  • произвольное количество сервисов и переменных
  • поддержка значений для памяти, временных интервалов, bool и произвольных чисел/строк
  • sparkline-графики
  • отображение максимальных значений
  • отображение упавших/рестартовавших сервисов
  • авто-ресайз при изменении размеров шрифта или окна


Введение

В стандартной библиотеке Go есть очень полезный пакадж expvar, который позволяет одной строчкой добавить вывод дебаг информации в json-формате по адресу /debug/vars. По-умолчанию выводятся данные об использовании памяти и работе сборщика мусора (GC), и легко добавляются любые свои метрики/счетчики/переменные. Обычно эти данные собирает отдельный процесс, который кладет в какую-нибудь time-series базу данных, и затем это превращается в удобные и красивые дашборды.

Но зачастую, даже во время разработки или отладочных сессий, хочется просто мониторить состояние программы, быть уверенным, что ресурсы используются адекватно нагрузкам, нет утечек памяти или горутин и так далее. Поднимать для этого целую инфраструктуру для мониторинга — часто накладно и неоправданно, а сырой json не сильно презентабелен.

Именно для таких случаев и была за пару выходных написана программа для мониторинга переменных expvar прямо в терминале, которая требует почти нулевую конфигурацию и не использует никаких сторонних баз и ресурсов. Программа использует отличнейший пакадж TermUI от любителя терминалов gizak (посмотрите его домашнюю страничку!).

Установка

Установка программы, как и любой другой программы на Go предельно проста:

go get github.com/divan/termui

Надеюсь, $GOPATH/bin у вас прописан в $PATH.

Использование

expvar

Короткое объяснение

import _ "expvar"

Длинное объяснение

Если вы еще не знакомы с пакетом expvar, то краткое объяснение и инструкция.
В функции init() этого пакета написано следующее:

func init() { 	http.HandleFunc("/debug/vars", expvarHandler) 	Publish("cmdline", Func(cmdline)) 	Publish("memstats", Func(memstats)) }

Первая строка регистрирует хендлер для обработки URL "/debug/vars" для стандартного http.DefaultServeMux из стандартного пакета net/http. Если вы пока не знаете, как устроен net/http, возможно это будет отдельной статьей, но сейчас вам достаточно знать, что если ваша программа стартанет стандартный http-сервер (скажем, http.ListenAndServe(":1234", nil)), то у нее автоматически появится обработчик GET-запроса по адресу /debug/vars. И ответ этого запроса будет по умолчанию содержать примерно следующий JSON:

$ curl -s http://localhost:1234/debug/vars | json_pp | head {    "cmdline" : [       "./expvar.demo",       "-bind=:1234",    ],    "memstats" : {       "NumGC" : 5,       "Alloc" : 114016,       "DebugGC" : false,       "HeapObjects" : 519,       "HeapSys" : 868352,       "StackInuse" : 180224, 

Это JSON-репрезентация двух переменных, определенных следующими двумя строчками — командная строка и текущие значения runtime.Memstats. Последняя содержит массу подробностей про текущее использование памяти и работу сборщика мусора, большая часть из которых ну уж слишком подробная. Обычно для мониторинга используются значения Alloc, Sys, HeapAlloc — реально используемая память, запрошенная у OS, используемая память в куче соответственно.

Поскольку init() вызывает автоматически при импорте пакаджа, в программе достаточно добавить одну строчку:

import _ "expvar"

expvarmon

Данная же программа предельно проста — она с указанным интервалом вычитывает этот JSON для указанного сервиса или сервисов, и отображает его в удобном для мониторинга виде, при этом показывает sparkline-графики для числовых значений. Все что ей нужно для запуска, это порт или «хост: порт» сервиса(-ов) которые вы хотите мониторить. К примеру:

expvarmon -ports="80" expvarmon -ports="23000-23010,80" expvarmon -ports="80,remoteapp.corp.local:80-82" 

Можно указывать как одну, так и 30+ портов/сервисов — на сколько у вас хватит размеров терминала.
Программа может также мониторить саму себя:

expvarmon -self

Интервал по умолчанию — 5 секунд, но можно указать меньше или больше. Имейте ввиду, что слишком короткий интервал не рекомендован, так как даже обновление memstats влияет на сборщик мусора и увеличивает паузы. Если ваша аппликация бежит под хорошой нагрузкой, сильно короткий интервал (100ms) может повлиять на продуктивность.

expvarmon -self -i 5m expvarmon -self -i 30s

По умолчанию мониторятся следующие переменные:

  • mem:memstats.Alloc
  • mem:memstats.Sys
  • mem:memstats.HeapAlloc
  • mem:memstats.HeapInuse
  • memstats.EnableGC
  • memstats.NumGC
  • duration:memstats.PauseTotalNs

Значения переменных берутся в том виде, в каком они есть в JSON, с записью через точку. Модификаторы «mem:», «duration:», «str:» — опциональны, и влияют на форматирование/отображение. Если указывать переменные без модификатора, то они будут отображаться как есть. Модификатор «mem:» будет конвертировать значения в «KB, MB, etc» представление, а «duration:» — конвертировать int64 значение в time.Duration(«ns, ms, s, m, etc»). Модификатор «str:» просто говорит, что значение не цифровое, и sparkline-график для этой переменной рисовать не нужно.

К примеру:

expvarmon -ports="80" -vars="mem:memstats.Alloc,duration:Response.Mean,Goroutines,str:Uptime"

Опять же, можно указать как одну переменную, так и пару десятков, насколько у вас хватит размера терминала.

В программе есть два режима — для одного сервиса и для нескольких сервисов. В первом случае графики отображаются для всех переменных, во втором — только для первых двух, в том порядке, в котором они указаны. Для графиков отображаются максимальные значения, которые наблюдались за сессию мониторинга.

Дополнительно

Expvarmon отображает иконками сервисы, которые падали и которые лежат в данный момент. К сожалению, если интервал опроса больше, чем время падения/рестарта сервиса, то падение сервиса программа не словит.

Также важно понимать, что данные никуда не записываются и не сохраняются. На графиках показываются последние значения — и масштаб графиков зависит от интервала и размера терминала. Ни зума, ни истории, ни поиска по времени тут нет. Это решение для более простой задачи — мгновенного мониторинга текущих значений.

Благодаря возможностям TermUI, программа динамически меняет размеры всех виджетов при изменении размера шрифта или окна терминала.

Дополнительные переменные

Лично мне в стандартном перечне переменных expvar не хватает двух вещей — количества запущенных горутин и аптайм сервиса. Вот демо-враппер, который экспортит три дополнительные переменные. Просто подключаете его в свою программу, одним импортом.

package myexpvars  import ( 	"expvar" 	"math/rand" 	"runtime" 	"time" )  var ( 	startTime = time.Now().UTC() )  // goroutines is an expvar.Func compliant wrapper for runtime.NumGoroutine function. func goroutines() interface{} { 	return runtime.NumGoroutine() }  // uptime is an expvar.Func compliant wrapper for uptime info. func uptime() interface{} { 	uptime := time.Since(startTime) 	return int64(uptime) }  // responseTime fake var. func responseTime() interface{} { 	resp := time.Duration(rand.Intn(1000)) * time.Millisecond 	return int64(resp) }  func init() { 	expvar.Publish("Goroutines", expvar.Func(goroutines)) 	expvar.Publish("Uptime", expvar.Func(uptime)) 	expvar.Publish("MeanResponse", expvar.Func(responseTime)) } 

Что делать, если используется сторонний http-роутер, вместо стандартного

Многие веб-сервисы на Go пишутся с использованием дополнительных веб-фреймворков или более продвинутых http-роутеров. expvar из коробки с ними работать не будет. Для него вам нужно будет таки стартануть стандартный http.ListenAndServer() на другом порту. А это даже лучше, так как открывать наружу /debug/vars крайне не рекомендуется, если речь идет о публичных веб-сервисах.

Если же вы используете стандартный net/http, но хотите, чтобы expvar был на другом порту, проще всего сделать так. «Основной» ServeMux запустить следующим образом:

mux := http.NewServerMux() server := &http.Server{Addr: “:80”, Handler: mux} server.ListenAndServe()

а /debug/vars повесить на стандартный

http.ListenAndServe(":1234", nil)

Скриншоты

Ссылки

Github: github.com/divan/expvarmon
Expvar docs: golang.org/pkg/expvar
runtime.Memstats: golang.org/pkg/runtime/#MemStats

ссылка на оригинал статьи http://habrahabr.ru/post/257593/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *