Bonjour les amis.

Dans ce tutoriel, nous allons mettre en place une stack ELK plus prometheus, sous docker, avec un très beau docker compose.

L’objectif de cette stack est de valoriser les logs de notre reverse-proxy en y ajoutant des informations (de géolocalisation par exemple) et visualiser l’état du trafic en temps réel.

En bonus, on ajoutera une belle visualisation des informations systèmes de notre hôte et des informations de notre installation docker… Cool!

Dans un précédent billet, nous avons mis en place un reverse proxy traefik. Je vais partir du postulat que nous en sommes au même point.

Introduction

Vous avez probablement déjà entendu parler de la suite « elastic » connue sous l’acronyme ELK, pour Elasticsearch / Logstash / Kibana. Un ensemble d’outils en partie open source et en partie libre.

Dans le cadre de ce post, parler de ELK est un abus de langage puisque nous allons en fait utiliser EK et deux « beats », puis faire semblant d’utiliser prometheus.

Concrètement, nous allons utiliser:

  • Elasticsearch: La base de données, qui va stocker nos informations.
  • Kibana: L’interface web, qui va nous permettre de visualiser les informations.
  • Filebeat: L’outil qui va récupérer les logs et les mettre en formes dans la base de données.
  • Metricbeat: L’outil qui va récupérer les différents « metriques » AKA « informations » et les mettre en forme dans la base de données.

Vous remarquez le grand absent qu’est le « L », pour Logstash. En effet, après avoir effectué différents tests, je me suis rendu compte que dans le cadre d’une installation personnelle, la plus value de Logstash+Filebeat par rapport a Filebeat tout seul était quasi nulle.

De plus, Elasticsearch et Logstash étant tout les deux en Java, ils sont très consommateurs de ressources, ce qui est souvent un facteur d’abandon de la solution. Filebeat lui est très léger.

Nous utiliserons la licence « basic » par défaut, qui est gratuite et ne nécessite aucun enregistrement.

Je suis réellement parti de zéro, car je n’avais encore jamais même visualisé une interface Kibana. Mais passé les quelques heures de lectures de documentation et de debug, nous avons enfin une solution qui fonctionne bien et agréable a regarder.

On y va? Commençons par télécharger les images maintenant en tâche de fond pour ne pas perdre de temps:

docker pull docker.elastic.co/elasticsearch/elasticsearch:7.6.2 && docker pull docker.elastic.co/kibana/kibana:7.6.2 && docker pull docker.elastic.co/beats/filebeat:7.6.2 && docker pull docker.elastic.co/beats/metricbeat:7.6.2

1 – Préparer les logs…

Commençons par le début. Si on veut visualiser nos logs (access.log), il faut savoir ou ils sont et ce que c’est.

Reprenons notre fichier de configuration traefik.toml et vérifions la partie « log »:


################################################################
# Traefik logs configuration
################################################################

[log]
  level = "INFO" #DEBUG, INFO, WARN, ERROR, FATAL, PANIC
  filePath = "/var/log/traefik/traefik.log"
  format = "common"

################################################################
# Access logs configuration
################################################################

[accessLog]
  filePath = "/var/log/traefik/access.log"
  format = "common"
  [accessLog.fields]
    defaultMode = "keep"
    [accessLog.fields.names]
      defaultMode = "keep"
    [accessLog.fields.headers]
      defaultMode = "keep"

Nous indiquons l’emplacement de celles-ci, ainsi que ce qu’ils contiennent. J’ai choisi de tout garder.

Maintenant, on vérifie que l’on a bien un volume pour ces logs. Car il faudra le donner a Filebeat. Que dit notre [traefik] docker-compose?

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /etc/localtime:/etc/localtime:ro
      - ${PWD}/conf/traefik.toml:/traefik.toml:ro
      - ${PWD}/conf/services.toml:/etc/traefik/services.toml
      - ${PWD}/logs:/var/log/traefik
      - ${PWD}/conf/acme.json:/acme.json

Ok, les logs sont bien contenus dans un volume qui va persister.

2 – Préparation du stack

Nous allons créer un nouveau répertoire efk qui va contenir les fichiers de configuration de la stack, ainsi que le docker-compose. Notre répertoire se présente comme suit:

user@nuc:/mnt/efk$ ll /mnt/efk
total X
drwxr-xr-x  6 docker docker     4096 avril 28 14:26 .
drwxr-xr-x 16 docker docker     4096 avril 26 16:15 ..
drwxr-xr-x  2 docker docker     4096 avril 28 17:56 config
-rw-r--r--  1 docker docker     2129 avril 28 17:57 docker-compose.yml
drwxr-xr-x  3 docker docker     4096 avril 28 14:23 elasticsearch_data
drwxr-xr-x  3 docker docker     4096 avril 28 18:11 filebeat_data
drwxr-xr-x  2 docker docker     4096 avril 28 18:11 metricbeat_data

Nous avons donc:

  • Trois sous répertoires nommés « elasticsearch_data », « filebeat_data » et « metricbeat_data » qui vont contenir les données de chacun des outils.
  • Un sous répertoire « config » contiendra les fichiers de configurations.
  • Le docker-compose.

3 – Elasticsearch

Pour Elasticsearch toute la configuration va passer par des variables d’environnement passés dans le docker-compose.

version: '3.4'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2
    container_name: elasticsearch
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ./elasticsearch_data:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      - discovery.type=single-node
      - http.host=0.0.0.0
      - transport.host=127.0.0.1
      - xpack.security.enabled=false
      - ES_JAVA_OPTS=-Xms750m -Xmx750m
      - bootstrap.memory_lock=true
      - xpack.license.self_generated.type=basic
    ulimits:
      memlock:
        soft: -1
        hard: -1
    network_mode: "web"
    restart: always

Comme d’habitude, on ouvre les ports, on monte le fuseau horaire, un volume de données, on définit un réseau, etc….

Parlons des spécificités:

  • La partie « ulimit » est fournie dans la documentation relative a docker.
  • « discovery.type=single-node » signifie que l’on ne travaille pas dans un cluster, mais avec un seul « noeud ».
  • « http.host=0.0.0.0« , rien à dire la dessus.
  • « transport.host=127.0.0.1« , cette option m’a bien fait galérer. Ne mettez pas « localhost » car vous aurez de nombreux messages d’erreur.
  • « xpack.security.enabled=false » désactive une option payante.
  • « ES_JAVA_OPTS=-Xms750m -Xmx750m » indique qu’elasticsearch peut utiliser au maximum 750Mo de ram.
  • « bootstrap.memory_lock=true » vérouille les 750Mo de ram, juste pour elasticsearch car sinon les performances diminuent.
  • « xpack.license.self_generated.type=basic » indique le type de licence utilisée.

Avec ceci, vous n’aurez normalement qu’un message d’erreur au tout début du lancement, qui indique:

OpenJDK 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.

Malgré mes recherches, je n’ai pas réussi a le résoudre. Et vu que ce n’est qu’un « warning » qui n’apparaît qu’une fois….

Avec ceci, votre base de données sera fonctionnelle.

4 – Kibana

Occupons nous maintenant de la partie visualisation et modifions notre docker-compose:

  kibana:
    image: docker.elastic.co/kibana/kibana:7.6.2
    container_name: kibana
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ./config/kibana.yml:/usr/share/kibana/config/kibana.yml
    ports:
      - "5601:5601"
    network_mode: "web"
    depends_on:
      - elasticsearch
    restart: always

Rien de foufou ici, on indique cependant un fichier de configuration « kibana.yml » qui est dans le répertoire « config ». Allons le voir:


server.name: kibana
server.host: "0.0.0.0"
elasticsearch.hosts: [ "http://elasticsearch:9200" ]
elasticsearch.username: "kibana"
elasticsearch.password: "kibanapassword"
xpack.monitoring.ui.container.elasticsearch.enabled: "false"
xpack.encryptedSavedObjects.encryptionKey: "ivayutasffsjrjbroeovgceangjdudvhbuz"
xpack.security.encryptionKey: "btruschnryxkvixdmemmepbwapbcgtbrrqw"
xpack.reporting.encryptionKey: "qcguozbfiqhkhnncofsnfbsckmfqguiudvg"
  • server.name: kibana / server.host: « 0.0.0.0 » / elasticsearch.hosts: [ « http://elasticsearch:9200 » ] sont à laisser tels quels, ils permettent à kibana de trouver la base de données.
  • « xpack.monitoring** » désactive une option payante.
  • elasticsearch.username: « kibana » / elasticsearch.password: « kibanapassword » sont les identifiants par défaut de kibana pour accéder à elasticsearch
  • « xpack*** » sont des clés de chiffrement que vous pouvez générer aléatoirement. Il faut au moins 32 caractères et ils permettent par exemple de déchiffrer les exports que vous faites et d’autres choses comme ça. Ainsi, pas de problèmes si vous redémarrez ou si vous créez une nouvelle instance. (ça évite aussi pas mal de messages d’erreur).

Maintenant, on devrait avoir un kibana fonctionnel. Attention, le lancement est long. Vous ne pourrez pas accéder au panel (ip:5601) avant une minute ou deux.

5 – Filebeat

C’est bien beau tout ça, on a une base de données vide et une interface qui n’a rien a afficher…. Remédions à ça et allons manger des logs…

Reprenons notre docker-compose et ajoutons un service:

  filebeat:
    image: "docker.elastic.co/beats/filebeat:7.6.2"
    container_name: filebeat
    user: root
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ./config/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
      - /mnt/traefik/logs:/var/log/traefik/
      - ./filebeat_data:/usr/share/filebeat/data
    network_mode: "web"
    depends_on:
      - elasticsearch
    restart: always

Quelques précisions ici:

  • Il faut spécifier un utilisateur root,
  • Monter un fichier de configuration qui doit impérativement être possédé par root (chown root:root filebeat.yml).
  • Et monter les logs de traefik dans « /var/log/traefik/ ».

Allons voir le contenu du fichier filebeat.yml:

output.elasticsearch:
  hosts: ["elasticsearch:9200"]

setup.kibana:
  host: "http://192.168.XX.XX:5601"

setup.dashboards.enabled: true

filebeat.modules:
  - module: traefik
    access:
      enabled: true
      var.paths: ["/var/log/traefik/access.log*"]

Nous indiquons à filebeat que:

  • Il doit envoyer les données a elasticsearch (la valeur ne doit pas être modifiée).
  • Kibana est accessible à l’adresse http://192.168.XX.XX:5601 (ceci est a adapter).
  • Il doit envoyer les dashboards a kibana (vous aurez tout pleins de dashboards dans kibana, mais pas beaucoup d’utiles finalement.
  • Il doit charger le module traefik ou la localisation des logs est précisé.

6 – Premier test

Je sais que ça vous démange…. Alors allons-y, allons tester

user@nuc:/mnt/efk$ docker-compose up -d

Si tout vas bien, 3 containers ont du être créés. Vérifions les logs..

user@nuc:/mnt/efk$ docker-compose logs

Pas de gros soucis? Alors allons voir notre interface kibana qui devrait être accessible si vous avez attendu quelques minutes:

En premier lieu, on nous demande de créer un index, qui corresponde à ce que l’on souhaite indexer (nos logs récupérées par filebeat).

On remarque le nommage de ce que récupère filebeat: « filebeat-7.6.2-2020.04.28*** ».

En gros « outil »-« version »-« date » »*** ». Si l’on veut que notre index englobe tous nos logs, on indique quoi? Aller, on réfléchit!

Voilà, on met « filebeat*« . Bravo.

Enfin, on déclare que le filtre se fera sur le timestamp.

A ce propos, c’est l’heure de vérifier que kibana reçoit nos logs.

On clique sur « Discover« , puis « Logs« .

Ceci fait, on sélectionne le module « Traefik« .

Et tout en bas, on clique sur « Check data« . Si la réponse est verte, on a gagné… Plus qu’à cliquer sur « Traefik logs dashboard » pour avoir une belle visualisation.

Cool…… Mais attendez…. A côté de « Logs », il y avait « Metrics ». On va voir?

Ne fantasmez pas, ce module ne fonctionne pas avec la version 2 de traefik. Il repose sur l’ancien système de metrics. Dommage….

Peut être pas si dommage que ça en fait… Regardons de plus près… Il y a aussi Prometheus qui est également proposé par Traefik.

Mais il y a aussi Docker. Et System. Aller, on va faire un grand Chelem…

7 – Traefik Metrics

Passons aux metriques et demandons a Traefik de les gérer pour Prometheus.

Modifions notre traefik.toml:

################################################################
# Metrics configuration
################################################################
    	
[metrics]
  [metrics.prometheus]

Nous n’avons qu’à rajouter cela. Toutes les autre options ont une valeur par défaut qui suffit.

On relance maintenant notre container et on vérifie notre api/metrics:

user@nuc:/mnt/efk$ curl localhost:8080/metrics
# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
**********

Si l’on a une réponse valide, c’est que tout vas bien, Traefik exporte nos métriques.

Passons au suivant…

8 – Prometheus

Alors la, Prometheus ne va pas être content, mais j’ai trouvé un truc sympa.

En créant le container Prometheus et en le configurant, je me suis rendu compte que je pouvais consulter les metrics en entrant IP:PORT/metrics de Prometheus et que le résultat était le même que IP:PORT/metrics de Traefik.

En clair, lorsque que l’on choisi les metriques Prometheus, Traefik les formate directement.

Je me suis dit qu’il y avait peut-être moyen de tromper metricbeat en lui faisant croire qu’il récupérait les données de Prometheus, alors qu’en fait, il les prenait de Traefik.

Pari réussi, cela nos évite un container, donc de la ressource et des erreurs potentielles.

Good news. Il ne reste plus qu’à gérer Metricbeat.

9 – Metricbeat

Reprenons le docker-compose de efk:

  metricbeat:
    image: docker.elastic.co/beats/metricbeat:7.6.2
    container_name: metricbeat
    cap_add:
      - SYS_PTRACE
      - DAC_READ_SEARCH
    user: root
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ./config/metricbeat.docker.yml:/usr/share/metricbeat/metricbeat.yml:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /sys/fs/cgroup:/hostfs/sys/fs/cgroup:ro
      - /proc:/hostfs/proc:ro
      - /run/systemd/private:/run/systemd/private
      - /:/hostfs:ro
      - ./metricbeat_data:/usr/share/metricbeat/data
    network_mode: "web"
    depends_on:
      - elasticsearch
    restart: always

Plusieurs nouveautés ici:

  • « cap_add« : par défaut, certains droits sont retirés à l’utilisateur root de docker. Mais metricbeat a besoin de ces droits si l’on souhaite qu’il monitore le système hôte.
  • Nous montons une fichier de configuration qui comme pour filebeat doit impérativement appartenir a root.
  • Les autres points de montage servent soit pour le monitoring du système hôte, soit de docker.

Allons voir notre fichier de configuration:

metricbeat.config:
  modules:
    path: ${path.config}/modules.d/*.yml
    reload.enabled: true

setup.kibana:
  host: "http://192.168.XX.XX:5601"

setup.dashboards.enabled: true

output.elasticsearch:
  hosts: ["elasticsearch:9200"]

metricbeat.autodiscover:
  providers:
    - type: docker
      hints.enabled: true
  
metricbeat.modules:
#########PROMETHEUS#########
- module: prometheus
  period: 10s
  hosts: ["localhost:8080"] #Ici l'on met l'adresse de l'API traefik
  metrics_path: /metrics
###########DOCKER###########
- module: docker
  metricsets:
    - "container"
    - "cpu"
    - "diskio"
    - "healthcheck"
    - "info"
    - "image"
    - "memory"
    - "network"
  hosts: ["unix:///var/run/docker.sock"]
  period: 10s
  enabled: true
###########SYSTEM###########
- module: system
  metricsets:
    - cpu             # CPU usage
    - load            # CPU load averages
    - memory          # Memory usage
    - network         # Network IO
    - process         # Per process metrics
    - process_summary # Process summary
    - socket_summary  # Socket summary
    - core           # Per CPU core usage
    - diskio         # Disk IO
    - filesystem     # File system usage for each mountpoint
    - fsstat         # File system summary metrics
    - socket         # Sockets and connection info (linux only)
    - service        # systemd service information
  enabled: true
  period: 10s
  processes: ['.*']
  # Configure the metric types that are included by these metricsets.
  cpu.metrics:  ["percentages"]  # The other available options are normalized_percentages and ticks.
  core.metrics: ["percentages"]  # The other available option is ticks.
  # These options allow you to filter out all processes that are not
  # in the top N by CPU or memory, in order to reduce the number of documents created.
  # If both the `by_cpu` and `by_memory` options are used, the union of the two sets
  # is included.
  process.include_top_n:
    # Set to false to disable this feature and include all processes
    enabled: true

Une partie est commune a filebeat, le reste concerne les trois modules que l’on souhaite activer:

Pour le module Prometheus, dans les champ « hosts », on indique l’IP et le PORT de notre API traefik.

Voilà, tout devrait être bon. Lançons notre service…

user@nuc:/mnt/efk$ docker-compose up -d metricbeat

Maintenant, il faut configurer ces nouveaux modules dans kibana, rien de sorcier, ce n’est ni plus, ni moins que ce que nous avons fait pour filebeat:

Nous allons créer un index pour metricbeat:

Puis allons vérifier que l’on reçoit bien les données de « Docker », « Prometheus » et « System »:

Conclusions

Sans aucune modification des visuels, voici ce que vous aurez:

Pour les logs:

Le système hôte:

Prometheus: (celui la n’est pas top)

Et enfin pour Docker: (celui la est super cool)

Bien sûr, maintenant, avec ces éléments, charge à vous de vous faire un dashboard qui vous plaît.

En dehors du warning dont je vous ai parlé plus tôt pour elasticsearch, les seuls messages d’avertissements que vous devriez avoir, sont de metricbeat qui vous informe que les modules « system » et « docker » sont en bêta.

On a plus qu’à profiter!

Laisser un commentaire