Aller au contenu

Reprenez le contrôle de vos logs avec Loki

·13 mins
observabilité loki alloy promtail grafana conteneur kubernetes cncf helm prometheus logs
Romain Boulanger
Auteur
Romain Boulanger
Architecte Infra/Cloud avec une touche de DevSecOps
Sommaire

Mise à jour de l’article :

3 mars 2025 : À la suite de l’annonce de la dépréciation de Promtail, l’article a été modifié pour utiliser Alloy, son successeur.

Préambule
#

L’observabilité est un élément fondamental pour assurer la fiabilité et les performances des applications déployées, particulièrement pour comprendre leurs comportements et identifier les problèmes rencontrés.

Cela devient incontournable dans un monde conteneurisé où il y a différentes couches à surveiller, notamment de l’application à l’infrastructure qui héberge, pourquoi pas, une solution d’orchestration comme Kubernetes.

L’observabilité, je vous en ai déjà parlé sur ce blog à travers l’utilisation de Prometheus pour la partie sur le monitoring et notamment la récolte des métriques et la définition d’alertes.

Oui mais qu’est-ce qu’il y a derrière de ce mot “observabilité” ?

Oui mais c’est quoi l’observabilité ?
#

L’observabilité repose sur trois piliers fondamentaux : les métriques, les logs et les traces :

  • Les métriques représentent des indicateurs en fonction d’une période de temps. Par exemple : le CPU ou le nombre de requête avec code http 5xx sur les 10 dernières minutes ;
  • Les logs modélisent l’activité de l’application sous forme de journaux horodatés ;
  • Les traces, dernier pilier, permettent de suivre le parcours d’une requête à travers différentes briques applicatives ou non.

On a souvent tendance à négliger l’écriture des logs, ces derniers sont bien plus que de simples messages textuels. Ils sont les témoins de chaque événement ou erreur qui se produit dans une application ou infrastructure.

Bien évidemment cela dépend de comment les développeurs de l’application ou l’outil ont construit ces journaux. Plusieurs points peuvent être évoqués :

  • Penser à la personne qui devra opérer l’application, l’outil voire l’infrastructure pour construire des messages clairs et explicites ;
  • Avoir différents niveaux : INFO, WARNING, DEBUG, ou autres, avec la possibilité de sélectionner le bon niveau sans surconsommer du stockage inutile
  • Éviter de stocker des informations sensibles comme des numéros de carte de crédit ou identifiant de carte d’identité.

Cette liste n’est pas exhaustive bien évidemment, mais la création de logs représente un enjeu majeur pour étudier le comportement d’un système, encore plus dans un monde conteneurisé où la nature distribuée et éphémère de ces derniers pose de nombreux défis.

Kubernetes et les logs : un enjeu majeur
#

L’orchestration de conteneurs avec Kubernetes représente aujourd’hui un standard pour déployer et gérer les applications. Cependant la partie log peut rapidement devenir complexe.

En effet, ces précieux logs peuvent provenir de différentes sources :

  • Les conteneurs : Pièce indispensable dans cette approche, chaque conteneur génère ses propres journaux, reflétant l’activité de l’application qu’il héberge ;

  • Les Pods ou autres objets Kubernetes : Un niveau au-dessus des conteneurs, les Pods peuvent avoir des logs spécifiques liés à leur cycle de vie, cela est accessible via la commande kubectl get events par exemple ou via kubectl describe po ... ;

  • Les composants du/des Controlplane : Les composants comme l’API Server, le Scheduler ou encore le Controller Manager génèrent des logs cruciaux pour comprendre l’état du cluster et les actions entreprises par ce dernier ;

  • Les nœuds du cluster : En d’autres termes, les machines physiques ou virtuelles qui composent le cluster, ces dernières produisent également des logs système notamment à travers les composants installés comme le kubelet accessible via systemd.

Comme dit plus haut, les conteneurs sont très éphémères et Kubernetes rajoute une couche de complexité par rapport à la mise l’échelle des conteneurs ou le fait de garder une application hautement disponible en répartissant différents conteneurs du même type sur plusieurs machines, par exemple.

Un Pod peut être créé, détruit et recréé en quelques secondes, emportant avec lui l’ensemble de ses logs s’ils ne sont pas correctement collectés et centralisés.

Le deuxième défi est forcément le stockage et l’indexation sans venir faire grossir de manière exponentielle l’espace disque ou venir baisser drastiquement les performances.

C’est pourquoi le choix d’un outil reprenant ces caractéristiques est une étape cruciale. Il est donc temps d’introduire Loki !

Loki
#

À l’aventure avec Loki
#

Loki de Grafana Labs se décrit comme une solution performante pour gérer les logs. Il est d’ailleurs fait pour être hautement disponible et multi-tenant tout en s’inspirant de Prometheus qui est un standard dans la gestion de métriques.

En effet, Loki propose un langage de requêtes de log s’appelant LogQL très similaire à PromQL, il offre un système pour définir des alertes et enfin, cerise sur le gâteau, il s’intègre parfaitement de manière native avec Grafana pour fournir une interface graphique.

Il est d’ailleurs référencé au sein de la Cloud Native Computing Foundation (CNCF) dans la grande famille de l’observabilité.

La CNCF avec Loki

L’observabilité au sein de la CNCF en février 2025

Traditionnellement, les outils de gestion de logs classiques, par exemple Elastic Stack, indexent le contenu intégral des messages, ce qui permet des recherches textuelles rapides mais au prix d’une consommation importante de ressources, notamment le CPU.

C’est pourquoi, Loki révolutionne le principe en indexant uniquement les métadonnées des logs sous forme de labels, plutôt que le contenu même, ce qui n’est pas à négliger.

Les labels et Kubernetes c’est une histoire d’amour, vous pourrez donc retrouver les labels personnalisés de vos objets directement au sein de Loki mais aussi d’autres informations très utiles :

  • Le namespace
  • Le nom du Pod
  • Le nom du conteneur
  • Le nom du nœud
  • Le niveau de log
  • La sortie capturée (stdout, stderr, etc.)

Exemple de log avec Loki

Exemple de log récupéré par Loki

Loki présente différents mode d’installation, mais il a été créé pour être nativement facilement mis à l’échelle grâce à une architecture très décomposée.

Architecture
#

L’architecture pour mettre en place une plateforme de logs avec Loki s’appuie sur trois composants principaux :

  • Alloy est l’agent qui collecte les logs tout en attachant des labels pour les envoyer vers Loki ;

  • Loki centralise le stockage et l’indexation. Cette dernière est basée sur les labels. Il embarque avec lui de manière optionnelle un stockage objet du nom de Minio pour gérer le stockage à grande échelle. Il est possible de sélectionner d’autres types de stockage comme AWS S3, Google Cloud Storage ou encore Azure Blob Storage ;

  • Grafana : L’outil de visualisation par excellence. Il dispose d’une très grande base communautaire avec des tableaux de bords prêts à l’emploi.

En ce qui concerne Loki, il est séparé de plusieurs composants :

  • Distributor est chargé de traiter les demandes de push entrantes des clients ;
  • Ingester est responsable de la persistance des données et de leur transfert vers un stockage à long terme ;
  • Query Frontend est un service optionnel qui fournit les points d’extrémité de l’API du composant Querier pour accélérer la lecture ;
  • Querier est responsable de l’exécution des requêtes Log Query Language (LogQL) ;
  • Index Gateway est chargé de traiter et de servir les requêtes de métadonnées. Le format recommandé de ce dernier est TSDB (Time Series Database), le même que celui de Prometheus ;
  • Compactor est utilisé pour compacter les multiples fichiers d’index produits par l’Ingester et expédiés vers le stockage objet avec un fichier d’index par jour ;
  • Ruler gère et évalue les expressions de règles et/ou d’alertes définies.

Si vous désirez en savoir plus sur les caractéristiques techniques, je vous recommande de consulter cet excellent article au sein de la documentation officielle.

Les modes d’installation
#

Loki présente différents mode d’installation pour s’adapter à la complexité de votre cas d’utilisation mais aussi vous donner la flexibilité désirée pour répondre aux problématiques de scalabilité :

  • Single Binary : Très intéressant pour les environnements qui ne demandent pas de haute disponibilité, avec un maximum de quelques dizaines de Go par jour. Tous les composants sont contenus dans un seul et même binaire ;

  • Distributed : Utile pour les déploiements à grande échelle, Loki est décomposé en micro-services (ingester, querier, distributor, etc.), permettant une scalabilité fine et de fournir une haute disponibilité. Ce mode est réservé pour les très gros volumes de logs allant jusque 1 To par jour ;

  • Simple Scalable : Le meilleur des deux mondes entre les installations précédentes, Loki est représenté sous trois “gros” composants : read, write, et backend facile à gérer tout en reprenant les caractéristiques du mode Distributed en termes de disponibilité et d’ingestion de logs.

Il est évident que ce choix dépend de votre besoin, même si le mode Simple Scalable semble répondre à toutes les attentes en mettant en avant les forces de Loki définies plus haut.

Pour en savoir plus, vous pouvez consulter les différents schémas pour chaque mode à cette adresse.

Il est temps de passer à l’action !

À vos claviers !
#

Pour installer le tout, rien de mieux qu’un dépôt Git prêt à l’emploi !

axinorm/logs-with-loki

Set up and configure Loki with Promtail and Grafana to analyse logs in your Kubernetes cluster.

null
0
0

N’hésitez donc pas à télécharger le contenu mais aussi à modifier les valeurs des trois fichiers values qui serviront à installer Loki, Alloy et enfin Grafana.

Loki
#

Première étape, Loki, il faut maintenant faire un choix au niveau de la méthode d’installation. Pour ma part, j’ai pris SimpleScalable car c’est un bon compromis pour exploiter les forces de Loki au besoin.

deploymentMode: SimpleScalable

Pour ce mode, Loki demande un stockage objet, c’est pourquoi Minio rentre en scène via un sous-chart que l’on peut activer :

# -- Configuration for the minio subchart
minio:
  enabled: true

Pour ceux qui ne connaissent pas cet outil, Minio est un stockage objet reprenant les caractéristiques de S3 d’AWS qui permet de bénéficier d’une mise à l’échelle simple et efficace que ce soit sur des infrastructures on-premise ou sur le Cloud.

Sans oublier de définir le schéma de configuration qui permettra à Loki de stocker les données :

loki:
[...]
  schemaConfig:
    configs:
      - from: 2025-02-01
        object_store: s3
        store: tsdb
        schema: v13
        index:
          prefix: index_
          period: 24h

Il est également possible de spécifier le nombre de replicas pour chacun des trois composants du mode SimpleScalable :

# Configuration for the write pod(s)
write:
  # -- Number of replicas for the write
  replicas: 1
# --  Configuration for the read pod(s)
read:
  # -- Number of replicas for the read
  replicas: 1
# --  Configuration for the backend pod(s)
backend:
  # -- Number of replicas for the backend
  replicas: 1

Dans un objectif de démonstration, j’ai mis uniquement une seule replicas, mais vous pouvez très bien adapter cela pour vos besoins surtout dans un contexte de haute disponibilité.

Le fichier de Loki est à présent configuré, il reste à le déployer. Avant de commencer, n’oubliez pas de configurer la source pour récupérer les charts de Grafana :

helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

Puis de procéder à l’installation :

helm install loki grafana/loki --version 6.27.0 --namespace observability --create-namespace --values ./values-loki.yaml

Quelques secondes plus tard…

$ kubectl -n observability get po
NAME                          READY   STATUS    RESTARTS   AGE
loki-backend-0                2/2     Running   0          110s
loki-canary-688x8             1/1     Running   0          110s
loki-chunks-cache-0           2/2     Running   0          110s
loki-gateway-c589f7fd-zskwb   1/1     Running   0          110s
loki-minio-0                  1/1     Running   0          110s
loki-read-64787b5cff-6c4b8    1/1     Running   0          110s
loki-results-cache-0          2/2     Running   0          110s
loki-write-0                  1/1     Running   0          110s

Ça semble pas mal, on peut donc avancer !

Alloy
#

Alloy, le collecteur de logs se configure très simplement :

alloy:
  # -- Overrides the chart's computed fullname. Used to change the full prefix of
  # resource names.
  fullnameOverride: "alloy"

  ## Various Alloy settings. For backwards compatibility with the grafana-agent
  ## chart, this field may also be called "agent". Naming this field "agent" is
  ## deprecated and will be removed in a future release.
  alloy:
    configMap:
      # -- Create a new ConfigMap for the config file.
      create: true
      # -- Content to assign to the new ConfigMap.  This is passed into `tpl` allowing for templating from values.
      content: |-
        [...]
        loki.write "default" {
          endpoint {
            url       = "http://loki-gateway/loki/api/v1/push"
            tenant_id = "1"
          }
          external_labels = {}
        }

    mounts:
      # -- Mount /var/log from the host into the container for log collection.
      varlog: true

Il impératif de lui préciser la gateway pour pousser les logs au sein de Loki avec le tenant_id car oui, par défaut Loki est multi-tenant et procure une isolation des données.

Vous pouvez très bien désactiver ce comportement en spécifiant l’attribut auth_enabled: false au sein de Loki et venir supprimer cette information dans le fichier ci-dessus.

Au sein de la configMap, la configuration peut être éditée pour ajouter, remplacer ou supprimer des labels. Pour ma part, j’ai transformé la configuration de Promtail venant de mon ancienne installation. Si vous voulez en savoir plus, ce lien vous explique comment faire.

Dernier point, n’oubliez pas de monter le répertoire qui contient vos logs (mounts.varlog) sous peine de ne rien avoir à collecter.

Allez ! c’est le moment de déployer Alloy :

helm install alloy grafana/alloy --version 0.12.1 --namespace observability --create-namespace --values ./values-alloy.yaml

Alloy fonctionne en mode DaemonSet, assez logique pour récupérer les logs de l’ensemble des nœuds du cluster. Dans mon cas, j’ai deux Pods pour deux nœuds :

$ kubectl -n observability get po
NAME                          READY   STATUS    RESTARTS   AGE
[...]
alloy-jhlvq                   1/1     Running   0          94s
alloy-vsbzb                   1/1     Running   0          94s

Grafana
#

Reste la partie visualisation, peut-être la plus importante pour donner du sens à cet article mais surtout de consulter et visualiser ces précieux journaux !

Grafana a besoin d’une source de données (datasources) pour lire le contenu de Loki :

## Configure grafana datasources
## ref: http://docs.grafana.org/administration/provisioning/#datasources
##
datasources:
  datasources.yaml:
    apiVersion: 1
    datasources:
    # Loki DataSource
    - name: Loki
      uid: loki
      type: loki
      url: http://loki-read:3100/
      access: proxy
      orgId: 1
      jsonData:
        httpHeaderName1: 'X-Scope-OrgID'
      secureJsonData:
        httpHeaderValue1: '1'

On vient donc ajouter Loki en spécifiant le service loki-read pour récupérer le contenu que l’on a besoin. Sans oublier les entêtes HTTP pour la configuration multi-tenant de Loki.

Deuxième étape, récupérer un tableau de bord (ou plusieurs) de la communauté disponible sur le site de Grafana.

Simple et efficace, j’ai choisi celui-ci. Il permet de filtrer sur le namespace, conteneur et stream avec la possibilité de faire une requête personnalisée.

Pour cela, il faut définir un endroit dans Grafana pour placer ce tableau de bord dans un dossier :

# Configure grafana dashboard providers
## ref: http://docs.grafana.org/administration/provisioning/#dashboards
##
## `path` must be /var/lib/grafana/dashboards/<provider_name>
##
dashboardProviders:
 dashboardproviders.yaml:
   apiVersion: 1
   providers:
   - name: 'Logs'
     orgId: 1
     folder: 'Logs'
     type: file
     disableDeletion: false
     editable: true
     options:
       path: /var/lib/grafana/dashboards/logs

Et surtout demander à Grafana de récupérer ce dernier avec l’ID et la révision associée :

## Configure grafana dashboard to import
## NOTE: To use dashboards you must also enable/configure dashboardProviders
## ref: https://grafana.com/dashboards
##
## dashboards per provider, use provider name as key.
##
dashboards:
  logs:
    loki:
      gnetId: 15141
      revision: 1
      datasource: Loki

Pour des questions de simplicité, j’ai choisi de désactiver l’authentification :

## Grafana's primary configuration
## NOTE: values in map will be converted to ini format
## ref: http://docs.grafana.org/installation/configuration/
##
grafana.ini:
  auth.anonymous:
    enabled: true
    org_role: Admin
  auth:
    disable_login_form: true

Bien, bien ! il est temps de déployer cette dernière pièce pour avoir une plateforme de log fonctionnelle :

helm install grafana grafana/grafana --version 8.10.1 --namespace observability --create-namespace --values ./values-grafana.yaml

Tout semble bon, là aussi :

kubectl -n observability get po
NAME                          READY   STATUS    RESTARTS   AGE
grafana-7dccdbb78-2bbjm       1/1     Running   0          60s

Consulter les logs
#

Si comme moi, vous avez configuré Grafana sans Ingress, le plus simple est de faire port-forward pour y accéder :

kubectl -n observability port-forward svc/grafana 8080:80

Une fois dans votre navigateur, connectez-vous sur localhost:8080 et allez dans Dashboards > Logs > Loki Kubernetes Logs.

Et tada ! Vous retrouvez le tableau de bord configuré plus haut avec la possibilité de consulter vos logs !

Loki avec Grafana

Vue depuis Grafana

Le mot de la fin
#

Loki, accompagné d’Alloy et Grafana sont les compagnons idéaux pour afficher et collecter les journaux nécessaires à l’étude des comportements au sein de Kubernetes.

Simple à déployer et configurable à souhait, Loki est une solution fiable et robuste qui s’intègre parfaitement à l’écosystème Cloud Native dédié à l’observabilité initié par Prometheus.

Je vous propose de se retrouver le mois prochain pour un article sur le dernier pilier de l’observabilité : les traces !

Articles connexes

Surveiller votre cluster Kubernetes et vos applications avec kube-prometheus-stack
·8 mins
kubernetes monitoring prometheus alertmanager grafana k3s cncf
Récapitulatif de fin d'année 2023
·4 mins
récapitulatif 2023 googlecloud cloud architect terraform gitlab ci kubecon aws network voxxed luxembourg childpipeline runner cks sécurité kubernetes observability prometheus gitops flamingo
Mettre à l'échelle facilement sur Kubernetes avec Keda
·7 mins
kubernetes keda yaml rabbitmq scale cron helm