Grafana: установка и настройка, дашборды, Prometheus и Zabbix

Grafana: установка и настройка, дашборды, Prometheus и Zabbix
Быстрый ответ
Grafana сама ничего не собирает и не хранит. Это витрина: она рисует графики из данных, которые лежат в Prometheus, Zabbix, PostgreSQL и других источниках. Рабочая установка сводится к четырём шагам:
  • Поставить Grafana из официального репозитория или запустить образ grafana/grafana в Docker.
  • Зайти на порт 3000, сменить пароль admin и закрыть регистрацию.
  • Добавить источник данных: Prometheus для метрик серверов или плагин Zabbix для готового мониторинга.
  • Импортировать готовый дашборд или собрать свой, затем спрятать Grafana за nginx с HTTPS.

Пустые панели с надписью No data почти всегда означают неверный адрес источника или выбранный не тот временной диапазон.

Установка Grafana на Ubuntu 24.04 из официального репозитория занимает пять команд:


# На сервере grafana-01 (Ubuntu 24.04): установка Grafana OSS из репозитория Grafana Labs
sudo apt-get install -y apt-transport-https wget gnupg
sudo mkdir -p /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt-get update && sudo apt-get install -y grafana && sudo systemctl enable --now grafana-server

Диагноз: почему Grafana после установки показывает пустоту

Поставил Grafana, открыл порт 3000, залогинился. А там пусто: ни графиков, ни метрик, ни одного сервера. Знакомо?

Смотри. Grafana — это не система мониторинга в чистом виде. Это слой визуализации. Она не опрашивает серверы, не хранит историю и не знает ничего о твоей инфраструктуре, пока ты не подключишь к ней источник данных. Метрики живут в Prometheus, в Zabbix, в PostgreSQL, в InfluxDB. Grafana приходит к ним с запросом и рисует ответ.

Отсюда главная ошибка новичков: поставить Grafana и ждать, что она «сама что-то покажет». Не покажет. Система мониторинга на Grafana — это всегда связка: сборщик плюс хранилище плюс витрина.

Что получишь после статьи. Рабочую Grafana 13 двумя способами: на пакетах и в Docker Compose. Связку с Prometheus и node_exporter для метрик Linux. Подключение Zabbix через официальный плагин, чтобы видеть проблемы и графики Zabbix в одном дашборде. HTTPS через nginx, Grafana на PostgreSQL вместо SQLite, бэкап, обновление и разбор ошибок.

Время: 40-60 минут на всё. Если тебе нужна только Grafana с Zabbix, уложишься в 20.

Что понадобится:

  • Сервер или виртуалка grafana-01 с Ubuntu 24.04, адрес 10.10.0.20, 2 vCPU и 2-4 ГБ памяти.
  • Root или sudo на этой машине.
  • Для связки с Zabbix: работающий Zabbix server 7.0 на zbx-server, 10.10.0.10, и права администратора в его веб-интерфейсе.
  • Домен или внутреннее DNS-имя для HTTPS, например grafana.example.local.

Что будет в статье:

  • Архитектура: кто кого опрашивает и где лежат данные.
  • Семь причин, почему панели показывают No data.
  • Установка Grafana на пакетах и в Docker Compose вместе с Prometheus.
  • Плагин Zabbix для Grafana: настройка с API-токеном и готовые дашборды.
  • Свой дашборд с переменными, nginx с HTTPS, PostgreSQL как база Grafana, работа с API.
  • Проверка, ошибки с командами, аналоги Grafana.
  • Мониторинг самой Grafana, бэкап, обновление и FAQ.

Архитектура: где Grafana, а где данные

Посмотри на схему до установки. Половина вопросов «почему не работает» снимается, когда понятно, кто к кому ходит.

%%{init: {
  'theme': 'base',
  'themeVariables': {
    'primaryColor': '#ffffff',
    'primaryTextColor': '#1e293b',
    'primaryBorderColor': '#94a3b8',
    'lineColor': '#64748b',
    'fontSize': '15px',
    'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
  },
  'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
    U["Браузер пользователя"] --> N["nginx с HTTPS"]
    N --> G["Grafana"]
    G --> GDB["База Grafana: дашборды и пользователи"]
    G --> P["Prometheus"]
    G --> Z["Zabbix API"]
    G --> S["PostgreSQL как источник"]
    NE["node_exporter на серверах"] --> P
    ZA["Агенты Zabbix"] --> ZS["Zabbix server"]
    ZS --> Z
    style U fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style N fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style G fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
    style GDB fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
    style P fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style Z fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style S fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style NE fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style ZA fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style ZS fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af

Вот тут важно: у Grafana есть своя база, но в ней лежат только дашборды, пользователи, настройки источников и правила алертов. Метрики там не хранятся. По умолчанию это файл SQLite в /var/lib/grafana/grafana.db. Потерял этот файл — потерял все дашборды, но ни одной точки на графиках.

Каждый раз, когда ты открываешь дашборд, происходит вот что:

%%{init: {
  'theme': 'base',
  'themeVariables': {
    'primaryColor': '#f8fafc',
    'primaryTextColor': '#1e293b',
    'primaryBorderColor': '#94a3b8',
    'noteBkgColor': '#fefce8',
    'noteTextColor': '#713f12',
    'noteBorderColor': '#fbbf24',
    'actorBkg': '#f8fafc',
    'actorBorder': '#94a3b8',
    'actorTextColor': '#1e293b',
    'fontSize': '15px',
    'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
  },
  'sequence': {
    'mirrorActors': false,
    'messageAlign': 'center',
    'actorMargin': 120,
    'width': 160,
    'noteMargin': 12
  }
}}%%
sequenceDiagram
    participant B as Браузер
    participant G as Grafana
    participant P as Prometheus
    B->>G: Открыть дашборд
    G->>P: Запрос PromQL за период
    P->>G: Временные ряды
    G->>B: Данные для панелей
    Note over G,P: Запрос идёт с сервера Grafana, не из браузера

Последняя строка схемы — самая важная. Адрес источника данных Grafana открывает со своего сервера. Если Grafana в контейнере, то localhost — это сам контейнер, а не хост. На этом спотыкается каждый второй, кто ставит Grafana в Docker.

Причины: почему в Grafana нет данных

Прежде чем ставить и настраивать, держи список того, что ломается чаще всего. Половина пунктов закрывается правильной установкой, вторая половина — пониманием схемы выше.

# Причина Почему ломает
1 Источник данных не добавлен или добавлен с ошибкой Панели есть, а спрашивать данные не у кого. Импортированный дашборд ищет источник, которого нет
2 Адрес источника указан как localhost при Grafana в Docker Контейнер стучится сам в себя и получает connection refused
3 Не тот временной диапазон или часовой пояс Данные есть, но за другой период. Расхождение часов на сервере сдвигает точки
4 Плагин Zabbix установлен, но не включён Без включения приложения тип источника Zabbix недоступен
5 У пользователя Zabbix нет прав на группы узлов API честно отвечает пустым списком хостов, и Grafana рисует пустоту
6 Prometheus не собирает цель или метки в запросе не те Запрос корректный, но совпадений нет. Цель в состоянии down
7 Неверный root_url за обратным прокси Интерфейс грузится частично, запросы к API уходят не туда, ловишь бесконечные редиректы

Третья причина стоит отдельного упоминания. Однажды я час разбирался, почему дашборд пустой у коллеги, а у меня полный. Оказалось, он в правом верхнем углу случайно выставил период «Last 5 minutes» на дашборде, где метрика собиралась раз в 15 минут. Grafana всё показывала правильно. Пустоту.

Рецепт: установка и настройка Grafana пошагово

Системные требования

На момент публикации актуальна Grafana 13.2.4 от 13 октября 2026 года. Ветка 13 вышла в апреле 2026, параллельно получают исправления безопасности 13.1, 13.0 и 12.4. Перед установкой проверь свежие релизы на странице загрузки Grafana и сроки поддержки веток на endoflife.date.

Компонент Версия на момент публикации Комментарий
ОС Ubuntu 24.04 LTS, Debian 12, RHEL 9 Любой современный 64-битный Linux
Grafana OSS 13.2.4 Пакет grafana, Docker-образ grafana/grafana
Prometheus 3.13.4 LTS или 3.15.0 Для продакшна бери LTS
node_exporter 1.12.1 Ставится на каждый наблюдаемый Linux-сервер
Плагин Zabbix 6.9.1 Требует Grafana 11.6 и новее
Zabbix server 7.0 LTS или 7.4 Для входа по API-токену нужен Zabbix 5.4 и новее
База Grafana SQLite по умолчанию, PostgreSQL 14+ PostgreSQL для нескольких инстансов и надёжности

Версии Prometheus и node_exporter сверяй на странице загрузки Prometheus, плагина Zabbix в релизах grafana-zabbix на GitHub.

Таблица портов

Порт Протокол Сервис Назначение Доступен снаружи?
443 TCP nginx HTTPS-доступ пользователей к Grafana Да, для пользователей
80 TCP nginx Редирект на HTTPS и выпуск сертификата Да, только редирект
3000 TCP Grafana Веб-интерфейс и API Нет, только localhost за nginx
9090 TCP Prometheus API запросов и веб-интерфейс Нет, только Grafana и админы
9100 TCP node_exporter Отдача метрик сервера Нет, только с адреса Prometheus
443 или 80 TCP Zabbix frontend API Zabbix для плагина Только с адреса Grafana
5432 TCP PostgreSQL База Grafana или прямое подключение к БД Zabbix Нет

Пакеты или Docker: что выбрать

Критерий Пакеты apt/dnf Docker Compose
Скорость первого запуска 5 минут 5 минут, если Docker уже стоит
Prometheus рядом Ставится отдельно Поднимается тем же файлом
Обновление apt upgrade Смена тега образа
Диагностика в 3 ночи Один процесс, один лог, systemd Плюс сеть контейнеров и права на тома
Когда выбирать Отдельная машина под Grafana Стек мониторинга целиком на одном хосте, лаб, быстрый старт

Мой выбор для продакшна — пакеты на отдельной виртуалке. Для стенда, домашней лаборатории или когда нужен сразу весь стек Grafana плюс Prometheus — Docker Compose. Ниже оба пути, выбирай один.

Подготовка: проверь среду

Время на сервере должно быть синхронизировано. Расхождение часов между Grafana, Prometheus и Zabbix даёт сдвинутые графики и странные дыры в данных.


# На сервере grafana-01: имя, ОС и синхронизация времени
hostnamectl
sudo apt-get update && sudo apt-get install -y chrony curl jq sqlite3
sudo systemctl enable --now chrony
timedatectl show -p NTPSynchronized

Результат: NTPSynchronized=yes. Если no, проверь, что UDP 123 до NTP-сервера не режется.

Если планируешь Zabbix, сразу проверь, что API Zabbix доступен с машины Grafana. Grafana будет ходить именно отсюда.


# На сервере grafana-01: отвечает ли API Zabbix
curl -s -X POST -H 'Content-Type: application/json-rpc' \
  -d '{"jsonrpc":"2.0","method":"apiinfo.version","params":{},"id":1}' \
  http://10.10.0.10/api_jsonrpc.php

Ожидаемый ответ: JSON с полем result и версией Zabbix, например 7.0.25. Если пусто или 404, путь к API другой. В пакетной установке Zabbix на Apache это часто /zabbix/api_jsonrpc.php.

Путь А, шаг 1. Установи Grafana из репозитория

Зачем официальный репозиторий: в репозиториях дистрибутивов Grafana либо нет, либо она устаревшая. Репозиторий Grafana Labs даёт актуальные версии и обновления безопасности через обычный apt upgrade.


# На сервере grafana-01 (Ubuntu/Debian): ключ и репозиторий Grafana Labs
sudo apt-get install -y apt-transport-https wget gnupg
sudo mkdir -p /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
sudo chmod 644 /etc/apt/keyrings/grafana.asc
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt-get update
sudo apt-get install -y grafana

Пакет grafana — это OSS-редакция. Есть ещё grafana-enterprise: бесплатная без лицензии, функционально равна OSS, а платные функции включаются ключом. Для внутреннего мониторинга бери grafana, лицензия AGPLv3 тут понятнее.

Для RHEL 9 репозиторий описывается файлом:


# На сервере grafana-01 (RHEL 9): репозиторий Grafana Labs и установка
sudo tee /etc/yum.repos.d/grafana.repo > /dev/null <<'EOF'
[grafana]
name=grafana
baseurl=https://rpm.grafana.com
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://rpm.grafana.com/gpg.key
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
EOF
sudo dnf install -y grafana

Результат: пакет встал, появились /etc/grafana/grafana.ini и служба grafana-server.

Если загрузка возвращает 451
С части российских адресов серверы Grafana Labs отдают ошибку 451 geofence blocked. Официально это фиксировали для Enterprise-сборок, но встречается и на других загрузках. Рабочие обходы: собственное зеркало репозитория в Nexus или Artifactory, загрузка пакета на машине за пределами блокировки с переносом через scp, или образ из корпоративного Docker-реестра. Принцип один: скачай один раз, храни у себя, ставь из своего источника.

# На машине с доступом к репозиторию: скачать .deb конкретной версии без установки
apt-get download grafana=13.2.4
# На ней же: перенести пакет на сервер Grafana
scp grafana_13.2.4_amd64.deb admin@10.10.0.20:/tmp/

# На сервере grafana-01: поставить из файла
sudo apt-get install -y /tmp/grafana_13.2.4_amd64.deb

Путь А, шаг 2. Базовая настройка grafana.ini

Зачем: настройки по умолчанию рассчитаны на «поставил и посмотрел». Регистрация открыта для приглашённых, Grafana слушает все интерфейсы, отправляет телеметрию наружу. Для продакшна закрываем лишнее.

Не правь grafana.ini целиком. Найди нужные секции и раскомментируй строки. Все параметры можно задать и переменными окружения вида GF_SECTION_KEY, это удобно в Docker.


# /etc/grafana/grafana.ini на сервере grafana-01: что поменять
[server]
http_addr = 127.0.0.1
http_port = 3000
domain = grafana.example.local
root_url = https://grafana.example.local/

[analytics]
reporting_enabled = false
check_for_updates = false
check_for_plugin_updates = false

[security]
cookie_secure = true
disable_gravatar = true

[users]
allow_sign_up = false
allow_org_create = false

[auth.anonymous]
enabled = false

Разберём неочевидное. http_addr = 127.0.0.1 заставляет Grafana слушать только локальный интерфейс: снаружи к ней пустит только nginx. Пока nginx не настроен, временно оставь пустое значение, иначе не зайдёшь в интерфейс с рабочей машины. root_url должен совпадать с адресом, по которому пользователи открывают Grafana. Ошибка в нём даёт редиректы на localhost:3000 и сломанные ссылки в уведомлениях.

cookie_secure = true включай только когда заработает HTTPS. По HTTP браузер не сохранит такую куку, и ты не сможешь залогиниться.

Путь А, шаг 3. Запуск и первый вход


# На сервере grafana-01: запуск, автозапуск и проверка
sudo systemctl daemon-reload
sudo systemctl enable --now grafana-server
sudo systemctl status grafana-server --no-pager
curl -s http://127.0.0.1:3000/api/health | jq

Результат: статус active running, а /api/health возвращает database ok и версию. Открой в браузере адрес сервера на порту 3000, логин admin, пароль admin. Grafana сразу попросит сменить пароль. Смени. Не нажимай Skip.

Пароль admin/admin на внутреннем сервере, который «никто не найдёт», — это как ключ под ковриком. Его найдёт первый же сканер в сети, а заодно и все сохранённые в источниках учётки.

Если пароль нужно задать до первого входа, например в скрипте развёртывания, используй команду сброса. В Grafana 13 отдельный бинарник grafana-cli больше не работает, вместо него подкоманда grafana cli:


# На сервере grafana-01: задать пароль admin из консоли
sudo grafana cli --homepath /usr/share/grafana --config /etc/grafana/grafana.ini admin reset-admin-password 'НовыйСложныйПароль'

Путь Б. Grafana и Prometheus в Docker Compose

Зачем: одним файлом поднять весь стек: Grafana, Prometheus и node_exporter, с заранее подключённым источником данных. Через пять минут у тебя графики хоста, на котором всё запущено.

Про образы. С ветки 12.4 репозиторий grafana/grafana-oss больше не обновляется. Бери grafana/grafana, это и есть OSS. Тег всегда фиксируй конкретной версией: latest в продакшне — это лотерея при каждом docker compose pull.


# На сервере grafana-01: структура каталогов стека
sudo mkdir -p /opt/monitoring/{prometheus,grafana/provisioning/{datasources,dashboards,plugins},grafana/dashboards}
cd /opt/monitoring

# На сервере grafana-01: docker-compose.yml стека Grafana + Prometheus + node_exporter
sudo tee /opt/monitoring/docker-compose.yml > /dev/null <<'EOF'
services:
  grafana:
    image: grafana/grafana:13.2.4
    container_name: grafana
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      GF_SECURITY_ADMIN_USER: admin
      GF_SECURITY_ADMIN_PASSWORD__FILE: /run/secrets/grafana_admin
      GF_USERS_ALLOW_SIGN_UP: "false"
      GF_ANALYTICS_REPORTING_ENABLED: "false"
      GF_SERVER_ROOT_URL: https://grafana.example.local/
      GF_PLUGINS_PREINSTALL: alexanderzobnin-zabbix-app@6.9.1
      ZABBIX_API_TOKEN: ${ZABBIX_API_TOKEN:-}
    secrets:
      - grafana_admin
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning:ro
      - ./grafana/dashboards:/var/lib/grafana/dashboards:ro
    depends_on:
      - prometheus

  prometheus:
    image: prom/prometheus:v3.13.4
    container_name: prometheus
    restart: unless-stopped
    command:
      - --config.file=/etc/prometheus/prometheus.yml
      - --storage.tsdb.path=/prometheus
      - --storage.tsdb.retention.time=30d
    ports:
      - "127.0.0.1:9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus-data:/prometheus

  node-exporter:
    image: prom/node-exporter:v1.12.1
    container_name: node-exporter
    restart: unless-stopped
    pid: host
    command:
      - --path.rootfs=/host
    volumes:
      - /:/host:ro,rslave

secrets:
  grafana_admin:
    file: ./grafana_admin.txt

volumes:
  grafana-data:
  prometheus-data:
EOF

Пароль admin не пишем в compose-файл открытым текстом. Он лежит в отдельном файле с правами 600, а Grafana читает его через переменную с суффиксом __FILE.


# На сервере grafana-01: файл с паролем admin для контейнера
openssl rand -base64 24 | sudo tee /opt/monitoring/grafana_admin.txt > /dev/null
sudo chmod 600 /opt/monitoring/grafana_admin.txt
sudo cat /opt/monitoring/grafana_admin.txt

Конфиг Prometheus. Три цели: сам Prometheus, node_exporter этого хоста и метрики самой Grafana. Имена целей — это имена сервисов из compose-файла, контейнеры видят друг друга по ним во внутренней сети Docker.


# На сервере grafana-01: конфиг Prometheus
sudo tee /opt/monitoring/prometheus/prometheus.yml > /dev/null <<'EOF'
global:
  scrape_interval: 30s
  evaluation_interval: 30s

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: ['prometheus:9090']

  - job_name: node
    static_configs:
      - targets: ['node-exporter:9100']
        labels:
          host: grafana-01

  - job_name: grafana
    static_configs:
      - targets: ['grafana:3000']
EOF

Теперь provisioning: источник данных Prometheus, который Grafana создаст сама при старте. Обрати внимание на адрес: http://prometheus:9090, а не localhost. Это ровно та ошибка из причины номер два.


# На сервере grafana-01: автоматическое подключение Prometheus к Grafana
sudo tee /opt/monitoring/grafana/provisioning/datasources/prometheus.yaml > /dev/null <<'EOF'
apiVersion: 1
datasources:
  - name: Prometheus
    uid: prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: false
    jsonData:
      timeInterval: 30s
EOF

И провайдер дашбордов: Grafana будет подхватывать JSON-файлы из каталога и обновлять их при изменении.


# На сервере grafana-01: провайдер дашбордов из файлов
sudo tee /opt/monitoring/grafana/provisioning/dashboards/files.yaml > /dev/null <<'EOF'
apiVersion: 1
providers:
  - name: files
    folder: Infrastructure
    type: file
    disableDeletion: true
    updateIntervalSeconds: 30
    allowUiUpdates: false
    options:
      path: /var/lib/grafana/dashboards
EOF

Запускаем.


# На сервере grafana-01: старт стека и проверка
cd /opt/monitoring
sudo docker compose up -d
sudo docker compose ps
sudo docker compose logs grafana --tail 30
curl -s http://127.0.0.1:3000/api/health | jq
curl -s http://127.0.0.1:9090/api/v1/targets | jq '.data.activeTargets[] | {job: .labels.job, health}'

Результат: три контейнера в статусе running, /api/health отвечает database ok, все три цели Prometheus в состоянии up. Вход в Grafana: admin и пароль из grafana_admin.txt.

Тома grafana-data и prometheus-data — именованные, Docker сам выставит на них правильные права. Если вместо них примонтируешь каталог хоста, учти: процесс Grafana в контейнере работает от UID 472, и каталог должен быть доступен ему на запись. Иначе контейнер упадёт с сообщением, что GF_PATHS_DATA недоступен для записи.

Шаг 4. Подключи Prometheus и метрики серверов

В Docker-варианте Prometheus уже подключён через provisioning. На пакетной установке подключаем руками или тем же YAML-файлом в /etc/grafana/provisioning/datasources/.

В интерфейсе: Connections → Data sources → Add data source → Prometheus. В поле Prometheus server URL впиши адрес, по которому Prometheus доступен с машины Grafana, например http://10.10.0.30:9090. Нажми Save & test. Должно появиться сообщение об успешном запросе к API Prometheus.

На каждый наблюдаемый Linux-сервер ставь node_exporter. Из пакетов дистрибутива проще всего, но версия там бывает старой. Для единообразия бери бинарник с GitHub и systemd-юнит.


# На каждом наблюдаемом Linux-сервере: node_exporter 1.12.1 как служба
cd /tmp
wget https://github.com/prometheus/node_exporter/releases/download/v1.12.1/node_exporter-1.12.1.linux-amd64.tar.gz
tar -xzf node_exporter-1.12.1.linux-amd64.tar.gz
sudo install -m 755 node_exporter-1.12.1.linux-amd64/node_exporter /usr/local/bin/
sudo useradd --system --no-create-home --shell /usr/sbin/nologin node_exporter
sudo tee /etc/systemd/system/node_exporter.service > /dev/null <<'EOF'
[Unit]
Description=Prometheus node_exporter
After=network-online.target

[Service]
User=node_exporter
ExecStart=/usr/local/bin/node_exporter
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter
curl -s http://127.0.0.1:9100/metrics | head -n 5

Порт 9100 открой только для адреса Prometheus. node_exporter отдаёт без авторизации много сведений о системе: ядро, файловые системы, сетевые интерфейсы.


# На каждом наблюдаемом сервере (UFW): пускать к node_exporter только Prometheus
sudo ufw allow from 10.10.0.20 to any port 9100 proto tcp

Новый сервер добавляется в prometheus.yml строкой в targets. После правки перечитай конфиг без перезапуска:


# На сервере grafana-01 (Docker-вариант): перечитать конфиг Prometheus
sudo docker compose -f /opt/monitoring/docker-compose.yml kill -s SIGHUP prometheus

Шаг 5. Импортируй готовый дашборд Grafana

Зачем: собирать первый дашборд с нуля — долго и бессмысленно. Для node_exporter есть проверенный годами дашборд Node Exporter Full с идентификатором 1860 в каталоге дашбордов Grafana.

Импорт в интерфейсе: Dashboards → New → Import, вставь 1860 в поле ID, нажми Load, выбери источник Prometheus и нажми Import. Если сервер Grafana не ходит в интернет, скачай JSON дашборда на рабочей машине и загрузи файлом через то же окно импорта.

Через provisioning то же самое делается файлом. Скачай JSON и положи в каталог дашбордов. Одно условие: в JSON из каталога источник указан переменной, которую нужно заменить на uid источника.


# На сервере grafana-01: дашборд Node Exporter Full через provisioning
cd /tmp
curl -sL https://grafana.com/api/dashboards/1860/revisions/latest/download -o node-exporter-full.json
sed -i 's/${DS_PROMETHEUS}/prometheus/g' node-exporter-full.json
sudo cp node-exporter-full.json /opt/monitoring/grafana/dashboards/

Через 30 секунд дашборд появится в папке Infrastructure. На пакетной установке путь каталога задаётся в своём файле провайдера, например /var/lib/grafana/dashboards.

В Grafana 13 есть и встроенный помощник: раздел Suggested dashboards подбирает готовые дашборды под твои источники данных. Для Prometheus он показывает, насколько дашборд совпадает с реальными метриками. Функция в публичном превью, но для старта удобна.

Шаг 6. Zabbix и Grafana: подключи плагин Zabbix

Связка zabbix grafana — самая частая причина, по которой в Grafana приходят админы. Zabbix отлично собирает и алертит, но его графики и дашборды на большом экране выглядят как привет из 2012. Плагин Zabbix для Grafana берёт данные через API Zabbix и рисует их нормально.

На стороне Zabbix. Создай отдельного пользователя только на чтение. Не подключай Grafana под Admin: плагин хранит учётку, и любой редактор источника в Grafana получит полные права в Zabbix.

Что создать в Zabbix Где Значение
Группа пользователей Пользователи → Группы пользователей grafana-readers, права «Чтение» на нужные группы узлов
Пользователь Пользователи → Пользователи grafana, роль User role, группа grafana-readers
API-токен Пользователи → API токены Для пользователя grafana, со сроком действия

Права на группы узлов — вот тут важно. Без них API отвечает успешно, но пустым списком хостов. В Grafana это выглядит как «всё подключилось, а данных нет», и это причина номер пять из таблицы.

На стороне Grafana. Установи плагин. В Docker-варианте он уже ставится переменной GF_PLUGINS_PREINSTALL. На пакетах:


# На сервере grafana-01 (пакетная установка): плагин Zabbix
sudo grafana cli --homepath /usr/share/grafana plugins install alexanderzobnin-zabbix-app
sudo systemctl restart grafana-server
sudo grafana cli --homepath /usr/share/grafana plugins ls

Плагин нужно не только установить, но и включить. Это отдельное действие: Administration → Plugins and data → Plugins, найди Zabbix и нажми Enable. Пока приложение не включено, тип источника Zabbix в списке не появится.

Включение можно сделать файлом provisioning, тогда это переживёт пересоздание контейнера:


# На сервере grafana-01 (Docker-вариант): включить приложение Zabbix при старте
sudo tee /opt/monitoring/grafana/provisioning/plugins/zabbix.yaml > /dev/null <<'EOF'
apiVersion: 1
apps:
  - type: alexanderzobnin-zabbix-app
    org_id: 1
    disabled: false
EOF

Теперь источник данных. Через provisioning с API-токеном, без пароля пользователя:


# На сервере grafana-01 (Docker-вариант): источник Zabbix с API-токеном
sudo tee /opt/monitoring/grafana/provisioning/datasources/zabbix.yaml > /dev/null <<'EOF'
apiVersion: 1
datasources:
  - name: Zabbix
    uid: zabbix
    type: alexanderzobnin-zabbix-datasource
    url: http://10.10.0.10/api_jsonrpc.php
    editable: false
    jsonData:
      authType: token
      trends: true
      trendsFrom: '7d'
      trendsRange: '4d'
      cacheTTL: '1h'
      timeout: 30
    secureJsonData:
      apiToken: $ZABBIX_API_TOKEN
EOF
sudo chmod 640 /opt/monitoring/grafana/provisioning/datasources/zabbix.yaml

Конструкция $ZABBIX_API_TOKEN — подстановка переменной окружения: Grafana раскрывает такие переменные в файлах provisioning. В compose-файле переменная уже передаётся контейнеру, осталось положить сам токен в файл .env рядом с compose-файлом и закрыть его правами 600.


# На сервере grafana-01 (Docker-вариант): токен Zabbix в .env и перезапуск Grafana
echo 'ZABBIX_API_TOKEN=вставь_сюда_токен_из_Zabbix' | sudo tee /opt/monitoring/.env > /dev/null
sudo chmod 600 /opt/monitoring/.env
cd /opt/monitoring && sudo docker compose up -d grafana

Параметры trends разберу отдельно, они сильно влияют на скорость. Zabbix хранит детальную историю недолго, а дальше только тренды по часам. С trends: true плагин для периодов длиннее 7 дней берёт тренды вместо истории. Дашборд за месяц открывается за секунду, а не за минуту с нагрузкой на базу Zabbix.

Через интерфейс то же самое: Connections → Data sources → Add → Zabbix. URL API, Auth type: API token, вставь токен, включи Trends, нажми Save & test. Успешный тест показывает версию Zabbix API.

На вкладке Dashboards источника Zabbix лежат готовые дашборды плагина. Импортируй Zabbix Server Dashboard: он сразу покажет, работает ли связка. Дальше самая полезная вещь в плагине — панель Problems. Это текущие проблемы Zabbix с фильтрами по группам и важности, удобные для вывода на экран дежурной смены.

Хорошая новость для тех, кто хочет держать алерты в Zabbix: и не надо их переносить. Zabbix алертит, Grafana показывает. Каждый делает то, что умеет лучше.

Шаг 7. Прямое подключение к базе Zabbix для тяжёлых дашбордов

Когда на дашборде сотни метрик за месяц, API Zabbix начинает тормозить. Плагин умеет брать историю напрямую из базы Zabbix, а через API — только список хостов и элементов. Это включается опцией Direct DB Connection в настройках источника Zabbix.

Для этого нужен отдельный пользователь базы Zabbix только на чтение таблиц истории и трендов. Никогда не подключай Grafana к базе Zabbix под учёткой самого Zabbix: одна ошибка в SQL-запросе панели, и ты пишешь в боевую базу.

Только чтение, только нужные таблицы
Пользователь для Grafana должен иметь право SELECT только на таблицы истории и трендов. Никаких прав на запись и никаких прав на таблицы пользователей и настроек Zabbix. Подключение разреши только с адреса сервера Grafana.

-- На сервере БД Zabbix (PostgreSQL), под суперпользователем: пользователь только на чтение
CREATE ROLE grafana_ro WITH LOGIN PASSWORD 'СложныйПароль';
GRANT CONNECT ON DATABASE zabbix TO grafana_ro;
GRANT USAGE ON SCHEMA public TO grafana_ro;
GRANT SELECT ON history, history_uint, trends, trends_uint TO grafana_ro;

# pg_hba.conf на сервере БД Zabbix: доступ только с адреса Grafana
host    zabbix    grafana_ro    10.10.0.20/32    scram-sha-256

# На сервере БД Zabbix: применить pg_hba.conf без перезапуска
sudo -u postgres psql -c "SELECT pg_reload_conf();"

В Grafana добавь источник PostgreSQL с этим пользователем, а в источнике Zabbix включи Direct DB Connection и выбери его. Для MySQL логика та же: CREATE USER с привязкой к адресу Grafana и GRANT SELECT на четыре таблицы.

Шаг 8. Свой дашборд Grafana с переменными

Готовые дашборды хороши для старта, но рано или поздно нужен свой: один экран, на котором видно ровно то, что важно твоей команде. Главный приём — переменные. Один дашборд обслуживает все серверы через выпадающий список, а не двадцать копий с разными именами.

Создай дашборд: Dashboards → New → New dashboard. Открой настройки дашборда, вкладка Variables, добавь переменную:

Поле Значение Зачем
Name instance Имя, по которому переменная подставляется в запросы
Type Query Список берётся из источника, а не вбивается руками
Data source Prometheus Откуда брать значения
Query label_values(node_uname_info, instance) Все серверы, с которых приходит node_exporter
Multi-value Включено Можно выбрать несколько серверов сразу

Теперь панели. Запросы PromQL, которые нужны почти на любом дашборде по Linux-серверам:


# Загрузка CPU в процентах
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle", instance=~"$instance"}[5m])) * 100)

# Использование памяти в процентах
(1 - node_memory_MemAvailable_bytes{instance=~"$instance"} / node_memory_MemTotal_bytes{instance=~"$instance"}) * 100

# Заполнение файловых систем в процентах, без временных
100 - (node_filesystem_avail_bytes{instance=~"$instance", fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{instance=~"$instance", fstype!~"tmpfs|overlay"} * 100)

# Входящий трафик по интерфейсам, бит в секунду
rate(node_network_receive_bytes_total{instance=~"$instance", device!~"lo|veth.*|docker.*"}[5m]) * 8

Обрати внимание на =~ вместо =. С включённым Multi-value переменная превращается в регулярное выражение вида srv1|srv2, и точное равенство с ним не сработает. Это классическая причина, по которой панель показывает данные для одного сервера и пустоту при выборе двух.

Единицы измерения задай в настройках панели: Standard options → Unit, для процентов Percent (0-100), для трафика bits/sec. Пороги в Thresholds: зелёный до 70, жёлтый до 90, красный выше. Порог 90% для диска я ставлю на всё по умолчанию: лучше лишний жёлтый цвет, чем звонок в субботу.

Для Zabbix-источника запросы строятся не на PromQL, а выбором Group, Host, Item в редакторе. Переменные работают так же: тип Query, источник Zabbix, запрос вида Group: Linux servers, Host: /.*/. Плагин понимает регулярные выражения в слэшах.

Сохранённый дашборд экспортируй в JSON: Share → Export → Save to file. Храни экспорт в Git. Так у тебя появляется история изменений, а дашборд можно восстановить за минуту. В Grafana 13 для этого есть и встроенный Git Sync, он вышел в общий доступ во всех редакциях.

Шаг 9. HTTPS через nginx

Зачем: Grafana на голом HTTP передаёт пароли и куки сессии открытым текстом. Плюс nginx даёт один порт 443 наружу и закрывает 3000.


# На сервере grafana-01: nginx
sudo apt-get install -y nginx

Конфиг ниже рассчитан на готовый сертификат, внутренний или от Let’s Encrypt. Блок для /api/live/ нужен для живых обновлений через WebSocket: без него дашборды работают, но в логах сыпятся ошибки соединения и не работают живые панели.


# На сервере grafana-01: виртуальный хост nginx для Grafana
sudo tee /etc/nginx/sites-available/grafana.conf > /dev/null <<'EOF'
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    server_name grafana.example.local;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name grafana.example.local;

    ssl_certificate     /etc/ssl/certs/grafana.crt;
    ssl_certificate_key /etc/ssl/private/grafana.key;
    ssl_protocols TLSv1.2 TLSv1.3;

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:3000;
    }

    location /api/live/ {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:3000;
    }
}
EOF
sudo ln -s /etc/nginx/sites-available/grafana.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

После этого включи в grafana.ini http_addr = 127.0.0.1 и cookie_secure = true, проверь root_url и перезапусти Grafana. Firewall открывает только 80 и 443.


# На сервере grafana-01: применить настройки и закрыть всё лишнее
sudo systemctl restart grafana-server
sudo ufw allow 443/tcp
sudo ufw allow 80/tcp
sudo ufw allow from 10.10.0.0/24 to any port 22 proto tcp
sudo ufw enable
sudo ss -tlnp | grep -E ':3000|:443'

Результат: Grafana слушает 3000 только на 127.0.0.1, nginx слушает 443 на всех интерфейсах.

Шаг 10. Grafana на PostgreSQL вместо SQLite

Зачем: SQLite прекрасна для одного инстанса и сотни дашбордов. Проблемы начинаются при нескольких экземплярах Grafana за балансировщиком, при большом числе пользователей и при частых сохранениях: появляются ошибки database is locked. PostgreSQL снимает эти вопросы и бэкапится стандартными средствами вместе с остальными базами.


# На сервере БД (или на grafana-01): база и отдельный пользователь для Grafana
sudo -u postgres psql -c "CREATE ROLE grafana WITH LOGIN PASSWORD 'СложныйПароль';"
sudo -u postgres psql -c "CREATE DATABASE grafana OWNER grafana;"

# /etc/grafana/grafana.ini на сервере grafana-01: секция базы
[database]
type = postgres
host = 127.0.0.1:5432
name = grafana
user = grafana
password = СложныйПароль
ssl_mode = disable

Отдельный пользователь с правами только на свою базу — обязательно. Не давай Grafana суперпользователя postgres. Если база на другой машине, ssl_mode = require и доступ в pg_hba.conf только с адреса Grafana.

Вот тут важно: смена типа базы не переносит данные. Новая Grafana на PostgreSQL стартует пустой. Перенос делай заранее: выгрузи дашборды через API, как показано в разделе про бэкап, и загрузи их обратно. Источники данных проще описать в provisioning, чем переносить.

Шаг 11. Grafana API: сервисный аккаунт и токен

API нужен для бэкапа дашбордов, автоматизации и проверок из скриптов. Ходить в API под логином admin нельзя: создай сервисный аккаунт. Administration → Users and access → Service accounts → Add service account, роль Viewer для чтения или Editor для импорта, затем Add service account token.


# На любой машине с доступом к Grafana: проверка токена и список дашбордов
export GF_URL="https://grafana.example.local"
export GF_TOKEN="glsa_вставь_сюда_токен"
curl -s -H "Authorization: Bearer $GF_TOKEN" "$GF_URL/api/search?type=dash-db" | jq -r '.[] | "\(.uid)  \(.title)"'

Результат: список uid и названий дашбордов. Пустой массив значит, что у сервисного аккаунта нет прав на папки.

Проверка: как убедиться, что Grafana работает правильно

«Интерфейс открывается» — это не проверка. Проверяем по слоям: процесс, база, источники, данные, внешний доступ.


# На сервере grafana-01: процесс, порт и здоровье
systemctl is-active grafana-server
sudo ss -tlnp | grep 3000
curl -s http://127.0.0.1:3000/api/health | jq

В Docker-варианте вместо systemctl смотри docker compose ps. Ответ /api/health с database ok означает, что Grafana жива и база доступна.

Источники данных проверяются через API тем же запросом, что выполняет кнопка Save & test:


# На любой машине с доступом к Grafana: здоровье всех источников данных
for uid in $(curl -s -H "Authorization: Bearer $GF_TOKEN" "$GF_URL/api/datasources" | jq -r '.[].uid'); do
  echo -n "$uid: "
  curl -s -H "Authorization: Bearer $GF_TOKEN" "$GF_URL/api/datasources/uid/$uid/health" | jq -r '.status + " " + .message'
done

Для проверки сервисному аккаунту нужна роль Editor или Admin, Viewer не видит настройки источников. Ожидаешь OK у каждого источника. Любое ERROR с текстом сразу говорит, куда смотреть.

Prometheus и его цели:


# На сервере grafana-01: состояние целей Prometheus
curl -s http://127.0.0.1:9090/api/v1/targets | jq -r '.data.activeTargets[] | "\(.labels.job) \(.labels.instance) \(.health) \(.lastError)"'

Все цели в up, поле ошибки пустое. Цель в down с ошибкой connection refused значит, что node_exporter не запущен или закрыт firewall.

Логи Grafana. На пакетной установке файл /var/log/grafana/grafana.log, в Docker — логи контейнера.


# На сервере grafana-01: ошибки в логе Grafana
sudo grep -E 'level=(error|warn)' /var/log/grafana/grafana.log | tail -n 20
sudo docker compose -f /opt/monitoring/docker-compose.yml logs grafana --tail 50 | grep -E 'level=(error|warn)'

И внешний доступ: открой https://grafana.example.local с рабочей машины, залогинься, открой импортированный дашборд и переключи период на Last 1 hour. Графики заполнены, переменная instance показывает серверы.

Grafana работает, если
Служба запущена, /api/health отвечает database ok, все источники данных проходят проверку, цели Prometheus в состоянии up, а дашборд за последний час заполнен данными. Интерфейс открывается по HTTPS, а порт 3000 снаружи недоступен.

Осложнения: ошибки Grafana и как их лечить

Всё не так плохо, как ты думаешь. Всё намного хуже: в Grafana ошибка источника данных и ошибка запроса выглядят одинаково — пустой панелью. Поэтому сначала схема, потом конкретика.

%%{init: {
  'theme': 'base',
  'themeVariables': {
    'primaryColor': '#ffffff',
    'primaryTextColor': '#1e293b',
    'primaryBorderColor': '#94a3b8',
    'lineColor': '#64748b',
    'fontSize': '15px',
    'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
  },
  'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
    A["Панель показывает No data"] --> B{"Источник проходит тест?"}
    B -->|нет| C["Проверь URL и доступ с сервера Grafana"]
    B -->|да| D{"Запрос работает в Explore?"}
    D -->|нет| E["Исправь запрос или метки"]
    D -->|да| F{"Период и часовой пояс верные?"}
    F -->|нет| G["Поправь диапазон и время"]
    F -->|да| H["Проверь переменные дашборда"]
    style A fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#9a3412
    style B fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style D fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style F fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style C fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
    style E fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
    style G fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
    style H fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d

Главный инструмент диагностики — раздел Explore. Он выполняет тот же запрос, что и панель, но без настроек визуализации и переменных. Если в Explore данные есть, а на панели нет — проблема в панели или переменных. Если нет и там — в источнике или запросе.

Ошибка Причина Решение
No data на панели Запрос, период, переменные или источник Пройди схему выше, начни с Explore
Bad Gateway или connection refused при тесте источника localhost в URL при Grafana в Docker, firewall Имя сервиса или IP хоста, проверка curl изнутри контейнера
Нет типа источника Zabbix Плагин установлен, но не включён Administration → Plugins → Zabbix → Enable
Zabbix: пустой список хостов Нет прав на группы узлов у пользователя Zabbix Права «Чтение» группе grafana-readers
grafana-cli не работает на 13.x Отдельный бинарник больше не поддерживается grafana cli с подкомандой
451 geofence blocked при загрузке Гео-ограничение серверов Grafana Labs Своё зеркало или перенос пакета вручную
Бесконечный редирект или белая страница за nginx root_url не совпадает с реальным адресом Выровнять root_url и server_name

Источник в Docker: connection refused

Причина: в URL источника стоит localhost или 127.0.0.1. Для Grafana в контейнере это сам контейнер. Решение: если Prometheus в том же compose-файле, указывай имя сервиса. Если сервис на хосте, указывай IP хоста в сети или host.docker.internal с соответствующей настройкой.


# На сервере grafana-01: проверить доступность источника ИЗНУТРИ контейнера Grafana
sudo docker exec grafana wget -qO- http://prometheus:9090/-/healthy
sudo docker exec grafana wget -qO- http://10.10.0.10/api_jsonrpc.php --post-data='{"jsonrpc":"2.0","method":"apiinfo.version","params":{},"id":1}' --header='Content-Type: application/json-rpc'

Если изнутри контейнера ответа нет, Grafana его тоже не получит. Чини сеть, а не Grafana.

Плагин Zabbix: «Not authorized» или пустые списки

Причина: токен истёк, у пользователя нет прав на API или на группы узлов. С Zabbix 7.0 доступ к API регулируется ролью пользователя: если в роли API выключен или ограничен список методов, плагин получит отказ.


# На сервере grafana-01: проверить токен Zabbix и видимость хостов
export ZBX_TOKEN="вставь_сюда_токен"
curl -s -X POST http://10.10.0.10/api_jsonrpc.php \
  -H 'Content-Type: application/json-rpc' \
  -H "Authorization: Bearer $ZBX_TOKEN" \
  -d '{"jsonrpc":"2.0","method":"host.get","params":{"output":["host"],"limit":5},"id":1}' | jq

Ошибка авторизации — проблема в токене или роли. Пустой result — нет прав на группы узлов. Список хостов есть — Zabbix в порядке, ищи проблему в настройке источника в Grafana.

Старые дашборды не открываются после обновления

Причина: в Grafana 12 полностью удалена поддержка плагинов на AngularJS. Старые панели вроде Worldmap или устаревших сторонних графиков перестают отображаться. Встроенные устаревшие панели вроде старого Graph Grafana мигрирует автоматически, сторонние Angular-плагины — нет.

Решение: до обновления пройди по дашбордам и замени Angular-панели на современные аналоги. Найти их помогает встроенный Grafana Advisor в 13-й ветке: он проверяет устаревшие плагины и источники с ошибками.


# На сервере grafana-01: список установленных плагинов и их версий
sudo grafana cli --homepath /usr/share/grafana plugins ls

Забыл пароль admin

Причина банальна. Решение — сброс из консоли сервера, данные не теряются.


# На сервере grafana-01 (пакетная установка): сброс пароля admin
sudo grafana cli --homepath /usr/share/grafana --config /etc/grafana/grafana.ini admin reset-admin-password 'НовыйСложныйПароль'

# На сервере grafana-01 (Docker-вариант): то же внутри контейнера
sudo docker exec -it grafana grafana cli admin reset-admin-password 'НовыйСложныйПароль'

В Docker-варианте переменная GF_SECURITY_ADMIN_PASSWORD работает только при первом создании базы. Поменять пароль через неё на живой Grafana нельзя, и это частый источник недоумения.

Контейнер не стартует: GF_PATHS_DATA is not writable

Причина: вместо именованного тома в /var/lib/grafana примонтирован каталог хоста, например /opt/monitoring/grafana-data, и он принадлежит root. Grafana в контейнере работает от UID 472 и писать туда не может.


# На сервере grafana-01: выдать права каталогу данных Grafana на хосте
sudo chown -R 472:0 /opt/monitoring/grafana-data
sudo docker compose -f /opt/monitoring/docker-compose.yml up -d grafana

database is locked в логах

Причина: SQLite не справляется с параллельной записью, обычно при массовом импорте дашбордов, большом числе пользователей или базе на медленном сетевом диске. Если это разовая ошибка при импорте, повтори операцию. Если повторяется регулярно, переходи на PostgreSQL по шагу 10.


# На сервере grafana-01: как часто встречается блокировка базы
sudo grep -c 'database is locked' /var/log/grafana/grafana.log

Графики сдвинуты по времени

Причина: на сервере источника или Grafana не синхронизировано время, либо часовой пояс дашборда стоит UTC, а пользователи ждут местное время. Проверь синхронизацию командой timedatectl на всех машинах. Часовой пояс задаётся в настройках дашборда, у пользователя в профиле и глобально в Administration → Default preferences.

Альтернативы: аналоги Grafana и когда Grafana не нужна

Grafana — не единственный способ смотреть на метрики. Иногда её ставят по моде туда, где хватает встроенных средств.

Инструмент Когда подходит Чем хуже или лучше Grafana
Дашборды Zabbix Весь мониторинг в Zabbix, экран нужен дежурным Ничего не надо ставить, но визуально беднее и менее гибко
Веб-интерфейс Prometheus Разовые запросы и отладка PromQL Только запросы и простые графики, без дашбордов
Kibana или OpenSearch Dashboards Основные данные — логи в Elasticsearch или OpenSearch Сильнее в поиске по логам, слабее в метриках
Yandex DataLens Бизнес-аналитика, отчёты для менеджеров BI-инструмент, а не оперативный мониторинг
Perses Дашборды как код в Kubernetes, проект CNCF Моложе и беднее по плагинам, зато открыт и декларативен
Netdata Детальные метрики одного сервера в реальном времени без настройки Быстрый старт, но не единая витрина для разных источников
Grafana Cloud Нет желания держать свою инфраструктуру Без администрирования, но данные мониторинга уходят наружу

Почему в основном рецепте всё же Grafana на своём сервере. Она единственная из списка одинаково хорошо работает с Prometheus, Zabbix, PostgreSQL, Loki и десятками других источников. Один экран для всего вместо пяти вкладок браузера — это то, ради чего её ставят.

Логи, трейсы и Alloy: когда расширять стек

Вокруг Grafana Labs выросло семейство продуктов. Ставить их все сразу не нужно, но знать, что за что отвечает, полезно.

Продукт Что делает Когда нужен
Loki Хранит логи и отдаёт их в Grafana Нужно смотреть логи рядом с метриками на одном дашборде
Tempo Хранит распределённые трейсы Микросервисы, OpenTelemetry, поиск медленных запросов
Alloy Универсальный сборщик метрик, логов и трейсов Один агент вместо node_exporter, Promtail и коллектора OpenTelemetry
Mimir Масштабируемое хранилище метрик, совместимое с Prometheus Миллионы рядов и долгий срок хранения

Grafana Alloy пришёл на смену Grafana Agent и Promtail, которые сняты с развития. Если начинаешь собирать логи в Loki сегодня, бери Alloy, а не Promtail из старых статей. На момент публикации актуальна ветка Alloy 1.18, проверь свежий релиз в документации Alloy.

Для Proxmox есть два пути. Встроенный в Proxmox VE сервер метрик отправляет данные в InfluxDB или Graphite, а Grafana их рисует. Или шаблон Proxmox VE by HTTP в Zabbix, и тогда Proxmox приезжает в Grafana через плагин Zabbix. Если Zabbix уже есть, второй путь проще: не нужно ещё одно хранилище.

Профилактика: мониторинг, бэкап, безопасность и обновление Grafana

Grafana, которую никто не мониторит, падает в самый неудобный момент. Обычно за пять минут до того, как директор открывает дашборд на совещании. Закон подлости в мониторинге работает с точностью до секунды.

Мониторинг самой Grafana

Grafana отдаёт свои метрики в формате Prometheus по адресу /metrics. В Docker-варианте Prometheus уже собирает их, цель grafana в prometheus.yml. На пакетной установке добавь цель с адресом 127.0.0.1:3000. Метрики для алертов:

Метрика Что показывает Когда тревожиться
up{job=»grafana»} Отвечает ли Grafana Значение 0
grafana_http_request_duration_seconds Время ответа интерфейса и API Рост медианы в разы
grafana_datasource_request_total Запросы к источникам по кодам ответа Рост ответов с ошибками
process_resident_memory_bytes Память процесса Постоянный рост без падений

Если основной мониторинг — Zabbix, добавь в него проверку HTTP-доступности Grafana: веб-сценарий или элемент HTTP agent на https://grafana.example.local/api/health с триггером на код ответа отличный от 200. Мониторинг витрины должен жить вне витрины.

Резервное копирование

Что бэкапить Путь Как часто Зачем
База Grafana /var/lib/grafana/grafana.db или база PostgreSQL Ежедневно Дашборды, пользователи, источники, алерты
Конфигурация /etc/grafana При изменении и ежедневно grafana.ini, provisioning
Дашборды в JSON Выгрузка через API Ежедневно Восстановление отдельного дашборда без отката всей базы
Docker-стек /opt/monitoring без томов При изменении compose-файл, конфиги, provisioning
Не копируй файл SQLite на живой Grafana
Простое копирование grafana.db во время работы может дать повреждённую копию, если Grafana в этот момент пишет в базу. Используй встроенную команду резервного копирования SQLite: она делает согласованный снимок без остановки сервиса.

# На сервере grafana-01: скрипт бэкапа базы, конфигов и дашбордов
sudo tee /usr/local/sbin/grafana-backup.sh > /dev/null <<'EOF'
#!/bin/bash
set -euo pipefail
DIR=/var/backups/grafana/$(date +%F)
mkdir -p "$DIR/dashboards"
# Согласованный снимок SQLite
sqlite3 /var/lib/grafana/grafana.db ".backup '$DIR/grafana.db'"
# Конфигурация
tar -czf "$DIR/etc-grafana.tar.gz" /etc/grafana
# Дашборды в JSON через API
source /etc/grafana-backup.env
for uid in $(curl -s -H "Authorization: Bearer $GF_TOKEN" "$GF_URL/api/search?type=dash-db" | jq -r '.[].uid'); do
  curl -s -H "Authorization: Bearer $GF_TOKEN" "$GF_URL/api/dashboards/uid/$uid" | jq '.dashboard' > "$DIR/dashboards/$uid.json"
done
find /var/backups/grafana -mindepth 1 -maxdepth 1 -type d -mtime +14 -exec rm -rf {} +
EOF
sudo chmod 750 /usr/local/sbin/grafana-backup.sh
printf 'GF_URL=http://127.0.0.1:3000\nGF_TOKEN=glsa_токен_сервисного_аккаунта\n' | sudo tee /etc/grafana-backup.env > /dev/null
sudo chmod 600 /etc/grafana-backup.env
sudo /usr/local/sbin/grafana-backup.sh && ls -lh /var/backups/grafana/$(date +%F)/

# На сервере grafana-01: бэкап каждый день в 03:15
echo '15 3 * * * root /usr/local/sbin/grafana-backup.sh' | sudo tee /etc/cron.d/grafana-backup

Для Grafana на PostgreSQL замени строку с sqlite3 на pg_dump -Fc базы grafana. Копию забирай за пределы сервера с центральной стороны, через rsync с сервера резервных копий. Бэкап на том же диске защищает от удаления дашборда, но не от смерти диска.

Восстановление целиком: останови Grafana, верни grafana.db и /etc/grafana, выставь владельца grafana, запусти. Отдельный дашборд восстанавливается проще, через импорт JSON-файла в интерфейсе или запросом к API.


# На сервере grafana-01: полное восстановление из бэкапа
sudo systemctl stop grafana-server
sudo cp /var/backups/grafana/2026-10-20/grafana.db /var/lib/grafana/grafana.db
sudo tar -xzf /var/backups/grafana/2026-10-20/etc-grafana.tar.gz -C /
sudo chown grafana:grafana /var/lib/grafana/grafana.db
sudo systemctl start grafana-server

Автозапуск

Пакетная установка: systemctl enable из шага 3 уже включил автозапуск. Добавь страховку на падение через drop-in, не трогая юнит пакета:


# На сервере grafana-01: автоперезапуск Grafana при падении
sudo mkdir -p /etc/systemd/system/grafana-server.service.d
printf '[Service]\nRestart=on-failure\nRestartSec=10s\n' | sudo tee /etc/systemd/system/grafana-server.service.d/restart.conf
sudo systemctl daemon-reload
systemctl show grafana-server -p Restart

Docker-вариант: restart: unless-stopped в compose-файле и включённая служба docker. Проверь, что Docker стартует при загрузке: systemctl is-enabled docker.

Безопасность

Grafana хранит учётные данные ко всем подключённым источникам. Взлом Grafana — это ключи от Prometheus, Zabbix и баз данных. Минимальный набор защиты:

  • Firewall: снаружи только 443 и 80, SSH только с админской сети, порт 3000 закрыт.
  • Сменённый пароль admin, закрытая регистрация, выключенный анонимный доступ.
  • Отдельные пользователи только на чтение в Zabbix и в базах-источниках.
  • HTTPS через nginx и cookie_secure.
  • fail2ban на SSH и на попытки входа в Grafana через лог nginx.
  • SSH только по ключам, без root-логина.
  • Ежедневные бэкапы за пределами сервера.

У Grafana есть встроенная защита от перебора паролей: после серии неудачных попыток вход для логина временно блокируется. Не отключай её. fail2ban добавит блокировку по IP на уровне firewall.


# На сервере grafana-01: fail2ban для SSH
sudo apt-get install -y fail2ban
printf '[sshd]\nenabled = true\nbackend = systemd\nmaxretry = 5\nbantime = 1h\n' | sudo tee /etc/fail2ban/jail.d/sshd.local
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
Не отрежь себе доступ
Перед отключением входа по паролю проверь вход по ключу из второй сессии и держи первую открытой до конца проверки.

# На сервере grafana-01: SSH только по ключам
printf 'PermitRootLogin no\nPasswordAuthentication no\nKbdInteractiveAuthentication no\n' | sudo tee /etc/ssh/sshd_config.d/10-hardening.conf
sudo sshd -t && sudo systemctl reload ssh

Обновление Grafana без сюрпризов

Grafana выходит часто: новая минорная ветка примерно раз в два месяца, патчи безопасности чаще. Обновлять нужно, но с порядком.

Что проверить до обновления. Прочитай What’s new и раздел breaking changes целевой версии в руководстве по обновлению Grafana. Запусти Grafana Advisor и убедись, что все плагины совместимы. Сделай бэкап базы скриптом выше.

Миграции базы необратимы
При старте новой версии Grafana выполняет миграции своей базы. Старая версия с мигрированной базой может не запуститься. Поэтому откат — это всегда старый пакет плюс бэкап базы, сделанный до обновления. Без бэкапа отката нет.

# На сервере grafana-01 (пакеты): обновление с бэкапом
sudo /usr/local/sbin/grafana-backup.sh
sudo apt-get update
apt-cache policy grafana
sudo apt-get install -y grafana
sudo systemctl status grafana-server --no-pager
curl -s http://127.0.0.1:3000/api/health | jq -r .version

# На сервере grafana-01 (Docker): обновление сменой тега
sudo /usr/local/sbin/grafana-backup.sh
sudo sed -i 's#grafana/grafana:13.2.4#grafana/grafana:НОВАЯ_ВЕРСИЯ#' /opt/monitoring/docker-compose.yml
cd /opt/monitoring && sudo docker compose pull grafana && sudo docker compose up -d grafana

В Docker-варианте бэкап-скрипт нужно адаптировать: база лежит в томе grafana-data. Самый простой способ — остановить контейнер и скопировать том через временный контейнер, или выполнить sqlite3 .backup внутри тома.

Откат пакетной установки:


# На сервере grafana-01: откат на предыдущую версию с возвратом базы
sudo systemctl stop grafana-server
sudo apt-get install -y --allow-downgrades grafana=ВЕРСИЯ_ДО_ОБНОВЛЕНИЯ
sudo cp /var/backups/grafana/ДАТА_ДО_ОБНОВЛЕНИЯ/grafana.db /var/lib/grafana/grafana.db
sudo chown grafana:grafana /var/lib/grafana/grafana.db
sudo systemctl start grafana-server
sudo apt-mark hold grafana

apt-mark hold сними, когда проблема решится: sudo apt-mark unhold grafana. Иначе через полгода найдёшь Grafana, застрявшую на версии с закрытой уже уязвимостью. Рождённый в legacy рефакторинга не боится, но патчи безопасности любит.

FAQ: частые вопросы про Grafana

Почему Grafana не показывает данные после настройки?

Чаще всего источник данных указан с неверным адресом, особенно localhost при Grafana в Docker, или выбран неподходящий временной диапазон. Проверь кнопкой Save & test в настройках источника, затем выполни запрос в Explore. Если в Explore данные есть, проблема в панели или переменных дашборда.

Как проверить, что Grafana работает правильно?

Запроси /api/health: ответ database ok значит, что сервис и база в порядке. Затем проверь здоровье каждого источника через /api/datasources/uid/ИДЕНТИФИКАТОР/health и открой дашборд за последний час. Для постоянного контроля собирай метрики Grafana с /metrics в Prometheus или проверяй /api/health из Zabbix.

Что делать, если забыл пароль admin в Grafana?

Сбрось его из консоли сервера командой grafana cli admin reset-admin-password с новым паролем. Дашборды и настройки не пострадают. В Grafana 13 используй именно подкоманду grafana cli: отдельный бинарник grafana-cli в этой ветке не работает.

Чем Grafana отличается от Zabbix?

Zabbix — полноценная система мониторинга: сам собирает данные, хранит историю, считает триггеры и рассылает уведомления. Grafana только визуализирует данные из других систем, в том числе из Zabbix. Хорошая связка: Zabbix собирает и алертит, Grafana показывает красивые и гибкие дашборды через плагин Zabbix.

Prometheus и Grafana: в чём разница и нужны ли оба?

Prometheus собирает и хранит метрики, умеет считать правила и отправлять алерты через Alertmanager. Grafana строит по этим метрикам дашборды. У Prometheus есть простой веб-интерфейс для запросов, но для дашбордов его не хватает, поэтому их почти всегда ставят вместе.

Как подключить Zabbix к Grafana?

Установи плагин alexanderzobnin-zabbix-app, включи его в Administration → Plugins и добавь источник Zabbix с адресом api_jsonrpc.php. Для входа используй API-токен отдельного пользователя Zabbix с правами только на чтение нужных групп узлов. Включи trends, чтобы длинные периоды открывались быстро.

Бесплатна ли Grafana?

Да, Grafana OSS бесплатна и распространяется под лицензией AGPLv3. Редакцию Enterprise тоже можно ставить бесплатно, платные функции в ней включаются лицензионным ключом. Для внутреннего мониторинга компании ограничений лицензии OSS обычно достаточно с запасом.

Как установить Grafana в Docker?

Используй образ grafana/grafana с фиксированной версией, примонтируй том в /var/lib/grafana и опубликуй порт 3000. Готовый compose-файл со стеком Grafana, Prometheus и node_exporter есть в пути Б этой статьи. Образ grafana/grafana-oss с ветки 12.4 больше не обновляется, не бери его.

Прогноз: что теперь работает

Мы поставили Grafana 13 и связали её с двумя мирами: Prometheus с node_exporter для метрик Linux и Zabbix через плагин с API-токеном и трендами. Grafana спрятана за nginx с HTTPS, порт 3000 снаружи закрыт, пароли и токены не лежат открытым текстом в compose-файлах. Есть готовые дашборды, свой дашборд с переменными на все серверы и понятный путь на PostgreSQL, когда SQLite станет тесно.

Дальше Grafana живёт сама: systemd или Docker поднимают её при падении, бэкап базы и дашбордов каждую ночь уходит в отдельный каталог, а обновление сводится к бэкапу и одной команде. Следующий логичный шаг — логи в Loki через Alloy рядом с метриками. Тогда при инциденте на одном экране будет видно и что сломалось, и почему.

Не заработало?
Если панели пустые или источник не проходит тест, пиши в комментарии, разберёмся. Приложи версию Grafana, способ установки, тип источника и текст ошибки из Save and test или из лога Grafana.
Андрей А.
Author: Андрей А.

25 years in IT — from a field engineer at MegaFon to head of department. I rebuild IT infrastructure that’s falling apart and turn it into something predictable. I know what chaos costs. I help businesses get out of a crisis: cleaning up legacy systems, stabilizing what’s breaking, and setting up processes you can actually plan around.

Оставайтесь на связи

Рецепты от IT-боли. Без воды, без рекламы, без маркетинговой шелухи.

Подписаться на IT-Аптеку →
Поделитесь:

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх