Способ GitOps для управления источниками данных Grafana в масштабах страны


Проблема

Я работаю в корпоративной организации, и передо мной поставили задачу улучшить систему мониторинга. Поскольку система мониторинга является централизованной системой, используемой для всей организации, мы должны сделать ее простой в использовании для различных команд в организации. В системе используется Grafana для части визуализации. Я не буду упоминать бэкенд Grafana в этом посте. Если вам интересно, вы можете обратиться к моей статье Ультрамониторинг с Victoria Metrics.

В прошлом источники данных Grafana добавлялись вручную через WebUI. Мы хотим избежать подобных операций. Вместо этого их следует автоматизировать настолько, насколько это возможно. Кроме того, нам необходимо следовать практике GitOps для управления и отслеживания/аудита изменений.

Решение

Благодаря функции Grafana Provisioning. В Grafana можно управлять источниками данных, добавив один или несколько YAML-конфигурационных файлов в каталог provisioning/datasources. Каждый конфигурационный файл может содержать список источников данных, которые будут добавляться или обновляться во время запуска. Если источник данных уже существует, то Grafana обновляет его в соответствии с файлом конфигурации.

В сочетании с API reload provisioning configurations мы можем достичь цели без необходимости перезапускать Grafana при каждом изменении источников данных.

Идея заключается в том, что файлы конфигурации источников данных Grafana будут храниться в репозитории Git. Затем с помощью AWS Automation синхронизируем конфигурации с серверами Grafana. Структура Git-репозитория выглядит следующим образом:

.
├── team-1
│   ├── clickhouse-2.yaml
│   └── cloudwatch-1.yaml
├── team-2
│   ├── clickhouse-1.yaml
│   └── influxdb-1.yaml
├── team-3
│   ├── elasticsearch-1.yaml
│   └── victoria-metrics-1.yaml
└── team-4
    ├── mysql-1.yaml
    └── prometheus-1.yml
Вход в полноэкранный режим Выход из полноэкранного режима

Решение представляет собой комбинацию AWS Automation Runbook и Secret Manager, поэтому это защищенное, полностью управляемое AWS бессерверное решение.

На следующей диаграмме представлена высокоуровневая архитектура решения:

Но подождите!!! Почему Secret Manager находится на диаграмме архитектуры?
Чтобы ответить на этот вопрос, давайте посмотрим, что источник данных хранится в репозитории:

name: Prometheus Example 1
type: prometheus
access: proxy
url: http://123.123.1.1:9090
user: "username"
password: "password"
basicAuth: "false"
jsonData:
  httpMethod: POST
Вход в полноэкранный режим Выход из полноэкранного режима

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

Вернемся к архитектурной диаграмме. Вот как происходит процесс:

  1. Администраторы создают секрет для хранения учетных данных источника данных (может быть автоматизированный портал и/или чатбот).
  2. Администраторы рассматривают и объединяют PR
  3. Когда PR объединен, конвейер GitHub/Gitlab запускает предопределенную рунную книгу автоматизации.
  4. Runbook выполняет шаги из документов SSM и получает секреты из Secret Manager
  5. Runbook выполняет определенные шаги для создания файла инициализации источников данных и вызывает Grafana API для перезагрузки источников данных.

Runbook состоит из трех основных шагов:

  • Извлечь репозиторий из GitHub/Gitlab на сервер Grafana
  • Получение учетных данных источника данных из Secret Manager
  • Сгенерировать файлы инициализации источников данных с учетными данными.

Секреты, хранящиеся в Secret Manager, будут иметь имя в следующем формате:

Например, prod/grafana/datasource/team-3/elasticsearch-1.

Секретное значение хранится в формате JSON. Например:

{
  "username": "elasticUser",
  "password": "elasticP@ssw0rD"
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Каждый секрет будет иметь два обязательных тега. Это:

  • env: prod/qa/dev
  • secret-type: grafana-datasource.

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

name: Elasticsearch Example 1
type: elasticsearch
access: proxy
url: http://elasticsearc.example.com:9200
user: "@team-3/elasticsearch-1:username"
password: "@team-3/elasticsearch-1:password"
database: logs-index
basicAuth: true
jsonData:
  esVersion: 7.7.0
  includeFrozen: false
  logLevelField: ""
  logMessageField: ""
  maxConcurrentShardRequests: 5
  timeField: "@timestamp"
Вход в полноэкранный режим Выйти из полноэкранного режима

На шаге #2 в runbook я пишу Python-скрипт для получения секретных значений из Secret Manager и передачи их на шаг #3. Сценарий Python возвращает секреты в формате JSON в следующей структуре:

{
  "team-1": {
    "clickhouse-2": {
      "username": "team-1-clickhouse-2-username",
      "password": "team-1-clickhouse-2-password"
    }
  },
  "team-2": {
    "mysql-1": {
      "username": "mysql-1-username",
      "password": "mysql1P@ssword"
    }
  },
  "team-3": {
    "victoria-metrics-1": {
      "authorizationToken": "vict0ri@Metric$Tok3n"
    },
    "elasticsearch-1": {
      "username": "elasticUser",
      "password": "elasticP@ssw0rD"
    }
  }
}

Вход в полноэкранный режим Выйти из полноэкранного режима

На шаге #3 в runbook я также пишу небольшой сценарий Python для объединения файлов источников данных в репозитории в файл Grafana data source provisioning file, а также заменяю держателей секретов на секретные значения из Secret Manager.
Конфигурация Grafana data source provisioning выглядит следующим образом:

[root@grafana datasources]# pwd
/var/lib/grafana/provisioning/datasources

[root@grafana datasources]# ll
total 16
-rw-r--r-- 1 root root 362 May 22 11:00 team-1.yaml
-rw-r--r-- 1 root root 628 May 22 11:00 team-2.yaml
-rw-r--r-- 1 root root 669 May 22 11:00 team-3.yaml
-rw-r--r-- 1 root root 515 May 22 11:00 team-4.yaml
Вход в полноэкранный режим Выход из полноэкранного режима

/var/lib/grafana/provisioning/datasources/team-3.yaml

apiVersion: 1
datasources:
- access: proxy
  basicAuth: true
  database: logs-index
  jsonData:
    esVersion: 7.7.0
    includeFrozen: false
    logLevelField: ''
    logMessageField: ''
    maxConcurrentShardRequests: 5
    timeField: '@timestamp'
  name: Elasticsearch Example 1
  password: elasticP@ssw0rD
  type: elasticsearch
  url: http://elasticsearc.example.com:9200
  user: elasticUser
- access: proxy
  isDefault: true
  jsonData:
    httpHeaderName1: Authorization
  name: Victoria Metrics Example 1
  secureJsonData:
    httpHeaderValue1: Bearer vict0ri@Metric$Tok3n
  type: prometheus
  url: http://ultra-metrics.com
Войти в полноэкранный режим Выйти из полноэкранного режима

Оцените статью
Procodings.ru
Добавить комментарий