SigNoz es una plataforma de observabilidad open source construida sobre OpenTelemetry que reúne trazas distribuidas, métricas y logs en una misma interfaz y los guarda en ClickHouse. Ofrece de serie las vistas típicas de un APM: latencia p50/p95/p99 por servicio, tasa de errores, operaciones más lentas y el detalle de cada traza. En este tutorial instalarás SigNoz con Docker Compose en Ubuntu 24.04, instrumentarás una pequeña aplicación Python sin tocar su código y verás sus trazas en el panel.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 4 vCPU, 8 GB de RAM y 30 GB libres en disco. ClickHouse es el componente que más memoria y disco consume.
- Un usuario no root con privilegios
sudo. - Docker Engine y el plugin de Docker Compose instalados desde el repositorio oficial de Docker.
- Git instalado (
sudo apt install git).
SigNoz usa estos puertos:
| Puerto | Uso |
|---|---|
| 8080 | Interfaz web y API de SigNoz |
| 4317 | Recepción OTLP por gRPC |
| 4318 | Recepción OTLP por HTTP |
Paso 1: Descargar SigNoz
SigNoz publica su despliegue para Docker en su repositorio principal. Clona la rama main en /opt:
cd /opt
sudo git clone -b main --depth 1 https://github.com/SigNoz/signoz.git
sudo chown -R "$USER":"$USER" /opt/signoz
cd /opt/signoz/deploy/docker
Revisa el archivo de Compose antes de arrancarlo para ver qué servicios y volúmenes crea:
less docker-compose.yaml
Encontrarás, entre otros, ClickHouse (almacenamiento), ZooKeeper (coordinación de ClickHouse), el servicio signoz (API, interfaz y alertas), el colector de OpenTelemetry que recibe los datos de tus aplicaciones y unos contenedores de migración que preparan el esquema de ClickHouse. Los datos se guardan en volúmenes de Docker, así que sobreviven a reinicios y actualizaciones.
Paso 2: Arrancar SigNoz
Levanta todos los servicios:
docker compose up -d --remove-orphans
La primera vez tarda varios minutos: se descargan las imágenes y las migraciones crean las tablas en ClickHouse. Comprueba el estado:
docker compose ps
Los servicios principales deben aparecer como running (o healthy), y los de migración como terminados con exited (0), que es lo esperado. Comprueba que la interfaz responde:
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/
200
Si algún servicio se reinicia en bucle, consulta sus logs con docker compose logs --tail 100 <servicio>, usando el nombre que muestra docker compose ps.
Paso 3: Proteger los puertos
Docker publica los puertos saltándose las reglas de UFW, así que 8080, 4317 y 4318 quedan accesibles desde Internet aunque UFW no los permita. El puerto 8080 da acceso al panel y los puertos OTLP no tienen autenticación: cualquiera podría enviarte datos.
Si tus aplicaciones corren en el mismo servidor, lo más sencillo es publicar los puertos solo en 127.0.0.1. Busca en docker-compose.yaml las líneas de ports de los servicios signoz y del colector y antepón la IP de loopback, por ejemplo:
ports:
- "127.0.0.1:8080:8080"
Haz lo mismo con 4317:4317 y 4318:4318, y aplica el cambio:
docker compose up -d
Si las aplicaciones están en otros servidores, usa una red privada entre ellos o un firewall externo que solo permita sus IP en 4317 y 4318. Para acceder al panel desde fuera, ponlo detrás de un proxy inverso con HTTPS, o usa un túnel SSH desde tu equipo:
ssh -L 8080:localhost:8080 your_user@your_server_ip
Con el túnel abierto, el panel está en http://localhost:8080 en tu navegador.
Paso 4: Crear la cuenta de administrador
Abre el panel en el navegador. La primera vez, SigNoz muestra un formulario de registro: introduce tu nombre, el nombre de la organización, un email y una contraseña robusta. Esa cuenta es la de administrador; después podrás invitar a otros usuarios desde Settings.
Tras iniciar sesión, la sección Services estará vacía: todavía ninguna aplicación envía datos.
Paso 5: Preparar una aplicación Python de ejemplo
Para ver SigNoz en acción necesitas una aplicación que genere trazas. Crea una aplicación Flask mínima en un entorno virtual:
sudo apt install python3-venv
mkdir -p ~/demo-app
cd ~/demo-app
python3 -m venv venv
source venv/bin/activate
pip install flask requests
Crea el archivo de la aplicación:
nano ~/demo-app/app.py
import random
import time
import requests
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "ok"
@app.route("/lento")
def lento():
time.sleep(random.uniform(0.2, 1.2))
return "respuesta lenta"
@app.route("/externo")
def externo():
r = requests.get("https://example.com", timeout=5)
return f"example.com respondió {r.status_code}"
@app.route("/error")
def error():
raise RuntimeError("fallo de prueba")
La aplicación tiene una ruta rápida, una lenta, una que llama a un servicio externo y otra que falla, suficientes para ver latencias, llamadas salientes y errores en SigNoz.
Paso 6: Instrumentar la aplicación con OpenTelemetry
OpenTelemetry ofrece instrumentación automática para Python: un lanzador (opentelemetry-instrument) que detecta las librerías usadas (Flask, requests, clientes de bases de datos...) y crea las trazas sin modificar el código. Con el entorno virtual activo, instala la distribución y el exportador OTLP:
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install
opentelemetry-bootstrap revisa los paquetes instalados e instala las instrumentaciones correspondientes, en este caso las de Flask y requests.
El destino y el nombre del servicio se configuran con variables de entorno estándar de OpenTelemetry. Si la aplicación está en otro servidor, sustituye localhost por la IP de SigNoz:
export OTEL_SERVICE_NAME=demo-app
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=produccion
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_TRACES_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=none
Arranca la aplicación a través del lanzador:
opentelemetry-instrument flask --app app run --port 5000
En otra terminal, genera tráfico contra las cuatro rutas durante un par de minutos:
for i in $(seq 1 50); do
curl -s -o /dev/null http://127.0.0.1:5000/
curl -s -o /dev/null http://127.0.0.1:5000/lento
curl -s -o /dev/null http://127.0.0.1:5000/externo
curl -s -o /dev/null http://127.0.0.1:5000/error
sleep 1
done
Para una aplicación real, en lugar de flask run usa el mismo lanzador delante del servidor de producción, por ejemplo opentelemetry-instrument gunicorn app:app, y define las variables en la unidad systemd o en el entorno del contenedor.
Paso 7: Analizar las trazas en SigNoz
Vuelve al panel. En Services aparecerá demo-app con su latencia p99, la tasa de errores y las operaciones por segundo. Al entrar en el servicio verás:
- Key Operations: cada ruta (
GET /,GET /lento,GET /externo,GET /error) con su latencia y errores.GET /lentodestacará por latencia yGET /errortendrá un 100 % de errores. - External calls: las llamadas a
example.comhechas conrequests, con su duración.
Pulsa en una operación para ir a sus trazas. En la vista de una traza de GET /externo verás el span del servidor Flask y, anidado, el span de la petición HTTP saliente: así se localiza qué parte de una petición consume el tiempo. En las trazas de GET /error el span aparece marcado como error y, en sus eventos, la excepción RuntimeError con su traza de pila.
En Traces (o Traces Explorer) puedes filtrar por servicio, duración mínima, código de estado o cualquier atributo, por ejemplo todas las peticiones de más de 1 segundo.
Paso 8: Instrumentar otros lenguajes
El mismo enfoque funciona con cualquier lenguaje soportado por OpenTelemetry, cambiando solo cómo se carga la instrumentación. Las variables OTEL_* son las mismas.
En Node.js, instala el paquete de autoinstrumentación en tu proyecto:
npm install @opentelemetry/api @opentelemetry/auto-instrumentations-node
Y arranca la aplicación cargándolo antes que tu código, enviando por OTLP HTTP al puerto 4318:
export OTEL_SERVICE_NAME=mi-api-node
export OTEL_TRACES_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
node --require @opentelemetry/auto-instrumentations-node/register app.js
En Java, descarga el agente oficial y añádelo a la JVM:
curl -fLO https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
export OTEL_SERVICE_NAME=mi-servicio-java
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
java -javaagent:./opentelemetry-javaagent.jar -jar mi-aplicacion.jar
El agente de Java usa OTLP por HTTP (puerto 4318) por defecto, por eso el endpoint apunta a ese puerto.
Solución de problemas
El servicio no aparece en Services. Comprueba desde el servidor de la aplicación que el puerto OTLP está accesible con nc -zv your_signoz_ip 4317. Revisa que el protocolo coincide con el puerto (gRPC en 4317, HTTP en 4318) y los logs del colector con docker compose logs --tail 50 seguido del nombre del servicio del colector que muestra docker compose ps.
ClickHouse se reinicia o el servidor se queda sin memoria. Con menos de 8 GB de RAM, ClickHouse puede ser terminado por el OOM killer. Compruébalo con sudo dmesg | grep -i oom y aumenta la memoria del servidor.
El disco se llena. Las trazas y los logs ocupan mucho con tráfico alto. Ajusta la retención de cada tipo de dato en Settings > General y reduce el volumen con muestreo en las aplicaciones, por ejemplo OTEL_TRACES_SAMPLER=parentbased_traceidratio y OTEL_TRACES_SAMPLER_ARG=0.1 para conservar el 10 % de las trazas.
Tras actualizar, algún contenedor falla. Para actualizar SigNoz, descarga los cambios con git pull en /opt/signoz y ejecuta de nuevo docker compose up -d --remove-orphans en deploy/docker. Si has editado docker-compose.yaml, git puede negarse a actualizar: guarda tus cambios con git stash, actualiza y vuelve a aplicarlos.
Conclusión
Tienes SigNoz funcionando con Docker Compose y una aplicación Python enviando trazas y métricas por OpenTelemetry, con latencias, llamadas externas y errores visibles en el panel. Como siguientes pasos, instrumenta tus servicios reales con las mismas variables OTEL_*, crea alertas sobre latencia p99 o tasa de errores en Alerts, y envía los logs de tus servidores al colector para correlacionarlos con las trazas.
