대규모 Grafana 데이터 소스를 관리하는 GitOps 방식

문제



나는 기업 조직에서 일하고 있으며 모니터링 시스템을 개선하는 작업을 할당 받았습니다. 모니터링 시스템은 조직 전체에 사용되는 중앙 집중식 시스템이므로 조직 내 여러 팀에서 쉽게 사용할 수 있도록 해야 합니다. 시스템은 시각화 부분에 Grafana를 사용합니다. 이 게시물에서는 Grafana의 백엔드에 대해 언급하지 않겠습니다. 궁금하신 분들은 제 포스팅을 참고해주세요

과거에는 Grafana 데이터 소스가 WebUI를 통해 수동으로 추가되었습니다. 우리는 그러한 종류의 작업을 피하고 싶습니다. 대신 최대한 자동화해야 합니다. 또한 변경 사항을 관리하고 추적/감사하려면 GitOps 방식을 따라야 합니다.

해결책



Grafana Provisioning 기능 덕분입니다. provisioning/datasources 디렉터리에 하나 이상의 YAML 구성 파일을 추가하여 Grafana에서 데이터 소스를 관리할 수 있습니다. 각 구성 파일에는 시작 중에 추가되거나 업데이트되는 데이터 소스 목록이 포함될 수 있습니다. 데이터 소스가 이미 있는 경우 Grafana는 구성 파일과 일치하도록 이를 업데이트합니다.

reload provisioning configurations API과 결합하면 데이터 소스가 변경될 때마다 Grafana를 다시 시작하지 않고도 목표를 달성할 수 있습니다.

아이디어는 Grafana 데이터 소스 구성 파일이 Git 리포지토리에 보관된다는 것입니다. 그런 다음 AWS 자동화를 사용하여 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 RunbookSecret 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


데이터 소스에는 자격 증명 정보가 필요할 수 있으며 보안 문제로 이어지는 리포지토리에 일반 텍스트로 남겨 둘 수 없습니다.

아키텍처 다이어그램으로 돌아가 보겠습니다. 프로세스 작동 방식은 다음과 같습니다.
  • 관리자가 데이터 소스의 자격 증명을 저장하기 위한 비밀을 생성합니다(포털 및/또는 챗봇 자동화 가능)
  • 관리자가 PR을 검토하고 병합합니다
  • .
  • PR이 병합되면 GitHub/Gitlab 파이프라인이 사전 정의된 자동화 런북을 트리거함
  • Runbook이 SSM 문서에서 단계를 실행하고 Secret Manager에서 암호를 가져옵니다
  • .
  • Runbook은 정의된 단계를 실행하여 데이터 소스 프로비저닝 파일을 생성하고 Grafana API를 호출하여 데이터 소스를 다시 로드합니다.

  • Runbook에는 세 가지 주요 단계가 있습니다.
  • GitHub/Gitlab에서 Grafana 서버로 리포지토리를 가져옵니다
  • .
  • Secret Manager에서 데이터 소스 자격 증명 가져오기
  • 자격 증명을 사용하여 데이터 소스 프로비저닝 파일 생성



  • Secret Manager에 저장된 비밀의 이름은 다음 형식으로 지정됩니다.{env}/grafana/datasource/{team}/{datasource-name}예. 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"
    


    Runbook의 2단계에서는 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단계에서는 리포지토리의 데이터 소스 파일을 Grafana 데이터 소스 프로비저닝 파일에 결합하고 비밀 보유자를 Secret Manager의 비밀 값으로 대체하는 작은 Python 스크립트도 작성합니다.
    Grafana 데이터 소스 프로비저닝 구성은 다음과 같습니다.

    [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
    

    좋은 웹페이지 즐겨찾기