zalua
zalua copied to clipboard
Сборщик метрик
ZaLua
Цели:
- легко расширяемый мониторинг написаный на скриптовом языке
- использовать транспорт zabbix-agent
- публиковать метрики для prometheus
Решение:
толстый go-бинарь с встроенным lua-интерпретатором
Запуск и получение метрик
При запуске проверяется что по сокету /tmp/zalua-mon.sock отвечает сервер на ping-pong сообщение,
иначе запускается демон-сервер который начинает слушать этот сокет.
демон пытает запустить /etc/zalua/init.lua dsl которого описан ниже.
Доступные команды клиента:
-v, -version, --version
Get version
-e, --execute-file, execute file (without server)
Execute dsl from file (for testing case)
-k, -kill, --kill, kill
Kill server
-m, -metrics, --list-metrics, metrics
List of known metrics
-p, -plugins, --plugins, plugins
List of running plugins
-g, -get, --get, --get-metric, get <metric>
Get metric value
-ping, --ping, ping
Ping pong game
Пример вывода:
$ zalua -p
/etc/zalua/plugins/bad_plugin.lua false /etc/zalua/plugins/buddyinfo_plugin.lua at 1: parse error
/etc/zalua/plugins/numa_plugin.lua false <no error>
/etc/zalua/plugins/snmp_plugin.lua true <no error>
$ zalua -m
system.disk.read_bytes[/video] 23454385.65 1495748781
system.disk.read_ops[/video] 46.46 1495748781
system.disk.utilization[/video] 51.02 1495748781
system.disk.write_bytes[/video] 6872556.41 1495748781
system.disk.write_ops[/video] 25.72 1495748781
runit.problem Found problem with runit services: 'nginx' has linked, but isn't running 1495748781
$ zalua -g system.tcp.active
0.00
$ zalua -g 'system.disk.write_ops point=/video'
25.72
Пример UserParameter:
UserParameter=disk.utilization[*], /usr/bin/zalua -g system.disk.$1.utilization
lua-DSL
-
plugin:
p = plugin.new(filename)загрузить плагинp:run()запустить плагинp:stop()остановить плагин вызвав ошибкуp:is_running()запущен или нет плагинp:error()текст последний ошибки или nilp:was_stopped()остановлен ли был плагин при помощи stop()
-
metrics:
metrics.set(key, val, [<ttl>, <tags>||<tags>, <ttl>])установить значение метрики key, val может быть string, number. ttl по дефолту 300 секундmetrics.set_speed(key, val, ...)тоже самое, но считает скорость измеренияmetrics.set_counter_speed(key, val, ...)тоже самое, но считает только положительную скорость измеренияmetrics.get(key, [<tags>])получить значение метрики keymetrics.list()список{key:"xxx", value:"xxx", at:xxx, tags: {"k":"v"}}metrics.delete(key, [<tags>])удалить значение метрики key
-
prometheus:
prometheus.listen(str)разместить метрики для prometheus на указанном адресе
-
prometheus_gauge:
gauge = prometheus_gauge.new({help = "help..", name = "name..", "namespace" = "...", "subsystem" = "..."})зарегистрировать gaugegauge:add(number)gauge:set(number)
-
prometheus_gauge_labels:
gauge, err = prometheus_gauge_labels.new({..., labels = {"vec1", "vec2"}})зарегистрировать vector gauge, перерегистрация может вызвать ошибку если векторы не совпадаютgauge:add({vec1 = "value_vec_1", vec2 = "value_vec_2"}, number)gauge:set(...)
-
prometheus_counter:
counter = prometheus_counter.new({help = "help..", name = "name..", "namespace" = "...", "subsystem" = "..."})зарегистрировать countercounter:inc()counter:add(number)
-
prometheus_counter_lables:
counter, err = prometheus_counter_lables.new({..., labels = {"vec1", "vec2"}})зарегистрировать vector counter, перерегистрация может вызвать ошибку если векторы не совпадаютcounter:inc({vec1 = "value_vec_1", vec2 = "value_vec_2"})counter:add(..., number)
-
postgres:
db, err = postgres.open({database="xxx", host="127.0.0.1", user="xxx", password="xxx"})открыть коннектrows, err, column_count, row_count = db:query()выполнить запросdb:close()закрыть коннект
-
tcp:
telnet, err = tcp.open("xxx:xxx")открыть коннектerr = telnet:write("xxxx")записать в коннектtelnet:close()закрыть коннект
-
tac:
scanner = tac.open("filepath")открыть файлscanner:line()получить последнюю линию (closure), в случае отсутвия таковой вернеться nilscanner:close()закрыть файл
-
ioutil:
ioutil.readfile(filename)вернуть содержимое файла
-
crypto:
crypto.md5(string)вернуть md5 от строки
-
cmd:
state, err = cmd.exec(string)выполнить exec через shell, возвращает{"code"=0, "stderr"="", "stdout"=""}
-
goruntime:
goruntime.goosпорт runtime.GOOSgoruntime.goarchпорт runtime.GOARCH
-
parser: позволяет загрузить при помощи https://golang.org/pkg/plugin/ библиотеку с переменная которая вернет интерфейс:
type Parser interface { ProcessData(string) (map[string]string, error) }p, err = parser.load(filename.so, variable_name="NewParser")загрузить плагин с экспортированным именемvariable_nameв filename.sotable, err = p:parse(str)обработать строчку
-
strings:
strings.split(str, delim)порт golang strings.split()strings.has_prefix(str1, str2)порт golang strings.hasprefix()strings.has_suffix(str1, str2)порт golang strings.hassuffix()strings.trim(str1, str2)порт golang strings.trim()
-
json:
json.encode(N)lua-table в stringjson.decode(N)string в lua-table
-
yaml:
yaml.decode(N)string в lua-table
-
filepath:
filepath.base(filename)порт golang filepath.Base()filepath.dir(filename)порт golang filepath.Dir()filepath.ext(filename)порт golang filepath.Ext()filepath.glob(mask)порт golang filepath.Glob(), в случае ошибки возращает nil.
-
xmlpath:
table, err = xmlpath.parse(data, path)возвращает таблицу с обработанойdataпо xmlpathpath
-
http:
result, err = http.get(url)возвращаетresult = {body, code}и ошибку, захардкожен 10секундный таймаут.result, err = http.unescape(url)порт url.QueryUnescape(query)result = http.escape(url)порт url.QueryEscape(query)
-
goos:
stat = goos.stat(filename)goos.stat возвращает таблицу с полямиstat = {size, is_dir, mod_time}, в случае ошибки возращает nil.goos.pagesize()возвращет pagesize
-
syscall:
stat, err = syscall.statfs("/mount/point")возвращаетstat = {size, free, avail, files, files_free}
-
time:
time.sleep(N)проспать N секундtime.unix()время в секундахtime.unix_nano()время в наносекундахtime.parse("2006-Jan-02", "2018-Mar-02")golang порт time.Parse(), возвращает unixts и ошибку
-
log:
log.error(msg)сообщение в лог с уровнем errorlog.info(msg)с уровнем info
-
tls_util:
tls_util.cert_not_after("server_name", ["server_name:443" || "address:port"])возвращает unixts и ошибку
-
human:
human.time(int)возвращает время в текстовом формате
Примеры плагинов
Diskstat

- пытается сопоставить блочному девайсу /mount/pount
- рассчитывает await и utilization по тем блочным девайсам, по которым ядро не ведет статистику (mdraid)
IO

- суммирует /proc/pid/io
- рассчитывает эффективность чтения из vfs cache как соотношение логического и физического чтения rchar/read_bytes
SNMP
данные из /proc/net/snmp

CPU
cpu time (не нормированное по кол-ву CPU)

состояние процессов

interrupts и context switching

NUMA
статистика выделения памяти и доступа к ней
