Введение

Здравствуйте. В данной статье будет выполнено рассмотрение и установка инструмента Grafana Faro для осуществления сбора данных мониторинга из Frontend приложений написанных на JavaScript. Также, перед непосредственным использованием Grafana Faro, выполним установку в кластере Kubernetes всех остальных необходимых компонентов Grafana стека.

Инструмент Grafana Faro представляет собой набор пакетов, подключение которых осуществляется в Frontend JavaScript приложениях. Подключаемые пакеты позволяют выполнить сбор различных данных мониторинга, существуют следующие пакеты:

  • grafana/faro-web-sdk или grafana/faro-react (при использовании React) — данные пакеты осуществляют сбор таких данных, как:

    • Метрики Core Web Vitals — скорость загрузки основного контента (LCP), время ожидания до первого взаимодействия с контентом (FID), совокупное смещение макета (CLS), время до первого байта (TTFB), первая отрисовка содержимого (FCP);

    • Необработанные исключения;

    • Сообщения выведенные в консоль;

    • События изменения view;

    • События начала и продолжения сессий;

  • grafana/faro-web-tracing — пакет осуществляет интеграцию в приложение сбора трейсов на основе OpenTelemetry.

Далее данные мониторинга полученные с помощью пакетов Grafana Faro отправляются в Grafana Alloy, который выполняет обработку полученных метрик и после этого метрики полученные из пакетов:

  • grafana/faro-web-sdk или grafana/faro-react (при использовании React) - отправляются в систему логирования Grafana Loki;

  • grafana/faro-web-tracing - отправляются в систему трассировки Grafana Tempo. 

Отображение и визуализация полученных метрик осуществляется с использованием Grafana. 

Общую структурную схему работы рассматриваемого решения для сбора данных мониторинга из Frontend JavaScript приложений можно увидеть на рисунке ниже:

Установка компонентов Grafana стека в Kubernetes

Для осуществления сбора данных мониторинга выполним создание тестовый среды с установкой инфраструктурных компонентов Grafana стека в кластера Kubernetes используя Helm. 

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

$ kubectl create namespace monitoring

Далее выполним подключение Helm репозитория Grafana с помощью команды:

$ helm repo add grafana https://grafana.github.io/helm-charts

Установка Grafana Loki

Выполним создание файла values-loki.yaml содержащий значения Helm для выполнения установка Grafana Loki, в данном примере значения подготовлены для установки в монолитном режиме с отключенной авторизацией и использованием S3 хранилища MinIO:

loki:
  auth_enabled: false
  commonConfig:
    replication_factor: 1
  schemaConfig:
    configs:
      - from: 2024-04-01
        store: tsdb
        object_store: s3
        schema: v13
        index:
          prefix: loki_index_
          period: 24h
  ingester:
    chunk_encoding: snappy
  tracing:
    enabled: true
  querier:
    max_concurrent: 2
gateway:
  enabled: false
deploymentMode: SingleBinary
singleBinary:
  replicas: 1
chunksCache:
  writebackSizeLimit: 10MB
minio:
  enabled: true
backend:
  replicas: 0
read:
  replicas: 0
write:
  replicas: 0
ingester:
  replicas: 0
querier:
  replicas: 0
queryFrontend:
  replicas: 0
queryScheduler:
  replicas: 0
distributor:
  replicas: 0
compactor:
  replicas: 0
indexGateway:
  replicas: 0
bloomCompactor:
  replicas: 0
bloomGateway:
  replicas: 0

Далее выполним установку Grafana Loki используя команду:

$ helm install loki grafana/loki --namespace=monitoring --values values-loki.yaml

Установка Grafana Tempo

Подготовим файл values-tempo.yaml c добавлением значений Helm для установки Grafana Tempo, в данном примере будут использоваться все стандартные значения с единственным изменением — включением постоянного хранилища:

persistence: 
  enabled: true

Выполним установку Grafana Tempo:

$ helm install tempo grafana/tempo --namespace=monitoring --values values-tempo.yaml

Установка Grafana Alloy 

Далее выполним создание Helm Values файла values-alloy.yaml для установки Grafana Alloy, в данном примере выполнена настройка конфигурационного файла Alloy в котором подключен приёмник Faro с отправкой в Loki и отправку через OpenTelemetry Collector в Tempo. Дополнительно в настройках приёмника Faro выполняется конфигурирование:

  • используемого порта;

  • настройки CORS;

  • ключа API через который будет выполнять отправку метрик Faro;

  • максимальный разрешённый размер полезной нагрузки;

  • указание rate лимита.

Также в values файле выполнено добавление extraPort для Faro, так-как по умолчанию в helm значениях создаётся только порт для web-интерфейса Grafana Alloy. Дополнительно выполнено включение ingress c указанием порта Faro, в дальнейшем указанный адрес в ingress будет использоваться при подключении Grafana Faro в Frontend JS приложениях, важно чтобы указанный адрес был доступ для подключения со стороны клиентов использующих приложение, иначе отправка метрик будет невозможна:

alloy:
  configMap:
    create: true
    content: |-
        faro.receiver "frontend_applications" {
            server {
                listen_address           = "0.0.0.0"
                listen_port              = 8030
                cors_allowed_origins     = ["http://example-frontend-app.local.ru"]
                api_key                  = "api-key-app"
                max_allowed_payload_size = "5MiB"

                rate_limiting {
                    rate = 100
                }
            }

            sourcemaps { }

            output {
                logs   = [loki.process.logs_process.receiver]
                traces = [otelcol.exporter.otlp.trace_write.input]
            }
        }

        loki.process "logs_process" {
            forward_to = [loki.write.logs_write.receiver]

            stage.logfmt {
                mapping = { "kind" = "", "service_name" = "", "app_name" = ""}
            }

            stage.labels {
                values = { "kind" = "kind", "service_name" = "service_name", "app" = "app_name" }
            }
        }

        loki.write "logs_write" {
            endpoint {
                url = "http://loki.monitoring.svc.cluster.local:3100/loki/api/v1/push"
            }
        }

        logging {
            level = "info"
        }

        otelcol.receiver.otlp "default" {
            grpc {
                include_metadata = true
            }

            output {
                metrics = []
                logs    = []
                traces  = [otelcol.exporter.otlp.trace_write.input]
            }
        }

        otelcol.exporter.otlp "trace_write" {
            retry_on_failure {
                max_elapsed_time = "1m0s"
            }

            client {
                endpoint = "tempo.monitoring.svc.cluster.local:4317"
                tls {
                  insecure = true
                }                
            }
        }
  extraPorts:
  - name: "faro"
    port: 8030
    targetPort: 8030
    protocol: "TCP"
ingress:
  enabled: true
  path: /
  faroPort: 8030
  pathType: Prefix
  hosts:
    - alloy.local

Выполним установку Grafana Alloy с помощью команды:

$ helm install alloy grafana/alloy --namespace=monitoring --values values-alloy.yaml

Проверим корректность установки Grafana Alloy с помощью web-интерфейса, для этого выполним перенаправление порта с помощью команды:

$ kubectl port-forward svc/alloy 8080:12345 -n monitoring

Далее в браузере перейдём по адресу http://localhost:8080/ и увидим успешное подключение всех компонентов:

Установка Grafana

Выполним подготовку значений Helm в файле values-grafana.yaml для установки Grafana, в созданном values файле добавлены значения источников данных Loki и Tempo, а также выполнено создание постоянного хранилища и ingress:

persistence:
  enabled: true
ingress:
  enabled: true
  hosts:
  - grafana.local
datasources:
 datasources.yaml:
   apiVersion: 1
   datasources:
   - name: loki
     type: loki
     url: http://loki.monitoring.svc.cluster.local:3100
     access: proxy
     isDefault: true
   - name: tempo
     type: tempo
     url: http://tempo.monitoring.svc.cluster.local:3100
     access: proxy

Выполним установку Grafana:

$ helm install grafana grafana/grafana --namespace=monitoring --values values-grafana.yaml

После завершения установки Grafana выполним получения пароля для пользователя admin с помощью команды:

$ kubectl get secret --namespace monitoring grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo

Перейдём в web-интерфейс Grafana по адресу добавленному в ingress, далее в разделе Dashborad, выберем New — Import и выполним импорт дашборда для отображения будущих собираемых метрик. JSON дашборда для Grafana Faro доступен в официальном GitHub репозитории.

Подключение Grafana Faro в приложении

Выполним установку пакетов Grafana Faro используя команду:

$ npm install --save @grafana/faro-web-sdk @grafana/faro-web-tracing

Далее в Frontend JavaScript приложении выполним добавление кода осуществляющий инициализацию Grafana Faro для сбора метрик и трейсов. В коде необходимо указание: названия приложения, его версии, а также API ключа и адреса ингресса Grafana Alloy созданного нами ранее при его установке:

import { getWebInstrumentations, initializeFaro } from '@grafana/faro-web-sdk';
import { TracingInstrumentation } from '@grafana/faro-web-tracing';

const faro = initializeFaro({
  url: 'http://alloy.local/collect',
  apiKey: 'api-key-app',
  instrumentations: [...getWebInstrumentations(), new TracingInstrumentation()],
  app: {
    name: 'example-frontend-app',
    version: '1.0.0',
  },
});

Просмотр данных мониторинга

Выполним запуск Frontend JavaScript приложения и перейдём в импортируемый нами ранее дашборд Grafana в котором мы увидим поступающие данные мониторинга:

Полные логи поступающие из Frontend приложения можно увидеть в Grafana перейдя в раздел Explore, далее выбрав источник Loki и выполнив запрос LogQL-{app="название_frontend_приложения"}

Поступающие трейсы в Tempo можно увидеть выбрав источник Tempo, далее в Query type необходимо выбрать TraceQL и ввести запрос- {rootServiceName="название_frontend_приложения"}

Дополнительная документация по использованию Grafana Faro:

Комментарии (0)