использовать 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)

Поток такой:

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, red 1

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"}

Лучшие практики

Готовый минимальный сценарий

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)