TL;DR
Хотите отслеживать трафик на статическом блоге без Matomo и Google Analytics?
Рассказываю, как с помощью OpenTelemetry, Prometheus, Grafana и небольшого самописного скрипта на JS (Otela) можно элегантно и эффективно следить за посещаемостью — при этом полностью сохранив контроль над своими данными.
Проблема
Проблема с Matomo в том, что он определяется как трекер большинством блокировщиков рекламы — независимо от того, используете ли вы его для сбора данных об аудитории или для полноценного трекинга. А на техническом блоге почти все посетители используют блокировщики рекламы, из-за чего статистика получается искажённой. Кроме того, как мне кажется, Matomo работает не слишком быстро, а данные обновляются с задержкой — иногда приходится ждать до следующего дня.
Я уже успел поработать со стеком Grafana + OpenTelemetry в проекте Baywatch, и мне захотелось применить эту же связку для замены Matomo. К тому же у меня уже был настроен сервер со всеми необходимыми компонентами.
Другая проблема – если сайт статический, размещенный на Github Pages или других “Pages”, у вас нет доступа к логам веб-сервера, поэтому для их сбора необходимо использовать JS-клиент.
Otela — JS-клиент
Первый шаг – это JavaScript-клиент, который будет «стучаться» в Open Telemetry collector с информацией о посещённой страницей. У OpenTelemetry есть SDK, с помощью которого можно передавать метрики и трассировки.
Передавать сигнал «ping» в виде метрики невозможно, т.к. это не сочетается с принципами работы OpenTelemetry и Prometheus. Дело в том, что передается только значение 1, и счетчик сбрасывается на каждом визите.
Зато хорошо работает отправка интервалов (spans). При каждом посещении Otela отправляет интервал с атрибутами посещенных страниц (заголовок, ссылка и т.д.) в OpenTelemetry collector.
Проект Otela доступен github: github.com/Marthym/otela
Проект Otela вырос из одного из примеров в SDK OpenTelemetry и распространяется с открытым исходным кодом. Файл otela.js собирается с помощью WebPack, что позволяет максимально сократить его размер — его легко подключить к любой странице.
Что такое интервал?
В контексте OpenTelemetry интервал представляет собой отдельную операцию или событие при трассировке. Это основная единица наблюдения. Каждый раз, когда пользователь посещает страницу на вашем сайте с Otela, создаётся интервал, который фиксирует это «действие»: посещённый URL, заголовок страницы, использованный браузер, источник (реферер) и другие данные. Этот интервал затем отправляется в OpenTelemetry Collector. Он содержит настраиваемые атрибуты, которые можно извлекать и трансформировать для генерации метрик, используемых в Prometheus. Короче говоря, интервал — это информационно обогащённый и структурированный лог, идеально подходящий для отслеживания взаимодействий пользователей на статическом сайте.
Установка
Чтобы установить Otela, достаточно добавить следующий JavaScript-код в конец HTML-страницы:
<!-- Otela --> <script> var _ota=window._ota=window._ota||{};_ota.t="your.opentelemetry.server"; (function(){ var t=document,e=t.createElement("script"),a=t.getElementsByTagName("script")[0]; e.async=!0;e.src="https://github.com/Marthym/otela/releases/download/1.0.0/otela.js";a.parentNode.insertBefore(e,a) })(); </script> <!-- Конец кода Otela -->
Также нужно настроить OpenTelemetry Collector для приёма интервалов, а также указать источник JS-скрипта. В примерах используется артефакт с GitHub, но на практике лучше встраивать otela.js прямо в исходный код сайта — это снижает риск блокировки скрипта по URL и улучшает загрузку.
Конфигурация OpenTelemetry
Для сбора интервалов, отправленных Otela, необходимо настроить OpenTelemetry Collector таким образом, чтобы он принимал входящие интервалы, преобразовывал их в метрики и записывал в Prometheus.
Receiver (приёмник)
receivers: otlp/otela: protocols: http: endpoint: ':4318' cors: allowed_origins: - http://* - https://* max_age: 7200
На стороне приёмника важно правильно настроить CORS. В зависимости от ваших задач, постарайтесь максимально ограничить доступ. В приведённом примере CORS настроены довольно свободно, но если вы отслеживаете только один сайт — лучше ужесточить конфигурацию.
Processors (процессоры)
processors: batch: filter/otela: spans: include: match_type: strict services: - "blog.ght1pc9kc.fr" - "swr.ght1pc9kc.fr" attributes/otela: actions: - key: referrer pattern: ^https?:\/\/(?P<dummy>[^@\n]+@)?(?P<referrer>[^:\/\n?]+) action: extract - key: dummy action: delete - key: referrer value: direct action: insert
Сначала фильтруются входящие данные — остаются только интервалы, поступающие с отслеживаемых сайтов. Затем обрабатываются атрибуты интервалов в зависимости от того, что именно нужно сохранить. В нашем случае мы не сохраняем полный referrer, а оставляем только домен.
Connector (коннектор)
Остаётся только преобразовать интервалы в метрики — то есть просто посчитать интервалы, чтобы получить количество посещений. В OpenTelemetry для этого есть специальные коннекторы. Они позволяют связать конвейер интервалов напрямую с конвейером метрик.
connectors: spanmetrics/otela: namespace: otela histogram: explicit: buckets: [6ms, 10ms, 100ms, 250ms, 500ms] dimensions: - name: navigator - name: os - name: platform - name: referrer - name: title service: pipelines: traces: receivers: [otlp/otela] processors: [filter/otela, attributes/otela] exporters: [spanmetrics/otela] metrics: receivers: [spanmetrics/otela] processors: [batch] exporters: [prometheus]
Таким образом, мы подключаем коннектор одновременно к выходу трассировок и ко входу метрик. Помимо подсчёта интервалов, коннектор «span-to-metrics» позволяет рассчитывать процентильные значения.
Получение IP-адресов
Чтобы отличать пользователей друг от друга, нужно получать их IP-адреса. Однако сделать это средствами JavaScript невозможно — для этого потребуются дополнительные запросы и, скорее всего, придётся передавать данные стороннему сервису.
Здесь на помощь приходит фронтенд на базе NginX, который должен стоять перед OpenTelemetry Collector. Он знает IP-адрес клиента, отправляющего интервал, и может передать эту информацию в OpenTelemetry через заголовки запроса.
location / { try_files $uri $uri/ @proxy; } location @proxy { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port 443; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-GeoIP-Latitude $geoip_latitude; proxy_set_header X-GeoIP-Longitude $geoip_longitude; proxy_pass $upstream; }
А с помощью модуля GeoIP для NginX можно дополнительно определить географические координаты посетителей и отображать их на карте в Grafana.
Со стороны OpenTelemetry конфигурация выглядит следующим образом:
receivers: otlp/otela: protocols: http: endpoint: ':4318' include_metadata: true cors: allowed_origins: - http://* - https://* max_age: 7200 connectors: spanmetrics/otela: namespace: otela histogram: explicit: buckets: [6ms, 10ms, 100ms, 250ms, 500ms] dimensions: - name: ip - name: navigator - name: os - name: platform - name: referrer - name: title - name: latitude - name: longitude attributes/otela: actions: - key: referrer pattern: ^https?:\/\/(?P<dummy>[^@\n]+@)?(?P<referrer>[^:\/\n?]+) action: extract - key: dummy action: delete - key: referrer value: direct action: insert - key: ip from_context: X-Real-IP action: upsert - key: latitude from_context: X-GeoIP-Latitude action: upsert - key: longitude from_context: X-GeoIP-Longitude action: upsert
Важно указать параметр include_metadata: true, который позволяет извлекать заголовки NginX в процессорах OpenTelemetry. Затем достаточно извлечь эти значения и добавить их в атрибуты интервалов перед отправкой в Prometheus.
🔥Важно учесть🔥: сбор IP-адресов и геоданных значительно увеличивает кардинальность атрибутов метрик, что влияет на требования к объёму хранимых данных в Prometheus. Поэтому следует осторожно использовать эту конфигурацию и установить ограничения на объём хранения.
--storage.tsdb.retention.time=90d --storage.tsdb.retention.size=4GBВ этом примере срок хранения данных ограничен 90 днями, а объём хранилища — 4 ГБ.
Помещаем результат Grafana
Наконец после небольшой настройки в Grafana мы получаем дашборд, который выглядит следующим образом:

Да, видно, что этот блог посещают лишь несколько любопытных технарей 😄 — хотя, справедливости ради, это был снимок трафика за выходные. Обычно трафик увеличивается в будние дни.
Вы можете найти JSON дашборда в этом gist.
С помощью счётчика интервалов и настроенных атрибутов дашборд показывает:
- Общее количество посещений
- Количество уникальных посетителей
- Источники трафика
- Какие страницы были посещены и как часто
- А ещё — благодаря геолокации, точки на карте 🗺️
Заключение
Все конфигурационные файлы доступны в этом gist.
В итоге, эта конфигурация идеально подходит под мои нужды и является отличной заменой Matomo. К тому же, благодаря Otela, все данные хранятся и контролируются в Франции. Ничто не покидает мои серверы.
Однако решение не без недостатков. Prometheus не предназначен для хранения такого типа данных. Для логов лучше подойдёт что-то вроде Loki. На данный момент Otela — это всего лишь концепт, и я ещё не нашёл наилучший способ перенаправить данные в Loki.
В продолжение темы рекомендуем вам ознакомиться с книгой «Изучаем OpenTelemetry: современный мониторинг систем».
ссылка на оригинал статьи https://habr.com/ru/articles/902732/
Добавить комментарий