← Все заметки
Мониторинг и безопасность

Мониторинг Asterisk 13 через node_exporter textfile collector, Prometheus и Grafana

Production-подход к мониторингу Asterisk 13 через systemd timer, custom CLI collector, node_exporter textfile metrics, Prometheus и Grafana.

Статус: устаревшееОпубликовано: 2026-09-16Обновлено: 2026-09-16

Проверено на: AlmaLinux 8.10 · Asterisk 13.38.3 · chan_sip · node_exporter textfile collector · Prometheus · Grafana

Контекст

Для PBX недостаточно обычного Linux-мониторинга.

CPU, память и disk metrics показывают состояние хоста, но не отвечают на прикладные вопросы:

активен ли asterisk.service?
отвечает ли Asterisk CLI?
есть ли ожидаемый SIP listener?
сколько сейчас активных calls/channels?
сколько chan_sip peers online/offline?
доступны ли ожидаемые SIP trunks?
не перестал ли работать сам collector?

В production-схеме эти проверки выполняет короткоживущий collector, который раз в 30 секунд опрашивает локальный Asterisk CLI и публикует метрики через textfile collector node_exporter.

Цепочка выглядит так:

Asterisk 13.38.3 / chan_sip
        |
        v
fm-asterisk-metrics.timer
        |
        v
fm-asterisk-metrics.service
        |
        v
/usr/local/sbin/fm-asterisk-metrics.sh
        |
        +--> fm_asterisk.prom
        +--> fm_asterisk_collector.prom
        |
        v
node_exporter textfile collector
        |
        v
Prometheus
        |
        v
Grafana / alerting

Статья помечена как legacy, потому что действующий сервер работает на Asterisk 13.38.3. Сам monitoring сейчас production и реально используется, но Asterisk 13 давно снят с upstream support.

Для будущего Asterisk 22 нужен отдельный PJSIP-aware monitoring, а не перенос текущего chan_sip parser без изменений.

Подтверждённый production baseline

OS: AlmaLinux 8.10
Asterisk: 13.38.3
SIP stack в collector: chan_sip
Asterisk service: asterisk.service
collector: /usr/local/sbin/fm-asterisk-metrics.sh
collector service: fm-asterisk-metrics.service
collector timer: fm-asterisk-metrics.timer
collection interval: 30 секунд
node_exporter listener: один внутренний management address, TCP/9100
textfile directory: /var/lib/node_exporter/textfile_collector

Текущие output files:

fm_asterisk.prom
fm_asterisk_collector.prom

Разделение application data и collector state на два файла — важная часть схемы.

Timer и oneshot service

Timer использует:

[Timer]
OnBootSec=30s
OnUnitActiveSec=30s
AccuracySec=1s
Unit=fm-asterisk-metrics.service

Сам service — Type=oneshot:

[Service]
Type=oneshot
User=root
Group=root
ExecStart=/usr/local/sbin/fm-asterisk-metrics.sh
TimeoutStartSec=25s
Nice=10

Поэтому inactive (dead) между успешными запусками — нормальное состояние. Health определяется состоянием timer, последним exit status и свежестью метрик.

TimeoutStartSec=25s также не даёт зависшему collector пересекаться бесконечно со следующим 30-секундным запуском.

systemd hardening

В production unit включены:

NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictRealtime=true
ReadWritePaths=/var/lib/node_exporter/textfile_collector

Collector сейчас работает от root, потому что ему нужны локальные process/socket checks и Asterisk CLI. При этом запись в filesystem ограничена каталогом textfile collector.

Перевод на отдельную service account возможен позже, но только после проверки доступа ко всем нужным локальным данным без расширения других прав.

Host guard

В скрипте есть явная проверка hostname перед сбором метрик.

Логика выглядит так:

EXPECTED_HOST="pbx01"
HOST="$(hostname -s)"
[[ "$HOST" == "$EXPECTED_HOST" ]] || fail "wrong host"

Это защищает от ситуации, когда environment-specific collector случайно копируют на другой сервер и он начинает публиковать правдоподобные, но неверные metrics.

Реальный production hostname в публичной статье не публикуется.

Минус такого подхода очевиден: перенос collector на другой PBX требует осознанно изменить ожидаемый hostname.

Ограниченные по времени Asterisk CLI calls

Application metrics собираются Asterisk CLI-командами, обёрнутыми в /usr/bin/timeout.

В текущей реализации используются эквиваленты:

timeout 10 asterisk -rx 'core show uptime'
timeout 10 asterisk -rx 'core show channels count'
timeout 10 asterisk -rx 'sip show peers'

Это важная failure boundary: зависший CLI не может навсегда заблокировать timer-driven collector.

Collector завершает run как failed, если ожидаемая CLI-команда не выполнилась или её output не удалось корректно распарсить.

Service и process health

Collector публикует:

fm_asterisk_service_up
fm_asterisk_processes
fm_asterisk_cli_up

Эти метрики покрывают разные классы отказов.

Например:

Их лучше показывать отдельно, а не сворачивать в один synthetic status.

Проверка SIP listener

Production collector проверяет ожидаемый UDP/5060 socket и дополнительно убеждается, что им владеет процесс Asterisk.

Публично это можно представить так:

ss -H -lunp |
  grep -F '<pbx-management-ip>:5060' |
  grep -F 'asterisk'

и metric:

fm_asterisk_sip_listener_up{transport="udp",port="5060"} 1

Реальный внутренний IP в публичной статье не публикуется.

Это лучше, чем обычный ss | grep 5060, потому что проверяется и bind address, и owner process.

Uptime и время с последнего reload

Collector парсит core show uptime и публикует:

fm_asterisk_uptime_seconds
fm_asterisk_reload_age_seconds

Shell helper переводит текст с weeks/days/hours/minutes/seconds в целое число секунд.

fm_asterisk_reload_age_seconds особенно полезен при расследовании: можно быстро увидеть, начался ли инцидент после reload конфигурации.

Parser зависит от формата CLI output Asterisk 13. При обновлении платформы его нужно regression-test’ить, а не считать автоматически совместимым.

Channels и calls

core show channels count преобразуется в:

fm_asterisk_active_channels
fm_asterisk_active_calls
fm_asterisk_calls_processed_total

fm_asterisk_calls_processed_total объявлен как Prometheus counter, потому что это число обработанных calls с момента старта Asterisk.

Для rate используйте:

rate(fm_asterisk_calls_processed_total[5m])

или:

increase(fm_asterisk_calls_processed_total[1h])

Не стоит alert’ить по raw cumulative value. После restart Asterisk counter закономерно сбрасывается, а Prometheus counter functions умеют учитывать resets.

Состояние chan_sip peers

Production collector выполняет:

sip show peers

и парсит summary в:

fm_asterisk_sip_peers_total
fm_asterisk_sip_monitored_online
fm_asterisk_sip_monitored_offline
fm_asterisk_sip_unmonitored_online
fm_asterisk_sip_unmonitored_offline

В реальном production snapshot есть десятки monitored peers и ненулевое количество offline peers.

Это важный момент: offline peer count не должен автоматически считаться аварией. Телефон может быть выключен, пользователь отсутствовать, remote endpoint может быть отключён намеренно.

Alert должен учитывать ожидаемую population endpoints или конкретные critical peers, а не правило offline > 0 для всех случаев.

Parser завязан на chan_sip summary format и не подходит для будущего PJSIP-based Asterisk 22.

Мониторинг SIP trunks

Текущий collector фильтрует provider SIP peers по определённому prefix из sip show peers и публикует aggregate и per-trunk metrics.

Сейчас используются:

fm_asterisk_multifon_peers
fm_asterisk_multifon_ok
fm_asterisk_multifon_problem
fm_asterisk_trunk_up{trunk="<sanitized-trunk-name>"}

Реальные имена production trunks в публичную статью лучше не выносить.

Collector считает peer healthy, если строка sip show peers содержит status OK (...).

Это нужно трактовать правильно: это reachability/qualify-style signal, а не доказательство полноценной телефонии.

Trunk может отвечать на SIP OPTIONS, но реальный call path всё равно может не работать из-за authentication, routing, dialplan, provider или RTP.

Synthetic calls — отдельный monitoring layer.

Разделение collector state и application data

Сильная сторона текущей реализации — разделение:

fm_asterisk.prom
fm_asterisk_collector.prom

Основной data file содержит последнюю валидную application snapshot.

State file содержит:

fm_asterisk_collector_success
fm_asterisk_collector_timestamp_seconds

При любой ошибке fail-handler записывает:

fm_asterisk_collector_success 0

с новым collector timestamp.

Старый application data file при этом остаётся нетронутым.

Так различаются три состояния:

collector вообще перестал запускаться
collector запускается, но падает
collector здоров и application data свежие

Freshness данных

В успешный application snapshot также пишется:

fm_asterisk_data_timestamp_seconds

Получается два независимых timestamp:

Полезные проверки:

time() - fm_asterisk_collector_timestamp_seconds > 120

означает, что collector/timer устарел.

fm_asterisk_collector_success == 0

означает, что последний run collector завершился ошибкой.

time() - fm_asterisk_data_timestamp_seconds > 120

означает, что валидные application metrics перестали обновляться.

Это устойчивее, чем один success gauge.

Atomic publication

Оба output file создаются через mktemp в target directory и публикуются через mv -f.

Принцип:

TMP="$(mktemp /var/lib/node_exporter/textfile_collector/.fm_asterisk.XXXXXX)"
# complete write
mv -f "$TMP" /var/lib/node_exporter/textfile_collector/fm_asterisk.prom

Temporary names не заканчиваются на .prom, поэтому node_exporter не пытается читать частично записанный файл.

Это соответствует рекомендованному atomic-write pattern для textfile collector.

Интеграция с node_exporter

Production node_exporter работает под отдельной account и явно использует textfile directory:

/usr/local/bin/node_exporter \
  --web.listen-address=<pbx-management-ip>:9100 \
  --collector.textfile.directory=/var/lib/node_exporter/textfile_collector

В отличие от ранее разобранного 1С-host, здесь node_exporter уже bind’ится на один внутренний management address, а не на 0.0.0.0.

Firewall/routing всё равно должен разрешать TCP/9100 только monitoring path.

Проверка полной цепочки

Проверяем timer:

systemctl status fm-asterisk-metrics.timer --no-pager
systemctl list-timers fm-asterisk-metrics.timer --all

Делаем один ручной run:

systemctl start fm-asterisk-metrics.service
systemctl status fm-asterisk-metrics.service --no-pager

Для успешного oneshot ожидается exit status 0, после чего unit вернётся в inactive (dead).

Проверяем files:

cat /var/lib/node_exporter/textfile_collector/fm_asterisk_collector.prom
cat /var/lib/node_exporter/textfile_collector/fm_asterisk.prom

При наличии promtool проверяем exposition syntax:

cat /var/lib/node_exporter/textfile_collector/fm_asterisk.prom |
  promtool check metrics

И затем node_exporter:

curl -fsS http://<pbx-management-ip>:9100/metrics |
  grep '^fm_asterisk_'

Полезные alert rules

Точные durations и ожидания зависят от роли PBX, но структура может быть такой.

Collector перестал запускаться

time() - fm_asterisk_collector_timestamp_seconds > 120

Последний run failed

fm_asterisk_collector_success == 0

Application data устарели

time() - fm_asterisk_data_timestamp_seconds > 120

Asterisk service или CLI недоступен

fm_asterisk_service_up == 0
fm_asterisk_cli_up == 0

SIP listener отсутствует

fm_asterisk_sip_listener_up{transport="udp",port="5060"} == 0

Критичный provider trunk недоступен

fm_asterisk_trunk_up{trunk="<critical-trunk>"} == 0

Не стоит использовать универсальное правило:

fm_asterisk_sip_monitored_offline > 0

если не гарантировано, что каждый monitored endpoint обязан быть online постоянно.

Структура Grafana dashboard

Практичный dashboard можно построить так:

PBX overview
  -> service / process / CLI state
  -> collector state и freshness
  -> uptime / reload age
  -> active calls / channels
  -> call processing rate

SIP
  -> listener state
  -> peer totals и online/offline split
  -> provider trunk aggregate state
  -> critical per-trunk state

Host
  -> CPU / RAM / filesystem / network
  -> node_exporter availability

Так application signals остаются рядом с host metrics, но не смешиваются в одну модель.

Что этот monitoring не доказывает

Collector даёт полезное состояние PBX, но не подтверждает end-to-end telephony service.

Он не доказывает, что:

входящий DID доходит до нужного extension
исходящий call реально проходит через provider
RTP audio работает в обе стороны
dialplan корректно маршрутизирует каждый call
DTMF работает
IVR/application flow проходит полностью

Для этого нужны synthetic calls, provider-side monitoring, RTP/media checks или application-specific tests.

Граница миграции Asterisk 13 -> Asterisk 22

Этот collector нельзя просто перенести на Asterisk 22 без изменений.

Текущая реализация зависит от:

chan_sip
sip show peers
формата CLI output Asterisk 13
provider rows со status OK (...)

Asterisk 22 — LTS release, а Asterisk 13 уже upstream EOL. Поэтому для Asterisk 22 monitoring нужно проектировать вокруг реальных PJSIP objects после миграции.

При переносе надо сохранить monitoring intent, а не exact command syntax:

service health
CLI health
SIP/PJSIP transport state
endpoint/AOR/contact state
trunk state
active channels/calls
call throughput
collector freshness

Новый collector должен быть проверен на реальном Asterisk 22 CLI output до замены legacy source.

Rollback и обслуживание

Перед изменением collector сохраняйте:

/etc/systemd/system/fm-asterisk-metrics.service
/etc/systemd/system/fm-asterisk-metrics.timer
/usr/local/sbin/fm-asterisk-metrics.sh
node_exporter unit / drop-ins
Prometheus rules
Grafana dashboards/provisioning

Изменение monitoring collector не должно требовать изменения самой Asterisk configuration.

После Asterisk update или изменения CLI behavior перепроверьте:

core show uptime parser
core show channels count parser
sip show peers summary parser
provider-trunk row parser
metric exposition syntax
collector/data timestamps

Проверочный список

[ ] fm-asterisk-metrics.timer active
[ ] latest oneshot run exited 0
[ ] collector success = 1
[ ] collector timestamp свежий
[ ] data timestamp свежий
[ ] Asterisk service/process/CLI metrics совпадают с реальностью
[ ] SIP listener соответствует реальному socket
[ ] channel/call counters меняются во время реальных calls
[ ] peer summary совпадает с Asterisk CLI
[ ] critical trunk states совпадают с ожидаемыми peers
[ ] node_exporter публикует fm_asterisk_* только по monitoring path
[ ] alerts различают failure collector и failure PBX

Ссылки