Привет всем,
В данный момент телеграмм используется как основной источник сбора всех уведомлений/нотификаций по проблемам в инфраструктуре. И от обилия систем, на выходе имеем нехилый такой набор различных чатов.
С момента появления возможности создания приватного канала с топиками, мы преобрели возможность всю эту вакханалию сгрупировать в рамках одного чата, и классифицировать проблемы по отдельным топикам. Ну идея крутая подумал я, и принялся к реализации.
Начнем от самого простого этапа к сложному.
Создание Канала/Топика
Тут думаю все просто, сначала создаем приватный чат, в настройках канала включаем поддержку топиков.
Внутри чата создадим наш первый топик, в который и будет в последующем слать уведомления.
Далее обратимся к @BotFather для создания собственнго бота. Наш бот будет неким посредников в отправке сообщений из alertmanager в телеграмм.
Процесс создания бота можно пропустить, на выходе у вас должен быть API token
от созданного бота.
И наконец не забываем добавить бота в группу, в качестве простого пользователя.
Отправка через Curl
Для отправки сообщения в топик через утилиту curl, для начала нужно получить данные группы и топика. Как правило это chat_id
и message_thread_id
.
Что бы определить эти данные, в топик пишем рандомное сообщение при этом тегаем нашего бота:
Далее в браузере открываем такой линк:
https://api.telegram.org/bot{INSERT_BOT_TOKEN}/getUpdates
В значение INSERT_BOT_TOKEN
подставляем токен от бота.
Браузер отобразит массив, из которого мы находим chat_id
и message_thread_id
.
Затем открываем командной строку и экспортируем в переменные окружения переменную со значением API токена от бота:
export TG_BOT={INSERT_BOT_TOKEN}
Опять же в значение INSERT_BOT_TOKEN
подставляем токен.
И после при помощи курл отправляем первое сообщение:
curl -X POST -H 'Content-Type: application/json' \
-d '{"reply_to_message_id": "4", "chat_id": "-1002124751773", "text": "This is a test message cUrl"}' \
https://api.telegram.org/bot$TG_BOT/sendMessage
В консоле ответом отобразится результат запроса, и если все корретно то прилетит сообщение:
Отправка через Alertmanager
На текущий момент Alertmanager
не умеет отправлять сообщения в топики. Для получения этой фишки, скачаем его исходный код, внесем в него изменения. Далее соберем новый билд менеджера.
Подключаемся на сервер, и ставим и софты для сборки.
[root@mon ~]# yum install go nmp git make
Клонируем репозиторий алерт менеджера:
[root@mon opt]# git clone https://github.com/prometheus/alertmanager.git
Далее прежде чем собрать новый билд, вносим изменения в исходный код. Проваливаемся во внутрь репозитория:
[root@mon opt]# cd alertmanager
Затем в редакторе открываем файл config/notifiers.go
, находим структуру TelegramConfig
, и добавляем новое поле:
[root@mon alertmanager]# vim config/notifiers.go
---
type TelegramConfig struct {
NotifierConfig `yaml:",inline" json:",inline"`
HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"`
APIUrl *URL `yaml:"api_url" json:"api_url,omitempty"`
BotToken Secret `yaml:"bot_token,omitempty" json:"token,omitempty"`
BotTokenFile string `yaml:"bot_token_file,omitempty" json:"token_file,omitempty"`
ChatID int64 `yaml:"chat_id,omitempty" json:"chat,omitempty"`
Message string `yaml:"message,omitempty" json:"message,omitempty"`
DisableNotifications bool `yaml:"disable_notifications,omitempty" json:"disable_notifications,omitempty"`
ParseMode string `yaml:"parse_mode,omitempty" json:"parse_mode,omitempty"`
// Добавил новое поле - ReplyToMessageID
MessageThreadId int64 `yaml:"message_thread_id,omitempty" json:"message_thread_id,omitempty"`
}
Сохраняем и выходим.
Далее идем в notify/telegram/telegram.go
внутри файла находим функцию Notify
, и добавляем:
[root@mon alertmanager]# vim notify/telegram/telegram.go
---
func (n *Notifier) Notify(ctx context.Context, alert ...*types.Alert) (bool, error) {
var (
err error
data = notify.GetTemplateData(ctx, n.tmpl, alert, n.logger)
tmpl = notify.TmplText(n.tmpl, data, &err)
)
if n.conf.ParseMode == "HTML" {
tmpl = notify.TmplHTML(n.tmpl, data, &err)
}
key, ok := notify.GroupKey(ctx)
if !ok {
return false, fmt.Errorf("group key missing")
}
messageText, truncated := notify.TruncateInRunes(tmpl(n.conf.Message), maxMessageLenRunes)
if truncated {
level.Warn(n.logger).Log("msg", "Truncated message", "alert", key, "max_runes", maxMessageLenRunes)
}
n.client.Token, err = n.getBotToken()
if err != nil {
return true, err
}
message, err := n.client.Send(telebot.ChatID(n.conf.ChatID), messageText, &telebot.SendOptions{
// Добавил ReplyTo в SendOptions
ReplyTo: &telebot.Message{ID: int(n.conf.MessageThreadId)},
DisableNotification: n.conf.DisableNotifications,
DisableWebPagePreview: true,
})
if err != nil {
return true, err
}
level.Debug(n.logger).Log("msg", "Telegram message successfully published", "message_id", message.ID, "chat_id", message.Chat.ID)
return false, nil
}
В данном случаи внесли изменения только в метод Send
:
message, err := n.client.Send(telebot.ChatID(n.conf.ChatID), messageText, &telebot.SendOptions{
// Добавил ReplyTo в SendOptions
ReplyTo: &telebot.Message{ID: int(n.conf.MessageThreadId)},
DisableNotification: n.conf.DisableNotifications,
DisableWebPagePreview: true,
})
Сохраняемся и выходим.
Итак мы внеслм изменения в двух местах, теперь собирем новый билд для проверки работы нового функционала.
Внутри репозитория запускаем сборку, командой:
[root@mon alertmanager]# make build
В процессе сборки, если сборка будет ломаться на этапе с реактом, то достаточно обновить npm на более современную версию. По крайней мере мне помог этот вариант. Для обновления достаточно выполнить команды:
[root@mon alertmanager]# yum module reset nodejs -y
[root@mon alertmanager]# yum module enable nodejs:18 -y
[root@mon alertmanager]# yum update npm -y
Также для запуска сервиса потребуется конфигурационный alertmanager, напишем его:
[root@mon alertmanager]# vim /etc/alertmanager/alertmanager.yml
---
route:
group_by: ['alertname']
group_wait: 10s
group_interval: 10s
repeat_interval: 5m
receiver: 'telegram-api'
receivers:
- name: 'telegram-api'
telegram_configs:
- bot_token: '<СЮДА ВСТАВЛЯЕМ API TOKEN>'
api_url: 'https://api.telegram.org'
chat_id: 'СЮДА CHAT_ID'
parse_mode: ''
message_thread_id: ' СЮДА TOPIC_ID'
message: "🚨 Alertmanager 🚨\n--\n🔺 Alertname: {{ .GroupLabels.alertname}}\n🔺 Severity: {{ .CommonLabels.severity }}\n📌 {{ range .Alerts }}{{ .Annotations.summary }}\n{{ end }}"
Ну и запускаем алерт менеджера:
[root@mon alertmanager]# ./alertmanager --config.file=/etc/alertmanager/alertmanager.yml
Попробуем отправить тестовый алерт, на порт alertmanager через curl:
[root@mon ~]# curl -w '\n' -i --header 'Content-Type: application/json' --data '[{"labels":{"alertname":"test"},"endsAt":"2024-10-5T00:10:53-03:00"}]' http://localhost:9093/api/v1/alerts
Теперь если в браузере открыть UI алерт менеджера, то увидем новый алерт в очереди:
И соответвенно этот алерт прилетит в наш ранее созданный топик:
Новый бинарь закидываем сюда /usr/local/bin/
:
[root@mon alertmanager]# mv alertmanager /usr/local/bin/
Затем создадим новый systemd-юнит для запуска алертменеджера как сервис:
[root@mon alertmanager]# vim /etc/systemd/system/alertmanager.service
---
[Unit]
Description=Alertmanager
Wants=network-online.target
After=network-online.target
[Service]
User=alertmanager
Group=alertmanager
Type=simple
WorkingDirectory=/etc/alertmanager/
ExecStart=/usr/local/bin/alertmanager \
--config.file=/etc/alertmanager/alertmanager.yml
[Install]
WantedBy=multi-user.target
Перечитываем конфиг systemd, и запускаем сервис:
[root@mon alertmanager]# systemctl daemon-reload
[root@mon alertmanager]# systemctl enable --now alertmanager
Ну и на этом все, задача решена =)