Tarantool это платформа in-memory вычислений с гибкой схемой
данных. На её основе можно создать распределённое хранилище,
веб-сервер, высоконагруженное приложение или, в конце концов,
сервис, включающий в себя всё вышеперечисленное. Но какой бы ни
была ваша промышленная задача, однажды настанет момент, когда её
решение придётся мониторить. В этой статье я хочу дать обзор
существующих средств для мониторинга приложения на базе Tarantool и
пройтись по основным кейсам работы с ними.
Я работаю в команде, которая занимается разработкой, внедрением
и поддержкой готовых решений на основе Tarantool. Для вывода наших
приложений в эксплуатацию на контуре заказчика было необходимо не
только разобраться в текущих возможностях мониторинга, но и
доработать их. Большая часть доработок в результате вошла в те или
иные стандартные пакеты. Данный материал является текстовой
выжимкой этого опыта, и может пригодиться тем, кто решит пройти по
той же тропе.
Базовое
конфигурирование и использование логов
Для работы с логами в приложениях на базе Tarantool существует
пакет
log. Это встроенный модуль, который присутствует в
каждой инсталляции Tarantool. Процесс по умолчанию заполняет лог
сообщениями о своём состоянии и состоянии используемых пакетов.
Каждое сообщение лога имеет свой уровень детализации. Уровень
логирования Tarantool характеризуется значением параметра
log_level
(целое число от 1 до 7):
SYSERROR
.
ERROR
сообщения log.error(...)
.
CRITICAL
.
WARNING
сообщения log.warn(...)
.
INFO
сообщения log.info(...)
.
VERBOSE
сообщения
log.verbose(...)
.
DEBUG
сообщения log.debug(...)
.
Значение параметра log_level
N
соответствует логу, в который попадают сообщения уровня детализации
N
и всех предыдущих уровней детализации <
N
. По умолчанию log_level
имеет значение
5 (INFO)
. Чтобы настроить этот параметр при
использовании Cartridge, можно воспользоваться
cartridge.cfg
:
cartridge.cfg( { ... }, { log_level = 6, ... } )
Для отдельных процессов настройка производится при помощи вызова
box.cfg
:
box.cfg{ log_level = 6 }
Менять значение параметра можно непосредственно во время работы
программы.
Стандартная стратегия логирования: писать об ошибках в
log.error()
или log.warn()
в зависимости
от их критичности, отмечать в log.info()
основные
этапы работы приложения, а в log.verbose()
писать
более подробные сообщения о предпринимаемых действиях для отладки.
Не стоит использовать log.debug()
для отладки
приложения, этот уровень диагностики в первую очередь предназначен
для отладки самого Tarantool. Не рекомендуется также использовать
уровень детализации ниже 5 (INFO)
, поскольку в случае
возникновения ошибок отсутствие информационных сообщений затруднит
диагностику. Таким образом, в режиме отладки приложения
рекомендуется работать при log_level 6 (VERBOSE)
, в
режиме штатной работы при log_level 5 (INFO)
.
local log = require('log')log.info('Hello world')log.verbose('Hello from app %s ver %d', app_name, app_ver) -- https://www.lua.org/pil/20.htmllog.verbose(app_metainfo) -- type(app_metainfo) == 'table'
В качестве аргументов функции отправки сообщения в лог
(log.error
/log.warn
/log.info
/log.verbose
/log.debug
)
можно передать обычную строку, строку с плейсхолдерами и аргументы
для их заполнения (аналогично string.format()
) или
таблицу (она будет неявно преобразована в строку методом
json.encode()
). Функции лога также работают с
нестроковыми данными (например числами), приводя их к строке c
помощью tostring()
.
Tarantool поддерживает два формата логов: plain
и
json
:
2020-12-15 11:56:14.923 [11479] main/101/interactive C> Tarantool 1.10.8-0-g2f18757b72020-12-15 11:56:14.923 [11479] main/101/interactive C> log level 52020-12-15 11:56:14.924 [11479] main/101/interactive I> mapping 268435456 bytes for memtx tuple arena...
{"time": "2020-12-15T11:56:14.923+0300", "level": "CRIT", "message": "Tarantool 1.10.8-0-g2f18757b7", "pid": 5675 , "cord_name": "main", "fiber_id": 101, "fiber_name": "interactive", "file": "\/tarantool\/src\/main.cc", "line": 514}{"time": "2020-12-15T11:56:14.923+0300", "level": "CRIT", "message": "log level 5", "pid": 5675 , "cord_name": "main", "fiber_id": 101, "fiber_name": "interactive", "file": "\/tarantool\/src\/main.cc", "line": 515}{"time": "2020-12-15T11:56:14.924+0300", "level": "INFO", "message": "mapping 268435456 bytes for memtx tuple arena...", "pid": 5675 , "cord_name": "main", "fiber_id": 101, "fiber_name": "interactive", "file": "\/tarantool\/src\/box\/tuple.c", "line": 261}
Настройка формата происходит через параметр
log_format
так же, как для параметра
log_level
. Подробнее о форматах можно прочитать в
соответствующем разделе
документации.
Tarantool позволяет выводить логи в поток stderr, в файл, в
конвейер или в системный журнал syslog. Настройка производится с
помощью параметра log
. О том, как конфигурировать
вывод, можно прочитать в
документации.
Обёртка логов
Язык Lua предоставляет широкие возможности для замены на ходу
практически любого исполняемого кода. Здесь я хотел бы поделиться
способом помещения стандартных методов логирования в
функцию-обёртку, который мы использовали при реализации сквозного
логирования в своих приложениях. Стоит отметить, что способ этот
расположен в "серой" зоне легальности, так что прибегать к нему
стоит только при отсутствии возможностей решить проблему более
элегантно.
local log = require('log')local context = require('app.context')local function init() if rawget(_G, "_log_is_patched") then return end rawset(_G, "_log_is_patched", true) local wrapper = function(level) local old_func = log[level] return function(fmt, ...) local req_id = context.id_from_context() if select('#', ...) ~= 0 then local stat stat, fmt = pcall(string.format, fmt, ...) if not stat then error(fmt, 3) end end local wrapped_message if type(fmt) == 'string' then wrapped_message = { message = fmt, request_id = req_id } elseif type(fmt) == 'table' then wrapped_message = table.copy(fmt) wrapped_message.request_id = req_id else wrapped_message = { message = tostring(fmt), request_id = req_id } end return old_func(wrapped_message) end end package.loaded['log'].error = wrapper('error') package.loaded['log'].warn = wrapper('warn') package.loaded['log'].info = wrapper('info') package.loaded['log'].verbose = wrapper('verbose') package.loaded['log'].debug = wrapper('debug') return trueend
Данный код обогащает информацию, переданную в лог в любом
поддерживаемом формате, идентификатором запроса
request_id
.
Подключение метрик
Для работы с метриками в приложениях Tarantool существует пакет
metrics.
Это модуль для создания коллекторов метрик и взаимодействия с ними
в разнообразных сценариях, включая экспорт метрик в различные базы
данных (InfluxDB, Prometheus, Graphite). Материал основан на
функционале версии 0.6.0.
Чтобы установить metrics в текущую директорию,
воспользуйтесь стандартной командой:
tarantoolctl rocks install metrics 0.6.0
Чтобы добавить пакет в список зависимостей вашего приложения,
включите его в соответствующий пункт
rockspec
-файла:
dependencies = { ..., 'metrics == 0.6.0-1',}
Для приложений, использующих фреймворк Cartridge,
пакет metrics предоставляет специальную роль
cartridge.roles.metrics
. Включение этой роли на всех
процессах кластера упрощает работу с метриками и позволяет
использовать конфигурацию Cartridge для настройки пакета.
Встроенные метрики
Сбор встроенных метрик уже включён в состав роли
cartridge.roles.metrics
.
Для включения сбора встроенных метрик в приложениях, не
использующих фреймворк Cartridge, необходимо выполнить следующую
команду:
local metrics = require('metrics')metrics.enable_default_metrics()
Достаточно выполнить её единожды на старте приложения, например
поместив в файл init.lua
.
В список метрик по умолчанию входят:
- информация о потребляемой Lua-кодом RAM;
- информация о текущем состоянии файберов;
- информация о количестве сетевых подключений и объёме сетевого
трафика, принятого и отправленного процессом;
- информация об использовании RAM на хранение данных и индексов
(в том числе метрики slab-аллокатора);
- информация об объёме операций на спейсах;
- характеристики репликации спейсов Tarantool;
- информация о текущем времени работы процесса и другие
метрики.
Подробнее узнать о метриках и их значении можно в
соответствующем разделе
документации.
Для пользователей Cartridge также существует специальный набор
встроенных метрик для мониторинга состояния кластера. На данный
момент он включает в себя метрику о количестве проблем в кластере,
разбитых по уровням критичности (аналогична Issues в WebUI
Cartridge).
Плагины для экспорта
метрик
Пакет metrics поддерживает три формата экспорта метрик:
prometheus
, graphite
и json
.
Последний можно использовать, например, в связке Telegraf +
InfluxDB.
Чтобы настроить экспорт метрик в формате json
или
prometheus
для процессов с ролью
cartridge.roles.metrics
, добавьте соответствующую
секцию в конфигурацию кластера:
metrics: export: - path: '/metrics/json' format: json - path: '/metrics/prometheus' format: prometheus
Экспорт метрик в формате json
или
prometheus
без использования кластерной конфигурации
настраивается средствами модуля http
так же, как любой другой маршрут.
local json_metrics = require('metrics.plugins.json')local prometheus = require('metrics.plugins.prometheus')local httpd = require('http.server').new(...)httpd:route( { path = '/metrics/json' }, function(req) return req:render({ text = json_metrics.export() }) end)httpd:route( { path = '/metrics/prometheus' }, prometheus.collect_http)
Для настройки graphite
необходимо добавить в код
приложения следующую секцию:
local graphite = require('metrics.plugins.graphite')graphite.init{ host = '127.0.0.1', port = 2003, send_interval = 60,}
Параметры host
и port
соответствуют
конфигурации вашего сервера Graphite, send_interval
периодичность отправки данных в секундах.
При необходимости на основе инструментов пакета metrics
можно создать собственный плагин для экспорта. Советы по написанию
плагина можно найти в соответствующем разделе
документации.
Добавление
пользовательских метрик
Ядро пакета metrics составляют коллекторы метрик,
созданные на основе примитивов Prometheus:
counter
предназначен для хранения одного
неубывающего значения;
gauge
предназначен для хранения одного
произвольного численного значения;
summary
хранит сумму значений нескольких
наблюдений и их количество, а также позволяет вычислять перцентили
по последним наблюдениям;
histogram
агрегирует несколько наблюдений в
гистограмму.
Cоздать экземпляр коллектора можно следующей командой:
local gauge = metrics.gauge('balloons')
В дальнейшем получить доступ к объекту в любой части кода можно
этой же командой.
Каждый объект коллектора способен хранить сразу несколько
значений метрики с разными лейблами. Например, в результате
кода
local gauge = metrics.gauge('balloons')gauge:set(1, { color = 'blue' })gauge:set(2, { color = 'red' })
внутри коллектора возникнет два различных значения метрики. Для
того чтобы каким-либо образом изменить значение конкретной метрики
в коллекторе, необходимо указать соответствующие ей лейблы:
gauge:inc(11, { color = 'blue' }) -- increase 1 by 11
Лейблы концепт, который также был вдохновлён Prometheus
используются для различения измеряемых характеристик в рамках одной
метрики. Кроме этого, они могут быть использованы для агрегирования
значений на стороне базы данных. Рассмотрим рекомендации по их
использованию на примере.
В программе есть модуль server
, который принимает
запросы и способен сам их отправлять. Вместо того, чтобы
использовать две различных метрики
server_requests_sent
и
server_requests_received
для хранения данных о
количестве отправленных и полученных запросов, следует использовать
общую метрику server_requests
с лейблом
type
, который может принимать значения
sent
и received
.
Подробнее о коллекторах и их методах можно прочитать в
документации пакета.
Заполнение
значений пользовательских метрик
Пакет metrics содержит полезный инструмент для
заполнения коллекторов метрик коллбэки. Рассмотрим принцип его
работы на простом примере.
В вашем приложении есть буфер, и вы хотите добавить в метрики
информацию о текущем количестве записей в нём. Функция, которая
заполняет соответствующий коллектор, задаётся следующим
образом:
local metrics = require('metrics')local buffer = require('app.buffer')metrics.register_callback(function() local gauge = metrics.gauge('buffer_count') gauge.set(buffer.count())end)
Вызов всех зарегистрированных коллбэков производится
непосредственно перед сбором метрик в плагине экспорта. С таким
подходом метрики, отправляемые наружу, всегда будут иметь наиболее
актуальное значение.
Мониторинг HTTP-трафика
Пакет metrics содержит набор инструментов для подсчёта
количества входящих HTTP-запросов и измерения времени их обработки.
Они предназначены для работы со встроенным пакетом http,
и подход будет отличаться в зависимости от того, какую версию вы
используете.
Чтобы добавить HTTP-метрики для конкретного маршрута при
использовании пакета http 1.x.x, вам необходимо обернуть
функцию-обработчик запроса в функцию
http_middleware.v1
:
local metrics = require('metrics')local http_middleware = metrics.http_middlewarehttp_middleware.build_default_collector('summary', 'http_latency')local route = { path = '/path', method = 'POST' }local handler = function() ... endhttpd:route(route, http_middleware.v1(handler))
Для хранения метрик можно использовать коллекторы
histogram
и summary
.
Чтобы добавить HTTP-метрики для маршрутов роутера при
использовании пакета http 2.x.x, необходимо
воспользоваться следующим подходом:
local metrics = require('metrics')local http_middleware = metrics.http_middlewarehttp_middleware.build_default_collector('histogram', 'http_latency')router:use(http_middleware.v2(), { name = 'latency_instrumentation' })
Рекомендуется использовать один и тот же коллектор для хранения
всей информации об обработке HTTP-запросов (например, выставив в
начале коллектор по умолчанию функцией
build_default_collector
или
set_default_collector
). Прочитать больше о
возможностях http_middleware можно в
документации.
Глобальные лейблы
В пункте "Добавление пользовательских метрик" вводится понятие
лейблов, предназначенных для различения измеряемых характеристик в
рамках одной метрики. Их выставление происходит непосредственно при
измерении наблюдения в рамках одного коллектора. Однако это не
единственный тип лейблов, которые поддерживает пакет
metrics.
Для того чтобы прибавить к каждой метрике какой-то общий для
процесса или группы процессов Tarantool (например, имя машины или
название приложения) лейбл, необходимо воспользоваться механизмом
глобальных лейблов:
local metrics = require('metrics')local global_labels = {}-- постоянное значениеglobal_labels.app = 'MyTarantoolApp'-- переменные конфигурации кластера (http://personeltest.ru/aways/www.tarantool.io/ru/doc/latest/book/cartridge/cartridge_api/modules/cartridge.argparse/)local argparse = require('cartridge.argparse')local params, err = argparse.parse()assert(params, err)global_labels.alias = params.alias-- переменные окружения процессаlocal host = os.getenv('HOST')assert(host)global_labels.host = hostmetrics.set_global_labels(global_labels)
Обратите внимание, что метод set_global_labels
полностью перезаписывает таблицу глобальных лейблов при каждом
вызове.
Роль cartridge.roles.metrics
по умолчанию
выставляет alias процесса Tarantool в качестве глобального
лейбла.
Важно иметь в виду, что глобальные лейблы добавляются в метрики
непосредственно в процессе сбора и не влияют на структуру хранения
метрик в коллекторах. Во избежание проблем не стоит использовать
один и тот же лейбл и как глобальный, и как локальный, т.е.
непосредственно передаваемый коллектору при измерении
наблюдения.
Мониторинг внешних
параметров
Tarantool не только позволяет организовывать сбор внутренних
метрик о работе приложения, но и способен выступать как агент
мониторинга внешних параметров. Подмодуль psutils пакета
metrics является примером реализации такого поведения.
С помощью psutils можно настроить сбор метрик об
использовании CPU процессами Tarantool. Его информация основывается
на данных /proc/stat
и /proc/self/task
.
Подключить сбор метрик можно с помощью следующего кода:
local metrics = require('metrics')metrics.register_callback(function() local cpu_metrics = require('metrics.psutils.cpu') cpu_metrics.update()end)
Возможность писать код на Lua делает Tarantool гибким
инструментом, позволяющим обходить различные препятствия. Например,
psutils возник из необходимости следить за использованием
CPU вопреки отказу администраторов со стороны заказчика "подружить"
в правах файлы /proc/*
процессов Tarantool и плагин
inputs.procstat
Telegraf, который использовался на
местных машинах в качестве основного агента.
Пакет metrics сам по себе способен решить многие
прикладные проблемы, но, конечно же, далеко не все, особенно если
речь идёт о внешнем мониторинге. Но стоит иметь в виду, что
возможность обогатить его с помощью кода всегда остаётся в
распоряжении разработчика, и, если возникает необходимость, этой
возможностью уж точно не стоит пренебрегать.
Визуализация метрик
Хранение метрик в
Prometheus
Настройка пути для экспорта метрик Tarantool в формате
Prometheus описана в пункте "Плагины для экспорта метрик". Ответ
запроса по такому маршруту выглядит следующим образом:
...# HELP tnt_stats_op_total Total amount of operations# TYPE tnt_stats_op_total gaugetnt_stats_op_total{alias="tnt_router",operation="replace"} 1tnt_stats_op_total{alias="tnt_router",operation="select"} 57tnt_stats_op_total{alias="tnt_router",operation="update"} 43tnt_stats_op_total{alias="tnt_router",operation="insert"} 40tnt_stats_op_total{alias="tnt_router",operation="call"} 4...
Чтобы настроить сбор метрик в Prometheus, необходимо добавить
элемент в массив scrape_configs
. Этот элемент должен
содержать поле static_configs
с перечисленными в
targets
URI всех интересующих процессов Tarantool и
поле metrics_path
, в котором указан путь для экспорта
метрик Tarantool в формате Prometheus.
scrape_configs: - job_name: "tarantool_app" static_configs: - targets: - "tarantool_app:8081" - "tarantool_app:8082" - "tarantool_app:8083" - "tarantool_app:8084" - "tarantool_app:8085" metrics_path: "/metrics/prometheus"
В дальнейшем найти метрики в Grafana вы сможете, указав в
качестве job
соответствующий job_name
из
конфигурации.
Пример готового docker-кластера Tarantool App + Prometheus +
Grafana можно найти в репозитории
tarantool/grafana-dashboard.
Хранение метрик в InfluxDB
Чтобы организовать хранение метрик Tarantool в InfluxDB,
необходимо воспользоваться стеком Telegraf + InfluxDB и настроить
на процессах Tarantool экспорт метрик в формате json
(см. пункт "Плагины для экспорта метрик"). Ответ формируется
следующим образом:
{ ... { "label_pairs": { "operation": "select", "alias": "tnt_router" }, "timestamp": 1606202705935266, "metric_name": "tnt_stats_op_total", "value": 57 }, { "label_pairs": { "operation": "update", "alias": "tnt_router" }, "timestamp": 1606202705935266, "metric_name": "tnt_stats_op_total", "value": 43 }, ...}
HTTP-плагин Telegraf требует заранее указать список возможных
тегов для метрики. Конфигурация, учитывающая все стандартные
лейблы, будет выглядеть следующим образом:
[[inputs.http]] urls = [ "http://tarantool_app:8081/metrics/json", "http://tarantool_app:8082/metrics/json", "http://tarantool_app:8083/metrics/json", "http://tarantool_app:8084/metrics/json", "http://tarantool_app:8085/metrics/json" ] timeout = "30s" tag_keys = [ "metric_name", "label_pairs_alias", "label_pairs_quantile", "label_pairs_path", "label_pairs_method", "label_pairs_status", "label_pairs_operation" ] insecure_skip_verify = true interval = "10s" data_format = "json" name_prefix = "tarantool_app_" fieldpass = ["value"]
Список urls
должен содержать URL всех интересующих
процессов Tarantool, настроенные для экспорта метрик в формате
json
. Обратите внимание, что лейблы метрик попадают в
Telegraf и, соответственно, InfluxDB как теги, название которых
состоит из префикса label_pairs_
и названия лейбла.
Таким образом, если ваша метрика имеет лейбл с ключом
mylbl
, то для работы с ним в Telegraf и InfluxDB
необходимо указать в пункте tag_keys
соответствующего
раздела [[inputs.http]]
конфигурации Telegraf значение
ключа label_pairs_mylbl
, и при запросах в InfluxDB
ставить условия на значения лейбла, обращаясь к тегу с ключом
label_pairs_mylbl
.
В дальнейшем найти метрики в Grafana вы сможете, указав
measurement
в формате
<name_prefix>http
(например, для указанной выше
конфигурации значение measurement
tarantool_app_http
).
Пример готового docker-кластера Tarantool App + Telegraf +
InfluxDB + Grafana можно найти в репозитории
tarantool/grafana-dashboard.
Стандартный дашборд
Grafana
Для визуализации метрик Tarantool с помощью Grafana на
Official & community built dashboards опубликованы
стандартные дашборды. Шаблон состоит из панелей для мониторинга
HTTP, памяти для хранения данных вместе с индексами и операций над
спейсами Tarantool. Версию для использования с Prometheus можно
найти
здесь, а для InfluxDB
здесь. Версия для Prometheus также содержит набор панелей для
мониторинга состояния кластера, агрегированной нагрузки и
потребления памяти.
Чтобы импортировать шаблон дашборды, достаточно вставить
необходимый id или ссылку в меню Import на
сервере Grafana. Для завершения процесса импорта необходимо задать
переменные, определяющие место хранения метрик Tarantool в
соответствующей базе данных.
Генерация
дашбордов Grafana с grafonnet
Стандартные дашборды Grafana были созданы с помощью инструмента
под названием grafonnet.
Что это за заморский зверь и как мы к нему пришли?
С самого начала перед нами стояла задача поддерживать не одну, а
четыре дашборды: два похожих проекта, экземпляр каждого из которых
находился в двух разных зонах. Изменения, такие как переименование
метрик и лейблов или добавление/удаление панелей, происходили чуть
ли не ежедневно, но после творческой работы по проектированию
улучшений и решению возникающих проблем непременно следовало
механическое накликивание изменений мышью, умножавшееся в своём
объёме на четыре. Стало ясно, что такой подход следует
переработать.
Одним из первых способов решить большинство возникающих проблем
было использование механизма динамических переменных
(Variables) в Grafana. Например, он позволяет объединить
дашборды с метриками из разных зон в одну с удобным переключателем.
К сожалению, мы слишком быстро столкнулись с проблемой:
использование механизма оповещений (Alert) не совместимо с
запросами, использующими динамические переменные.
Любой дашборд в Grafana по сути представляет собой некоторый
json. Более того, платформа позволяет без каких-либо затруднений
экспортировать в таком формате существующие дашборды. Работать с
ним в ручном режиме несколько затруднительно: размер даже
небольшого дашборда составляет несколько тысяч строк. Первым
способом решения проблемы был скрипт на Python, который заменял
необходимые поля в json, по сути превращая один готовый дашборд в
другой. Когда разработка библиотеки скриптов пришла к задаче
добавления и удаления конкретных панелей, мы начали осознавать, что
пытаемся создать генератор дашбордов. И что эту задачу уже кто-то
до нас решал.
В открытом доступе можно найти несколько проектов, посвящённых
данной теме. К счастью или несчастью, проблема выбора решилась
быстро: на контуре заказчика для хранения метрик мы
безальтернативно пользовались InfluxDB, а поддержка запросов к
InfluxDB хоть в какой-то форме присутствовала только в
grafonnet.
grafonnet
opensource-проект под эгидой Grafana, предназначенный для
программной генерации дашбордов. Он основан на языке
программирования jsonnet языке для
генерации json. Сам grafonnet представляет собой набор шаблонов для
примитивов Grafana (панели и запросы разных типов, различные
переменные) с методами для объединения их в цельный дашборд.
Основным преимуществом grafonnet является используемый в нём
язык jsonnet. Он прост и минималистичен, и всегда имеет дело с
json-объектами (точнее, с их местной расширенной версией, которая
может включать в себя функции и скрытые поля). Благодаря этому на
любом этапе работы выходной объект можно допилить под себя, добавив
или убрав какое-либо вложенное поле, не внося при этом изменений в
исходный код.
Начав с форка проекта и сборки нашей дашборды на основе этого
форка, впоследствии мы оформили несколько Pull Request-ов в
grafonnet на основе наших изменений. Например, один из них добавил
поддержку запросов в InfluxDB на основе визуального редактора.
Код наших стандартных дашбордов расположен в репозитории
tarantool/grafana-dashboard. Здесь же находится готовый
docker-кластер, состоящий из стеков Tarantool App + Telegraf +
InfluxDB + Grafana, Tarantool App + Prometheus + Grafana. Его можно
использовать для локальной отладки сбора и обработки метрик в вашем
собственном приложении.
При желании из стандартных панелей можно собрать дашборд,
расширенный панелями для отображения собственных метрик
приложения.
На что смотреть?
В первую очередь, стоит следить за состоянием самих процессов
Tarantool. Для этого подойдёт, например, стандартный
up
Prometheus. Можно соорудить простейший healthcheck
самостоятельно:
httpd:route( { path = '/health' }, function(req) local body = { app = app, alias = alias, status = 'OK' } local resp = req:render({ json = body }) resp.status = 200 return resp end)
Рекомендации по мониторингу внешних параметров ничем
принципиально не отличаются от ситуации любого другого приложения.
Необходимо следить за потреблением памяти на хранение логов и
служебных файлов на диске. Заметьте, что файлы с данными
.snap
и .xlog
возникают даже при
использовании движка memtx
(в зависимости от
настроек). При нормальной работе нагрузка на CPU не должна быть
чересчур большой. Исключение составляет момент восстановления
данных после рестарта процесса: построение индексов может загрузить
все доступные потоки процессора на 100 % на несколько минут.
Потребление RAM удобно разделить на два пункта: Lua-память и
память, потребляемая на хранение данных и индексов. Память,
доступная для выполнения кода на Lua, имеет ограничение в 2 Gb на
уровне Luajit. Обычно приближение метрики к этой границе
сигнализирует о наличии какого-то серьёзного изъяна в коде. Более
того, зачастую такие изъяны приводят к нелинейному росту
используемой памяти, поэтому начинать волноваться стоит уже при
переходе границы в 512 Mb на процесс. Например, при высокой
нагрузке в наших приложениях показатели редко выходили за предел
200-300 Mb Lua-памяти.
При использовании движка memtx
потреблением памяти
в рамках заданного лимита memtx_memory
(он же метрика
quota_size
) заведует slab-аллокатор. Процесс
происходит двухуровнево: аллокатор выделяет в памяти ячейки,
которые после занимают сами данные или индексы спейсов.
Зарезервированная под занятые или ещё не занятые ячейки память
отображена в quota_used
, занятая на хранение данных и
индексов arena_used
(только данных
items_used
). Приближение к порогу
arena_used_ratio
или items_used_ratio
свидетельствует об окончании свободных зарезервированных ячеек
slab, приближение к порогу quota_used_ratio
об окончании доступного места для резервирования ячеек. Таким
образом, об окончании свободного места для хранения данных
свидетельствует приближение к порогу одновременно метрик
quota_used_ratio
и arena_used_ratio
. В
качестве порога обычно используют значение 90 %. В редких случаях в
логах могут появляться сообщения о невозможности выделить память
под ячейки или данные даже тогда, когда
quota_used_ratio
, arena_used_ratio
или
items_used_ratio
далеки от порогового значения. Это
может сигнализировать о дефрагментации данных в RAM, неудачном
выборе схем спейсов или неудачной конфигурации
slab-аллокатора. В такой ситуации необходима консультация
специалиста.
Рекомендации по мониторингу HTTP-нагрузки и операций на спейсах
также являются скорее общими, нежели специфичными для Tarantool.
Необходимо следить за количеством ошибок и временем обработки
запроса в зависимости от кода ответа, а также за резким
возрастанием нагрузки. Неравномерность нагрузки по операциям на
спейсы может свидетельствовать о неудачном выборе ключа
шардирования.
Заключение
Как этот материал, так и пакет metrics назвать
"всеохватными" и "универсальными" на данный момент нельзя.
Открытыми или находящимися на данный момент в разработке являются
вопросы метрик репликации, мониторинга движка vinyl, метрики event
loop и полная документация по уже существующим методам
metrics.
Не стоит забывать о том, что metrics
и
grafana-dashboard являются opensource-разработками.
Если при работе над своим проектом вы наткнулись на ситуацию,
которая не покрывается текущими возможностями пакетов, не
стесняйтесь внести предложение в Issues или поделиться
вашим решением в Pull Requests.
Надеюсь, что данный материал помог закрыть некоторые вопросы,
которые могли возникнуть при решении задачи мониторинга приложения
на базе Tarantool. А если какие-то из них так и не получили ответа,
всегда можно обратиться напрямую в наш чат в Telegram.
Полезные ссылки: