Loki est un système de gestion de logs open source et conçu par Grafana Labs. Il est connu pour être facile d’utilisation et très performant. Loki est conçu pour être utilisé avec Grafana via l’usage d’un équivalent au LogQL, un langage de requête similaire à celui de Prometheus (pour en savoir plus sur Prometheus, cliquez ici).

Dans cet article, nous allons voir comment installer l’ensemble des composants nécessaires pour utiliser Loki et Promtail. Nous verrons aussi comment stocker les logs sur un stockage objet (Minio) pour avoir une haute disponibilité dans un cluster de Loki.

Architecture de Loki

L’architecture de Loki est composée de trois éléments principaux :

  • Loki : le serveur de stockage de logs, il fait office de base de données pour les logs.
  • Promtail : l’agent de collecte de logs, il est responsable de la collecte des logs et de leur envoi à Loki.
  • Grafana : l’interface utilisateur pour visualiser les logs stockés dans Loki.

Astuce

Promtail peut être remplacé par d’autres agents de collecte de logs, comme Fluentd, Fluent Bit, ou Logstash (nous n’aborderons que la partie Promtail dans cet article).

Un des avantages de Loki est qu’il permet de stocker les logs sur un stockage objet (comme S3, GCS, ou Azure Blob Storage) ou sur un système de fichiers local.

Nous allons commencer par utiliser le stockage local, puis nous utiliserons Minio pour stocker les logs sur un stockage objet afin d’avoir une haute disponibilité.

Pourquoi sur Minio ?

Parce que l’usage du stockage local ne permet pas d’avoir une haute disponibilité. En effet, Loki ne supporte pas le clustering avec un stockage local (à moins de bricoler quelque chose avec un stockage NFS mais .. berk ). ⬇️

Running Loki clustered is not possible with the filesystem store unless the filesystem is shared in some fashion (NFS for example). However using shared filesystems is likely going to be a bad experience with Loki just as it is for almost every other application.

Installation de Loki et Promtail

Pour cette primo-installation, je vais passer par le dépôt officiel de Grafana:

mkdir -p /etc/apt/keyrings/
wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor > /etc/apt/keyrings/grafana.gpg
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | tee /etc/apt/sources.list.d/grafana.list

Une fois les dépôts ajoutés, nous pouvons installer Loki et Promtail à coup de apt:

apt update
apt install -y promtail loki

Pour vérifier que Loki soit bien fonctionnel, je peux afficher le /metrics de Loki:

$ curl 192.168.1.73:3100/metrics -s | tail -n 5
ring_member_tokens_owned{name="scheduler"} 1
# HELP ring_member_tokens_to_own The number of tokens to own in the ring.
# TYPE ring_member_tokens_to_own gauge
ring_member_tokens_to_own{name="compactor"} 1
ring_member_tokens_to_own{name="scheduler"} 1

Remarque

Qu’est-ce que le /metrics ?

C’est le chemin utilisé par Prometheus pour récupérer les métriques exposées par Loki. Pour approfondir l’usage de ces données, vous pouvez faire un tour sur mon article sur Prometheus.

Par défaut, Promtail va envoyer les logs dans le fichier /var/log/messages à Loki via l’URL http://localhost:3100/loki/api/v1/push. On peut alors installer logcli, un utilitaire en ligne de commande pour interagir avec Loki:

apt install logcli -y

Une fois installé, nous pouvons afficher les logs envoyés par Promtail:

$ logcli labels job
2024/02/07 07:09:56 http://localhost:3100/loki/api/v1/label/job/values?end=1707286196102901828&start=1707282596102901828

Si, comme moi, vous n’avez qu’une URL qui s’affiche en réponse, c’est que Promtail n’a pas encore envoyé de log à Loki (réponse vide). C’est parce que dans ma configuration, Promtail ne surveille que le fichier /var/log/messages … qui n’existe pas sur mon système. Je vais alors remplacer ce fichier par /var/log/dpkg.log pour avoir des logs à afficher et …

$ logcli labels job
2024/02/07 07:19:26 http://localhost:3100/loki/api/v1/label/job/values?end=1707286766300993936&start=1707283166300993936
varlogs

Victoire ! Promtail a bien envoyé des logs à Loki. Je peux aussi les afficher avec ma première requête de LogQL '{job="varlogs"}' :

$ logcli query '{job="varlogs"}'
2024/02/07 07:21:59 http://localhost:3100/loki/api/v1/query_range?direction=BACKWARD&end=1707286919240803766&limit=30&query=%7Bjob%3D%22varlogs%22%7D&start=1707283319240803766
2024/02/07 07:21:59 Common labels: {filename="/var/log/dpkg.log", job="varlogs"}
2024-02-07T07:18:43+01:00 {} 2024-02-06 19:09:38 status installed logcli:amd64 2.9.4
2024-02-07T07:18:43+01:00 {} 2024-02-06 19:09:38 status half-configured logcli:amd64 2.9.4
2024-02-07T07:18:43+01:00 {} 2024-02-06 19:09:38 status unpacked logcli:amd64 2.9.4
2024-02-07T07:18:43+01:00 {} 2024-02-06 19:09:38 configure logcli:amd64 2.9.4 <none>
2024-02-07T07:18:43+01:00 {} 2024-02-06 19:09:38 startup packages configure
2024-02-07T07:18:43+01:00 {} 2024-02-06 19:09:38 status unpacked logcli:amd64 2.9.4
2024-02-07T07:18:43+01:00 {} 2024-02-06 19:09:38 status half-installed logcli:amd64 2.9.4
2024-02-07T07:18:43+01:00 {} 2024-02-06 19:09:38 install logcli:amd64 <none> 2.9.4
2024-02-07T07:18:43+01:00 {} 2024-02-06 19:09:38 startup archives unpack
2024-02-07T07:18:43+01:00 {} 2024-02-06 18:30:11 status installed promtail:amd64 2.9.4
2024-02-07T07:18:43+01:00 {} 2024-02-06 18:30:09 status half-configured promtail:amd64 2.9.4
2024-02-07T07:18:43+01:00 {} 2024-02-06 18:30:09 status unpacked promtail:amd64 2.9.4
2024-02-07T07:18:43+01:00 {} 2024-02-06 18:30:09 configure promtail:amd64 2.9.4 <none>
2024-02-07T07:18:43+01:00 {} 2024-02-06 18:30:09 status installed loki:amd64 2.9.4
2024-02-07T07:18:43+01:00 {} 2024-02-06 18:30:08 status half-configured loki:amd64 2.9.4
2024-02-07T07:18:43+01:00 {} 2024-02-06 18:30:08 status unpacked loki:amd64 2.9.4

Installer Grafana

Grafana est un outil de visualisation de données open source. Il est souvent utilisé pour visualiser les métriques de Prometheus, mais il peut aussi être utilisé pour visualiser les logs stockés dans Loki.

J’installe Grafana sur un autre serveur que celui hébergeant Loki. Le dépôt de Grafana est le même que celui de Loki.

mkdir -p /etc/apt/keyrings/
wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor > /etc/apt/keyrings/grafana.gpg
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | tee /etc/apt/sources.list.d/grafana.list
apt update
apt install -y grafana
systemctl enable --now grafana-server

Grafana est disponible sur le port :3000 par défaut. Pour y accéder, il suffit de se rendre sur l’URL http://<ip>:3000 et de se connecter avec les identifiants par défaut (admin/admin).

On va ensuite ajouter la source de données Loki dans le menu Configuration > Data Sources > Add data source.

Source Connection

En dehors de l’URI de Loki (qu’est http://loki-01:3100 dans mon cas), je vais laisser les autres champs par défaut.

Via l’interface web de Grafana, on peut aller dans l’onglet Explore pour visualiser les logs stockés dans Loki.

Le formulaire de requête est le Query Builder, il permet de construire (et d’exécuter) des requêtes pour Loki. Je peux, par exemple, sélectionner le job varlogs pour afficher le contenu du fichier /var/log/dpkg.log sur la machine loki.

Le LogQL

Introduction à LogQL

LogQL est un langage de requête conçu spécifiquement pour interroger et filtrer des journaux dans Loki. Il permet aux utilisateurs d’extraire des données pertinentes à partir de grands ensembles de journaux de manière efficace.

Principes de base de LogQL

  1. Sélection des journaux :

    • Utilisez la clause {} pour sélectionner les journaux à partir desquels vous souhaitez extraire des données. Par exemple, {job="nginx"} sélectionne tous les journaux provenant du travail (job) “nginx”. Si plusieurs jobs portent le même nom nginx, vous pouvez rajouter de la précision à votre requête en proposant d’autres labels ( {job="nginx", node="nginx-server-01"} )
  2. Filtrer les journaux :

    • Utilisez les opérateurs logiques tels que ==, !=, =~, !~ pour filtrer les journaux en fonction de critères spécifiques. Par exemple, {job="nginx"} |~ "error" sélectionne les journaux du travail “nginx” qui contiennent le mot “error”.
  3. Agrégation des résultats :

    • Utilisez les fonctions d’agrégation telles que sum(), count(), min(), max(), etc., pour agréger les résultats des journaux sélectionnés. Par exemple, {job="nginx"} |~ "error" | count() comptera le nombre total de journaux contenant le mot “error” provenant du travail “nginx”.
  4. Limitation des résultats :

    • Utilisez la clause | limit X pour limiter le nombre de résultats renvoyés par votre requête. Par exemple, {job="nginx"} |~ "error" | limit 10 renverra les 10 premiers journaux contenant le mot “error” provenant du travail “nginx”.

Avant de continuer plus loin, je vais sécuriser les échanges entre Grafana et Loki en utilisant un certificat.

Chiffrement TLS

Pour sécuriser les échanges entre Grafana et Loki, je vais utiliser un certificat validé par une autorité de certification locale. Dans mon lab, j’utilise mkcert, un outil qui permet de générer des certificats à l’aide d’une autorité de certification que je peux installer sur mes machines.

Installation de MKCert

MKCert est disponible dans les dépôts officiels de la plupart des distributions Linux.

apt install mkcert
apk add mkcert
yum install mkcert
nix-env -i mkcert

Générer une autorité de certification

Pour que les certificats soient considérés comme valides par Grafana/Loki, il faut que l’autorité de certification soit ajoutée au système de confiance (trust-store) de notre hôte.

$ mkcert -install
Created a new local CA 💥
The local CA is now installed in the system trust store! ⚡️

Il est possible de copier notre CA pour l’ajouter sur d’autres machines (pour que les certificats soient considérés comme valides par ces machines).

$ mkcert -CAROOT
/home/quentinj/.local/share/mkcert

La clé publique de cette autorité de certification doit être ajoutée sur la machine Grafana (pour que celle-ci accepte les certificats signés par cette autorité).

$ scp $(mkcert -CAROOT)/rootCA.pem grafana.monitoring.une-pause-cafe.fr:/usr/local/share/ca-certificates/mkcert.crt
$ ssh grafana.monitoring.une-pause-cafe.fr sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
rehash: warning: skipping ca-certificates.crt,it does not contain exactly one certificate or CRL
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.

Générer un certificat pour Loki

Sur mon poste local (ayant généré l’autorité de certification), je vais générer un certificat pour Loki:

mkcert -cert-file loki-01.monitoring.une-pause-cafe.fr.crt -key-file loki-01.monitoring.une-pause-cafe.fr.key loki-01.monitoring.une-pause-cafe.fr loki-01

J’obtiens deux fichiers, loki-01.monitoring.une-pause-cafe.fr.crt et loki-01.monitoring.une-pause-cafe.fr.key, qui sont respectivement le certificat et la clé privée valides pour:

  • loki-01.monitoring.une-pause-cafe.fr
  • loki-01

Sur la machine loki-01, je vais déplacer ces fichiers dans /etc/loki/ssl (que j’ai créé via mkdir -p /etc/loki/ssl):

scp loki-01.monitoring.une-pause-cafe.fr.crt loki-01.monitoring.une-pause-cafe.fr.key loki-01.monitoring.une-pause-cafe.fr:/etc/loki/ssl

Je vais ensuite modifier le fichier de configuration de Loki pour lui indiquer d’utiliser ces certificats :

server:
  http_listen_port: 3100
  http_tls_config: # <---- Ajout de cette section
    cert_file: /etc/loki/ssl/loki-01.monitoring.une-pause-cafe.fr.crt # <---- Certificat
    key_file: /etc/loki/ssl/loki-01.monitoring.une-pause-cafe.fr.key  # <---- Clé privée
  # [...]

Après avoir redémarré Loki, je peux vérifier que le chiffrement est bien en place en utilisant curl:

$ curl -I https://loki-01.monitoring.une-pause-cafe.fr:3100/metrics
HTTP/2 200
content-type: text/plain; version=0.0.4; charset=utf-8
[...]

Pas d’erreur, le chiffrement est bien en place !

⚠️ Ce certificat doit être reconnu par les machines qui vont se connecter à Loki (donc chaque agent Promtail).

J’en profite aussi pour rajouter le https dans l’URL de Loki dans Grafana.

Générer un certificat pour Grafana

Je vais générer un certificat pour Grafana de la même manière que celui pour Loki:

mkcert -cert-file grafana.monitoring.une-pause-cafe.fr.crt -key-file grafana.monitoring.une-pause-cafe.fr.key grafana.monitoring.une-pause-cafe.fr grafana

Je crée un dossier /etc/grafana/ssl sur le serveur Grafana et j’y déplace les fichiers grafana.monitoring.une-pause-cafe.fr.crt et grafana.monitoring.une-pause-cafe.fr.key.

scp grafana.monitoring.une-pause-cafe.fr.crt grafana.monitoring.une-pause-cafe.fr.key grafana.monitoring.une-pause-cafe.fr:/etc/grafana/ssl

Je m’assure que les certificats aient bien les permissions adéquates:

chmod 600 /etc/grafana/ssl/*
chown grafana:grafana /etc/grafana/ssl/*

Je vais ensuite modifier le fichier de configuration de Grafana pour lui indiquer d’utiliser ces certificats (dans /etc/grafana/grafana.ini):

# [...]
[server]
protocol = https
cert_file = /etc/grafana/ssl/grafana.monitoring.une-pause-cafe.fr.crt
cert_key = /etc/grafana/ssl/grafana.monitoring.une-pause-cafe.fr.key
# [... ]

Après un redémarrage, je peux vérifier que le chiffrement est bien en place via mon navigateur :

Grafana accept SSL


Utiliser un S3

Nous avons vu plus haut qu’il était obligatoire d’utiliser un stockage objet pour avoir une haute disponibilité avec Loki. Il est possible de passer par des services cloud comme S3, GCS, ou Azure Blob Storage, mais je vais utiliser Minio pour stocker les logs chez moi. Je vais donc m’installer une instance Minio, un serveur de stockage objet open source compatible avec l’API S3.

La machine sur laquelle je vais installer Minio sera minio.monitoring.une-pause-cafe.fr.

Installation de Minio

L’installation de Minio peut se faire via le fichier .deb disponible sur le site officiel. Je vais télécharger ce fichier et l’installer via dpkg:

wget https://dl.min.io/server/minio/release/linux-amd64/minio_20240206213622.0.0_amd64.deb
dpkg -i minio_20240206213622.0.0_amd64.deb

Minio est installé mais il nous faut encore le configurer. Pour se faire, je vais créer un fichier /etc/default/minio avec le contenu suivant:

MINIO_VOLUMES=/mnt/data
MINIO_ROOT_USER="quentinj"
MINIO_ROOT_PASSWORD="ItsAlwaysTimeForCoffee"
MINIO_OPTS="--console-address :9001"

Je vais aussi créer l’utilisateur minio-user (déjà utilisé dans le service installé via le deb) et lui donner les permissions sur le dossier /mnt/data:

adduser --system --no-create-home --group minio-user
chown minio-user:minio-user /mnt/data

Je démarre ensuite le service Minio :

systemctl enable --now minio

L’adresse de la console est accessible à l’URL http://minio.monitoring.une-pause-cafe.fr:9001. Je vais m’y connecter avec les identifiants quentinj et ItsAlwaysTimeForCoffee.

Avant toute chose, je vais créer un couple de clés Access/Secret key (pour m’authentifier sur l’API).

  • Access key : nxWo1sZ90TLasqUTydCs
  • Secret key : pHV1oz2m3QtYihk2KuetaxF4xGDBUOFpYTxhzWYT

Configurer le TLS pour Minio

Je vais générer un certificat wildcard pour Minio de la même manière que pour Loki et Grafana:

Information

Un wildcard est un certificat qui est valide pour un domaine et tous ses sous-domaines. Dans mon cas, le certificat sera valide pour *.minio.monitoring.une-pause-cafe.fr.

mkcert -cert-file minio.monitoring.une-pause-cafe.fr.crt -key-file minio.monitoring.une-pause-cafe.fr.key minio.monitoring.une-pause-cafe.fr '*.minio.monitoring.une-pause-cafe.fr' minio 

Pourquoi un certificat en wildcard ?

Parce que lorsqu’on utilise l’API S3, on rajoute le nom du bucket comme un sous domaine de l’URL. Par exemple, pour accéder au bucket loki, on utilise l’URL https://loki.minio.monitoring.une-pause-cafe.fr:9000. Si le certificat n’est valide que pour minio.monitoring.une-pause-cafe.fr, le client S3 va afficher une erreur de certificat.


Je crée un dossier /opt/minio/certs sur la machine Minio et j’y déplace les fichiers minio.monitoring.une-pause-cafe.fr.crt et minio.monitoring.une-pause-cafe.fr.key.

scp minio.monitoring.une-pause-cafe.fr.crt minio.monitoring.une-pause-cafe.fr.key minio.monitoring.une-pause-cafe.fr:/opt/minio/certs

Je dois aussi renommer ces fichiers en private.key et public.crt :

mv /opt/minio/certs/minio.monitoring.une-pause-cafe.fr.crt /opt/minio/certs/public.crt
mv /opt/minio/certs/minio.monitoring.une-pause-cafe.fr.key /opt/minio/certs/private.key

Je m’assure que seul l’utilisateur minio-user ait accès à ces fichiers :

chown minio-user:minio-user /opt/minio/certs/*
chmod 600 /opt/minio/certs/*

J’édite le fichier /etc/default/minio pour rajouter le chemin vers les certificats :

MINIO_VOLUMES=/mnt/data
MINIO_ROOT_USER="quentinj"
MINIO_ROOT_PASSWORD="ItsAlwaysTimeForCoffee"
MINIO_OPTS="--console-address :9001 --certs-dir /opt/minio/certs"

Un petit systemctl restart minio et j’accède bien à la console en TLS !

$ curl -I https://minio.monitoring.une-pause-cafe.fr:9001
HTTP/2 200
accept-ranges: bytes
content-security-policy: default-src 'self' 'unsafe-eval' 'unsafe-inline';
content-type: text/html
last-modified: Thu, 08 Feb 2024 18:04:41 GMT
referrer-policy: strict-origin-when-cross-origin
server: MinIO Console
x-content-type-options: nosniff
x-frame-options: DENY
x-xss-protection: 1; mode=block
content-length: 1310

Créer un bucket pour Loki (avec MC)

MC est un client en ligne de commande pour Minio. Il permet de manipuler notre instance Minio de manière simple et efficace. Nous allons l’utiliser pour créer notre bucket pour Loki.

Je l’installe sur mon poste local en téléchargeant le binaire:

wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin

Je vais ensuite configurer MC pour qu’il se connecte à mon instance Minio avec les clés que j’ai générées plus tôt:

$ mc alias set homelab-monitoring/ https://minio.monitoring.une-pause-cafe.fr:9000 nxWo1sZ90TLasqUTydCs "pHV1oz2m3QtYihk2KuetaxF4xGDBUOFpYTxhzWYT"
Added `homelab-monitoring` successfully.

Avertissement

  • ⚠️ Attention, le port utilisé pour mc est le :9000 et non le :9001.
  • Il est aussi possible d’utiliser les mêmes identifiants que ceux utilisés pour l’API S3 (dans mon cas, quentinj et ItsAlwaysTimeForCoffee), mais je vous recommande de créer des identifiants spécifiques pour chaque service.

Je vais ensuite créer un bucket pour Loki:

$ mc mb homelab-monitoring/loki
Bucket created successfully `homelab-monitoring/loki`.

Configurer Loki pour utiliser un S3

Je vais modifier le fichier de configuration de Loki pour lui indiquer d’utiliser Minio comme stockage objet pour la persistance des logs.

auth_enabled: false

server:
  http_listen_port: 3100
  http_tls_config:
    cert_file: /etc/loki/ssl/loki-01.monitoring.une-pause-cafe.fr.crt
    key_file: /etc/loki/ssl/loki-01.monitoring.une-pause-cafe.fr.key 

ingester:
  wal:
    enabled: true
    dir: /tmp/loki/wal
  chunk_idle_period: 5m
  chunk_retain_period: 30s
  chunk_block_size: 262144
  chunk_encoding: snappy

compactor:
  working_directory: /tmp/loki/compactor
  shared_store: s3
  compaction_interval: 5m
  retention_enabled: true

schema_config:
  configs:
  - from: 2020-05-15
    store: boltdb-shipper
    object_store: s3
    schema: v11
    index:
      prefix: index_
      period: 24h

common:
  replication_factor: 1
  path_prefix: /tmp/loki

storage_config:
 boltdb_shipper:
   active_index_directory: /tmp/loki/index
   cache_ttl: 24h
   cache_location: /tmp/loki/index_cache
   resync_interval: 5s
   shared_store: s3
 aws:
   s3: "https://nxWo1sZ90TLasqUTydCs:pHV1oz2m3QtYihk2KuetaxF4xGDBUOFpYTxhzWYT@minio:9000/loki"
   s3forcepathstyle: true
   bucketnames: loki
   region: us-east-1
   access_key_id: "nxWo1sZ90TLasqUTydCs" # Le compactor a besoin de ces variables pour fonctionner
   secret_access_key: "pHV1oz2m3QtYihk2KuetaxF4xGDBUOFpYTxhzWYT"
   endpoint: "https://minio.monitoring.une-pause-cafe.fr:9000"

Avec cette configuration, Loki va stocker ses logs en local et le compactor va les envoyer sur Minio pour les stocker de manière persistante.

Information

Le compactor est un composant de Loki qui est responsable de la compaction des logs. Il est nécessaire pour garder une taille raisonnable de la base de données de Loki. Il se charge aussi de faire de la déduplication des logs pour économiser de l’espace disque.

Interface web de Minio avec les objets

Cluster Loki

Loki supporte le clustering, c’est-à-dire qu’il est possible de lancer plusieurs instances de Loki pour avoir une haute disponibilité. La documentation officielle explique qu’un setup de production se fait dans un Kubernetes, mais je vais essayer de le faire sur mes machines virtuelles.

Les nœuds du cluster vont dialoguer via un Gossip Ring, un protocole de communication permettant à chaque nœud de partager des informations avec les autres.

Information

Comme vu plus haut, il convient d’utiliser un stockage objet pour la persistance des logs, le stockage local ne supportant pas le clustering. Le passage par le stockage objet était donc obligatoire pour la suite de cet article.

Je vais installer Loki sur deux autres machines, loki-02.monitoring.une-pause-cafe.fr et loki-03.monitoring.une-pause-cafe.fr pour avoir un cluster de trois nœuds.

Voici le fichier de configuration que j’ai utilisé pour les nœuds (en dehors des certificats à remplacer) :

auth_enabled: false

server:
  http_listen_port: 3100
  http_tls_config:
    cert_file: /etc/loki/ssl/loki-01.monitoring.une-pause-cafe.fr.crt # <---- Certificat à remplacer
    key_file: /etc/loki/ssl/loki-01.monitoring.une-pause-cafe.fr.key  # <---- Clé privée à remplacer
  grpc_server_max_recv_msg_size: 20971520
  grpc_server_max_send_msg_size: 20971520

distributor:
  ring:
    kvstore:
      store: memberlist

ingester:
  wal:
    enabled: true
    dir: /tmp/loki/wal
  lifecycler:
    ring:
      kvstore:
        store: memberlist
      replication_factor: 1
    final_sleep: 0s
  chunk_idle_period: 5m
  chunk_retain_period: 30s
  chunk_block_size: 262144
  chunk_encoding: snappy

compactor:
  working_directory: /tmp/loki/compactor
  shared_store: s3
  compaction_interval: 5m
  retention_enabled: true

memberlist:
  abort_if_cluster_join_fails: false
  bind_port: 7946
  join_members:
  - loki-01:7946
  - loki-02:7946
  - loki-03:7946
  max_join_backoff: 1m
  max_join_retries: 10
  min_join_backoff: 1s

schema_config:
  configs:
  - from: 2020-05-15
    store: boltdb-shipper
    object_store: s3
    schema: v11
    index:
      prefix: index_
      period: 24h

common:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: memberlist
  replication_factor: 3
  path_prefix: /tmp/loki

storage_config:
 boltdb_shipper:
   active_index_directory: /tmp/loki/index
   cache_ttl: 168h
   cache_location: /tmp/loki/index_cache
   resync_interval: 5s
   shared_store: s3
 aws:
   s3: "https://nxWo1sZ90TLasqUTydCs:pHV1oz2m3QtYihk2KuetaxF4xGDBUOFpYTxhzWYT@minio:9000/loki"
   s3forcepathstyle: true
   bucketnames: loki
   region: us-east-1 # <---- pour Minio, c'est la région par défaut
   access_key_id: "nxWo1sZ90TLasqUTydCs"
   secret_access_key: "pHV1oz2m3QtYihk2KuetaxF4xGDBUOFpYTxhzWYT"
   endpoint: "https://minio.monitoring.une-pause-cafe.fr:9000"

Je vais redémarrer Loki sur les trois machines pour que le clustering soit effectif.

févr. 10 09:38:31 loki-02 loki[2812]: level=info ts=2024-02-10T08:38:31.839709692Z caller=memberlist_client.go:592 msg="joining memberlist cluster succeeded" reached_nodes=2 elapsed_time=8.0811ms

Sur Loki-02, je vois bien que le nœud a rejoint le cluster ⬆️ !


Vous savez comment on peut vérifier l’état du cluster ? Avec du PromQL sur un Prometheus, bien sûr (Puisque c’est de la supervision) !

Voici la configuration que j’ai ajoutée à mon fichier de configuration de Prometheus pour qu’il scrape les métriques de Loki:

  - job_name: loki
    scrape_interval: 5s
    scheme: "https"
    scrape_timeout: 5s
    static_configs:
      - targets: ['loki-01.monitoring.une-pause-cafe.fr:3100', 'loki-02.monitoring.une-pause-cafe.fr:3100', 'loki-03.monitoring.une-pause-cafe.fr:3100']

On peut afficher l’état du cluster avec la requête suivante:

{__name__=~"loki_memberlist_.*cluster.*"}

Métriques

(un node_health à 0 veut dire que le nœud est Healthy.)

Mais concrètement, qu’est-ce que ça change pour le moment ? Promtail envoie toujours ses logs à Loki-01, qui les stocke sur Minio.

La prochaine étape est d’installer une VIP (Virtual IP) pour que Promtail envoie ses logs à la VIP, qui les répartira sur un des nœuds du cluster. Mais avant ça, nous pouvons d’ores et déjà vérifier du bon fonctionnement du clustering en redirigeant les requêtes des Promtails vers loki-02, puis interrogeant loki-01 et loki-02 pour voir si les logs sont bien accessibles sur les deux nœuds.

$ logcli query '{job="nginx"} |= `UneTasseDeCafe`' --addr="https://loki-01.monitoring.une-pause-cafe.fr:3100"
2024/02/13 18:22:34 https://loki-01.monitoring.une-pause-cafe.fr:3100/loki/api/v1/query_range?direction=BACKWARD&end=1707844954515007056&limit=30&query=%7Bjob%3D%22nginx%22%7D+%7C%3D+%60UneTasseDeCafe%60&start=1707841354515007056
2024/02/13 18:22:34 Common labels: {filename="/var/log/nginx/access.log", job="nginx", node="grafana"}
2024-02-13T18:18:48+01:00 {} 100.64.0.13 - - [13/Feb/2024:18:18:48 +0100] "GET /UneTasseDeCafe HTTP/1.1" 404 125 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0"
$ logcli query '{job="nginx"} |= `UneTasseDeCafe`' --addr="https://loki-02.monitoring.une-pause-cafe.fr:3100"
2024/02/13 18:22:38 https://loki-02.monitoring.une-pause-cafe.fr:3100/loki/api/v1/query_range?direction=BACKWARD&end=1707844958725211857&limit=30&query=%7Bjob%3D%22nginx%22%7D+%7C%3D+%60UneTasseDeCafe%60&start=1707841358725211857
2024/02/13 18:22:39 Common labels: {filename="/var/log/nginx/access.log", job="nginx", node="grafana"}
2024-02-13T18:18:48+01:00 {} 100.64.0.13 - - [13/Feb/2024:18:18:48 +0100] "GET /UneTasseDeCafe HTTP/1.1" 404 125 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0"

Les mêmes données sont bien présentes sur les deux nœuds du cluster !

Maintenant, passons à la mise en place de la VIP.

Mise en place de la VIP

La VIP est un point d’entrée unique pour les clients. Elle va répartir les requêtes entrantes sur un des nœuds fonctionnels du cluster. J’utilise keepalived pour mettre en place la VIP.

Sur chacun des nœuds du cluster, je vais installer keepalived:

apt install keepalived -y

Je vais ensuite créer un fichier /etc/keepalived/keepalived.conf sur loki-01 avec le contenu suivant:

vrrp_instance VI_1 {
        state MASTER
        interface ens18
        virtual_router_id 51
        priority 255
        advert_int 1
        authentication {
              auth_type PASS
              auth_pass CoffeeAddict
        }
        virtual_ipaddress {
              192.168.1.5/24
        }
}

Avertissement

⚠️ Attention, l’interface ens18 est spécifique à ma machine. Vous devez remplacer ens18 par l’interface réseau de votre machine.

L’usage du auth_pass CoffeeAddict est aussi un exemple, vous devez le remplacer par un mot de passe sécurisé (et le même sur tous les nœuds) ou utiliser un autre mode d’authentification.

Sur les autres nœuds, je vais créer ce même fichier, mais avec state BACKUP et une priorité plus faible.

vrrp_instance VI_1 {
        state BACKUP
        interface ens18
        virtual_router_id 51
        priority 254
        advert_int 1
        authentication {
              auth_type PASS
              auth_pass CoffeeAddict
        }
        virtual_ipaddress {
              192.168.1.5/24
        }
}

Je vais ensuite démarrer le service keepalived sur les trois machines :

systemctl enable --now keepalived

l’IP 192.168.1.5 est maintenant redirigée vers loki-01. Si ce nœud tombe, la VIP sera redirigée vers une autre machine du cluster.

Je vais créer l’entrée DNS loki.monitoring.une-pause-cafe.fr pour pointer vers cette VIP et créer le certificat pour cette adresse.

$ mkcert -cert-file loki.monitoring.une-pause-cafe.fr.crt -key-file loki.monitoring.une-pause-cafe.fr.key loki.monitoring.une-pause-cafe.fr loki

Je vais ensuite déplacer ces fichiers dans /etc/loki/ssl sur loki-01 et les copier sur les autres nœuds.

scp loki.monitoring.une-pause-cafe.fr.crt loki.monitoring.une-pause-cafe.fr.key loki-01.monitoring.une-pause-cafe.fr:/etc/loki/ssl
scp loki.monitoring.une-pause-cafe.fr.crt loki.monitoring.une-pause-cafe.fr.key loki-02.monitoring.une-pause-cafe.fr:/etc/loki/ssl
scp loki.monitoring.une-pause-cafe.fr.crt loki.monitoring.une-pause-cafe.fr.key loki-03.monitoring.une-pause-cafe.fr:/etc/loki/ssl
ssh loki-01.monitoring.une-pause-cafe.fr "chown loki /etc/loki/ssl/*"
ssh loki-02.monitoring.une-pause-cafe.fr "chown loki /etc/loki/ssl/*"
ssh loki-03.monitoring.une-pause-cafe.fr "chown loki /etc/loki/ssl/*"

Je vais ensuite modifier le fichier de configuration de Loki pour lui indiquer d’utiliser ces certificats :

ssh loki-01.monitoring.une-pause-cafe.fr "sed -i 's/loki-0[0-9].monitoring.une-pause-cafe.fr/loki.monitoring.une-pause-cafe.fr/g' /etc/loki/config.yml"
ssh loki-02.monitoring.une-pause-cafe.fr "sed -i 's/loki-0[0-9].monitoring.une-pause-cafe.fr/loki.monitoring.une-pause-cafe.fr/g' /etc/loki/config.yml"
ssh loki-03.monitoring.une-pause-cafe.fr "sed -i 's/loki-0[0-9].monitoring.une-pause-cafe.fr/loki.monitoring.une-pause-cafe.fr/g' /etc/loki/config.yml"

Je vais redémarrer le service Loki sur les trois machines pour que les changements soient pris en compte.

ssh loki-01.monitoring.une-pause-cafe.fr "systemctl restart loki"
ssh loki-02.monitoring.une-pause-cafe.fr "systemctl restart loki"
ssh loki-03.monitoring.une-pause-cafe.fr "systemctl restart loki"

Je vais ensuite modifier le fichier de configuration de Promtail pour lui indiquer d’envoyer ses logs à loki.monitoring.une-pause-cafe.fr:

clients:
- url: https://loki.monitoring.une-pause-cafe.fr:3100/loki/api/v1/push
# [...]

Sur Grafana, je modifie la source de données Loki pour lui indiquer d’utiliser loki.monitoring.une-pause-cafe.fr comme URL.

Changement addr loki

Une fois que tout est en place, je vais vérifier que les logs sont bien envoyés à loki.monitoring.une-pause-cafe.fr et que les requêtes sont bien réparties sur les trois nœuds du cluster.

$ logcli query '{job="nginx"} |= `UneTasseDeCafe`' --addr="https://loki.monitoring.une-pause-cafe.fr:3100"
# [...] 
2024/02/13 21:27:51 Common labels: {filename="/var/log/nginx/access.log", job="nginx", node="grafana"}
2024-02-13T21:27:48+01:00 {} 100.64.0.1 - - [13/Feb/2024:21:27:48 +0100] "GET /?UneTasseDeCafe HTTP/1.1" 200 409 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0"
2024/02/13 21:27:51 https://loki.monitoring.une-pause-cafe.fr:3100/loki/api/v1/query_range?direction=BACKWARD&end=1707856068916047554&limit=30&query=%7Bjob%3D%22nginx%22%7D+%7C%3D+%60UneTasseDeCafe%60&start=1707852471375877575

Conclusion

Nous avons vu comment mettre en place un cluster Loki avec un stockage objet, une VIP et un chiffrement TLS. Nous avons aussi vu comment superviser notre cluster avec Prometheus.

J’ai eu de grosses difficultés à mettre en place le clustering de Loki, principalement à cause de la documentation qui n’est pas très claire sur le sujet et qui est très orientée Kubernetes. J’ai un peu tâtonné pour trouver la bonne configuration.

Mine de rien, Loki est un outil beaucoup plus puissant que ce que je pensais, la documentation présente des paramètres qui peuvent vraiment être fine-grained en fonction de nos besoins. Je suis content de ce lab, j’ai appris beaucoup de choses sur Loki que j’espère pouvoir réutiliser dans le futur.

Je m’attaquerai peut-être à une installation de VictoriaLogs, une alternative à Loki qui est plus orientée performance (Elastic a aussi une solution similaire, mais j’ai peur des ressources demandés dans une configuration ‘minimale’).

En espérant que ce lab vous ait plu, je vous dis à bientôt pour un prochain article !