Aller au contenu

VictoriaMetrics et VictoriaLogs dans Kubernetes et Grafana

VictoriaMetrics et VictoriaLogs viennent de la même équipe et partagent la même philosophie : un binaire simple, peu gourmand en CPU, RAM et disque, et une installation identique dans Kubernetes. Je te propose donc de monter les deux ensemble : VictoriaMetrics pour tes métriques, VictoriaLogs pour tes logs, déployés en as code avec Terraform et Argo CD, puis branchés sur Grafana.

Présentation des deux briques

VictoriaMetrics

VictoriaMetrics est une base de données time series open source, compatible avec l'écosystème Prometheus : elle reçoit les métriques en remote_write, se requête en PromQL et dans son extension MetricsQL, avec une consommation CPU/RAM et disque réduite. Pour lui envoyer tes métriques, vois Vector ou Prometheus en mode Agent.

VictoriaLogs

VictoriaLogs est la base de données de logs open source de la même équipe. Elle stocke tes logs avec très peu de ressources, se requête avec son langage LogsQL, et ingère les logs via de nombreux protocoles (Elasticsearch bulk, Loki push, OpenTelemetry, syslog...). Côté collecte, tu peux par exemple lui envoyer tes logs avec Vector.

En un coup d'œil

VictoriaMetrics (single) VictoriaLogs (single)
Données Métriques (time series) Logs
Port HTTP par défaut 8428 9428
Langage de requête PromQL / MetricsQL LogsQL
Ingestion remote_write sur /api/v1/write, et d'autres formats /insert/elasticsearch/_bulk, /insert/loki/api/v1/push, /insert/opentelemetry/v1/logs, /insert/jsonline...
Interface web intégrée vmui vmui (sous /select/vmui)
Chart Helm victoria-metrics-single victoria-logs-single
Plugin Grafana victoriametrics-metrics-datasource victoriametrics-logs-datasource

Je te montre une stack complète (Vector, kube-state-metrics, VictoriaMetrics et Grafana) dans cet article.

Single ou cluster ?

Les deux outils existent en version single (un seul binaire qui fait tout) et en version cluster (composants séparés pour l'ingestion, la requête et le stockage, qui passent à l'échelle horizontalement). On part ici sur le mode single : il couvre la grande majorité des besoins et reste beaucoup plus simple à opérer.

Le mode single n'est pas hautement disponible

Une instance single, c'est un seul pod et un seul volume. Pendant un redémarrage ou une maintenance du nœud, l'ingestion et les requêtes sont coupées. Si tu ne peux pas te le permettre, ou si une seule machine ne suffit plus, c'est le moment de regarder la version cluster.

Pré-requis : le provider Terraform Argo CD

Pour la suite du cours je vais te donner l'ensemble du code réalisé avec Terraform. Cela va nous permettre d'amener résilience et robustesse : un seul terraform apply et on retrouve notre cluster en bonne santé :)

Utilisant déjà Argo CD comme outil pour déployer nos Helm charts, on va utiliser son provider Terraform. Je te montre la configuration que j'utilise, sachant que je suis sur minikube avec un Argo CD non exposé au net, donc juste appelable en local.

Ici tu peux utiliser soit le traditionnel username:password, soit un auth_token généré par Argo CD avec ton compte admin. On mentionne en contexte le nom de notre cluster, ainsi que le namespace où est installé notre argocd-server.

terraform {
  required_providers {
    argocd = {
      source  = "argoproj-labs/argocd"
      version = "7.17.2"
    }
  }
}

variable "argocd_admin_password" {
  type      = string
  sensitive = true
}

provider "argocd" {
  #auth_token                  = ".bjphcGlLZXkiLCJuYmYiOjE3MTQ"
  username                    = "admin"
  password                    = var.argocd_admin_password # ne jamais commiter de mot de passe en clair
  port_forward_with_namespace = "argocd"
  kubernetes {
    config_context = "minikube"
  }
}

Note

Renseigne la variable argocd_admin_password (par exemple via TF_VAR_argocd_admin_password) avec ton mot de passe, qui est dans le secret de ton cluster sous le nom de argocd-initial-admin-secret.

Déployer VictoriaMetrics

On déclare une Application Argo CD qui pointe sur le chart victoria-metrics-single :

resource "argocd_application" "victoria_metrics" {
  metadata {
    name      = "victoria-metrics"
    namespace = "argocd"
  }

  spec {

    destination {
      server    = "https://kubernetes.default.svc"
      namespace = "argocd"
    }

    sync_policy {
      automated {
        prune       = false
        self_heal   = true
        allow_empty = true
      }

      sync_options = ["Validate=true"]
      retry {
        limit = "5"
        backoff {
          duration     = "30s"
          max_duration = "2m"
          factor       = "2"
        }
      }
    }

    source {
      repo_url        = "https://victoriametrics.github.io/helm-charts"
      chart           = "victoria-metrics-single"
      target_revision = "0.49.0"

      helm {
        release_name = "victoria-metrics"
        values = yamlencode({
            # CUSTOM VALUES A AJOUTER
        })
      }
    }
  }
}

Le bloc values est vide pour garder l'exemple lisible, mais en pratique tu vas au minimum fixer la rétention et la taille du volume. Par défaut, le chart garde un mois de données (retentionPeriod: 1, l'unité par défaut étant le mois) :

1
2
3
4
5
6
7
8
9
values = yamlencode({
  server = {
    retentionPeriod = "30d" # unités h, d, w, y ; sans unité = mois ; minimum 24h
    persistentVolume = {
      enabled = true
      size    = "20Gi"      # à dimensionner selon ton volume de séries et ta rétention
    }
  }
})

Déployer VictoriaLogs

Même principe pour VictoriaLogs, avec le chart victoria-logs-single :

resource "argocd_application" "victoria_logs" {
  metadata {
    name      = "victoria-logs"
    namespace = "argocd"
  }

  spec {

    destination {
      server    = "https://kubernetes.default.svc"
      namespace = "argocd"
    }

    sync_policy {
      automated {
        prune       = false
        self_heal   = true
        allow_empty = true
      }

      sync_options = ["Validate=true"]
      retry {
        limit = "5"
        backoff {
          duration     = "30s"
          max_duration = "2m"
          factor       = "2"
        }
      }
    }

    source {
      repo_url        = "https://victoriametrics.github.io/helm-charts"
      chart           = "victoria-logs-single"
      target_revision = "0.13.10"

      helm {
        release_name = "victoria-logs"
        values = yamlencode({
            # CUSTOM VALUES A AJOUTER
        })
      }
    }
  }
}

Pour les logs, la rétention se règle en temps, mais aussi en espace disque, ce qui évite de saturer le volume quand une application se met à loguer beaucoup plus que prévu :

values = yamlencode({
  server = {
    retentionPeriod              = "14d"
    retentionMaxDiskUsagePercent = 80 # supprime les plus anciennes données au-delà de 80 % du disque
    persistentVolume = {
      enabled = true
      size    = "50Gi"
    }
  }
})

Namespace dédié

Pour simplifier, les deux applications sont déployées ici dans le namespace argocd. En production, dédie-leur un namespace (monitoring par exemple) : tu sépares les droits RBAC, les quotas et les NetworkPolicies de ton outil de déploiement et de ta stack d'observabilité. Pense alors à adapter les URLs des data sources plus bas.

Envoyer des données

Une fois les deux bases en place, il faut les alimenter :

  • Métriques : tout composant compatible Prometheus remote_write peut écrire dans VictoriaMetrics, par exemple :

    remote_write:
      - url: http://victoria-metrics-victoria-metrics-single-server.argocd.svc.cluster.local:8428/api/v1/write
    
  • Logs : choisis l'endpoint qui correspond à ton collecteur. Vector peut écrire en protocole Elasticsearch bulk, Grafana Alloy en protocole Loki, et un OpenTelemetry Collector en OTLP sur /insert/opentelemetry/v1/logs.

Pour vérifier rapidement que des logs arrivent, interroge directement l'API de VictoriaLogs (après un kubectl port-forward sur le port 9428) :

curl http://localhost:9428/select/logsql/query -d 'query=_time:5m error'

Attention aux stream fields

VictoriaLogs regroupe les logs par stream, identifié par un petit ensemble de champs (par exemple namespace, pod, container). Ces champs doivent avoir une faible cardinalité : n'y mets jamais un trace_id, un user_id ou une IP client, sinon tu crées un stream par valeur et les performances s'effondrent. Ces champs-là restent de simples champs de log, toujours requêtables.

Brancher VictoriaMetrics et VictoriaLogs sur Grafana

Installer les plugins

On ajoute les deux plugins officiels dans Grafana pour créer des data sources dédiées :

Avec l'image Docker officielle de Grafana, la variable d'environnement GF_PLUGINS_PREINSTALL (liste séparée par des virgules) installe les plugins au démarrage : GF_PLUGINS_PREINSTALL=victoriametrics-metrics-datasource,victoriametrics-logs-datasource. C'est la méthode documentée sur les versions récentes de Grafana, à la place de l'ancienne GF_INSTALL_PLUGINS.

Tip

VictoriaMetrics étant compatible PromQL, tu peux aussi te passer du plugin côté métriques et utiliser la data source Prometheus native de Grafana (type = "prometheus") avec l'URL http://<victoriametrics>:8428. Le plugin dédié reste utile pour profiter de MetricsQL. Côté logs, le plugin est indispensable.

Le provider Terraform Grafana

On installe le provider Grafana afin de faire nos data sources as code avec Terraform.

Ici on fait simple, notre Grafana n'étant pas exposé, on va simplement faire un port-forward sur le pod de ce service. Je l'expose sur http://localhost:3000/.

terraform {
  required_providers {
    grafana = {
      source  = "grafana/grafana"
      version = "4.50.0"
    }
  }
}

provider "grafana" {
  auth = "admin:admin"
  url  = "http://localhost:3000/"
}

Note

Pense à adapter le password que je renseigne avec le tien, qui est dans le secret de ton cluster sous le nom de grafana. Comme pour Argo CD, passe-le par une variable sensible plutôt qu'en clair dans ton code.

Créer les data sources

Il ne reste plus qu'à créer nos deux data sources basées sur ces plugins. Pense à mettre les endpoints de tes propres services VictoriaMetrics et VictoriaLogs.

resource "grafana_data_source" "vm" {
  type = "victoriametrics-metrics-datasource"
  name = "vm-datasource"
  url  = "http://victoria-metrics-victoria-metrics-single-server.argocd.svc.cluster.local:8428"
}

resource "grafana_data_source" "vl" {
  type = "victoriametrics-logs-datasource"
  name = "vl-datasource"
  url  = "http://victoria-logs-victoria-logs-single-server.argocd.svc.cluster.local:9428"
}

Tu as désormais accès à tes métriques et à tes logs depuis Grafana !

Premières requêtes

Côté métriques, tes requêtes PromQL habituelles fonctionnent telles quelles dans l'explorateur Grafana, par exemple le taux de requêtes HTTP en erreur :

sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)

Côté logs, LogsQL enchaîne des filtres puis des pipes séparés par | :

  • _time:5m error : les logs des 5 dernières minutes qui contiennent le mot error ;
  • {namespace="production"} error _time:1h : la même recherche sur une heure, limitée aux streams du namespace production ;
  • _time:5m error | stats count() errors : le nombre de logs en erreur ;
  • _time:5m | stats by (level) count() rows : la répartition des logs par niveau.

Dans Grafana, le filtre de temps est passé automatiquement par la data source : tu peux omettre _time et laisser le sélecteur de période faire le travail.

Pièges courants en production

  • Volume plein : une rétention en temps ne protège pas contre un pic de logs. Combine retentionPeriod et retentionMaxDiskUsagePercent (ou retentionDiskSpaceUsage) côté VictoriaLogs, et surveille l'espace disque de tes PVC.
  • Mises à jour de chart : épingle target_revision comme dans les exemples et lis le changelog du chart avant de monter de version, les valeurs par défaut évoluent.
  • Pas de HA en single : voir l'encart plus haut. Une alerte sur la disponibilité des deux pods est le minimum.
  • Identifiants Grafana et Argo CD : ne laisse jamais admin:admin ni un mot de passe en clair dans ton code Terraform ou ton state : passe par des variables sensibles ou un gestionnaire de secrets.
  • Cardinalité : côté métriques, une étiquette à forte cardinalité (ID utilisateur, URL complète) fait exploser le nombre de séries ; côté logs, c'est le piège des stream fields vu plus haut.

Pour aller plus loin