Перейти к содержимому

Uptime Kuma Discord Webhook: алерты из Grafana и Alertmanager

Алерты мониторинга в Discord: настройка Uptime Kuma discord webhook, contact point в Grafana, discord_configs в Alertmanager и cron-проверка на bash.

7 мин чтения Доступно также на English
На этой странице

Зачем слать алерты мониторинга в Discord

Падение, которое прилетело в #alerts, заметят за секунды; то же падение в почте увидят после обеда. Вебхуки делают это почти бесплатно: без бота, без OAuth, без серверного кода. Создаёте вебхук в «Настройки сервера → Интеграции → Вебхуки», копируете URL — и любой инструмент из этой статьи сможет в него постить.

Четыре варианта, по возрастанию трудозатрат:

  • Uptime Kuma — встроенный тип уведомления Discord: вставили URL и готово.
  • Grafana Alerting — contact point типа Discord.
  • Prometheus Alertmanager — receiver discord_configs, доступен начиная с Alertmanager 0.25.
  • Cron — короткий bash-скрипт для сервера, на котором ничего из перечисленного нет.

Дальше — цветовая кодировка embed, чтобы красная карточка читалась как «упало» без вчитывания, и защита от штормов, когда одна плохая ночь превращается в четыреста пингов.

Заведите отдельный вебхук под канал алертов (как получить URL) и относитесь к нему как к паролю: любой, у кого есть URL, может писать в канал, а единственное лечение — удалить вебхук и создать новый.

Uptime Kuma Discord webhook

В Uptime Kuma Discord — полноценный тип уведомления, никаких «generic webhook» с ручным маппингом полей.

  1. Откройте Settings → Notifications → Setup Notification.
  2. В Notification Type выберите Discord.
  3. Вставьте URL в поле Discord Webhook URL.
  4. По желанию задайте Bot Display Name (это подмена username, поэтому в нём не должно быть слов «discord» и «clyde») и Prefix Custom Message.
  5. Нажмите Test, затем Save. Поставьте галочку Apply on all existing monitors либо подключите уведомление точечно на странице редактирования монитора.

Формат карточки фиксированный: при DOWN — красный embed с заголовком «❌ Your service name went down. ❌» и полями Service Name, Service URL, Time и Error; при UP — зелёный embed, где вместо ошибки поле Ping. JSON вы не пишете; имя и префикс — единственные ручки оформления.

Префикс — место для пинга роли. Uptime Kuma отправляет его как content сообщения, так что <@&ROLE_ID> там упомянет дежурную роль над embed. Если пинг отрисовался обычным текстом — id неверный (скопируйте его в режиме разработчика: правый клик по роли → «Копировать ID роли»); как allowed_mentions решает, кого пинговать, — в статье про упоминания через вебхук.

Защита от шторма настраивается на каждом мониторе: Retries (сколько проверок подряд должно упасть, прежде чем монитор перейдёт в DOWN), Heartbeat Retry Interval и Resend Notification if Down X times consecutively (оставьте 0, чтобы получить ровно одно DOWN-сообщение).

Grafana Discord webhook (contact point)

В Grafana Alerting интеграция с Discord есть из коробки:

  1. Alerting → Contact points → Create contact point.
  2. Integration: Discord. Вставьте URL вебхука.
  3. Title и Message content оставьте по умолчанию ({{ template "default.title" . }} и {{ template "default.message" . }}).
  4. Test, затем Save.
  5. Alerting → Notification policies: направьте на этот contact point политику по умолчанию или дочернюю политику с матчерами по лейблам. Иначе contact point существует, но в него ничего не маршрутизируется.

Grafana отправляет сообщение текстом над embed; сам embed — красный, пока группа алертов горит, и зелёный после resolve, — несёт заголовок и ссылку обратно в Grafana. Заголовок и сообщение обрезаются под лимиты Discord (256 и 2000 символов), так что правило с тремя десятками лейблов не приведёт к 400.

То же самое в виде provisioning-YAML:

# provisioning/alerting/discord.yaml
apiVersion: 1
contactPoints:
  - orgId: 1
    name: discord-ops
    receivers:
      - uid: discord-ops
        type: discord
        settings:
          url: https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN
          title: '{{ template "default.title" . }}'
          message: '{{ template "default.message" . }}'
          use_discord_username: false
policies:
  - orgId: 1
    receiver: discord-ops
    group_by: ['alertname', 'instance']
    group_wait: 30s
    group_interval: 5m
    repeat_interval: 4h

Три поля с таймингами — и есть ваш контроль шторма: group_wait собирает алерты, пришедшие в первые 30 секунд, в одно сообщение; group_interval ограничивает, как часто группа присылает обновления; repeat_interval задаёт, как часто повторно отправлять всё ещё горящий алерт. С четырёхчасовым повтором падение на выходных даст несколько сообщений, а не сотни.

Alertmanager: receiver для Discord

Начиная с Alertmanager 0.25 интеграция с Discord встроенная; раньше приходилось ставить мост или заворачивать generic-webhook в прокси, который переформатировал payload, и если у вас до сих пор так — обновиться быстрее, чем чинить.

# alertmanager.yml
global:
  resolve_timeout: 5m

route:
  receiver: discord
  group_by: ['alertname', 'job']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - matchers:
        - severity = critical
      receiver: discord-critical
      repeat_interval: 1h

receivers:
  - name: discord
    discord_configs:
      - webhook_url: https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN
        send_resolved: true

  - name: discord-critical
    discord_configs:
      - webhook_url: https://discord.com/api/webhooks/OTHER_ID/OTHER_TOKEN
        send_resolved: true
        title: '{{ template "discord.default.title" . }}'
        message: '{{ template "discord.default.message" . }}'

inhibit_rules:
  - source_matchers:
      - severity = critical
    target_matchers:
      - severity = warning
    equal: ['alertname', 'instance']

Alertmanager отправляет один embed на уведомление: красный для firing, зелёный для resolved, описание рендерится из message и обрезается под лимит embed. Проверить конфиг и перечитать его без рестарта:

amtool check-config alertmanager.yml
curl -X POST http://localhost:9093/-/reload

Не коммитьте webhook_url (в новых релизах есть webhook_url_file; сверьтесь с документацией своей версии) и помните, что первая линия обороны — for: в правиле Prometheus: с for: 5m алерт не дойдёт даже до Alertmanager, пока условие не продержится пять минут.

Cron-проверка на bash

Для VPS без стека мониторинга — скрипт, который запускается раз в минуту и пишет в Discord только при смене состояния:

#!/usr/bin/env bash
# /usr/local/bin/healthcheck.sh
set -u

WEBHOOK_URL="${DISCORD_WEBHOOK_URL:?set DISCORD_WEBHOOK_URL}"
TARGET="https://example.com/health"
STATE_FILE="/var/tmp/healthcheck.state"
FAIL_THRESHOLD=3

# при ошибке соединения curl печатает 000
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 "$TARGET")
prev=$(cat "$STATE_FILE" 2>/dev/null || echo "up:0")
prev_status=${prev%%:*}
fails=${prev##*:}

if [[ "$code" == "200" ]]; then
  status=up; fails=0
else
  status=down; fails=$((fails + 1))
  # порог ещё не набран: оставляем прежнее состояние, молчим
  [[ $fails -lt $FAIL_THRESHOLD ]] && status=$prev_status
fi

echo "$status:$fails" > "$STATE_FILE"
[[ "$status" == "$prev_status" ]] && exit 0

if [[ "$status" == "down" ]]; then
  color=15548997; title="🔴 $TARGET упал"
else
  color=5763719;  title="🟢 $TARGET снова доступен"
fi

now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
curl -s -o /dev/null -w 'discord: %{http_code}\n' --max-time 10 \
  -H "Content-Type: application/json" \
  -d @- "$WEBHOOK_URL" <<EOF
{
  "embeds": [{
    "title": "$title",
    "color": $color,
    "fields": [
      {"name": "HTTP-статус", "value": "$code", "inline": true},
      {"name": "Откуда проверяли", "value": "$(hostname)", "inline": true}
    ],
    "timestamp": "$now"
  }]
}
EOF

В файле состояния лежит up:0 или down:N. Три не-200 ответа подряд переводят его в down и отправляют одну красную карточку; первый 200 после этого отправляет одну зелёную; всё между ними — тишина. Ставится через chmod +x и запись в crontab:

DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN
* * * * * /usr/local/bin/healthcheck.sh >> /var/log/healthcheck.log 2>&1

Чтобы восстановление обновляло красную карточку, а не добавляло зелёную рядом, отправьте DOWN-сообщение с ?wait=true, сохраните id из ответа в файл состояния и сделайте PATCH на /messages/{id}, когда сервис вернётся (редактирование и удаление сообщений вебхука).

Цветные embed для up и down

Держите палитру крошечной и никогда не меняйте, что какой цвет значит:

СостояниеЦветЗначение color
Down, firingкрасный #ED424515548997
Up, resolvedзелёный #57F2875763719
Деградация, warningжёлтый #FEE75C16705372
Обслуживание, infoblurple #5865F25793266

Правила, которые работают для любого инструмента:

  • color — десятичное целое. Hex-строка вроде "#ED4245" отклоняется с 400.
  • Дублируйте состояние в заголовке: push-уведомление на телефоне показывает только текст, и не все различают красный с зелёным.
  • Передавайте timestamp в ISO 8601 по UTC — Discord покажет его в часовом поясе читателя.
  • Когда собираете payload сами, держитесь лимитов embed (256 символов на заголовок, 4096 на описание, 25 полей); Grafana и Alertmanager обрежут за вас, bash-скрипт — нет.
  • Ещё десятичные значения — в гайде по цветам embed.

Как не устроить шторм алертов

Канал, который стреляет без остановки, замьютят, а замьюченный канал хуже, чем никакого.

  1. Никогда не алертить по одной упавшей проверке. Retries в Uptime Kuma, for: в правилах Prometheus, pending period в Grafana, FAIL_THRESHOLD в скрипте.
  2. Группировать. Одно сообщение со списком из двенадцати хостов лучше двенадцати сообщений; для этого и нужны group_by и group_wait.
  3. Повторять раз в часы, а не в минуты. repeat_interval: 4h для warning, 1h для critical.
  4. Подавлять. Inhibit-правило выше глушит warning, пока горит critical с теми же лейблами, — чтобы упавший хост не рапортовал заодно о двадцати упавших сервисах.
  5. Слать resolved в том же стиле. Зелёная карточка под каждой красной превращает канал в пары «упало — поднялось».
  6. Разводить severity на два вебхука. Критичное — в канал, который нельзя замьютить; информационное — в тот, который можно.
  7. Уважать rate limit. Пятьдесят мониторов, переключившихся разом, — это пятьдесят POST в одну секунду, и на часть из них Discord ответит 429. Повторяет ли инструмент отправку — зависит от инструмента; ваши скрипты обязаны соблюдать retry_after (rate limits вебхуков).

Частые ошибки

{"message":"Unknown Webhook","code":10015} на каждой отправке. URL обрезан (токены длинные, их легко потерять при вставке) или вебхук удалён. Создайте заново и обновите настройки инструмента.

Кнопка Test работает, а реальные алерты не приходят. Уведомление не привязано к монитору (Uptime Kuma), ни одна политика не ведёт на contact point (Grafana), receiver не упомянут в route (Alertmanager) или cron не получил переменную окружения (скрипт). Тестовый путь обходит все четыре проверки.

400 Bad Request от самописного payload. Неэкранированная кавычка или перенос строки в переменной, hex-строка вместо числа в color или поле сверх лимита. Проверьте через jq . или собирайте payload через jq -n --arg; полный список — в статье про ошибки вебхуков.

Alertmanager не стартует из-за неизвестного поля discord_configs. Бинарник старше 0.25. Обновитесь или временно вернитесь к webhook_configs с прокси.

429 Too Many Requests во время инцидента. Слишком много мониторов сработали в одно окно. Группируйте, поднимайте порог retries, соблюдайте retry_after.

Упоминание роли отображается текстом, а не пингом. Неверный id роли или роль с другого сервера. Скопируйте id в режиме разработчика (правый клик по роли → «Копировать ID роли»).

FAQ

Нужен ли Uptime Kuma бот Discord для отправки алертов?

Нет. Типу уведомления Discord нужен только URL вебхука; бот понадобится лишь для того, что вебхук не умеет, — например, для интерактивной кнопки «принял в работу».

Какая версия Alertmanager поддерживает discord_configs?

Alertmanager 0.25.0 и новее. Более старые версии отклоняют поле при загрузке конфига; обновитесь или заверните через generic webhook receiver и небольшой прокси.

Можно ли отправлять алерты Grafana или Alertmanager в тред?

Да. Оба инструмента делают POST ровно на тот URL, который вы указали, так что допишите ?thread_id=<id существующего треда> к URL вебхука. Форум-каналам нужен thread_name в теле запроса, а эти инструменты его не ставят.

Как перестать получать сообщение каждую минуту, пока сервис лежит?

Алертите по смене состояния, а не по каждой проверке. Uptime Kuma по умолчанию шлёт один DOWN и один UP; в Grafana и Alertmanager ставьте repeat_interval в часах; в cron-скрипте храните последнее состояние в файле и пишите только когда оно изменилось.

Что дальше

Начните с инструмента, который у вас уже крутится, ограничьте палитру красным, зелёным и жёлтым и настройте группировку до первого настоящего инцидента, а не после. Если нужна карточка красивее дефолтной — соберите JSON в конструкторе Discord Webhook и вставьте в скрипт.

Похожие статьи:

Теги: uptime kumagrafanaalertmanagerмониторингалертыdiscord webhookвебхук discord

Похожие статьи

Все статьи

Соберите это в визуальном редакторе

Embed, Components V2, кнопки и опросы с живым предпросмотром Discord. Бесплатно, без регистрации.