Сегодня появилась задачка настроить мониторинг внешних web-сервисов из вне инфраструктуры.

Условно запускаем инстанс где-нибудь на digital ocean, и начинаем выполнять пробы (проверки) на внешний контур инфры - http, ssh и прочее.

Таска достаточно простая, развернул забикс напихал туда проверок и поехали. Но хочется чего нибудь не тривиального и модного =)

Вообщем пришла идея попробовать развернуть схему Blackbox Exporter + Prometheus.

blackbox_exporter - экспортер позволяющий нам снимать метрики с эндпоинтов поверх протоколов http, icmp, tcp, grpc и прочего. Что касается метрик, то получаем данныe по статусу конечной точки (доступна/недоступна), время ее отклика. И самое вкусное это дата истечение ssl сертификатов.

prom-black-export-wall1.png (Workflow всей схемы)

Установка экспортера

Идем на github репозиторий и находим там последний релиз: https://github.com/prometheus/blackbox_exporter/releases/

Последний 0.23.0 / 2022-12-02, выбираем совместимый с нашей системой архив и качаем его на удаленный сервер:

[root@mon ~]# cd /opt/
[root@mon opt]# wget https://github.com/prometheus/blackbox_exporter/releases/download/v0.23.0/blackbox_exporter-0.23.0.linux-amd64.tar.gz

Распаковываем архив и проваливаемся внутрь:

[root@mon opt]# tar -zxvf blackbox_exporter-0.23.0.linux-amd64.tar.gz 
[root@mon opt]# cd blackbox_exporter-0.23.0.linux-amd64

Архив будет содержать бинарь с экпортером и файл конфигурации. В файле конфигурации описываются модули и пробы для проверок. По умолчанию, в конфиге прописаны почти все доступные пробы (http, tcp, grpc, ssh и прочее).

Создадим новый сервис для запуска експортера, сначала добавляем нового системного пользователя:

[root@mon ~]# useradd -c "Blackbox exporter user" -U --system -s /bin/false blackbox

В /etc добавляем каталог под конфигурации blackbox, и копируем туда конфиг:

[root@mon ~]# mkdir /etc/blackbox 
[root@mon ~]# cp /opt/blackbox_exporter-0.23.0.linux-amd64/blackbox.yml /etc/blackbox/

И не забываем сменить владельца/группы:

[root@mon ~]# chown -R blackbox:blackbox /etc/blackbox/

Бинарь с экспортером также закидываем в каталог - /usr/local/bin. И также меняем владельца/группу:

[root@mon ~]# cp /opt/blackbox_exporter-0.23.0.linux-amd64/blackbox_exporter /usr/local/bin/
[root@mon ~]# chown -R blackbox:blackbox /usr/local/bin/blackbox_exporter

Напишем systemd юнит, для сервиса:

[root@mon ~]# vim /etc/systemd/system/blackbox.service
---
[Unit]
Description=Blackbox Exporter Service
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=blackbox
Group=blackbox
ExecStart=/usr/local/bin/blackbox_exporter \
  --config.file=/etc/blackbox/blackbox.yml \
  --web.listen-address="127.0.0.1:9115"
Restart=always

[Install]
WantedBy=multi-user.target

Релоудим systemd и запускаем сервис:

[root@mon ~]# systemctl daemon-reload
[root@mon ~]# systemctl enable --now blackbox

Если на этом этапе нечего не закрашилось, можем перейти к тестированию работы экспортера.

Для проверки экспортера, достаточно кинуть curl запрос:

[root@mon ~]# curl http://localhost:9115/metrics
[root@mon ~]# curl 'localhost:9115/probe?target=ya.ru&module=http_2xx'

В результате выполнения команды, будет вывод с кучей метрик.

На страничке браузера будет опубликована страничка с результатами проб, и описанием конфигурации: prom-black-export-ui.png

Как я и говорил ранее по умолчанию в конфиге указано излишне проб. Для моей задачи достаточно настроить модуль http. Поэтому в конфиге удаляем все лишнее, и обновляем модули:

[root@mon ~]# vim /etc/blackbox/blackbox.yml
---
modules:
  http_2xx:
    prober: http
    timeout: 5s
    http:
      valid_http_versions: ["HTTP/1.1", "HTTP/2.0"]
      valid_status_codes: []  # Defaults to 2xx
      method: GET
      headers:
        Accept-Language: en-US
      follow_redirects: true
      fail_if_ssl: false
      fail_if_not_ssl: false
      tls_config:
        insecure_skip_verify: false
      preferred_ip_protocol: "ip4" # defaults to "ip6"
      ip_protocol_fallback: false  # no fallback to "ip6"
  • modules - Основное поле, содержащее список модулей.
  • http_2xx: - Имя модуля.
  • prober - В значение этой директивы указывается тип пробы (http, tcp, etc..).
  • timeout - Как понятно из название это таймаут, здесь указывается время в секундах. Через сколько секунд проба будет отброшена, в случаи долгово ожидания.
  • http: - Внутри этого контекста описывается конфиграция http пробы.
  • valid_http_versions - Указываются валидные версии http для этой пробы.
  • valid_status_codes - В этом поле указываются валидные коды ответов, по умолчанию это 200 status code.
  • method - Метод http запроса, по умолчанию это get-запрос.
  • headers - Тут перечисляются передаваемые в запросе хедеры.
  • follow_redirects - Здесь разрешаем или запрешаем редиректы для проб.
  • fail_if_ssl - Проба будет отброшена, если у конечной точки представлен ssl.
  • fail_if_not_ssl - Проба сфейлится, если у конечной точки ssl не представлен.
  • tls_config - В этом блоке описываеются конфигурация для tls.
  • preferred_ip_protocol - какую версию ip используем в этом модуле. по умолчанию тут ipv6.

Перезапускаем экспортера, и делаем тест пробы:

[root@mon ~]# systemctl restart blackbox

Тестовый запрос:

[root@mon ~]# curl 'localhost:9115/probe?target=https://example.com&module=http_2xx'
---
ts=2023-04-25T17:34:58.824445326Z caller=main.go:181 module=http_2xx target=https://example.com level=info msg="Probe succeeded" duration_seconds=0.702091381

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

Установка prometheus

Скрапить данные данные будем через прометеус, установим это на этот же сервер и далее сбиндим с экпортером.

Под сервис с прометеусом создаем еще одного системного пользователя:

[root@mon ~]# useradd -c "Prometheus system user" -U --system -s /bin/false prometheus

Создаем новые каталоги под нужды сервиса, требуется директории для конфигов и данных прометеуса:

# Каталог для данных
[root@mon ~]# mkdir /var/lib/prometheus

# Каталоги под конфиги
[root@mon ~]# mkdir -p /etc/prometheus/{rules,rules.d,files_sd}

Теперь идем на сайт, выбираем и качаем последний билд под нашу систему - https://prometheus.io/download/:

[root@mon ~]# cd /opt/
[root@mon opt]# wget https://github.com/prometheus/prometheus/releases/download/v2.37.6/prometheus-2.37.6.linux-amd64.tar.gz

Распаковываем архив:

[root@mon opt]# tar -zxvf prometheus-2.37.6.linux-amd64.tar.gz 

Копируем бинари:

[root@mon opt]# cd prometheus-2.37.6.linux-amd64
[root@mon prometheus-2.37.6.linux-amd64]# cp prometheus promtool /usr/local/bin/

Конфиги со смежными зависимостями закидываем в конфигурационный конфиг prometheus:

[root@mon prometheus-2.37.6.linux-amd64]# cp -r prometheus.yml consoles/ console_libraries/ /etc/prometheus/

Сразу же меняем пользователя/группу на каталогах:

[root@mon ~]# chown -R prometheus:prometheus /etc/prometheus
[root@mon ~]# chown -R prometheus:prometheus /var/lib/prometheus/
[root@mon ~]# chmod -R 774 /etc/prometheus

Ну и для запуска всего создаем systemd юнит:

[root@mon ~]# vim /etc/systemd/system/prometheus.service
---
[Unit]
Description=Prometheus
Documentation=https://prometheus.io/docs/introduction/overview/
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=prometheus
Group=prometheus
ExecReload=/bin/kill -HUP $MAINPID
ExecStart=/usr/local/bin/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --storage.tsdb.path=/var/lib/prometheus \
  --web.console.templates=/etc/prometheus/consoles \
  --web.console.libraries=/etc/prometheus/console_libraries \
  --web.listen-address=127.0.0.1:9090 \
  --web.external-url=

SyslogIdentifier=prometheus
Restart=always

[Install]
WantedBy=multi-user.target

Релоудим и запускаем прометеус:

[root@mon ~]# systemctl daemon-reload
[root@mon ~]# systemctl enable --now prometheus

По умолчанию, сейчас в конфиге прописана только одна цель для скрапинга метрик это сам прометеус. Отредактируем его конфиг, и добавим вторую цель с ссылкой на установленный ранее экспортер. Редактируем конфиг:

[root@mon ~]# vim /etc/prometheus/prometheus.yml 
---
  - job_name: 'blackbox'
    metrics_path: /probe
    params:
      module: [http_2xx]
    static_configs:
      - targets:
        - https://example.com
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: 127.0.0.1:9115

В поле static_configs > targets далее будем добавлять наши конечные точки. Сейчас для проверки я указал сайт заглушку.

    static_configs:
      - targets:
        - https://example.com

Перезапускаем, сервис с прометеусом.

[root@mon ~]# systemctl restart prometheus

На этом все, теперь можем сходить в WebUI прометеуса и чекнуть работу наших проб: prom-black-export-ui1.png

Настройка Grafana

Ну и почти заключительным этапом поставим графану, и прикрутем к ней прометеус. Идем на сайт https://packages.grafana.com/, для получени файла конфигурации нового репозитория. На сайте копируем данные, и вставляем в файл с описанием репы:

[root@mon ~]# cat /etc/yum.repos.d/grafana.repo
---
[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

Теперь через пакетный менеджер устанавлием:

[root@mon ~]# yum install grafana -y

Запускаем сервис,

[root@mon ~]# systemctl enable --now grafana-server

Установка завершена. Сервис запускается на 3000/tcp порту, его публиковать наружу не будем. Далее настроим nginx, для проксирования запросов внутрь.

Установка Nginx

В стандартных репозиториях для установки нам доступны версии nginx с 1.14 - 1.20. Для решения нашей задачи это вполне приемлимо.

[root@mon ~]# yum module list nginx
Failed to set locale, defaulting to C.UTF-8
Last metadata expiration check: 0:04:07 ago on Wed Apr 26 13:44:16 2023.
AlmaLinux 8 - AppStream
Name                 Stream                     Profiles                 Summary                      
nginx                1.14 [d][e]                common [d]               nginx webserver              
nginx                1.16                       common [d]               nginx webserver              
nginx                1.18                       common [d]               nginx webserver              
nginx                1.20                       common [d]               nginx webserver   

Переключаемся на версию 1.20, и ставим пакет:

[root@mon ~]# yum module reset nginx -y
[root@mon ~]# yum module enable nginx:1.20 -y
[root@mon ~]# yum install nginx -y 

Я предварительно добавил запись в dns, и закинул на сервер ssl-сертификат. Ну теперь можно перейти к написанию конфиги для nginx.

[root@mon ~]# vim /etc/nginx/conf.d/grafana.conf
---
map $http_upgrade $connection_upgrade {
  default upgrade;
  '' close;
}

upstream grafana {
  server localhost:3000;
}

server {
  listen 443 ssl http2;
  server_name	mon.nixhub.ru;

  ssl_certificate /etc/nginx/ssl/cert.crt;
  ssl_certificate_key /etc/nginx/ssl/cert.key;
  ssl_session_timeout 1d;
  ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
  ssl_session_tickets off;

  # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
  ssl_dhparam /etc/nginx/ssl/dhparam;

  # intermediate configuration
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
  ssl_prefer_server_ciphers off;

  # HSTS (ngx_http_headers_module is required) (63072000 seconds)
  add_header Strict-Transport-Security "max-age=63072000" always;

  # OCSP stapling
  ssl_stapling on;
  ssl_stapling_verify on;

  # replace with the IP address of your resolver
  resolver 127.0.0.1;

  root /usr/share/nginx/html;
  index index.html index.htm;

  location / {
    proxy_set_header Host $http_host;
    proxy_pass http://grafana;
  }

  location /api/live/ {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $http_host;
    proxy_pass http://grafana;
  }
}

server {
  listen 80;
  server_name	mon.nixhub.ru;

  location / {
    return 201 https://$host$request_uri;
  }
}

Запускаем nginx,

[root@mon ~]# systemctl enable nginx --now

И также не забываем открыть порт на фаере:

[root@mon ~]# firewall-cmd --add-port={80,443}/tcp --permanent

Nginx, настроен.

Допиливаем Графану

В браузере покдючаемся к grafane, дефолтный логин\пароль - admin\admin. При первом входе, нас попросят сменить пароль пароль администратора. Меняем и идем в настройки datasource. prom-black-export-db1.png

Жмем на добавление нового источника, в общем списке выбираем - prometheus: prom-black-export-ui2.png

После выбора источника откроется окно с его конфигурацией, и тут указываем данные для подключения: prom-black-export-ui3.png Так как весь стек развернут локально, в поле url указываем локальный сокет к прометеусу. И сохраняемся.

Нам остается стянуть с GrafanaLabs темплейт для blackbox_exporter. Идем на сайт и качаем шаблон - https://grafana.com/grafana/dashboards/7587-prometheus-blackbox-exporter/

Далее простым импортом добавляем шаблон. И по итогу будет такая симпотичная дешка: prom-black-export-dash.png

Так как планируется мониторинг десятка и более конечный точек, в конфиг прометеуса я тестово добавил еще несколько хостов.

    static_configs:
      - targets:
        - https://example.com
        - https://ya.ru
        - https://nixhub.ru

Перезапускаем прометеус, и смотрим дэшку в графане: prom-black-export-dash1.png Дешборд обновился, и тут уже другая красота.

Настройка Alertmanager

Данный с эндпоинтов у нас собираются, но мы хотим получать feedback по состоянию метрик в телеграмм. Установим менеджера, и настроим алерты в телегу.

Alertmanager ставится аналогичным способом, что и прометей. Создаем каталог под конфигурацию менеджера:

[root@mon ~]# mkdir /etc/alertmanager

Создаем системного пользователя:

[root@mon ~]# useradd -c "Alertmanager system user" -U --system -s /bin/false alertmanager

Для получения бинаря идем на сайт прометеуса и качаем ластецкий архив:

[root@mon ~]# cd /opt/
[root@mon opt]# wget https://github.com/prometheus/alertmanager/releases/download/v0.25.0/alertmanager-0.25.0.linux-amd64.tar.gz

Распаковываем архив, и копируем конфиг менеджера в созданный конфигурационный каталог. Бинарь копируем в /usr/local/bin

[root@mon opt]# tar -zxvf alertmanager-0.25.0.linux-amd64.tar.gz 
[root@mon opt]# cd alertmanager-0.25.0.linux-amd64

Раскидываем файлы:

[root@mon alertmanager-0.25.0.linux-amd64]# cp alertmanager.yml /etc/alertmanager/
[root@mon alertmanager-0.25.0.linux-amd64]# cp alertmanager /usr/local/bin/

На /etc/alertmanager каталоге меняем владельца/группу:

[root@mon ~]# chown -R alertmanager:alertmanager /etc/alertmanager/

Для запуска менеджера как сервис, создаем systemd-юнит:

[root@mon ~]# 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

Релоудим и запускаем сервис:

[root@mon ~]# systemctl daemon-reload
[root@mon ~]# systemctl enable --now alertmanager

Теперь остается связать прометеус с alertmanager, настроить триггеры для срабатывания алертов. Ну и включить отправку сообщений в телегу через настройку alertmanager.yml. Для понимания взаимосвязей сделал такую схему: prom-black-export-alert1.png

  • alerts.yml - файл с описанием правил оповещений. Здесь создадим триггер, который будет срабатывать при достижения определенного результата пробы.
  • prometheus.yml - основной конфиг прометеуса, в поле rules_files мы биндим файл - alerts.yml. А в контексте alerting связываемся с alert-менеджером через порт-сокет.
  • alertmanager.yml - файл конфигурации менеджера оповещений, в нем прописываем настройки для провайдеров оповещений (Почта, телеграмм, слак).

Создадим файл alerts.yml, и на первом этапе опишем триггер, который будет срабатывать при недоступности какой-либо ресурса.

[root@mon ~]# vim /etc/prometheus/alerts.yml
---
groups:
- name: alerts
  rules:
  - alert: EndpointDown
    expr: probe_success == 0
    for: 10s
    labels: 
      severity: "critical"
    annotations:
      summary:
        "Endpoint {{ $labels.instance }} is down."

Здесь мы создали триггер, который будет срабатывать при получении нулевого значения метрики probe_success с продолжительностью в 10 секунд. То есть если сайт будет недоступен в течении 10 секунд, то сработает этот тригер.

С помощью утилиты promtool можно чекнуть конфигурационный файл:

[root@mon ~]# /usr/local/bin/promtool check rules /etc/prometheus/alerts.yml
Checking /etc/prometheus/alerts.yml
  SUCCESS: 1 rules found

Теперь отредактируем конфигу прометеуса, укажем файл с правилами и добавим биндинг к alert-менеджеру:

[root@mon ~]# vim /etc/prometheus/prometheus.yml 
---
# Alertmanager configuration
alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - localhost:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  - alerts.yml

Перезапускаем прометей:

[root@mon ~]# systemctl restart prometheus

Ну и наконец, отредактируем конфиг alert-менеджера, пропишем настройки для отправки в телегу.

Я предварительно создал телеграмм бота, далее создал отдельный канал и туда же добавил этого бота. Соответвенно на выходе у меня были данные chatid - id от канала и api_token - токен для телеграммовского бота.

Редактируем конфиг alert-менеджера, и вносим изменения:

[root@mon ~]# 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_url: 'https://api.telegram.org'
      chat_id: <ID Группу, без ковычек>
      parse_mode: ''
      message: "🚨 Alertmanager 🚨\n--\n🔺 Alertname: {{ .GroupLabels.alertname}}\n🔺 Severity: {{ .CommonLabels.severity }}\n📌 {{ range .Alerts }}{{ .Annotations.summary }}\n{{ end }}"

Перезапускаем alertmanager:

[root@mon alertscripts]# systemctl restart alertmanager

С этим конфигом менеджер будет каждые пять минуть оправлять сообщения, пока проблема не будет решена. То есть, пока конечная точка вновь не станет доступна. Ради эксперемента добавляем в конфиг прометеуса не существующий url-адрес, и смотрим что прилетит в телеграмм группу: prom-black-export-tg.png

На этом основную часть работы можно считать решенной. На последок нам остается добавить правила, для мониторинга сертификатов от протухания. Снова открываем файл alerts.yml, и прописываем новое правило:

  - alert: SSLCertExpire
    expr: probe_ssl_earliest_cert_expiry{job="blackbox"} - time() < 86400 * 3
    for: 5m
    labels:
      severity: "warning"
    annotations:
      summary: "SSL certificate will expire in {{ $value | humanizeDuration }} (endpoint {{ $labels.instance }})"

В этом правиле сравнивается значение пробы probe_ssl_earlist_cert_expiry для каждого инстанса, если оно меньще трех дней, то будет скинут такой варнинг: prom-black-export-tg1.png

Перезапускаем сервис с прометеус, и радуемся еще одной закрытой таске =)