VictoriaMetrics: базовий моніторинг AWS, Linux, NGINX та PHP
5 (1)

Автор |  28/03/2026
Click to rate this post!
[Total: 1 Average: 5]

Міграція RTFM з DigitalOcean до AWS пройшла без проблем, і потроху “обживаюсь на новому місці”.

Інфраструктура нова, все нове – а тому перший час хочеться уважно постежити за станом серверів та блогу, а тому треба налаштувати базовий моніторинг для WordPress: NGINX, PHP-FPM, базу даних та інфраструктуру, на якій все це крутиться.

Сам стек моніторингу вже розгорнутий на домашньому NAS з FreeBSD – є VictoriaMetrics, VictoriaLogs, Grafana, vmalert та Alertmanager з відправкою алертів в Telegram та ntfy.sh.

По цьому стеку писав в серії постів по FreeBSD та домашньому NAS:

Інфраструктура в AWS

Створення інфраструктури описане в AWS: сетап базової інфраструктури для WordPress і AWS: власний EC2 в ролі NAT Gateway замість AWS Managed NAT Gateway.

Нагадаю, що в AWS є загалом:

  • VPC з 4 Subnets – 2 публічні, 2 приватні
  • Application Load Balancer з Target Group, в TG – один EC2
  • два EC2 instances з Amazon Linux 2023:
    • один з NGINX та PHP-FPM для самого WordPress
    • і окремий EC2 в ролі NAT Gateway
  • AWS RDS з MariaDB – сервер бази даних для WordPress

На обох EC2 піднятий WireGuard для підключення до домашньої мережі, де в ролі VPN Hub виступає MikriTik RB4011 і який роутить запити до VictoriaMetrics та VictoriaLogs – див. MikroTik: налаштування WireGuard та підключення Linux peers.

Планування моніторингу

Що є з сервісів, які треба помоніторити:

  • AWS RDS: стан серверу бази даних
  • AWS ALB: картина того, що відбувається на Load Balancer
  • AWS EC2: різні метрики по стану самих інстансів
  • NGINX: метрики веб-сервера
  • PHP-FPM: метрики воркерів FPM

Крім того, треба збирати системні логи операційної системи та логи NGINX і PHP.

Логи RDS теж можуть бути корисними – але це вже на випадок реальних проблем, а тоді вже можна просто подивитись в CloudWatch Logs.

Для збору метрик на EC2 використав:

  • node_exporter: базові метрики EC2 – CPU, RAM, диск, мережа
  • nginx_exporter: простенький, метрик мало, але нехай буде (окремо зробимо метрики з логів NGINX)
  • php_fpm_exporter: метрики PHP-FPM – процеси, використання воркерів, slow requests
  • yace_exporter: збирає з CloudWatch дефолтні метрики по стану ALB та RDS

Для логів поки взяв Fluent Bit, який писатиме до VictoriaLogs. Взагалі, пізніше для збору логів спробую vlagent, зараз робив “швиденько” – тому взяв те, що в мене вже працює на FreeBSD/NAS.

vlagent виглядає дуже цікаво, див. цікавий пост в блогах VictoriaMetrics – Benchmarking Kubernetes Log Collectors: vlagent, Vector, Fluent Bit, OpenTelemetry Collector, and more. Але релізнули місяці три тому (по стану на березень 2026), тому поки що може мати не всіх корисні плюшки.

Пізніше можна буде додати cloudflare-prometheus-exporter та process_exporter, або генерити власні метрики з Textfile для node_exporter.

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

Запускати будемо стандартно – з Docker та Docker Compose.

Встановлюємо Docker:

[root@ip-10-0-3-146 ~]# dnf install -y docker

[root@ip-10-0-3-146 ~]# systemctl enable --now docker

Docker Compose:

[root@ip-10-0-3-146 ~]# mkdir -p /usr/local/lib/docker/cli-plugins

[root@ip-10-0-3-146 ~]# curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose

[root@ip-10-0-3-146 ~]# chmod +x /usr/local/lib/docker/cli-plugins/docker-compose

Перевіряємо:

[root@ip-10-0-3-146 ~]# docker compose version
Docker Compose version v5.1.0

Запуск node_exporter

Створюємо каталог /opt/monitoring і в ньому файл /opt/monitoring/docker-compose.yml:

services:
  node_exporter:
    image: prom/node-exporter:latest
    container_name: node_exporter
    restart: unless-stopped
    pid: host
    network_mode: host
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--path.rootfs=/rootfs'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'

Щоб node_exporter бачив всі мережеві інтерфейси – задаємо network_mode: host, щоб всі PID – задаємо pid: host.

З точки зору security це не ідеально, бо контейнер з network_mode: host дає повний доступ до мережі хоста, а pid: host дає йому видимість всіх процесів. Але для моніторингу особистого блогу – нормально.

Запускаємо:

[root@ip-10-0-3-146 ~]# cd /opt/monitoring && docker compose up -d

Перевіряємо метрики:

[root@ip-10-0-3-146 ~]# curl -s http://localhost:9100/metrics | grep node_exporter_build
# HELP node_exporter_build_info A metric with a constant '1' value labeled by version, revision, branch, goversion from which node_exporter was built, and the goos and goarch for the build.
# TYPE node_exporter_build_info gauge
node_exporter_build_info{branch="HEAD",goarch="amd64",goos="linux",goversion="go1.25.3",revision="654f19dee6a0c41de78a8d6d870e8c742cdb43b9",tags="unknown",version="1.10.2"} 1

Налаштування vmagent на FreeBSD

Додаємо збір метрик до VictoriaMetrics. На FreeBSD для vmagent використовується конфіг /usr/local/etc/prometheus/prometheus.yml – додаємо туди новий таргет.

В мене в job_name: "node_exporter" вже є один таргет – 127.0.0.1:9100 для метрик самої FreeBSD – туди ж вписуємо 10.100.0.20:9100, де 10.100.0.20 – це адреса EC2 в мережі WireGuard (хоча потім створю Static DNS record на MikroTik):

global:
  scrape_interval: 15s

scrape_configs:

  - job_name: victoriametrics
    scrape_interval: 60s
    scrape_timeout: 30s
    metrics_path: "/metrics"
    static_configs:
    - targets:
      - 127.0.0.1:8428
      labels:
        project: victoriametrics

  - job_name: vmagent
    scrape_interval: 60s
    scrape_timeout: 30s
    metrics_path: "/metrics"
    static_configs:
    - targets:
      - 127.0.0.1:8429
      labels:
        project: vmagent

  - job_name: "node_exporter"
    static_configs:
      - targets:
          - "127.0.0.1:9100"
          - "10.100.0.20:9100"
...

Перевіряємо в VictoriaMetrics через метрику node_uname_info – маємо бачити обидва хости:

Запуск nginx_exporter

Для отримання метрик nginx_exporter використовує модуль stub_status. Додаємо окремий файл /etc/nginx/conf.d/nginx-status.conf:

server {
    listen 127.0.0.1:8080;

    location = /nginx_status {
        stub_status on;
        access_log off;
    }
}

Перевіряємо конфіг та перезавантажуємо NGINX:

[root@ip-10-0-3-146 ~]# nginx -t && systemctl reload nginx

Перевіряємо ендпоінт та дані від NGINX:

[root@ip-10-0-3-146 ~]# curl http://127.0.0.1:8080/nginx_status
Active connections: 6 
server accepts handled requests
 33310 33310 162229 
Reading: 0 Writing: 1 Waiting: 5

Додаємо nginx_exporter до Docker Compose file:

  nginx_exporter:
    image: nginx/nginx-prometheus-exporter:latest
    container_name: nginx_exporter
    restart: unless-stopped
    network_mode: host
    command:
      - '--nginx.scrape-uri=http://127.0.0.1:8080/nginx_status'

Перезапускаємо стек та додаємо таргет до vmagent:

  - job_name: "nginx_exporter"
    static_configs:
      - targets:
          - "10.100.0.20:9113"

Перезапускаємо vmagent та перевіряємо в VictoriaMetrics:

Або через curl напряму:

root@setevoy-nas:~ # curl -s 'http://localhost:8428/api/v1/query?query=nginx_connections_active' | jq
{
  "status": "success",
  "data": {
    "resultType": "vector",
    "result": [
      {
        "metric": {
          "__name__": "nginx_connections_active",
          "instance": "10.100.0.20:9113",
          "job": "nginx_exporter"
        },
        "value": [
          1773670644,
          "8"
        ]
      }
    ]
  },
  "stats": {
    "seriesFetched": "1",
    "executionTimeMsec": 0
  }
}

Запуск php-fpm exporter

Є два популярних експортери для PHP-FPM, хоча обидва давно не оновлюються – але працюють:

Для базового моніторингу WordPress-блогу різниця несуттєва – беремо hipages/php-fpm_exporter, він перевірений та стабільний.

Перевіряємо, що в конфігу PHP-FPM включена опція статусу pm.status_path – в мене файл конфігу FPM це /etc/php-fpm.d/rtfm.co.ua.conf:

...
; endpoint for fpm status page (use in nginx location)
pm.status_path = /status
...

Якщо не включена – додаємо і перезапускаємо PHP-FPM:

[root@ip-10-0-3-146 ~]# systemctl restart php-fpm.service

Додаємо окремий віртуалхост в NGINX, файл /etc/nginx/conf.d/fpm-status.conf, в allow дозволяємо доступ тільки з localhost:

server {
    listen 127.0.0.1:8081;
    server_name localhost;

    # fpm status page - local only
    location = /fpm-status {
        allow 127.0.0.1;
        deny all;

        include fastcgi_params;
        fastcgi_pass unix:/var/run/rtfm.co.ua-php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

Перезавантажуємо NGINX з nginx -t && systemctl reload nginx та перевіряємо ендпоінт:

[root@ip-10-0-3-146 ~]# curl http://localhost:8081/fpm-status
pool:                 rtfm.co.ua
process manager:      dynamic
start time:           16/Mar/2026:16:22:30 +0200
start since:          653
accepted conn:        218
listen queue:         0
max listen queue:     0
listen queue len:     0
idle processes:       2
active processes:     1
total processes:      3
max active processes: 3
max children reached: 0
slow requests:        0
memory peak:          40792064

PHP-FPM використовує Unix socket – тому монтуємо його в контейнер і передаємо URI з unix://:

  php_fpm_exporter_rtfm:
    image: hipages/php-fpm_exporter:latest
    container_name: php_fpm_exporter
    restart: unless-stopped
    network_mode: host
    volumes:
      - /var/run/rtfm.co.ua-php-fpm.sock:/var/run/rtfm.co.ua-php-fpm.sock
    environment:
      - PHP_FPM_SCRAPE_URI=unix:///var/run/rtfm.co.ua-php-fpm.sock;/fpm-status
      - PHP_FPM_FIX_PROCESS_COUNT=true

Перезапускаємо стек та перевіряємо метрики вже від експортера:

[root@ip-10-0-3-146 ~]# curl -s http://localhost:9253/metrics | grep phpfpm_up
# HELP phpfpm_up Could PHP-FPM be reached?
# TYPE phpfpm_up gauge
phpfpm_up{pool="rtfm.co.ua",scrape_uri="unix:///var/run/rtfm.co.ua-php-fpm.sock;/fpm-status"} 1

Додаємо нову scrape job до vmagent:

  - job_name: "php_fpm_exporter_rtfm"
    static_configs:
      - targets:
          - "10.100.0.20:9253"

Перезапускаємо vmagent та перевіряємо метрики в VictoriaMetrics:

Моніторинг AWS з YACE Exporter

Для AWS-метрик будемо використовувати yet-another-cloudwatch-exporter (YACE) – він забирає метрики з CloudWatch і віддає їх у форматі Prometheus. Трохи детальніше про нього писав у Prometheus: yet-another-cloudwatch-exporter – сбор метрик AWS CloudWatch, досі використовую на робочих проектах.

Документація по метриках:

Створення IAM Policy для YACE

EC2 вже має IAM Role – створював Instance Profile, коли робив AWS: ALB та Cloudflare – налаштування mTLS та AWS Security Rules.

До цієї ролі треба додати IAM Policy для YACE, яка надасть доступу до CloudWatch та iam:ListAccountAliases – щоб відображати ім’я акаунта замість числового ID в метриках:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:GetMetricData",
                "cloudwatch:GetMetricStatistics",
                "cloudwatch:ListMetrics",
                "tag:GetResources",
                "iam:ListAccountAliases"
            ],
            "Resource": "*"
        }
    ]
}

Зберігаємо Policy:

Підключаємо до IAM Role EC2 інстансу

Перевіряємо, що EC2 має доступ до CloudWatch – підключаємось по SSH, виконуємо з AWS CLI запит до CloudWatch:

[root@ip-10-0-3-146 ~]# aws cloudwatch list-metrics --namespace AWS/ApplicationELB --region eu-west-1
{
    "Metrics": [
        {
            "Namespace": "AWS/ApplicationELB",
            "MetricName": "HTTPCode_Target_3XX_Count",
            "Dimensions": [
                {
                    "Name": "TargetGroup",
                    "Value": "targetgroup/rtfm-tg/66df64e645b2b01d"
                },
                {
                    "Name": "LoadBalancer",
                    "Value": "app/rtfm-alb/cd76dd0d557838f8"
                }
            ]
        },
...

Конфігурація YACE

Створюємо конфіг /opt/monitoring/yace-config.yml. В exportedTagsOnMetrics вказуємо, які AWS-теги додавати до метрик – потім в Grafana і алертах можна буде виводити ім’я, а не ARN.

За збір метрик з CloudWatch платимо гроші, тому тут беремо тільки те, що дійсно корисне:

apiVersion: v1alpha1
discovery:

  exportedTagsOnMetrics:
    AWS/ApplicationELB:
      - Name
    AWS/RDS:
      - Name

  jobs:

    - type: AWS/ApplicationELB
      regions:
        - eu-west-1
      period: 300
      length: 300
      metrics:
        - name: HTTPCode_ELB_5XX_Count
          statistics:
            - Sum
          nilToZero: true
        - name: ActiveConnectionCount
          statistics:
            - Sum
          nilToZero: true

    - type: AWS/RDS
      regions:
        - eu-west-1
      period: 300
      length: 300
      metrics:
        - name: CPUUtilization
          statistics:
            - Average
          nilToZero: true

Додаємо YACE до Docker Compose file:

  yace_exporter:
    image: quay.io/prometheuscommunity/yet-another-cloudwatch-exporter:latest
    container_name: yace
    restart: unless-stopped
    network_mode: host
    volumes:
      - /opt/monitoring/yace-config.yml:/tmp/config.yml:ro
    command:
      - '--config.file=/tmp/config.yml'

Перезапускаємо стек та перевіряємо метрики:

[root@ip-10-0-3-146 ~]# curl -s http://127.0.0.1:5000/metrics | grep aws_
# HELP aws_applicationelb_active_connection_count_sum Help is not implemented yet.
# TYPE aws_applicationelb_active_connection_count_sum gauge
aws_applicationelb_active_connection_count_sum{account_id="264***286",dimension_AvailabilityZone="",dimension_LoadBalancer="app/rtfm-alb/cd76dd0d557838f8",name="arn:aws:elasticloadbalancing:eu-west-1:264***286:loadbalancer/app/rtfm-alb/cd76dd0d557838f8",region="eu-west-1",tag_Name="rtfm-alb-main"} 336
...

Додаємо таргет до vmagent:

  - job_name: "yace_exporter"
    static_configs:
      - targets:
          - "10.100.0.20:5000"

Перевіряємо метрики в VictoriaMetrics:

Автозапуск експортерів із Docker Compose через systemd

Щоб весь стек піднімався автоматично після перезавантаження EC2 – додаємо systemd-сервіс.

Створюємо файл /etc/systemd/system/monitoring.service:

[Unit]
Description=Monitoring exporters stack
Requires=docker.service
After=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/monitoring
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=60

[Install]
WantedBy=multi-user.target

Додаємо в автостарт та запускаємо:

[root@ip-10-0-3-146 ~]# systemctl daemon-reload
[root@ip-10-0-3-146 ~]# systemctl enable --now monitoring

VictoriaLogs та логи з Fluent Bit

Тепер логи. Основні логи – це NGINX та PHP errors. Їх будемо відправляти до VictoriaLogs на FreeBSD хості через http output – див. документацію VictoriaLogs по Fluentbit Setup.

Real IP в NGINX

Трафік до EC2 іде через Cloudflare та ALB, тому якщо нічого не налаштовувати – в логах NGINX замість реального IP клієнта буде адреса ALB. Cloudflare передає реальний IP у заголовку CF-Connecting-IP, а для NGINX є модуль ngx_http_realip_module, якому можна вказати з якого заголовка брати IP клієнта.

Додаємо до nginx.conf (не конфіг віртуалхоста, а конфіг самого NGINX), в секцію http {}:

http {

    # trust ALB (all traffic comes from within VPC)
    set_real_ip_from 10.0.0.0/16;
    # get real client IP from Cloudflare header
    real_ip_header CF-Connecting-IP;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
...

Перезавантажуємо NGINX та перевіряємо, що в логах з’явились реальні IP:

[root@ip-10-0-3-146 ~]# tail /var/log/nginx/rtfm.co.ua-access.log
94.139.42.59 - - [16/Mar/2026:17:29:14 +0200] "GET /ru/2021/11/29/ HTTP/1.1" 200 109096 "-" "kagi-fetcher/1.0"
2a01:4f8:242:3ce9::2 - - [16/Mar/2026:17:29:14 +0200] "GET /api/v1/instance/peers HTTP/1.1" 200 1976 "-" "Go-http-client/2.0"

Logrotate – ротація логів

В Amazon Linux NGINX вже йде з дефолтним конфігом для logrotate в файлі /etc/logrotate.d/nginx:

/var/log/nginx/*.log {
    create 0640 nginx root
    daily
    rotate 10
    missingok
    notifempty
    compress
    delaycompress
    sharedscripts
    postrotate
        /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
    endscript
}

Дефолтний конфіг ротує всі файли *.log в /var/log/nginx/, але для логів RTFM та логів PHP можна написати свій конфіг з окремими налаштуваннями:

/var/log/nginx/rtfm.co.ua-*.log /var/log/php/rtfm.co.ua/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    sharedscripts
    postrotate
        nginx -s reopen
    endscript
}

Установка Fluent Bit

Fluent Bit буде читати логи NGINX та PHP і відправляти їх до VictoriaLogs на домашньому NAS.

Додаємо репозиторій – створюємо файл /etc/yum.repos.d/fluent-bit.repo:

[fluent-bit]
name=Fluent Bit
baseurl=https://packages.fluentbit.io/amazonlinux/2023/
gpgcheck=1
gpgkey=https://packages.fluentbit.io/fluentbit.key
enabled=1

Встановлюємо fluent-bit:

[root@ip-10-0-3-146 ~]# dnf install -y fluent-bit

Створюємо каталог для збереження позицій у файлах (щоб після перезапуску Fluent Bit не читав логи з початку):

[root@ip-10-0-3-146 ~]# mkdir -p /var/lib/fluent-bit

Конфігурація Fluent Bit – парсери для NGINX та PHP

Основний конфіг /etc/fluent-bit/fluent-bit.conf в мене виглядає так:

[SERVICE]
    Flush        5
    Daemon       Off
    Log_Level    info
    Parsers_File /etc/fluent-bit/parsers-custom.conf

[INPUT]
    Name        tail
    Path        /var/log/nginx/rtfm.co.ua-access.log
    Tag         nginx.access
    DB          /var/lib/fluent-bit/nginx-access.db
    Parser      nginx_access

[INPUT]
    Name        tail
    Path        /var/log/nginx/rtfm.co.ua-error.log
    Tag         nginx.error
    DB          /var/lib/fluent-bit/nginx-error.db

[INPUT]
    Name        tail
    Path        /var/log/php/rtfm.co.ua/rtfm.co.ua-error.log
    Tag         php.error
    DB          /var/lib/fluent-bit/php-error.db

[FILTER]
    Name    record_modifier
    Match   nginx.access
    Record  host aws-rtfm-main
    Record  job nginx
    Record  log_type access
    Record  site rtfm.co.ua

[FILTER]
    Name    record_modifier
    Match   nginx.error
    Record  host aws-rtfm-main
    Record  job nginx
    Record  log_type error
    Record  site rtfm.co.ua

[FILTER]
    Name    record_modifier
    Match   php.error
    Record  host aws-rtfm-main
    Record  job php-fpm
    Record  log_type error
    Record  site rtfm.co.ua

[FILTER]
    Name    lua
    Match   nginx.access
    script  /etc/fluent-bit/make_msg.lua
    call    make_msg

[Output]
    Name         http
    Match        *
    host         nas.setevoy
    port         9428
    uri          /insert/jsonline?_stream_fields=stream,job,host,log_type,site&_msg_field=log&_time_field=date
    format       json_lines
    json_date_format iso8601
    compress     gzip

Тут:

  • [SERVICE]: глобальні параметри Fleunt Bit
  • [INPUT]: читаємо три файли, кожному задаємо власний tag, аби далі мати окремі фільтри
  • [FILTER]: тут з record_modifier по тегу з [INPUT] фільтруємо який саме лог модифікувати і додаємо нові поля, які потім можна використовувати в VictoriaLogs та алертах; у Fluent Bit на FreeBSD, де є власний NGINX і FPM має такі самі налаштування, тільки, звісно, інші значення полів
  • останній [FILTER] викликає Lua-скрипт для створення поля logs, див. нижче

В дефолтному конфігу Fluent Bit не було парсера для nginx_access – тому створив власний і підключив в [SERVICE] через файл /etc/fluent-bit/parsers-custom.conf:

[PARSER]
    Name        nginx_access
    Format      regex
    Regex       ^(?<remote_addr>[^ ]*) - (?<remote_user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>[^ ]*) (?<path>[^ ]*) (?<protocol>[^ ]*)" (?<status>[^ ]*) (?<bytes>[^ ]*) "(?<referer>[^"]*)" "(?<agent>[^"]*)"
    Time_Key    time
    Time_Format %d/%b/%Y:%H:%M:%S %z

Але тут випилюється поле _msg, яке VictoriaLogs очікує і без якого не дуже зручно дивитись в VMUI.

Пробував зробити з record_modifier, але врешті-решт просто навайбокодив скрипт на Lua, який створює поле log, яке потім передається до VictoriaLogs в &_msg_field=log:

function make_msg(tag, timestamp, record)
    record["log"] = record["remote_addr"] .. ' "' .. record["method"] .. ' ' .. record["path"] .. '" ' .. record["status"] .. ' "' .. (record["agent"] or "-") .. '"'
    return 1, timestamp, record
end

Запускаємо Fluent Bit:

[root@ip-10-0-3-146 ~]# systemctl enable --now fluent-bit

Перевіряємо логи в VictoriaLogs:

vmalert та алерти з логів у VictoriaLogs

Один з плюсів VictoriaLogs – це можливість писати алерти безпосередньо з логів. Колись писав детальніше в VictoriaLogs: створення Recording Rules з VMAlert, і є в частині FreeBSD: Home NAS, part 14 – логи з VictoriaLogs і алерти з VMAlert.

Приклад того, що написав собі – спочатку задані recording rules з exclude домашніх/робочих IP та адреси самого EC2, потім самі алерти:

groups:

  - name: aws-rtfm-nginx-access-metrics
    type: vlogs
    interval: 1m

    rules:

      - record: aws:rtfm:nginx:requests_total:rate
        expr: |
          {job="nginx", log_type="access"}
          | not (remote_addr:~"108.***.***.54|178.***.***.184")
          | stats rate() as requests_rate

      - record: aws:rtfm:nginx:requests_by_status:count
        expr: |
          {job="nginx", log_type="access"}
          | not (remote_addr:~"108.***.***.54|178.***.***.184")
          | stats by (status) count() as requests_count

      - record: aws:rtfm:nginx:requests_by_status:rate
        expr: |
          {job="nginx", log_type="access"}
          | not (remote_addr:~"108.***.***.54|178.***.***.184")
          | stats by (status) rate() as requests_rate

  - name: aws-rtfm-nginx-access-alerts

    rules:

      - alert: "NGINX: Too Many 5xx"
        expr: aws:rtfm:nginx:requests_by_status:count{status=~"5.."} > 1
        for: 1m
        labels:
          severity: warning
        annotations:
          summary: Server-side errors on rtfm.co.ua, users may be affected
          description: |-
            Domain: rtfm.co.ua
            HTTP status: {{ $labels.status }}
            Count: {{ $value }} req/min
            Grafana: https://grafana.setevoy/d/MsjffzSZz/nginx-exporter

      - alert: "NGINX: High Request Rate"
        expr: aws:rtfm:nginx:requests_total:rate > 10
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: Unusual traffic spike on rtfm.co.ua
          description: |-
            Domain: rtfm.co.ua
            Rate: {{ $value }} req/sec
            Grafana: https://grafana.setevoy/d/MsjffzSZz/nginx-exporter

  - name: aws-rtfm-php-error-metrics
    type: vlogs
    interval: 1m

    rules:

      - record: aws:rtfm:php:errors_total:count
        expr: |
          {job="php-fpm", log_type="error"}
          | stats count() as errors_count

  - name: aws-rtfm-php-error-alerts

    rules:

      - alert: "PHP-FPM: Too Many Errors"
        expr: aws:rtfm:php:errors_total:count > 5
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: Application errors on rtfm.co.ua
          description: |-
            Domain: rtfm.co.ua
            Count: {{ $value }} errors/min
            Grafana: https://grafana.setevoy/d/MsjffzSZz/nginx-exporter

  - name: aws-rtfm-php-fpm-alerts

    rules:

      - alert: "PHP-FPM: Slow Requests Detected"
        expr: increase(phpfpm_slow_requests{pool="rtfm.co.ua"}[5m]) > 0
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: PHP-FPM slow requests on rtfm.co.ua
          description: |-
            PHP-FPM slow requests detected during last {{ $for }}
            Domain: rtfm.co.ua
            Slow requests (last 5m): {{ $value }}
            Grafana: https://grafana.setevoy/d/MsjffzSZz/nginx-exporter

      - alert: "PHP-FPM: Pool Usage High"
        expr: phpfpm_active_processes{pool="rtfm.co.ua"} / phpfpm_total_processes{pool="rtfm.co.ua"} * 100 > 80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: FPM Pool usage high on rtfm.co.ua
          description: |-
            FPM Pool usage over 95% during last {{ $for }}
            Domain: rtfm.co.ua
            Pool used: {{ printf "%.2f" $value }}%
            Grafana: https://grafana.setevoy/d/MsjffzSZz/nginx-exporter

Рестаримо vmalert, перевіряємо в UI:

Alertmanager та алерти в Telegram і ntfy.sh

Про те, як створити Telegram-бота і налаштувати групу для алертів писав в пості EcoFlow: моніторинг з Prometheus та Grafana, тому тут опишу тільки конфіг Alertmanager – на FreeBSD це файл /usr/local/etc/alertmanager/alertmanager.yml.

В мене три роути і три ресівери – critical алерти дублюються через ntfy.sh, алерти по самій FreeBSD та NGINX/PHP йдуть в Telegram, плюс окремий Telegram канал для алертів EcoFlow:

global:
  resolve_timeout: 5m

route:
  receiver: "ntfy"
  group_by: ["alertname, status"]
  group_wait: 10s
  group_interval: 5m
  repeat_interval: 4h

  routes:

    - matchers:
        - severity="critical"
      receiver: "ntfy"
      continue: true

    - matchers: 
        - job="ecoflow_exporter"
      receiver: "telegram_ecoflow"

    - matchers: 
        - alertname =~ ".*"
      receiver: "telegram_system"

receivers:

  - name: "ntfy"
    webhook_configs:
      - url: "https://ntfy.sh/setevoy-nas-alertmanager-alerts"
        http_config:
          authorization:
            type: Bearer
            credentials: "***"
        send_resolved: true

  - name: telegram_system
    telegram_configs:
    - bot_token: "***"
      chat_id: -100***962
      api_url: https://api.telegram.org
      parse_mode: HTML
      message: |
        {{ if eq .Status "firing" }}🔥{{ else }}✅{{ end }} <b>{{ .CommonLabels.alertname }}</b>
        {{ range .Alerts }}
        <b>Status:</b> {{ .Status | toUpper }}
        {{ if .Labels.severity }}<b>Severity:</b> {{ .Labels.severity }}{{ end }}
        {{ if .Annotations.summary }}<b>Summary:</b> {{ .Annotations.summary }}{{ end }}
        {{ if .Annotations.description }}<b>Description:</b> {{ .Annotations.description }}{{ end }}
        {{ end }}

  - name: telegram_ecoflow
    telegram_configs:
    - bot_token: "***"
      chat_id: -100***981
      api_url: https://api.telegram.org
      parse_mode: HTML
      message: |
        {{ if eq .Status "firing" }}🔥{{ else }}✅{{ end }} <b>{{ .CommonLabels.alertname }}</b>
        {{ range .Alerts }}
        <b>Status:</b> {{ .Status | toUpper }}
        {{ if .Labels.severity }}<b>Severity:</b> {{ .Labels.severity }}{{ end }}
        {{ if .Annotations.summary }}<b>Summary:</b> {{ .Annotations.summary }}{{ end }}
        {{ if .Annotations.description }}<b>Description:</b> {{ .Annotations.description }}{{ end }}
        {{ end }}

І тепер маємо алерти в Telegram:

Grafana dashboard

Вже не буду описувати весь процес створення, пізніше викладу дашборду десь в GitHub, але в мене виглядає так:

І додатково є “small version” для відображення на 14-дюймовому екрані ноутбука:

Власне, на цьому і все.

Вийшло класно, корисно, вже відловив кілька проблем і перебанив пачку ботів 🙂

Loading