Дежурный проспал алерт: Grafana OnCall и управление инцидентами для облачной платформы

Задача клиента: алерты уходят в пустоту

В январе 2026 года к нам в АйТи Фреш обратилась компания «КлаудБейс» из Казани — провайдер облачной платформы, обслуживающий 50 корпоративных клиентов. Платформа работала на базе Proxmox с Ceph-хранилищем, а за мониторинг отвечали Zabbix и Prometheus + Alertmanager. Проблема была не в мониторинге как таковом — алерты генерировались исправно. Проблема была в людях, которые должны были на них реагировать.

Дежурный инженер получал уведомления в общий Telegram-чат команды из 6 человек. Ночью алерты тонули среди сотен сообщений. Три раза за последний квартал критические инциденты оставались без реакции более 40 минут — при SLA, гарантирующем реакцию за 15 минут. Один из инцидентов — падение Ceph OSD на 2 часа ночью — привёл к деградации хранилища для 12 клиентов.

«У нас нет проблем с мониторингом — у нас проблема с тем, что конкретный человек должен взять на себя инцидент прямо сейчас. Алерт в общем чате — это алерт ничей» — технический директор «КлаудБейс».

Аудит текущей системы оповещений

Мы подключились к инфраструктуре и задокументировали текущее состояние:

  • Alertmanager — настроен, отправлял все алерты в один Telegram-чат через webhook
  • Zabbix 6.4 — генерировал триггеры на хост-уровне (CPU, RAM, диск), но actions были настроены только на email
  • Дежурства — велись в Google Sheets, инженеры забывали проверять расписание
  • Эскалации — отсутствовали: если дежурный не реагировал, инцидент просто висел
  • Постмортемы — не велись, причины повторяющихся инцидентов не анализировались

Статистика за последние 3 месяца:

# Выгрузка из Alertmanager: среднее время реакции
curl -s http://alertmanager:9093/api/v2/alerts | \
  jq '[.[] | select(.status.state=="active") | 
  .startsAt] | length'
# Результат: 847 алертов за 90 дней
# Из них 23% — ночные (00:00-07:00)
# Среднее время реакции ночью: 47 минут
# SLA нарушен: 14 раз из 50 критических инцидентов

Почему Grafana OnCall

Мы рассмотрели три решения для управления инцидентами:

РешениеПлюсыМинусы
PagerDutyЛидер рынка, мощныйОт $21/user/мес, данные в US, зависимость от SaaS
Opsgenie (Atlassian)Интеграция с JiraОт $9/user/мес, ограничения free-плана
Grafana OnCall (OSS)Бесплатный, self-hosted, нативная интеграция с GrafanaТребует инфраструктуры для деплоя

Grafana OnCall победил: у «КлаудБейс» уже работал Grafana-стек для дашбордов, команда знала интерфейс, а self-hosted решение гарантировало контроль над данными и отсутствие абонентской платы. При 6 инженерах PagerDuty обошёлся бы в $1 500+/год — за эти деньги мы покрыли всё внедрение.

Установка Grafana OnCall

Grafana OnCall разворачивается как набор микросервисов. Мы выбрали Docker Compose для развёртывания — инфраструктура «КлаудБейс» уже использовала Docker на серверах мониторинга. Целевой сервер: Ubuntu 22.04, 4 vCPU, 8 ГБ RAM.

Docker Compose для Grafana OnCall

Создаём файл docker-compose.yml с полным стеком:

# /opt/grafana-oncall/docker-compose.yml
version: '3.8'

services:
  # Основной движок OnCall
  oncall-engine:
    image: grafana/oncall:v1.9.11
    restart: always
    environment:
      - DATABASE_TYPE=postgresql
      - DATABASE_HOST=oncall-db
      - DATABASE_PORT=5432
      - DATABASE_NAME=oncall
      - DATABASE_USER=oncall
      - DATABASE_PASSWORD=${DB_PASSWORD}
      - RABBITMQ_USERNAME=oncall
      - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD}
      - RABBITMQ_HOST=oncall-rabbitmq
      - RABBITMQ_PORT=5672
      - REDIS_URI=redis://oncall-redis:6379/0
      - SECRET_KEY=${SECRET_KEY}
      - BASE_URL=https://oncall.cloudbase.local
      - GRAFANA_API_URL=http://grafana:3000
    depends_on:
      - oncall-db
      - oncall-redis
      - oncall-rabbitmq
    ports:
      - "8080:8080"

  # Celery worker для фоновых задач
  oncall-celery:
    image: grafana/oncall:v1.9.11
    restart: always
    command: celery_worker
    environment:
      - DATABASE_TYPE=postgresql
      - DATABASE_HOST=oncall-db
      - DATABASE_PORT=5432
      - DATABASE_NAME=oncall
      - DATABASE_USER=oncall
      - DATABASE_PASSWORD=${DB_PASSWORD}
      - RABBITMQ_USERNAME=oncall
      - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD}
      - RABBITMQ_HOST=oncall-rabbitmq
      - REDIS_URI=redis://oncall-redis:6379/0
      - SECRET_KEY=${SECRET_KEY}
    depends_on:
      - oncall-db
      - oncall-redis
      - oncall-rabbitmq

  # PostgreSQL для хранения данных OnCall
  oncall-db:
    image: postgres:15-alpine
    restart: always
    environment:
      - POSTGRES_DB=oncall
      - POSTGRES_USER=oncall
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    volumes:
      - oncall-db-data:/var/lib/postgresql/data

  # Redis для кеширования и очередей
  oncall-redis:
    image: redis:7-alpine
    restart: always
    volumes:
      - oncall-redis-data:/data

  # RabbitMQ для Celery
  oncall-rabbitmq:
    image: rabbitmq:3.12-alpine
    restart: always
    environment:
      - RABBITMQ_DEFAULT_USER=oncall
      - RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASSWORD}
    volumes:
      - oncall-rabbitmq-data:/var/lib/rabbitmq

  # Grafana с плагином OnCall
  grafana:
    image: grafana/grafana:10.3.1
    restart: always
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
      - GF_INSTALL_PLUGINS=grafana-oncall-app
    ports:
      - "3000:3000"
    volumes:
      - grafana-data:/var/lib/grafana

volumes:
  oncall-db-data:
  oncall-redis-data:
  oncall-rabbitmq-data:
  grafana-data:

Создаём файл переменных и запускаем:

# Генерируем секреты
cat > /opt/grafana-oncall/.env <<EOF
DB_PASSWORD=$(openssl rand -base64 24)
RABBITMQ_PASSWORD=$(openssl rand -base64 24)
SECRET_KEY=$(openssl rand -base64 32)
GRAFANA_PASSWORD=CloudBase!Gr4f4n4
EOF

# Запускаем стек
cd /opt/grafana-oncall
docker compose up -d

# Проверяем, что все контейнеры запустились
docker compose ps
# NAME                 STATUS
# oncall-engine        Up (healthy)
# oncall-celery        Up
# oncall-db            Up (healthy)
# oncall-redis         Up (healthy)
# oncall-rabbitmq      Up (healthy)
# grafana              Up (healthy)

Подключение плагина OnCall в Grafana

После запуска активируем плагин в Grafana и связываем его с backend-сервером OnCall:

# Включаем плагин через API
curl -X POST http://admin:${GRAFANA_PASSWORD}@localhost:3000/api/plugins/grafana-oncall-app/settings \
  -H 'Content-Type: application/json' \
  -d '{
    "enabled": true,
    "jsonData": {
      "onCallApiUrl": "http://oncall-engine:8080"
    }
  }'

# Генерируем API-ключ для OnCall
curl -X POST http://admin:${GRAFANA_PASSWORD}@localhost:3000/api/auth/keys \
  -H 'Content-Type: application/json' \
  -d '{"name": "oncall-api-key", "role": "Admin"}'
# {"id":1,"name":"oncall-api-key","key":"eyJrIjoi..."}

Теперь в интерфейсе Grafana в боковом меню появился раздел OnCall с вкладками: Alert Groups, Integrations, Escalation Chains, Schedules, Users.

Интеграция с Alertmanager и Zabbix

Основная ценность Grafana OnCall — это единая точка входа для алертов из разных систем мониторинга. У «КлаудБейс» алерты приходили из двух источников, и нам нужно было свести их в единый поток.

Настройка интеграции с Alertmanager

В Grafana OnCall создаём интеграцию типа Alertmanager. Система генерирует уникальный webhook URL, который мы указываем в конфиге Alertmanager:

# /etc/alertmanager/alertmanager.yml
global:
  resolve_timeout: 5m

route:
  receiver: 'grafana-oncall'
  group_by: ['alertname', 'cluster', 'service']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    # Критические алерты — немедленно
    - match:
        severity: critical
      receiver: 'grafana-oncall'
      group_wait: 10s
      repeat_interval: 1h
    # Warning — группируем больше
    - match:
        severity: warning
      receiver: 'grafana-oncall'
      group_wait: 1m
      repeat_interval: 6h

receivers:
  - name: 'grafana-oncall'
    webhook_configs:
      - url: 'https://oncall.cloudbase.local/integrations/v1/alertmanager/abc123def456/'
        send_resolved: true
        http_config:
          tls_config:
            insecure_skip_verify: false

Перезагружаем Alertmanager:

# Проверяем конфиг
amtool check-config /etc/alertmanager/alertmanager.yml
# Checking '/etc/alertmanager/alertmanager.yml'  SUCCESS

# Перезагружаем
systemctl reload prometheus-alertmanager

# Тестируем отправку алерта
curl -X POST https://oncall.cloudbase.local/integrations/v1/alertmanager/abc123def456/ \
  -H 'Content-Type: application/json' \
  -d '[{
    "status": "firing",
    "labels": {
      "alertname": "TestAlert",
      "severity": "critical",
      "instance": "node-01:9090"
    },
    "annotations": {
      "summary": "Test alert from CLI"
    }
  }]'

Настройка интеграции с Zabbix

Для Zabbix создаём отдельную интеграцию типа Webhook. Grafana OnCall генерирует URL, а в Zabbix настраиваем Media Type:

# Zabbix: Administration → Media Types → Create
# Name: Grafana OnCall
# Type: Webhook
# Parameters:
#   URL: https://oncall.cloudbase.local/integrations/v1/webhook/xyz789abc012/
#   HTTPProxy: (пусто)
#   To: {ALERT.SENDTO}
#   Subject: {ALERT.SUBJECT}
#   Message: {ALERT.MESSAGE}

# Script в Media Type:
var params = JSON.parse(value);
var req = new HttpRequest();
req.addHeader('Content-Type: application/json');

var payload = {
    "alert_uid": "{EVENT.ID}",
    "title": "{HOST.NAME}: {TRIGGER.NAME}",
    "message": "{TRIGGER.DESCRIPTION}",
    "severity": "{TRIGGER.SEVERITY}",
    "status": "{TRIGGER.STATUS}",
    "host": "{HOST.NAME}",
    "host_ip": "{HOST.IP}",
    "trigger_id": "{TRIGGER.ID}",
    "event_id": "{EVENT.ID}",
    "event_time": "{EVENT.TIME}",
    "event_date": "{EVENT.DATE}"
};

var resp = req.post(params.URL, JSON.stringify(payload));
if (req.getStatus() !== 200) {
    throw 'OnCall returned HTTP ' + req.getStatus();
}
return 'OK';

Далее создаём Action в Zabbix:

# Configuration → Actions → Trigger actions → Create
# Name: Send to Grafana OnCall
# Conditions:
#   Trigger severity >= Warning
# Operations:
#   Send to User groups: All engineers
#   Send only to: Grafana OnCall (Media Type)

Теперь алерты из обоих систем — Alertmanager (для Prometheus-метрик) и Zabbix (для хостовых проверок) — приходят в единый интерфейс Grafana OnCall.

Политики эскалации и расписание дежурств

Ключевой функционал Grafana OnCall — это Escalation Chains (цепочки эскалации) и Schedules (расписания дежурств). Именно здесь решается проблема «алерт ничей» — каждый алерт привязывается к конкретному дежурному, а если он не реагирует, алерт автоматически уходит дальше по цепочке.

Создание расписания дежурств с ротацией

У «КлаудБейс» 6 инженеров. Мы создали два расписания: дневное и ночное/выходное:

# Через OnCall API создаём расписание
# Дневное дежурство: будни 09:00–21:00, ротация каждый день
curl -X POST https://oncall.cloudbase.local/api/v1/schedules/ \
  -H 'Authorization: Bearer ${ONCALL_API_KEY}' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Дневная смена",
    "type": "web",
    "team_id": null,
    "time_zone": "Europe/Moscow",
    "shifts": [
      {
        "name": "Day shift rotation",
        "type": "rolling_users",
        "start": "2026-01-13T09:00:00",
        "duration": 43200,
        "frequency": "daily",
        "interval": 1,
        "rolling_users": [
          ["user_id_1"],
          ["user_id_2"],
          ["user_id_3"]
        ],
        "by_day": ["MO", "TU", "WE", "TH", "FR"]
      }
    ]
  }'

# Ночное дежурство: будни 21:00–09:00 + выходные целиком
# Ротация каждые 3 дня среди оставшихся 3 инженеров
curl -X POST https://oncall.cloudbase.local/api/v1/schedules/ \
  -H 'Authorization: Bearer ${ONCALL_API_KEY}' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Ночная смена и выходные",
    "type": "web",
    "time_zone": "Europe/Moscow",
    "shifts": [
      {
        "name": "Night shift rotation",
        "type": "rolling_users",
        "start": "2026-01-13T21:00:00",
        "duration": 43200,
        "frequency": "daily",
        "interval": 1,
        "rolling_users": [
          ["user_id_4"],
          ["user_id_5"],
          ["user_id_6"]
        ],
        "by_day": ["MO", "TU", "WE", "TH", "FR"]
      },
      {
        "name": "Weekend rotation",
        "type": "rolling_users",
        "start": "2026-01-18T00:00:00",
        "duration": 86400,
        "frequency": "weekly",
        "rolling_users": [
          ["user_id_4"],
          ["user_id_5"],
          ["user_id_6"]
        ],
        "by_day": ["SA", "SU"]
      }
    ]
  }'

Каждый инженер видит своё расписание в интерфейсе Grafana и получает напоминание в Telegram за 12 часов до начала дежурства.

Настройка цепочек эскалации

Создаём три цепочки эскалации для разных уровней критичности:

# Цепочка для критических алертов (P1)
# Шаг 1: Уведомить текущего дежурного (Telegram + SMS + звонок)
# Шаг 2: Если нет ACK через 5 минут → уведомить следующего по ротации
# Шаг 3: Если нет ACK через 10 минут → уведомить тимлида
# Шаг 4: Если нет ACK через 15 минут → уведомить CTO

curl -X POST https://oncall.cloudbase.local/api/v1/escalation_chains/ \
  -H 'Authorization: Bearer ${ONCALL_API_KEY}' \
  -H 'Content-Type: application/json' \
  -d '{"name": "Critical (P1)"}'

# Добавляем шаги эскалации
# Шаг 1: Уведомить дежурного из расписания
curl -X POST https://oncall.cloudbase.local/api/v1/escalation_policies/ \
  -d '{
    "escalation_chain_id": "CHAIN_P1_ID",
    "type": "notify_on_call_from_schedule",
    "notify_on_call_from_schedule": "SCHEDULE_ID",
    "position": 0
  }'

# Шаг 2: Ждём 5 минут
curl -X POST https://oncall.cloudbase.local/api/v1/escalation_policies/ \
  -d '{
    "escalation_chain_id": "CHAIN_P1_ID",
    "type": "wait",
    "duration": 300,
    "position": 1
  }'

# Шаг 3: Уведомить следующего из ротации
curl -X POST https://oncall.cloudbase.local/api/v1/escalation_policies/ \
  -d '{
    "escalation_chain_id": "CHAIN_P1_ID",
    "type": "notify_on_call_from_schedule",
    "notify_on_call_from_schedule": "SCHEDULE_ID",
    "important": true,
    "position": 2
  }'

# Шаг 4: Ждём ещё 5 минут, затем уведомляем тимлида
curl -X POST https://oncall.cloudbase.local/api/v1/escalation_policies/ \
  -d '{
    "escalation_chain_id": "CHAIN_P1_ID",
    "type": "wait",
    "duration": 300,
    "position": 3
  }'

curl -X POST https://oncall.cloudbase.local/api/v1/escalation_policies/ \
  -d '{
    "escalation_chain_id": "CHAIN_P1_ID",
    "type": "notify_persons",
    "persons_to_notify": ["TEAM_LEAD_USER_ID"],
    "important": true,
    "position": 4
  }'

# Шаг 5: Ещё 5 минут — эскалация на CTO
curl -X POST https://oncall.cloudbase.local/api/v1/escalation_policies/ \
  -d '{
    "escalation_chain_id": "CHAIN_P1_ID",
    "type": "wait",
    "duration": 300,
    "position": 5
  }'

curl -X POST https://oncall.cloudbase.local/api/v1/escalation_policies/ \
  -d '{
    "escalation_chain_id": "CHAIN_P1_ID",
    "type": "notify_persons",
    "persons_to_notify": ["CTO_USER_ID"],
    "important": true,
    "position": 6
  }'

Для Warning-алертов (P2) цепочка проще: уведомление дежурного, ожидание 15 минут, эскалация на тимлида. Для Info-алертов (P3) — только уведомление в Telegram без эскалации.

Маршрутизация алертов по критичности

В каждой интеграции (Alertmanager, Zabbix) настраиваем Routes — правила, которые направляют алерты в нужную цепочку эскалации:

# В интерфейсе Grafana OnCall:
# Integrations → Alertmanager → Routes

# Route 1: severity == critical → Escalation Chain "Critical (P1)"
# Condition: {{ payload.commonLabels.severity == "critical" }}
# Escalation Chain: Critical (P1)

# Route 2: severity == warning → Escalation Chain "Warning (P2)"
# Condition: {{ payload.commonLabels.severity == "warning" }}
# Escalation Chain: Warning (P2)

# Default Route → Escalation Chain "Info (P3)"

# Для Zabbix-интеграции аналогично:
# Route 1: severity содержит "Disaster" или "High" → P1
# Condition: {{ payload.severity in ["Disaster", "High"] }}
# Route 2: severity == "Average" → P2
# Default → P3

Дополнительно настроили шаблоны группировки — алерты с одного хоста группируются в один инцидент, чтобы дежурный не получал 20 отдельных уведомлений, когда падает один сервер:

# Grouping Template для Alertmanager-интеграции:
{{ payload.commonLabels.alertname }}_{{ payload.commonLabels.instance }}

# Grafana OnCall сгруппирует:
# NodeDown_node-03:9100 — все алерты с node-03 в одну группу
# CephOSDDown_ceph-01:9283 — все алерты Ceph в другую

Каналы уведомлений: Telegram, SMS, голосовые звонки

Алерт бесполезен, если дежурный его не увидел. Мы настроили три канала уведомлений с разным уровнем «назойливости» для разных приоритетов.

Telegram-бот для уведомлений

Создаём Telegram-бота и подключаем к OnCall:

# 1. Создаём бота через @BotFather:
# /newbot → CloudBase OnCall Bot → @cloudbase_oncall_bot
# Получаем токен: 7123456789:AAH...

# 2. В Grafana OnCall → Settings → Chat Ops → Telegram
# Указываем токен бота
# Бот автоматически создаёт deep-link для верификации

# 3. Каждый инженер в Telegram пишет боту /start
#    и получает код верификации, который вводит в Grafana

# 4. Настройка персональных уведомлений (каждый инженер):
# OnCall → Users → Notification Rules:
#   Default notifications:
#     Step 1: Notify by Telegram
#   Important notifications:
#     Step 1: Notify by Telegram
#     Step 2: Wait 1 minute
#     Step 3: Notify by Phone Call
#     Step 4: Wait 2 minutes
#     Step 5: Notify by SMS

Telegram-уведомления содержат кнопки для быстрых действий: Acknowledge (принять инцидент), Resolve (закрыть), Silence (приглушить на время). Дежурный может управлять инцидентом прямо из мессенджера, не открывая браузер.

SMS и голосовые звонки для критических алертов

Для SMS и звонков используем Twilio — Grafana OnCall имеет встроенную интеграцию. Однако для российских номеров мы настроили альтернативный канал через SMS-центр:

# /opt/grafana-oncall/custom_notify.py
# Кастомный webhook для SMS через smsc.ru API

import requests
import os
from flask import Flask, request

app = Flask(__name__)

SMSC_LOGIN = os.environ['SMSC_LOGIN']
SMSC_PASSWORD = os.environ['SMSC_PASSWORD']

@app.route('/sms', methods=['POST'])
def send_sms():
    data = request.json
    phone = data.get('phone')
    message = data.get('message', 'OnCall Alert')
    
    resp = requests.get('https://smsc.ru/sys/send.php', params={
        'login': SMSC_LOGIN,
        'psw': SMSC_PASSWORD,
        'phones': phone,
        'mes': message[:160],
        'charset': 'utf-8'
    })
    return {'status': 'sent', 'response': resp.text}

@app.route('/call', methods=['POST'])
def make_call():
    data = request.json
    phone = data.get('phone')
    message = data.get('message', 'Критический алерт. Проверьте Grafana OnCall.')
    
    resp = requests.get('https://smsc.ru/sys/send.php', params={
        'login': SMSC_LOGIN,
        'psw': SMSC_PASSWORD,
        'phones': phone,
        'mes': f'voice={message}',
        'call': 1,
        'charset': 'utf-8'
    })
    return {'status': 'called', 'response': resp.text}

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=9099)

В Grafana OnCall подключаем кастомный webhook как канал уведомлений через Outgoing Webhooks. Теперь при Important-уведомлениях дежурный получает: Telegram → ожидание 1 мин → голосовой звонок → ожидание 2 мин → SMS. Не заметить невозможно.

Incident Timeline и Postmortem Workflow

Управление инцидентами не заканчивается на оповещении. Grafana OnCall ведёт полную хронологию каждого инцидента и позволяет проводить постмортемы — разбор причин, чтобы инцидент не повторился.

Автоматический timeline инцидента

Каждый инцидент в Grafana OnCall автоматически собирает timeline — цепочку событий с точными таймстемпами:

# Пример timeline инцидента #47:
# [2026-01-21 02:14:32 MSK] Alert received from Alertmanager
#   → CephOSDDown on ceph-02, severity: critical
# [2026-01-21 02:14:33 MSK] Escalation started: Critical (P1)
# [2026-01-21 02:14:34 MSK] Telegram sent to: Иванов А.С. (on-call)
# [2026-01-21 02:19:34 MSK] No ACK after 5 min → escalating
# [2026-01-21 02:19:35 MSK] Phone call to: Иванов А.С.
# [2026-01-21 02:21:12 MSK] Acknowledged by: Иванов А.С.
#   → Response time: 6 min 40 sec
# [2026-01-21 02:21:45 MSK] Note added: "Проверяю Ceph status"
# [2026-01-21 02:35:00 MSK] Note added: "OSD.4 упал из-за диска, 
#   запустил ceph osd repair"
# [2026-01-21 03:12:00 MSK] Resolved by: Иванов А.С.
#   → Resolution time: 57 min 28 sec

Вся эта информация сохраняется автоматически и доступна для анализа. Инженеры добавляют заметки в реальном времени через Telegram или интерфейс — это формирует базу знаний.

Postmortem workflow

Для каждого критического инцидента (P1) мы настроили обязательный постмортем. Grafana OnCall создаёт шаблон, который инженер заполняет после resolve:

# Шаблон постмортема (Markdown):
# Автоматически заполняется из timeline

## Postmortem: Incident #47 — CephOSDDown

**Date:** 2026-01-21
**Duration:** 57 min
**Severity:** P1 — Critical
**On-call:** Иванов А.С.
**Response time:** 6 min 40 sec (SLA: 15 min ✅)

### Timeline
(автозаполнение из OnCall)

### Root Cause
(заполняет инженер)
Диск /dev/sdd на ceph-02 начал возвращать ошибки ввода/вывода.
SMART показывал Reallocated_Sector_Ct = 148 (порог 140).
Мониторинг SMART-атрибутов отсутствовал.

### Impact
- 12 клиентов испытывали деградацию storage I/O
- Кластер работал на 2 OSD из 3 (66% capacity)

### Action Items
- [ ] Добавить мониторинг SMART через node_exporter
- [ ] Заказать SSD на замену для ceph-02
- [ ] Настроить предиктивный алерт на SMART degradation

Постмортемы хранятся в Grafana OnCall и экспортируются в Confluence через webhook. Раз в месяц команда проводит разбор всех постмортемов и выполняет action items.

SLA-трекинг и дашборды мониторинга

Внедрение Grafana OnCall позволило «КлаудБейс» не просто реагировать на инциденты, но и измерять качество своей работы. Мы создали дашборды для отслеживания SLA и метрик команды.

Метрики Grafana OnCall в Prometheus

Grafana OnCall экспортирует метрики в формате Prometheus. Настраиваем scrape:

# /etc/prometheus/prometheus.yml — добавляем job
scrape_configs:
  - job_name: 'grafana-oncall'
    scrape_interval: 30s
    static_configs:
      - targets: ['oncall-engine:8080']
    metrics_path: '/metrics'

# Ключевые метрики:
# oncall_alert_groups_total — общее количество инцидентов
# oncall_alert_groups_response_time_seconds — время реакции
# oncall_alert_groups_resolution_time_seconds — время решения
# oncall_escalations_total — количество эскалаций
# oncall_notifications_sent_total — отправленные уведомления

На основе этих метрик создаём Grafana-дашборд:

# Grafana Dashboard JSON (ключевые панели):

# Panel 1: SLA Compliance (Stat)
# Query: 
#   (count(oncall_alert_groups_response_time_seconds{severity="critical"} < 900)
#   / count(oncall_alert_groups_response_time_seconds{severity="critical"})) * 100
# Threshold: Green > 95%, Yellow > 90%, Red < 90%

# Panel 2: MTTR — Mean Time to Resolve (Gauge)
# Query:
#   avg(oncall_alert_groups_resolution_time_seconds{severity="critical"}) / 60
# Unit: minutes

# Panel 3: Escalation Rate (Time Series)
# Query:
#   rate(oncall_escalations_total[7d])
# Описание: Чем ниже — тем лучше. Значит дежурные реагируют без эскалации.

# Panel 4: On-Call Load by Engineer (Bar Gauge)
# Query:
#   sum by (user) (oncall_alert_groups_total{acknowledged_by!=""})
# Показывает, кто обработал больше всего инцидентов

Результаты внедрения

Через 2 месяца после внедрения Grafana OnCall мы собрали статистику и сравнили с показателями до:

МетрикаДо внедренияПосле внедрения
Среднее время реакции (P1)47 минут4 минуты 20 секунд
SLA compliance (15 мин на P1)72%98.5%
Пропущенные ночные алерты23% (не замечены)0%
MTTR (P1 инциденты)2 ч 15 мин38 минут
Количество эскалаций на тимлидаN/A8% инцидентов
Повторяющиеся инциденты34% (без анализа)12% (постмортемы)

Ключевой результат: за 2 месяца ни одного нарушения SLA по критическим инцидентам. Дежурный инженер всегда знает, что он дежурный, и всегда получает уведомление лично — по Telegram, звонком или SMS. Алерт больше не может остаться «ничьим».

«КлаудБейс» использовала улучшение SLA как маркетинговый аргумент — обновила SLA-гарантии для клиентов с 99.5% до 99.9%, что привлекло 7 новых корпоративных клиентов в первый же квартал.

Часто задаваемые вопросы

Да. Grafana OnCall доступен в двух вариантах: как часть Grafana Cloud (SaaS) и как полностью open-source self-hosted решение (OSS). В нашем кейсе мы использовали именно OSS-вариант, развёрнутый в Docker Compose на собственном сервере клиента. Функционал OSS-версии покрывает все ключевые сценарии: интеграции, эскалации, расписания, Telegram-уведомления. Отличия от Cloud-версии минимальны — в основном это отсутствие встроенного SMS/Voice через Twilio (мы решили это кастомным webhook) и мобильного приложения Grafana OnCall.

Grafana OnCall поддерживает множество интеграций — каждая система мониторинга подключается как отдельный источник (Integration). Для Alertmanager, Zabbix, Datadog, Elastic и десятков других систем есть готовые интеграции с webhook-URL. Все алерты от всех интеграций попадают в единый интерфейс, где к каждой интеграции привязываются свои Routes и Escalation Chains. Это позволяет направлять алерты из Zabbix по одной логике, а из Alertmanager — по другой, но дежурный видит всё в одном месте.

Само ПО бесплатно (open-source, лицензия AGPLv3). Затраты складываются из: серверных ресурсов (1 VM с 4 vCPU и 8 ГБ RAM достаточно для команды до 20 человек), работы по внедрению и настройке интеграций, а также стоимости SMS/Voice-шлюза для телефонных уведомлений. В кейсе «КлаудБейс» SMS через smsc.ru обходился примерно в 1 500 руб./мес. Общая стоимость внедрения силами «АйТи Фреш» составила значительно меньше годовой подписки на PagerDuty или Opsgenie для аналогичной команды.

Именно для этого существуют Escalation Chains (цепочки эскалации). Если дежурный не подтвердил (Acknowledge) инцидент в течение заданного времени (в нашем кейсе — 5 минут для критических алертов), OnCall автоматически переходит к следующему шагу: уведомляет другого инженера из расписания, затем тимлида, затем CTO. Каждый шаг может использовать более агрессивные каналы уведомлений — от Telegram до голосового звонка. Цепочка гарантирует, что кто-то обязательно возьмёт инцидент в работу.

Да. Grafana OnCall из коробки поддерживает Slack и Microsoft Teams для ChatOps — управление инцидентами прямо из чата. Для Telegram поддержка также встроена. Кроме того, через Outgoing Webhooks можно интегрировать любой мессенджер или систему уведомлений: Mattermost, Discord, VK Teams — достаточно написать простой webhook-приёмник, который будет принимать данные от OnCall и отправлять сообщения через API нужного мессенджера.

Нужна помощь с настройкой?

Специалисты АйТи Фреш помогут с внедрением и настройкой — 15+ лет опыта, обслуживание от 15 000 ₽/мес

📞 Связаться с нами
#Grafana OnCall настройка#управление инцидентами Grafana#Grafana OnCall Alertmanager интеграция#Grafana OnCall Zabbix#on-call schedule ротация дежурных#escalation policy настройка#Grafana OnCall Telegram уведомления#SLA трекинг инциденты