Il y a quelques semaines, je vous ai proposé un article expliquant la migration de Traefik 1 à Traefik 2. Je vous propose cette fois d'aborder un point crucial dans la mise en place d'une application, son monitoring.

Cet article explique comment j'ai mis en place mon dashboarding, il n'explique en aucun cas du dashboarding "entreprise" devant être plus fiable et complet sur certains points.

Durant cet article, je vais vous expliquer comment créer de manière très basique ce type de dashboard.

Technologies utilisées

Pour mettre en place ce dashboard, j'ai utilisé trois applications distinctes:

  • ElasticSearch : ElasticSearch est un produit permettant d'indexer et rechercher des données, il est souvent utilisé dans des stacks ELK (ElasticSearch LogStash Kibana) pour du logging ou en stand alone en tant que moteur de recherche.
  • FileBeat : FileBeat est un extracteur de logs léger créé par Elastic.co, éditeur d'ElasticSearch, son modèle est adapté aux containers. A noter que FileBeat peut aussi tourner en DaemonSet standalone pour extraire les logs d'un cluster Kubernetes complet.
  • Grafana : Grafana permet de créer des dashboards a partir de sources de données multiples, et notamment ElasticSearch, utilisé ici.

1ère étape : Mise en place d'ElasticSearch

Dans le modèle que je vous propose, ElasticSearch va servir à stocker et indexer nos logs. Nous allons donc déployer ElasticSearch dans un pod pour l'exploiter ensuite. Nous créons donc le fichier de description suivant :

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: elasticsearch
  labels:
    app: elasticsearch
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
      - name: elasticsearch
        image: elasticsearch:7.5.1
        imagePullPolicy: Always
        ports:
        - containerPort: 9200 # Port de requête
        - containerPort: 9300 # Port d'administration
        env:
        - name: discovery.type
          value: single-node  # Indique à ElasticSearch de fonctionner en mono noeud
        resources:
          limits:
            cpu: "0.5"
            memory: "2048Mi"  # Ram minimal requise

---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: elasticsearch
  name: elasticsearch
spec:
  type: ClusterIP
  ports:
  - port: 9200
    name: http
  - port: 9300
    name: http2
  selector:
    app: elasticsearch

Comme indiqué, en appliquant ce dernier, nous allons déployer :

  • Un pod contenant ElasticSearch en mono noeud
  • Un service permettant d'exposer les port de requête et d'administration d'ElasticSearch

Information importante - Environnement de production :

Dans un environnement de production, en entreprise, on prendra en compte les données suivantes pour créer notre cluster ElasticSearch :

  • Fonctionner minimum avec 3 noeuds
  • Activer du stockage persistant : Pour ma part je ne considère pas ces données comme critiques, en entreprise on choisira de persister les données
  • Activer l'authentification sur ElasticSearch

Vous pouvez retrouver ces informations sur le site officiel.


2ème étape : Mise en place d'un sidecar avec FileBeat et configuration de Traefik

Dans cette seconde étape, nous allons :

  • Configurer Traefik pour qu'il écrive ses accesslogs dans /var/log/traefik/access.log
  • Deployer FileBeat avec le module Traefik, qui va lui permettre de mappé nativement les champs des logs
  • Créer un emptyDir qui nous servira de répertoire d'échange entre nos deux containers dans le pod

Je ne vais pas revenir sur l'installation de Traefik, que j'avais décrit dans mon article précédent. De manière simple, mon pod Traefik tourne dans un namespace dédié, et dans un DaemonSet.

Nous allons donc modifier ce DaemonSet pour ajouter notre second container ainsi que notre répertoire d'échange.

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
 name: traefik-ingress-controller
 namespace: traefik
 labels:
   k8s-app: traefik-ingress-lb
   kubernetes.io/cluster-service: "true"
spec:
 template:
   metadata:
     labels:
       k8s-app: traefik-ingress-lb
       name: traefik-ingress-lb
   spec:
     hostNetwork: true
     dnsPolicy: ClusterFirstWithHostNet
     serviceAccountName: traefik-ingress-controller
     terminationGracePeriodSeconds: 60
     tolerations:
     - key: node-role.kubernetes.io/master
       effect: NoSchedule
     containers:
     - image: traefik:v2.1.1
       name: traefik-ingress-lb
       imagePullPolicy: Always
       volumeMounts:
         - mountPath: "/var/log/traefik"
           name: logs
         - mountPath: "/cert/"
           name: cert
       resources:
         requests:
           cpu: 100m
           memory: 20Mi
       args:
       - --providers.kubernetescrd
       - --accesslog=true
       - --accesslog.filepath=/var/log/traefik/access.log
       - --accesslog.fields.headers.defaultmode=keep
       - --entrypoints.web.address=:80
       - --entrypoints.websecure.address=:443
       - --certificatesresolvers.le.acme.email=masuperadressemail@monmail.com
       - --certificatesresolvers.le.acme.storage=/cert/acme.json
       - --certificatesResolvers.le.acme.httpChallenge.entryPoint=web
       ports:
       - name: web
         containerPort: 80
       - name: websecure
         containerPort: 443
       - name: admin
         containerPort: 8080
     - name: filebeat
       image: docker.elastic.co/beats/filebeat:7.5.1
       args: [
         "-c", "/etc/filebeat.yml",
         "-e",
       ]
       env:
       - name: ELASTICSEARCH_HOST
         value: elasticsearch.default
       - name: ELASTICSEARCH_PORT
         value: "9200"
       securityContext:
         runAsUser: 0
       resources:
           limits:
             memory: 200Mi
           requests:
             cpu: 100m
             memory: 100Mi
       volumeMounts:
         - name: config
           mountPath: /etc/filebeat.yml
           readOnly: true
           subPath: filebeat.yml
         - name: data
           mountPath: /usr/share/filebeat/data
         - name: logs
           mountPath: /var/log/traefik
           readOnly: true
     volumes:
     - name: logs
       emptyDir: {}
     - name: config
       configMap:
         defaultMode: 0600
         name: filebeat-config
     - name: data
       hostPath:
         path: /var/lib/filebeat-data
         type: DirectoryOrCreate
     - name: cert
       hostPath:
         path: /home/kube/traefik/certs/
         type: Directory


---
apiVersion: v1
kind: ConfigMap
metadata:
  name: filebeat-config
  namespace: traefik
  labels:
    k8s-app: filebeat
data:
  filebeat.yml: |-
    output.elasticsearch:
      hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}']
    filebeat.modules:
    - module: traefik
      access:
        enabled: true

Dans ce bloc de code, nous retrouvons donc :

  • Lignes 38 à 40 : Ajout des paramètres de lancement à Traefik pour activer les accesslog, les stocker dans /var/log/traefik/access.log et garder les headers dans les logs (qui sont droppés par défaut)
  • Ligne 27 et 28 : Ajout du point de montage décrit plus bas, qui permet d'avoir un répertoire d'échange entre nos deux containers
  • Ligne 53 à 81 : Création de notre container exécutant FileBeat, auquel on indique son fichier de configuration, les paramètres du endpoint ElasticSearch et les points de montage nécessaires
  • Ligne 83 et 84 : Le volume emptyDir créé pour échanger entre nos deux containers au sein du pod
  • Ligne 89 à 92: Répertoire persistant sur l'hôte, qui permet à FileBeat de stocker ses données, et notamment les pointeurs indiquant les fichiers déjà traités
  • Ligne 100 à 114 : Le fichier de configuration de filebeat, qui indique l'output, ElasticSearch, et d'activer le module Traefik

Une fois que nous appliquons ce fichier de description, nous avons donc traefik qui génère des logs qui sont envoyé vers ElasticSearch toutes les 30 secondes (configuration par défaut du module Traefik de FileBeat).

3ème étape : Déploiement de Grafana

Maintenant que nous extrayons et stockons nos accesslogs, nous allons maintenant déployer Grafana, qui nous permettra de les restituer.

En prérequis, j'ai créé sur mon hôte un répertoire "/home/kube/grafana/data" qui me servira à persister les données de Grafana, et notamment les dashboards.

Encore une fois, nous créons donc un fichier de description pour Kubernetes, contenant les informations suivantes:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: grafana
  labels:
    app: grafana
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: grafana
    spec:
      containers:
      - name: grafana
        volumeMounts:
        - name: grafana-data-storage
          mountPath: /var/lib/grafana
        image: grafana/grafana:6.5.2
        imagePullPolicy: Always
        ports:
        - containerPort: 3000
        env:
        - name: GF_INSTALL_PLUGINS
          value: "grafana-worldmap-panel 0.2.1,grafana-piechart-panel 1.3.9"
        resources:
          limits:
            cpu: "0.5"
            memory: "512Mi"
      volumes:
      - name: grafana-data-storage
        hostPath:
          path: /home/kube/grafana/data
          type: Directory

---

kind: Service
apiVersion: v1
metadata:
  labels:
    app: grafana
  name: grafana
spec:
  type: ClusterIP
  ports:
  - port: 3000
    name: http
  selector:
    app: grafana

Nous pouvons donc voir dans ce code :

  • Le déploiement de Grafana
  • L'installation de plugins complémentaires : WorldMap Panel (permettant d'avoir une map avec les points) et PieChart Panel, permettant d'avoir diagramme "camembert"
  • La création et l'utilisation du volume persistant
  • La création d'un service permettant de l'exposer via Traefik, non décrite ici car identique à mon article précédent

4ème étape : Configurer Grafana pour se connecter à ElasticSearch

Une fois que notre Grafana est déployé, nous pouvons donc nous connecter dessus avec le super couple login/password : admin/admin...

Nous créons ensuite un mot de passe fort pour Grafana.

Maintenant nous pouvons ajouter notre première source de données : ElasticSearch, que nous allons configurer comme ainsi :

Avec cette configuration, nous allons :

  • Connecter Grafana à ElasticSearch via le service créé précédemment
  • Indiquer l'index name par defaut de filebeat
  • Indiquer qu'il s'agit d'un ElasticSearch en version 7

En allant ensuite sur "Explore" et en cliquant sur "Logs" en haut, vous devriez maintenant voir vos logs Traefik.

Pour terminer

Maintenant que nous pouvons exploiter les accesslogs de Traefik, il est assez facile de les extraire pour en faire un dashboard comme je me le suis fait.

A noter que pour requêter ElasticSearch, il est nécessaire de le faire via le langage Lucene, j'utilise pour ma part une cheat sheet disponible ici.

Comme indiqué précédemment, le but de ce billet est simplement de vous décrire la méthode d'installation que j'ai utilisé, elle n'est sans doute pas parfaite, et il convient à vous de l'adapter à vos besoins.