Pré-configurer Grafana avec Docker-Compose

Image: Christiaan Colen / Flickr

Grafana est un excellent outil permettant de créer facilement des dashboards de monitoring en se branchant sur différentes sources de données. Ce projet est open-source et disponible sous la forme d’un conteneur docker, ce qui lui permet d’être intégré directement dans d’autres projets de plus grande envergure. C’est par exemple ce que j’ai fait avec Omeglast.

Pour facilité son inclusion dans un projet, il est nécessaire d’avoir une solution pour initialiser Grafana directement avec une configuration propre à notre projet (accès aux sources de données, dashboards initialisés, etc.)

Heureusement, Grafana avec quelques outils supplémentaires dispose de tous les éléments nécessaires.

Principe général

Grafana utilise par défaut un base de donnée SQLite pour stocker l’ensemble de sa configuration, mais il est possible, via quelques paramètre d’initialisation, de remplacer cette base par une autre base de notre choix, extérieur, et dont on aura un total contrôle.

Ensuite, il ne nous restera qu’à configurer une fois Grafana, de sauvegarder cette configuration dans un fichier, et de pouvoir ensuite initialiser automatique notre base avec ce fichier sauvegardé.

Pour info, l’ensemble du code source de ce tutoriel est disponible sur github.

Etape 1 : Installer Grafana avec docker-compose

Pour commencer, vous devez avoir docker et docker-compose d’installés, si ce n’est le cas, aller voir les tutoriels associés, c’est très simple.

On va donc commencer par initialiser Grafana seul, en utilisant docker-compose. Il suffit de copier le code suivant dans un fichier docker-compose.yml.

version: '2'
services:
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"

On lance l’application via docker-compose up, et après quelques secondes d’initialisation, l’application est disponible via l’url http://localhost:3000

A ce stade, Grafana fonctionne, mais vous avez tout à configurer, la première chose à faire sera donc d’exporter la configuration à l’extérieur du conteneur pour la rendre persistante. Il existe plusieurs options, la première consiste à simplement créer un nouveau volume dans lequel sera enregistré cette configuration (c’est ce que vous trouverez dans la documentation de grafana sur docker hub), mais il existe une autre option, bien plus sexy.

Etape 2 : Ajouter une base de stockage de la configuration

En effet, en lisant la documentation de Grafana d’une part et de son image docker d’autre part, on découvre deux choses:

  1. il est possible de configurer Grafana pour qu’il stocke sa configuration dans une base de données externe (comme PostgreSQL par exemple)
  2. il est possible de redéfinir toutes les variables du fichier de configuration via des variables d’environnement injectée via docker.

Alors allons-y, modifions notre fichier docker-compose.yml en conséquence:

version: '2'
volumes:
  grafana-pgdata:
services:
  # Grafana dedicated storage
  #
  grafana-storage:
    image: postgres:10-alpine
    volumes:
      - grafana-pgdata:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_USER=admin
      - POSTGRES_DB=grafana

  # Grafana Server
  #
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_DATABASE_TYPE=postgres
      - GF_DATABASE_HOST=grafana-storage
      - GF_DATABASE_NAME=grafana
      - GF_DATABASE_USER=admin
      - GF_DATABASE_PASSWORD=password

Nous avons donc :

  • créé un volume pour y stocker la base de donnée de PosgreSQL « grafana-pgdata« 
  • créé un conteneur PostgreSQL initialisé avec une base « grafana« 
  • modifié la configuration de Grafana pour utiliser notre base PostgreSQL pour sa configuration

Y a plus qu’à lancer notre commande docker-compose up.

CRACK.

Fail to initialize orm engine" logger=sqlstore error="Sqlstore::Migration failed err: dial tcp 172.23.0.2:5432: getsockopt: connection refused

Ok, ça plante, mais la raison est très simple, le premier lancement de PostgreSQL est assez long car il doit construire et initialiser la base de donnée, parallèlement, Grafana est beaucoup plus rapide, et donc n’arrivant pas à se connecter à PostgreSQL qui n’est pas encore disponible, s’arrête avec une erreur.

Si vous lancer PostgreSQL seul en premier, et Grafana ensuite avec quelques secondes d’interval, tout fonctionne.

Etape 3 : Ajouter un Healthcheck et séquencer le démarrage

Le Healthcheck est un petit bout de script qui permet de vérifier qu’un service est opérationnel. Et c’est ce que nous allons utiliser.

Nous allons donc ajouter un petit script qui va nous permettre d’attendre que PostgreSQL soit disponible avant de lancer Grafana.

Sur PostgreSQL, il y a une commande pg_isready qui nous permet de savoir si le serveur est prêt à traiter des requêtes ou non, et pour le lancer via docker, ça donne :

docker-compose exec grafana-storage pg_isready

Ajoutons ça dans un script start.sh qui va nous séquencer le démarrage :

#!/bin/bash

docker-compose up -d grafana-storage

until docker-compose exec grafana-storage pg_isready > /dev/null
do
    printf '.'
done
printf '\n'

docker-compose up

Cette fois-ci, même après avoir supprimer toutes les images, conteneurs et volumes, en lançant la commande ./start.sh tout fonctionne correctement.

Etape 4 : Exporter la sauvegarde

Nous avons donc maintenant Grafana qui fonctionne correctement et PostgreSQL qui contient toute la configuration de Grafana.

Ce dont nous avons maintenant besoin c’est de pouvoir extraire la configuration de cette base de donnée pour pouvoir la sauvegarder avec notre application et pour la réimporter plus tard, automatiquement à l’installation de notre futur projet.

Nous allons d’abord modifier notre fichier docker-compose.yml pour y ajouter un nouveau montage de dossier destiné à recevoir l’export de la base de données :

version: '2.1'
volumes:
  grafana-pgdata:
services:
  # Grafana dedicated storage
  #
  grafana-storage:
    image: postgres:10-alpine
    volumes:
      - grafana-pgdata:/var/lib/postgresql/data
      - ./export:/var/export
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_USER=admin
      - POSTGRES_DB=grafana
    healthcheck:
        test: ["CMD", "pg_isready"]
        interval: 30s
        timeout: 1s
        retries: 5

  # Grafana Server
  #
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_DATABASE_TYPE=postgres
      - GF_DATABASE_HOST=grafana-storage
      - GF_DATABASE_NAME=grafana
      - GF_DATABASE_USER=admin
      - GF_DATABASE_PASSWORD=password

Nous allons rajouter un petit script dump.sh nous permettant de faire un dump de la base dans ce dossier :

#!/bin/bash

docker-compose exec grafana-storage sh -c "pg_dump -U admin grafana > /var/export/dbexport.sql"

Maintenant, vous pouvez donc lancer votre application avec ./start.sh, puis via l’interface web créer votre configuration (source de donnée, dashboards, etc.), et une fois terminé, exporter cette configuration avec le script ./dump.sh.

Le fichier généré est alors disponible dans le dossier export/.

Etape 5 : Charger la configuration au démarrage

Voilà, nous avons sauvegardé la configuration, la dernière étape est de pouvoir charger cette configuration à la prochaine initialisation du projet.

Pour cette partie, c’est la documentation du conteneur PostgreSQL qui va nous donner la solution. En effet, il possible de spécifier un montage de dossier supplémentaire (/docker-entrypoint-initdb.dcontenant un (ou plusieurs) script qui seront chargé lors du premier lancement du conteneur (à l’initialisation donc).

Du coup, encore une petite modification de notre fichier docker-compose.yml :

version: '2.1'
volumes:
  grafana-pgdata:
services:
  # Grafana dedicated storage
  #
  grafana-storage:
    image: postgres:10-alpine
    volumes:
      - grafana-pgdata:/var/lib/postgresql/data
      - ./export:/var/export
      - ./initdb:/docker-entrypoint-initdb.d
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_USER=admin
      - POSTGRES_DB=grafana
    healthcheck:
        test: ["CMD", "pg_isready"]
        interval: 30s
        timeout: 1s
        retries: 5

  # Grafana Server
  #
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_DATABASE_TYPE=postgres
      - GF_DATABASE_HOST=grafana-storage
      - GF_DATABASE_NAME=grafana
      - GF_DATABASE_USER=admin
      - GF_DATABASE_PASSWORD=password

Nous pouvons maintenant mettre notre fichier précédemment sauvegardé dans le dossier local initdb/, et de lancer notre script ./start.sh.

Attention de bien supprimer tous les conteneurs et volumes précédemment créé, sinon docker-compose réutilisera ce qui existe déjà et n’utilisera pas la nouvelle configuration. Vous pouvez faire ce nettoyage avec la commande docker-compose down -v.

Et voilà, vous pouvez maintenant inclure ces fichiers dans votre projet pour avoir une instance de Grafana déjà configuré par rapport à vos besoins.