использовать textfile collector: скрипт пишет метрики в *.prom, node_exporter подмешивает их в /metrics, Prometheus их скрейпит, а Grafana рисует панели по PromQL-запросам. [github](https://github.com/prometheus/node_exporter)
Схема
node_exporter поддерживает textfile collector именно для batch/cron-задач и machine-level метрик, которые не нужно вычислять на каждом scrape, поэтому для результатов bash-скриптов это обычно лучше, чем Pushgateway. [github](https://github.com/prometheus/node_exporter)
Поток такой:
- cron запускает bash-скрипт;
- скрипт формирует файл
something.prom; node_exporterчитает все*.promиз заданной директории;- Prometheus получает эти метрики через обычный scrape
:9100/metrics; - Grafana строит графики и алерты по этим метрикам. [janikvonrotz](https://janikvonrotz.ch/2020/09/07/monitor-cron-jobs-with-prometheus-grafana-and-node-exporter/)
Node Exporter
Нужно запустить node_exporter с флагом --collector.textfile.directory=/var/lib/node_exporter/textfile_collector или с любым другим каталогом, который ты выберешь; collector читает все файлы с расширением *.prom из этой директории. [blog.csdn](https://blog.csdn.net/qq_40310224/article/details/142707327)
Пример для systemd:
[Unit]
Description=Node Exporter
After=network.target
[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter \
--collector.textfile.directory=/var/lib/node_exporter/textfile_collector
[Install]
WantedBy=multi-user.target
После этого создай каталог и выдай права так, чтобы cron-скрипт мог туда атомарно писать, а node_exporter — читать. Самое важное: писать нужно атомарно, то есть сначала во временный файл, потом mv в итоговый .prom, чтобы exporter не прочитал файл посередине записи. [github](https://github.com/prometheus-community/node-exporter-textfile-collector-scripts/blob/master/README.md)
Команды:
sudo mkdir -p /var/lib/node_exporter/textfile_collector
sudo chown root:node_exporter /var/lib/node_exporter/textfile_collector
sudo chmod 775 /var/lib/node_exporter/textfile_collector
sudo systemctl daemon-reload
sudo systemctl restart node_exporter
sudo systemctl status node_exporter
Проверка, что collector включен:
ps aux | grep node_exporter
curl -s http://127.0.0.1:9100/metrics | grep textfile | head
Если каталог подключен правильно, в выдаче обычно будут метрики самого textfile collector, а после появления твоего .prom появятся и кастомные метрики. node_exporter действительно добавляет содержимое *.prom в общий /metrics. [dbi-services](https://www.dbi-services.com/blog/monitoring-short-living-processes-with-prometheus/)
Формат метрик
Файл должен быть в Prometheus text exposition format, а имя файла обязано оканчиваться на .prom, иначе textfile collector его не обработает. [github](https://github.com/prometheus/node_exporter)
Минимальный пример:
myjob_exit_code 0
myjob_duration_seconds 2.418
myjob_last_run_unixtime 1784031000
Лучше сразу использовать HELP и TYPE:
# HELP myjob_exit_code Exit code of cron job (0=success)
# TYPE myjob_exit_code gauge
myjob_exit_code{job="backup_db"} 0
# HELP myjob_duration_seconds Duration of cron job in seconds
# TYPE myjob_duration_seconds gauge
myjob_duration_seconds{job="backup_db"} 12.43
# HELP myjob_last_run_unixtime Last successful or attempted run time
# TYPE myjob_last_run_unixtime gauge
myjob_last_run_unixtime{job="backup_db"} 1784031000
Для cron-задач почти всегда достаточно gauge, потому что ты сохраняешь последнее состояние, длительность, timestamp и exit code. Подход с записью timestamp/exit code для короткоживущих задач прямо рекомендуется для таких сценариев. [janikvonrotz](https://janikvonrotz.ch/2020/09/07/monitor-cron-jobs-with-prometheus-grafana-and-node-exporter/)
Bash-скрипт
Ниже рабочий шаблон для cron-задачи. Он пишет три полезные метрики: код выхода, длительность и время последнего запуска. Атомарная запись сделана через временный файл и mv, что соответствует рекомендуемой практике для textfile collector. [github](https://github.com/prometheus-community/node-exporter-textfile-collector-scripts/blob/master/README.md)
#!/usr/bin/env bash
set -euo pipefail
OUTDIR="/var/lib/node_exporter/textfile_collector"
JOB="backup_db"
TMPFILE="$(mktemp "${OUTDIR}/${JOB}.prom.XXXXXX")"
FINALFILE="${OUTDIR}/${JOB}.prom"
start_ts=$(date +%s)
run_job() {
/usr/local/bin/backup-db.sh
}
exit_code=0
if ! run_job; then
exit_code=$?
fi
end_ts=$(date +%s)
duration=$(( end_ts - start_ts ))
cat > "$TMPFILE" <<EOF
# HELP cron_job_exit_code Exit code of cron job (0=success)
# TYPE cron_job_exit_code gauge
cron_job_exit_code{job="${JOB}"} ${exit_code}
# HELP cron_job_duration_seconds Duration of cron job in seconds
# TYPE cron_job_duration_seconds gauge
cron_job_duration_seconds{job="${JOB}"} ${duration}
# HELP cron_job_last_run_unixtime Last run timestamp
# TYPE cron_job_last_run_unixtime gauge
cron_job_last_run_unixtime{job="${JOB}"} ${end_ts}
EOF
mv "$TMPFILE" "$FINALFILE"
exit 0
Если хочешь хранить еще и “успех/ошибка” в бинарном виде, добавь:
success=0
[[ $exit_code -eq 0 ]] && success=1
и метрику:
# HELP cron_job_success 1 if last run succeeded
# TYPE cron_job_success gauge
cron_job_success{job="backup_db"} 1
Пример cron:
*/5 * * * * /usr/local/sbin/cron-backup-exporter.sh
Практический момент: если метрика больше не нужна, файл .prom надо удалить, иначе node_exporter продолжит отдавать старые значения при каждом scrape. [reddit](https://www.reddit.com/r/grafana/comments/1n7bnfc/anyone_using_node_exporter_with_the_textfile/)
Проверка в Prometheus
Сначала проверь локально на ноде, что файл реально попал в exporter:
cat /var/lib/node_exporter/textfile_collector/backup_db.prom
curl -s http://127.0.0.1:9100/metrics | grep '^cron_job_'
Если здесь пусто — проблема не в Prometheus и не в Grafana, а в правах, формате файла или пути --collector.textfile.directory. [dbi-services](https://www.dbi-services.com/blog/monitoring-short-living-processes-with-prometheus/)
Дальше проверь на сервере Prometheus, что target node_exporter в статусе UP; затем в UI Prometheus выполни запросы:
cron_job_exit_code
cron_job_duration_seconds
cron_job_last_run_unixtime
Полезные рабочие запросы:
- Последний exit code по job:
max by (instance, job) (cron_job_exit_code)[janikvonrotz](https://janikvonrotz.ch/2020/09/07/monitor-cron-jobs-with-prometheus-grafana-and-node-exporter/) - Только ошибки:
max by (instance, job) (cron_job_exit_code) > 0 - Возраст последнего запуска в секундах:
time() - cron_job_last_run_unixtime - Задачи, которые давно не запускались, например больше 15 минут:
(time() - cron_job_last_run_unixtime) > 900
Если метрика видна на :9100/metrics, но не видна в Prometheus, смотри:
- target labels и
instance; scrape_interval;- relabeling;
- не режется ли job по
metric_relabel_configs.
Prometheus получает эти данные обычным scrape node_exporter, отдельный job для cron-метрик не нужен, если они уже встроены в /metrics. [github](https://github.com/prometheus/node_exporter)
Пример в Grafana
В Grafana добавь panel с источником Prometheus и используй такие запросы.
1. Таблица статуса cron-задач
Для Table panel:
max by (instance, job) (cron_job_exit_code)
Смысл простой: 0 — ок, >0 — ошибка; такой же пример с агрегацией по instance приводится в материале по мониторингу cron через Grafana и node_exporter. [janikvonrotz](https://janikvonrotz.ch/2020/09/07/monitor-cron-jobs-with-prometheus-grafana-and-node-exporter/)
Рекомендуемые настройки:
- Visualization:
Table - Value mappings:
0 => OK,1-255 => FAIL - Thresholds: green
0, red1
2. Время выполнения
Для Time series:
cron_job_duration_seconds{job="backup_db"}
Если несколько хостов:
max by (instance, job) (cron_job_duration_seconds)
3. Давность последнего запуска
Для Stat panel:
time() - cron_job_last_run_unixtime{job="backup_db"}
Настрой unit = seconds (s) или duration, и thresholds, например:
- green:
< 600 - yellow:
600-1800 - red:
> 1800
4. Последний успешный запуск
Если пишешь cron_job_success:
max by (instance, job) (cron_job_success)
Тогда в Stat/Table легко видеть “1 = success, 0 = failed”.
Пример дашборда
Хорошая практичная раскладка для Grafana:
- 1-й ряд:
Stat— “Возраст последнего запуска”, “Последний exit code”, “Последняя длительность”. - 2-й ряд:
Time series— графикcron_job_duration_secondsпо времени. - 3-й ряд:
Table— все cron job по всем серверам:max by (instance, job) (cron_job_exit_code). [janikvonrotz](https://janikvonrotz.ch/2020/09/07/monitor-cron-jobs-with-prometheus-grafana-and-node-exporter/)
Пример для переменной $instance:
max by (job) (cron_job_exit_code{instance="$instance"})
Пример для всех задач на выбранном хосте:
cron_job_duration_seconds{instance="$instance"}
Лучшие практики
- Пиши метрики в отдельный
.promна каждую задачу или на логически связанную группу задач, чтобы было проще дебажить. [dbi-services](https://www.dbi-services.com/blog/monitoring-short-living-processes-with-prometheus/) - Используй labels аккуратно:
job="backup_db"хорошо, а вотmessage="full stderr text"плохо, потому что это взрывает cardinality. Это общий принцип Prometheus-подхода, а для textfile collector он особенно важен. [github](https://github.com/prometheus/node_exporter) - Для атомарной записи используй
mvилиsponge; в community scripts прямо рекомендуют атомарную запись. [github](https://github.com/prometheus-community/node-exporter-textfile-collector-scripts/blob/master/README.md) - Для cron полезнее всего экспортировать не stdout скрипта, а уже нормализованный результат: код выхода, длительность, timestamp, размер обработанных данных, число ошибок. [github](https://github.com/prometheus/node_exporter)
- Если задача не запускается по расписанию, ты увидишь это не по exit code, а по “старому”
last_run_unixtime, поэтому timestamp-метрика обязательна для cron-мониторинга. [dbi-services](https://www.dbi-services.com/blog/monitoring-short-living-processes-with-prometheus/)
Готовый минимальный сценарий
1. Включаешь --collector.textfile.directory. [linkedin](https://www.linkedin.com/pulse/monitoring-stack-setup-part-3-node-exporter-shishir-khandelwal)
2. Cron-скрипт пишет backup_db.prom в формате Prometheus text. [github](https://github.com/prometheus/node_exporter)
3. Проверяешь:
curl -s http://127.0.0.1:9100/metrics | grep backup_db
4. В Prometheus проверяешь:
cron_job_exit_code{job="backup_db"}
5. В Grafana делаешь:
- Stat:
time() - cron_job_last_run_unixtime{job="backup_db"} - Time series:
cron_job_duration_seconds{job="backup_db"} - Table:
max by (instance, job) (cron_job_exit_code)