Aller au contenu
English
Outils medium

SigNoz : APM open-source tout-en-un

24 min de lecture

SigNoz est une plateforme APM (Application Performance Monitoring) open source qui unifie traces, métriques et logs dans une seule interface, basée sur OpenTelemetry. C'est une alternative auto-hébergée à Datadog ou New Relic : vos données de télémétrie restent chez vous, stockées dans ClickHouse.

Ce guide s'adresse aux profils intermédiaires et avancés. Vous allez déployer SigNoz (version v0.131.1) avec l'installateur Foundry, comprendre son architecture, instrumenter vos applications en OpenTelemetry, et créer dashboards et alertes.

  • Déployer SigNoz en Docker avec Foundry, en épinglant la version.
  • Comprendre l'architecture (collector, ClickHouse, metastore).
  • Instrumenter vos applications avec les SDK OpenTelemetry.
  • Créer des dashboards et des règles d'alerte.
  • Maîtriser la rétention et le sampling en production.

SigNoz offre les fonctionnalités des APM commerciaux sans les coûts de licence, avec un stockage on-premise de vos données. L'argument ne se limite pas au prix affiché : sur un APM en SaaS, la facture suit le volume de télémétrie ingéré, ce qui pousse à réduire l'instrumentation au moment précis où on en aurait le plus besoin. Avec un backend auto-hébergé, le coût redevient celui de vos disques et de votre CPU, que vous dimensionnez vous-même.

CaractéristiqueDescription
LicenceMIT (Enterprise disponible)
SignauxTraces, Métriques, Logs
StockageClickHouse
ProtocoleOpenTelemetry natif
UIDashboards, alertes, exceptions

SigNoz couvre les trois signaux de l'observabilité (traces, métriques, logs) et y ajoute un écran dédié aux exceptions. Ces quatre vues partagent le même stockage ClickHouse et les mêmes attributs de ressource OpenTelemetry, notamment service.name. C'est cette base commune qui permet de passer d'un signal à l'autre sans changer d'outil ni recoller les identifiants à la main.

Une trace reconstitue le trajet complet d'une requête à travers vos services, découpé en spans (une opération unitaire, avec sa durée et son parent). C'est ce qui permet de répondre à « quel service a consommé les 800 ms » plutôt qu'au simple constat « la page est lente ».

  • Visualisation de bout en bout des requêtes
  • Flamegraphs et gantt charts
  • Analyse des dépendances inter-services
  • Détection automatique des anomalies

Les métriques répondent aux questions de volume et de tendance, là où une trace explique un cas particulier. Le tableau de bord RED (Rate, Errors, Duration) constitue le point d'entrée standard pour un service : débit, taux d'erreur et latence sur une seule vue.

  • Métriques d'infrastructure (CPU, mémoire, réseau)
  • Métriques applicatives custom
  • Dashboards RED (Rate, Errors, Duration)
  • Agrégation et alerting

Les logs entrent par le même collecteur OTLP que les traces et héritent donc des mêmes attributs de ressource. C'est la condition de la corrélation : depuis un span lent, retrouver les lignes émises pendant son exécution sans changer d'application.

  • Collecte via OpenTelemetry
  • Corrélation traces ↔ logs
  • Recherche full-text
  • Agrégation et patterns

L'écran Exceptions regroupe les erreurs remontées par les SDK OpenTelemetry par stack trace identique, au lieu de les laisser noyées dans le flux de logs. Chaque groupe renvoie vers les traces concernées, ce qui donne le contexte de la requête qui a déclenché l'erreur.

  • Tracking automatique des erreurs
  • Groupement par stack trace
  • Lien vers les traces associées

Vos applications émettent de la télémétrie en OTLP vers le collecteur SigNoz, qui la range dans ClickHouse ; l'application SigNoz interroge ce stockage et sert l'interface. Les versions récentes ont regroupé l'ancien trio frontend + query-service dans un binaire unique et ajouté un metastore.

ComposantRôle
signozBinaire unifié : interface web (port 8080), API de requête, alerting
signoz-otel-collectorReçoit l'OTLP (gRPC 4317, HTTP 4318) et écrit dans ClickHouse
ClickHouse + ClickHouse KeeperStockage colonne des traces, métriques et logs
metastore (PostgreSQL)Métadonnées : dashboards, alertes, utilisateurs
ingesterTraitement et bufferisation avant écriture

Le point d'entrée pour vos applications est le collecteur (OTLP) ; le point d'entrée pour les humains est l'interface sur le port 8080.

SigNoz se déploie en Docker pour une évaluation ou un usage mono-serveur, et via Helm sur Kubernetes. Les deux chemins installent les mêmes composants : le collecteur OTLP, ClickHouse et l'application web. Le troisième onglet montre comment brancher une instance ClickHouse externe, utile si vous en administrez déjà une et ne souhaitez pas en empiler une seconde.

L'ancien install.sh et les manifests docker-compose du dépôt sont dépréciés. SigNoz s'installe désormais avec Foundry, son installateur officiel. On récupère le binaire foundryctl depuis les releases GitHub (pas de script piped-to-shell) :

Fenêtre de terminal
curl -fsSL https://github.com/SigNoz/foundry/releases/download/v0.2.11/foundry_linux_amd64.tar.gz | tar -xz
sudo install -m 755 foundry_linux_amd64/bin/foundryctl /usr/local/bin/foundryctl

On décrit le déploiement dans un fichier casting.yaml :

apiVersion: v1alpha1
kind: Installation
metadata:
name: signoz
spec:
deployment:
flavor: compose
mode: docker

Foundry valide Docker, génère les fichiers Compose et démarre la stack :

Fenêtre de terminal
foundryctl cast -f casting.yaml

L'interface est accessible sur http://localhost:8080. Prévoyez au moins 4 Go de RAM alloués à Docker.

Instrumenter consiste à faire émettre à votre application des traces et des métriques au format OpenTelemetry, puis à les envoyer au collecteur en OTLP. Deux réglages suffisent dans la plupart des cas : OTEL_RESOURCE_ATTRIBUTES fixe le nom de service (c'est lui qui apparaît dans l'interface, un service mal nommé devient introuvable), et OTEL_EXPORTER_OTLP_ENDPOINT désigne le collecteur. Les exemples ci-dessous s'appuient sur l'auto-instrumentation, qui couvre les frameworks HTTP et les clients de base de données courants sans toucher au code métier.

Le paquet opentelemetry-distro installe le SDK et la commande opentelemetry-bootstrap, qui détecte les bibliothèques présentes dans l'environnement et ajoute l'instrumentation correspondante. Le lancement passe ensuite par opentelemetry-instrument, qui charge ces agents avant votre code.

Fenêtre de terminal
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install
Fenêtre de terminal
OTEL_RESOURCE_ATTRIBUTES=service.name=my-service \
OTEL_EXPORTER_OTLP_ENDPOINT=http://signoz:4317 \
opentelemetry-instrument python app.py

En Node.js, le SDK doit être chargé avant le reste de l'application, sinon les modules déjà importés échappent à l'instrumentation. C'est le rôle de --require ./tracing.js, qui exécute le fichier de configuration avant app.js.

Fenêtre de terminal
npm install @opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-grpc
tracing.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: 'grpc://signoz:4317',
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
Fenêtre de terminal
node --require ./tracing.js app.js

Java passe par un agent JVM (-javaagent) : aucune ligne de code à modifier, l'instrumentation est injectée au chargement des classes. Les options se passent en propriétés système -Dotel.* plutôt qu'en variables d'environnement.

Fenêtre de terminal
# Télécharger l'agent
curl -L -o opentelemetry-javaagent.jar \
https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
# Lancer
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.service.name=my-service \
-Dotel.exporter.otlp.endpoint=http://signoz:4317 \
-jar myapp.jar

Go ne dispose pas d'agent équivalent : le langage étant compilé statiquement, l'initialisation du TracerProvider doit être écrite explicitement au démarrage. L'option WithInsecure() désactive TLS sur la connexion au collecteur, à ne conserver que si ce trafic reste sur un réseau de confiance.

import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/sdk/trace"
)
func initTracer() (*trace.TracerProvider, error) {
exporter, err := otlptracegrpc.New(
context.Background(),
otlptracegrpc.WithEndpoint("signoz:4317"),
otlptracegrpc.WithInsecure(),
)
if err != nil {
return nil, err
}
tp := trace.NewTracerProvider(
trace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
return tp, nil
}

Sur Kubernetes, l'opérateur OpenTelemetry évite de reconstruire chaque image applicative : il injecte l'agent d'instrumentation dans le pod à sa création, via un conteneur d'initialisation. Deux objets suffisent, une ressource Instrumentation qui décrit où envoyer la télémétrie, et une annotation posée sur le déploiement qui déclenche l'injection pour le langage voulu.

La ressource Instrumentation centralise l'endpoint du collecteur, les propagateurs de contexte et le sampler. Ici, parentbased_traceidratio avec l'argument 0.1 conserve 10 % des traces racines tout en respectant la décision déjà prise en amont par un service appelant, ce qui évite les traces tronquées au milieu d'une chaîne d'appels.

instrumentation.yaml
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: signoz-instrumentation
namespace: default
spec:
exporter:
endpoint: http://signoz-otel-collector.observability:4317
propagators:
- tracecontext
- baggage
sampler:
type: parentbased_traceidratio
argument: "0.1"
python:
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:0.64b0@sha256:be7d9c429a455f8e2f5f3e4a48078e28cfb0bf9731105213f2149e53e34b8ead
nodejs:
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:0.78.0@sha256:ade33daea5a214cdb053550b5cd95ae4abc8b96fa3f6fd3af4bb09bf06893d76
java:
image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:2.30.0@sha256:413b947eeeab911d982d12939485e34bc974eaea25c35c903017908ea73ce52b

L'injection ne concerne que les pods portant l'annotation correspondant à leur langage. Elle se place sur le template de pod du déploiement, pas sur le déploiement lui-même : une annotation placée au mauvais niveau ne déclenche rien et ne produit aucun message d'erreur.

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
metadata:
annotations:
# Auto-instrumentation Python
instrumentation.opentelemetry.io/inject-python: "true"
# Ou Java
# instrumentation.opentelemetry.io/inject-java: "true"
# Ou Node.js
# instrumentation.opentelemetry.io/inject-nodejs: "true"

SigNoz n'impose aucun agent de logs propriétaire : tout collecteur capable de parler OTLP convient. Deux montages couvrent la majorité des besoins, Fluent Bit si vos nœuds en hébergent déjà un, ou le collecteur OpenTelemetry si vous préférez une seule brique pour les trois signaux.

La sortie opentelemetry de Fluent Bit pousse les logs en HTTP sur le port 4318, avec le chemin /v1/logs. Le parser cri est indispensable sur un nœud Kubernetes : il découpe le format de ligne écrit par le runtime (horodatage, flux, marqueur de continuation) avant que le filtre kubernetes n'ajoute les métadonnées du pod.

fluent-bit.conf
[INPUT]
Name tail
Path /var/log/containers/*.log
Tag kube.*
Parser cri
[FILTER]
Name kubernetes
Match kube.*
Merge_Log On
[OUTPUT]
Name opentelemetry
Match *
Host signoz-otel-collector
Port 4318
Logs_uri /v1/logs
Tls off

Le receiver filelog lit les fichiers directement et leur applique des opérateurs de transformation, ici un json_parser qui éclate chaque ligne JSON en attributs interrogeables. Le pipeline logs de la section service est obligatoire : sans lui, le receiver est bien déclaré mais jamais activé, et aucune ligne ne part vers SigNoz.

otel-config.yaml
receivers:
filelog:
include:
- /var/log/app/*.log
operators:
- type: json_parser
exporters:
otlp:
endpoint: signoz-otel-collector:4317
service:
pipelines:
logs:
receivers: [filelog]
exporters: [otlp]

Un dashboard SigNoz rassemble des panneaux, chacun adossé à une requête exécutée sur ClickHouse. L'outil en fournit une série prête à l'emploi pour les cas standards ; les autres se construisent soit avec le query builder graphique, soit en important un fichier JSON.

Ces dashboards s'importent depuis l'interface et s'appuient sur les conventions de nommage OpenTelemetry. Ils n'affichent donc des données que si vos services renseignent correctement l'attribut service.name :

  • APM : Latence, throughput, erreurs par service
  • Infrastructure : Host metrics, Kubernetes
  • Database : Redis, PostgreSQL, MongoDB

Un dashboard s'exporte et se réimporte en JSON, ce qui permet de le versionner dans Git au même titre que le reste de votre infrastructure. Le champ queryType: builder indique que le panneau s'appuie sur le constructeur de requêtes plutôt que sur du SQL brut.

dashboard.json
{
"title": "My Service Dashboard",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"query": {
"queryType": "builder",
"aggregateOperator": "rate",
"aggregateAttribute": {
"key": "signoz_calls_total"
},
"filters": {
"items": [
{
"key": "service_name",
"value": "my-service",
"op": "="
}
]
}
}
}
]
}

Une règle d'alerte associe une requête, un seuil et une durée de maintien. Elle est réévaluée périodiquement, puis notifie les canaux configurés au préalable. Point de vigilance : sans canal déclaré, la règle bascule bien en état déclenché dans l'interface, mais personne n'est prévenu.

Deux champs commandent la sensibilité de la règle. frequency fixe l'intervalle d'évaluation, for la durée pendant laquelle la condition doit rester vraie avant de déclencher. Un for: 5m écarte les pics isolés qui se résorbent seuls et évite de réveiller l'astreinte pour rien.

alert-rule.yaml
alert: HighErrorRate
condition:
threshold:
compare: ">"
value: 5
query:
queryType: builder
aggregateOperator: rate
aggregateAttribute:
key: signoz_calls_total
type: Sum
filters:
items:
- key: service_name
value: api-gateway
op: "="
- key: status_code
value: "5.*"
op: "=~"
groupBy:
- service_name
frequency: 1m
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate on {{ $labels.service_name }}"

Un canal de notification se déclare une seule fois dans les paramètres, puis se rattache à autant de règles que nécessaire. SigNoz sait notifier :

  • Slack
  • PagerDuty
  • Email
  • Webhook
  • Microsoft Teams
  • Opsgenie

Le query builder couvre les besoins courants, mais certaines analyses réclament du SQL directement sur ClickHouse. Traces et logs vivent dans deux bases distinctes, signoz_traces et signoz_logs, dont les tables distributed_* agrègent les shards. Ces requêtes servent surtout à produire un rapport ponctuel ou à vérifier une donnée que l'interface n'expose pas.

Les durées sont stockées en nanosecondes dans durationNano ; la division par 1e6 les ramène en millisecondes. Le filtre sur timestamp n'est pas cosmétique : sans lui, la requête balaie toute la rétention et peut saturer le serveur.

-- Top 10 endpoints les plus lents
SELECT
serviceName,
name AS operation,
quantile(0.99)(durationNano) / 1e6 AS p99_ms
FROM signoz_traces.distributed_signoz_index_v2
WHERE timestamp > now() - INTERVAL 1 HOUR
GROUP BY serviceName, name
ORDER BY p99_ms DESC
LIMIT 10

Les attributs de ressource sont rangés dans deux tableaux parallèles, les clés d'un côté, les valeurs de l'autre. La fonction indexOf retrouve la position de service.name dans le tableau des clés pour aller lire la valeur au même rang.

-- Logs d'erreur par service
SELECT
resources_string_value[indexOf(resources_string_key, 'service.name')] AS service,
count() AS error_count
FROM signoz_logs.distributed_logs
WHERE severity_text = 'ERROR'
AND timestamp > now() - INTERVAL 1 HOUR
GROUP BY service
ORDER BY error_count DESC

ClickHouse ne supprime rien de lui-même : la rétention se pilote par des TTL définis signal par signal. Sans TTL, le disque grossit jusqu'à saturation, et c'est la panne la plus fréquente sur une instance SigNoz laissée en place plusieurs mois. Le sampling agit en amont, au niveau du collecteur, sur ce qui est réellement écrit.

Les TTL se règlent séparément pour les traces, les métriques et les logs, parce que leurs volumes n'ont rien de comparable. Le cold storage S3 déplace les données anciennes vers un stockage moins coûteux au lieu de les détruire, mais il faut l'activer explicitement avec cold.enabled: true, faute de quoi tout reste sur le disque local jusqu'à expiration du TTL.

values.yaml (Helm)
clickhouse:
cold:
enabled: true
type: s3
s3:
endpoint: s3.amazonaws.com
bucket: signoz-cold
accessKey: ${AWS_ACCESS_KEY}
secretAccess: ${AWS_SECRET_KEY}
ttl:
traces: 168h # 7 jours
metrics: 720h # 30 jours
logs: 336h # 14 jours
cold_storage_traces: 720h # 30 jours en cold

Le probabilistic_sampler tranche au niveau du collecteur, avant écriture dans ClickHouse : ce qu'il rejette est perdu définitivement. Sur un service à fort trafic, conserver 10 % suffit à suivre les tendances, mais vous ne retrouverez pas la trace exacte d'une requête client signalée par ticket. C'est l'arbitrage à assumer entre coût de stockage et capacité d'enquête.

otel-config.yaml
processors:
probabilistic_sampler:
sampling_percentage: 10 # Garder 10% des traces
service:
pipelines:
traces:
processors: [probabilistic_sampler, batch]

Une instance mono-nœud convient à un lab ou à une petite équipe. En production, deux composants méritent d'être redondés en priorité : le collecteur, point d'entrée de toute la télémétrie dont l'arrêt se traduit par une perte sèche de données, et ClickHouse, qui porte le stockage et l'ensemble des requêtes.

Le collecteur porte le plus grand nombre de réplicas car il encaisse le trafic OTLP de toutes vos applications. Côté ClickHouse, les réplicas servent la tolérance de panne et les shards la répartition du volume : ce sont deux besoins distincts, à dimensionner séparément plutôt qu'à augmenter ensemble par réflexe.

values.yaml
frontend:
replicaCount: 2
queryService:
replicaCount: 2
otelCollector:
replicaCount: 3
clickhouse:
replicaCount: 3
shards: 2
alertmanager:
replicaCount: 2

L'Ingress publie l'interface web sur le port 8080 du service signoz. L'annotation proxy-body-size relève la limite de taille de corps de requête appliquée par NGINX, sans quoi les envois volumineux (import d'un dashboard JSON, par exemple) sont refusés avec un code 413.

ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: signoz
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
rules:
- host: signoz.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: signoz
port:
number: 8080

Le choix se joue moins sur la liste des fonctionnalités que sur le nombre de briques à exploiter. Jaeger ne traite que les traces et suppose que métriques et logs vivent ailleurs. La stack Grafana couvre les trois signaux, mais avec trois backends distincts (Tempo, Prometheus, Loki) à déployer, dimensionner et mettre à jour. SigNoz échange cette modularité contre une base de données unique et une seule interface.

FeatureSigNozJaegerGrafana Stack
Traces✅ (Tempo)
Métriques✅ (Prometheus)
Logs✅ (Loki)
UI unifiéeTraces only
Alerting
StockageClickHouseES/CassandraMultiple
SetupSimpleSimpleComplexe

Ces cinq réflexes écartent les deux échecs les plus courants d'un déploiement SigNoz : le disque qui sature au bout de quelques semaines faute de rétention, et un outil qui affiche des courbes techniques que personne ne relie à l'activité réelle du produit.

  1. Utilisez le sampling en production

    Ne collectez pas 100% des traces pour les services à fort trafic.

  2. Configurez la rétention

    Définissez des TTL adaptés pour éviter l'explosion du stockage.

  3. Séparez les environnements

    Utilisez des namespaces ou instances séparés pour dev/staging/prod.

  4. Enrichissez avec les attributes

    span.set_attribute("user.id", user_id)
    span.set_attribute("order.total", total)
  5. Créez des dashboards métier

    Allez au-delà des métriques techniques avec des KPIs business.

La plupart des incidents se ramènent à trois causes : la télémétrie n'atteint pas le collecteur, ClickHouse ne suit plus la charge, ou les données ont déjà été supprimées par un TTL. Le tableau associe le symptôme visible à sa cause la plus probable ; les commandes qui suivent servent à trancher entre elles.

SymptômeCause probableSolution
Pas de tracesEndpoint incorrectVérifier OTLP endpoint
UI inaccessibleFrontend downkubectl logs signoz-frontend
Requêtes lentesClickHouse surchargéScale ou optimize queries
Données manquantesTTL dépasséVérifier rétention settings
Fenêtre de terminal
# Vérifier les composants
kubectl get pods -n observability -l app.kubernetes.io/name=signoz
# Logs du collector
kubectl logs -n observability -l app=signoz-otel-collector
# Test d'envoi OTLP
curl -X POST http://signoz:4318/v1/traces \
-H "Content-Type: application/json" \
-d '{}'

Quatre questions reviennent systématiquement avant un déploiement : ce que fait exactement SigNoz, comment l'installer, comment il se compare à la stack Grafana et quelle place y tient OpenTelemetry.

  • APM open-source complet : alternative à Datadog/New Relic
  • OpenTelemetry natif : pas d'agent propriétaire
  • Stockage ClickHouse : performant pour l'analytique
  • Traces + Métriques + Logs : vue unifiée
  • Self-hosted : vos données restent chez vous

La documentation officielle reste la référence pour les options de configuration que ce guide n'aborde pas, en particulier les paramètres avancés de ClickHouse et la liste complète des variables du chart Helm.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn