cstanislawski / grafana-db-exporter

Export your ClickOps'ed Grafana dashboards into the repository
Apache License 2.0
1 stars 0 forks source link
automation clickops exporter gitops grafana grafana-dashboard iac infrastructure-as-code monitoring

grafana-db-exporter

Automatically export Grafana dashboards to Git for version control and Infrastructure as Code workflows.

Why?

Usage

grafana-db-exporter is a utility tool meant to be utilized either as a periodic job on a CI/CD pipeline, or as a Kubernetes CronJob.

Configuration

All configuration is done via environment variables. Here's a complete reference grouped by functionality:

Git Configuration

Variable Required Default Description
SSH_URL "" Git repository SSH URL (e.g. git@github.com:org/repo.git)
SSH_KEY "" Path to SSH private key (supports rsa, ecdsa, ed25519)
SSH_USER "" Git commit author username
SSH_EMAIL "" Git commit author email
BASE_BRANCH main Branch to create new branches from
BRANCH_PREFIX grafana-db-exporter- Prefix for new branch names
SSH_KEY_PASSWORD "" SSH key passphrase if encrypted
SSH_KNOWN_HOSTS_PATH ✓* "" Path to known_hosts file (*required if SSH_ACCEPT_UNKNOWN_HOSTS=false)
SSH_ACCEPT_UNKNOWN_HOSTS false Skip host key verification

Grafana Configuration

Variable Required Default Description
GRAFANA_URL "" Grafana instance URL
GRAFANA_SA_TOKEN "" Service Account token (Viewer role is sufficient)

Export Configuration

Variable Required Default Description
REPO_SAVE_PATH "" Directory path in repository to save dashboards
IGNORE_FOLDER_STRUCTURE false Flatten Grafana folder hierarchy in export
DELETE_MISSING true Remove dashboards that no longer exist in Grafana
ADD_MISSING_NEWLINES true Ensure JSON files end with newline
DRY_RUN false Commit changes but don't push

Runtime Configuration

Variable Required Default Description
LOG_LEVEL info Log level (debug, info, warn, error)
ENABLE_RETRIES true Retry failed operations
NUM_OF_RETRIES 3 Maximum retry attempts
RETRY_INTERVAL 5 Seconds between retries

Example .env file:

# Git Configuration
SSH_URL=git@github.com:org/repo.git
SSH_KEY=/app/.ssh/id_rsa
SSH_USER=ci-bot
SSH_EMAIL=ci-bot@company.com
SSH_KNOWN_HOSTS_PATH=/app/.ssh/known_hosts

# Grafana Configuration
GRAFANA_URL=https://grafana.company.com
GRAFANA_SA_TOKEN=glsa_1234567890

# Export Configuration
REPO_SAVE_PATH=dashboards
DELETE_MISSING=true

# Runtime Configuration
LOG_LEVEL=info
ENABLE_RETRIES=true

Examples

Docker

Set the environment variables and mount the SSH key to the container:

docker run -v ~/.ssh:/app/.ssh \
  -e GRAFANA_URL=https://grafana.example.com \
  -e GRAFANA_SA_TOKEN=your-token \
  -e SSH_URL=git@github.com:org/repo.git \
  -e SSH_KEY=/app/.ssh/id_rsa \
  ghcr.io/cstanislawski/grafana-db-exporter:latest

Kubernetes CronJob

Create a ConfigMap with the known hosts of the Grafana instance and the repository, and a CronJob that runs the exporter with the required environment variables:

apiVersion: v1
kind: ConfigMap
metadata:
  name: github-known-hosts
data:
  # GitHub: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints
  # GitLab: https://docs.gitlab.com/ee/user/gitlab_com/index.html#ssh-host-keys-fingerprints
  # Bitbucket: https://confluence.atlassian.com/bitbucket/ssh-keys-935365775.html
  known_hosts: |
    github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
    github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
    github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
---
apiVersion: batch/v1
kind: CronJob
metadata:
  name: grafana-db-exporter
spec:
  schedule: "0 0 * * 1"
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: grafana-db-exporter
            image: ghcr.io/cstanislawski/grafana-db-exporter:latest
            volumeMounts:
            - name: github-known-hosts
              mountPath: /app/.ssh/known_hosts
              subPath: known_hosts
            env:
            - name: SSH_KEY
              valueFrom:
                secretKeyRef:
                  name: grafana-db-exporter
                  key: ssh-key
            - name: SSH_USER
              valueFrom:
                secretKeyRef:
                  name: grafana-db-exporter
                  key: ssh-user
            - name: SSH_EMAIL
              valueFrom:
                secretKeyRef:
                  name: grafana-db-exporter
                  key: ssh-email
            - name: SSH_URL
              value: "git@github.com:org/repo.git"
            - name: REPO_SAVE_PATH
              value: "grafana-dashboards"
            - name: GRAFANA_URL
              value: "https://grafana.example.com"
            - name: GRAFANA_SA_TOKEN
              valueFrom:
                secretKeyRef:
                  name: grafana-db-exporter
                  key: grafana-sa-token
            - name: BASE_BRANCH
              value: "main"
            - name: BRANCH_PREFIX
              value: "grafana-db-exporter-"
            - name: SSH_ACCEPT_UNKNOWN_HOSTS
              value: "false"
            - name: SSH_KNOWN_HOSTS_PATH
              value: "/app/.ssh/known_hosts"
            resources:
              requests:
                cpu: 100m
                memory: 128Mi
              limits:
                cpu: 500m
                memory: 512Mi
          volumes:
          - name: github-known-hosts
            configMap:
              name: github-known-hosts
              items:
              - key: known_hosts
                path: known_hosts
          restartPolicy: OnFailure

Integration with CI/CD

To integrate grafana-db-exporter with your CI/CD pipeline, you can set up a periodic job that runs the exporter. This way, you can keep your Grafana dashboards in sync with your repository.

In order to integrate the exported dashboards to be applied to the Grafana instance, you can set up your repository to deploy the changes in several ways:

Terraform

You can use the Grafana provider and source all of your dashboards from the REPO_SAVE_PATH directory.

More info: Terraform Implementation Example

Kubernetes

Basic

TBD

ArgoCD

TBD

Limitations

Multiple Grafana organizations / instances

If you need to export dashboards from multiple Grafana organizations, you will need to run multiple instances of grafana-db-exporter with different GRAFANA_SA_TOKEN and different GRAFANA_URL in case of multiple instances. The token is the only way to authenticate with the Grafana API, and it is also the token that represents the organization that the service account belongs to, limiting the access to the dashboards to the ones that the organization has access to.

More info: Grafana docs on Service accounts

Security Considerations