# Observabilidad ## una base para aplicaciones resilientes
**Lucas Di Cunzolo** Director: Miguel Angel Luengo · Codirector: Christian Adrian Rodriguez Asesor profesional: Leandro Di Tommaso
_Licenciatura en Informática — UNLP, Facultad de Informática_ _Marzo 2026_ ## Contexto El desarrollo de aplicaciones web ha experimentado cambios disruptivos en los últimos años: - Las **metodologías ágiles** aceleraron los ciclos de entrega - Surgió **DevOps** como respuesta a la necesidad de alinear desarrollo y operaciones - La infraestructura evolucionó hacia modelos **cloud-native** y contenedores - Kubernetes estandarizó la gestión de infraestructuras complejas ### La complejidad crece Con arquitecturas distribuidas y microservicios: - Múltiples servicios heterogéneos conviven en el mismo ecosistema - Cada componente puede escalar de forma independiente - Los contenedores son **efímeros**: visualizar logs ya no es trivial - Un incidente puede originarse en cualquier punto de la cadena > _Cuando algo falla en producción, ¿sabemos dónde mirar?_ ## Problema Los sistemas modernos exigen **99,9% de disponibilidad** Los servicios se degradan ante picos de demanda imprevistos Sin visibilidad interna, diagnosticar un incidente puede llevar horas
### ¿Cómo comprender el comportamiento de un sistema en su ambiente productivo, independientemente de la tecnología? ## Objetivo Identificar los mecanismos necesarios para **comprender y reconocer el desempeño** de los sistemas en producción, de forma agnóstica a la tecnología.
Con el fin de poder: - Anticipar potenciales problemas - Determinar bajo qué condiciones escalar un servicio - Reducir el tiempo de diagnóstico ante incidentes - Automatizar la resiliencia mediante telemetría ## Resiliencia Una aplicación es **resiliente** si puede adaptarse ante eventos nuevos sin degradar la calidad del servicio.
Estrategias estudiadas: | Estrategia | Descripción | |---|---| | Arquitecturas distribuidas | Alta disponibilidad mediante múltiples zonas | | Auto-sanado _(self-healing)_ | Health checks + reinicio automático ante fallos | | **Escalado adaptativo** | Escalar horizontal o verticalmente según indicadores |
> _Esta tesina se enfoca en la **adaptabilidad de servicio** como estrategia de resiliencia._ ## Estructura de la presentación 1. **Conceptos básicos** — métricas, logs y trazas 2. **OpenTelemetry** — el estándar unificador 3. **Stack LGTM** — Loki, Grafana, Tempo, Mimir 4. **Instrumentación** — WordPress, Redmine y Wagtail 5. **Despliegue en Kubernetes** — Helm, KEDA y escalado automático 6. **Conclusiones** ## 1. Conceptos básicos > _"Propiedad que le permite a las personas y máquinas observar, entender > y actuar en respuesta al estado interno de un sistema"_ Los sistemas exponen su estado a través de **señales**, clasificadas en tres pilares: - **Métricas** — valores numéricos agregables a lo largo del tiempo - **Logs** — eventos discretos con contexto textual - **Trazas** — flujo de un requerimiento a través de múltiples servicios ### Los tres pilares
| Pilar | Volumen | Característica | |---|---|---| | Métricas | Menor | Agrupables, ideales para alertas y escalado | | Logs | Mayor | Eventos no distribuidos, detalle de operaciones | | Trazas | Mayor | Eventos distribuidos, visibilidad end-to-end |
> _Fuente: CNCF TAG Observability Whitepaper_ ## Métricas Valores numéricos que representan el estado de un recurso en un instante de tiempo. Compuestas por: **nombre** · **timestamp** · **valor** · **labels**
Conjuntos de métricas estándar: | Método | Siglas | Aplica a | |---|---|---| | **USE** | Utilización · Saturación · Errores | Recursos (CPU, memoria, red) | | **RED** | Rate · Errors · Duration | Servicios web | | **Golden Signals** | Latencia · Tráfico · Errores · Saturación | Sistemas distribuidos (Google SRE) | ### Modelos de recolección
**Push** — el servicio envía los datos al backend - Lógica distribuida en cada agente - Complejidad crece con el número de servicios - Ejemplo: stack TICK (Telegraf · InfluxDB · Chronograf · Kapacitor)
**Pull** — el backend recupera los datos de los exporters - Configuración centralizada - Ejemplo: Prometheus + node-exporter ## Logs Describen **eventos discretos** que ocurren en un instante de tiempo. Características clave: - Representados por timestamp + mensaje (texto plano o estructurado) - Deben escribirse a **stdout/stderr** (factor 11 de Twelve-Factor App) - Formato estructurado (JSON, Logfmt) facilita el procesamiento centralizado
Formatos comunes: **CLF** (Nginx/Apache) · **Logfmt** · **Syslog** (RFC 3164 / RFC 5424) ## Trazas Describen el **flujo completo de un requerimiento** desde que ingresa al sistema hasta que retorna al usuario. Unidad mínima: el **Span** — representa una operación con duración medible
gantt dateFormat x axisFormat %Lms section GET /api/posts [52ms] GET /api/posts :0, 52 wpdb.query SELECT :5, 14 wpdb.query SELECT :16, 17 wpdb.connect :20, 29
### Correlación de señales Las tres señales pueden relacionarse entre sí mediante metadatos compartidos: | Mecanismo | Descripción | |---|---| | **Ventana temporal** | Filtrar las tres señales por el mismo instante | | **Request / Trace ID** | Navegar de logs a la traza exacta del requerimiento | | **Exemplars** | Enlace directo desde una métrica a la traza que la generó |
> _La correlación transforma datos aislados en información accionable_ ## 2. OpenTelemetry **El estándar unificador de telemetría en entornos cloud-native**
Proyecto de la **CNCF** (Cloud Native Computing Foundation), fusión de OpenTracing y OpenCensus en 2019. Unifica las tres señales bajo un único protocolo: **OTLP** _(OpenTelemetry Protocol)_ ### Evolución histórica
timeline 2012 : Twitter → Zipkin
Primer OSS de trazas 2015 : Uber → Jaeger
Escalabilidad + UI 2016 : CNCF → Prometheus
Métricas pull, OpenMetrics 2019 : OpenTracing + OpenCensus → OpenTelemetry 2023 : GrafanaLabs adopta OTLP
> _Antes de OTel: un vendor diferente por cada señal, vendor lock-in en cada capa_ ## Componentes de OpenTelemetry
| Componente | Rol | |---|---| | **API** | Interfaz para instrumentar código, agnóstica al backend | | **SDK** | Implementación de la API con pipelines de exportación | | **Auto-instrumentación** | Intercepta frameworks sin modificar el código fuente | | **OTel Collector** | Agente intermediario: recibe, procesa y exporta señales | | **OTLP** | Protocolo de transporte unificado (gRPC / HTTP) | ### OTel Collector Desacopla la generación de telemetría del almacenamiento:
flowchart LR A[Aplicaciones] -->|OTLP| R[Receiver] R --> P[Processor
batch · atributos · filtros] P --> E1[(Mimir
métricas)] P --> E2[(Loki
logs)] P --> E3[(Tempo
trazas)]
Ventajas: - Cambiar el backend **sin tocar las aplicaciones** - Buffer local ante indisponibilidad de las bases de datos - Conector `spanmetrics`: deriva métricas RED directamente desde trazas ## Instrumentación automática vs manual
**Automática** _(zero-code instrumentation)_ - Intercepta frameworks mediante hooks, monkey-patching o metaprogramación - No requiere modificar el código fuente - Soporte varía según el lenguaje y framework
**Manual** - Instala el SDK e incorpora llamadas explícitas para crear spans - Mayor control y granularidad - Necesaria cuando no hay auto-instrumentador disponible
> _En esta tesina: automática en PHP y Python · manual en Ruby_ ## 3. Stack LGTM Arquitectura de observabilidad open-source propuesta por **GrafanaLabs**
| Componente | Señal | Rol | |---|---|---| | **L**oki | Logs | Almacenamiento indexado por etiquetas | | **G**rafana | — | Visualización y correlación unificada | | **T**empo | Trazas | Almacenamiento eficiente multi-formato | | **M**imir | Métricas | Backend compatible con API de Prometheus |
El **OTel Collector** actúa como intermediario central mediante OTLP ## Grafana Mimir Almacenamiento de métricas de **alta cardinalidad** y retención configurable - Compatible con la API de Prometheus (PromQL) - Acepta protocolo OTLP desde noviembre 2023 - Escala horizontalmente para grandes volúmenes
Usado en esta tesina como fuente de métricas para **KEDA**: ```promql sum(rate(traces_spanmetrics_calls_total{service_name="wagtail"}[1m])) ``` ## Grafana Loki Almacenamiento de logs liviano: indexa **solo etiquetas**, no el contenido
Comparación con Elasticsearch (ELK): | | Loki | Elasticsearch | |---|---|---| | Indexado | Solo labels | Contenido completo | | Costo almacenamiento | Bajo | Alto | | Integración Grafana | Nativa | Plugin | | Consulta | LogQL | KQL / Lucene | ## Grafana Tempo Almacenamiento de trazas con integración nativa en Grafana - Admite formatos: **OTLP · Jaeger · Zipkin** - Navegación directa desde métricas y logs hacia trazas específicas - Sampling configurable: head-based, tail-based, probabilístico ## Arquitectura completa
flowchart LR WP["WordPress
PHP / OTLP HTTP :4318"] RM["Redmine
Ruby / Fluentd :8006"] WG["Wagtail
Python / OTLP gRPC :4317"] OC["OTel Collector"] MM[("Mimir")] LK[("Loki")] TP[("Tempo")] GF["Grafana"] WP --> OC RM --> OC WG --> OC OC --> MM & LK & TP MM & LK & TP --> GF
El conector `spanmetrics` genera métricas RED a partir de trazas, **sin instrumentación adicional** ## 4. Instrumentación Tres aplicaciones desarrolladas en lenguajes y frameworks diferentes, **sin modificar su código fuente**
| Aplicación | Lenguaje | Framework | Año | Instrumentación | |---|---|---|---|---| | **WordPress** | PHP | — | 2003 | Automática | | **Redmine** | Ruby | Rails | 2006 | Manual | | **Wagtail** | Python | Django | 2014 | Automática |
> _Ninguna fue diseñada teniendo en cuenta los 12 factores ni la observabilidad_ ## WordPress — PHP Instrumentación automática sin tocar el código fuente de WordPress **Dependencias:** 1. Extensión nativa PHP vía `pecl install opentelemetry protobuf` 2. Paquetes Composer: `opentelemetry-auto-wordpress` + SDK + exporter OTLP **Activación transparente:** ```ini ; otel.php.ini auto_prepend_file = ${OTEL_AUTOLOAD_PATH} ``` El intérprete ejecuta el autoload antes de cualquier script de WordPress, inicializando el SDK de forma completamente transparente. ## Wagtail — Python Instrumentación con **cero modificaciones** al código fuente ```bash # Durante la construcción de la imagen opentelemetry-bootstrap -a install # Entrypoint del contenedor CMD ["opentelemetry-instrument", "uwsgi", "/app/etc/uwsgi.ini"] ``` El wrapper `opentelemetry-instrument` intercepta el proceso antes de que la aplicación comience a procesar peticiones. ## Redmine — Ruby Instrumentación **manual** por falta de auto-instrumentador oficial en Ruby **Inicializador** `config/initializers/otel.rb`: ```ruby if ENV['OTEL_EXPORTER_OTLP_ENDPOINT'].present? require 'opentelemetry/sdk' require 'opentelemetry/exporter/otlp' require 'opentelemetry/instrumentation/all' OpenTelemetry::SDK.configure do |c| c.use_all() c.logger = Rails.logger end end ``` La librería `opentelemetry-instrumentation-all` instrumenta ActiveRecord, Action Pack, Net::HTTP y Redis mediante **monkey-patching**. ### Redmine — Fluentd para logs El SDK de Ruby **no soportaba exportar logs vía OTLP** al momento de implementar. Solución: Fluentd como puente entre Rails y el OTel Collector. El formatter incluye `trace_id` y `span_id` en cada línea de log: ```ruby if ENV['OTEL_EXPORTER_OTLP_ENDPOINT'].present? map[:span_id] = OpenTelemetry::Trace.current_span.context.hex_span_id map[:trace_id] = OpenTelemetry::Trace.current_span.context.hex_trace_id end ``` Esto preserva la **correlación entre logs y trazas** en Grafana. ## Contenerización Cada aplicación sigue el patrón de **imagen dual** (multi-stage build):
flowchart TD CF["Containerfile
multi-stage"] APP["stage: app
aplicación instrumentada"] NGX["stage: nginx
proxy reverso + archivos estáticos"] CF --> APP CF --> NGX
Toda la configuración se inyecta por **variables de entorno** (factor 3): ```bash OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 OTEL_SERVICE_NAME=wordpress OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production ``` Ambiente local con **Docker Compose**: infraestructura LGTM + las tres apps ## 5. Despliegue en Kubernetes Objetivo: demostrar el **escalado automático impulsado por telemetría** en un entorno equivalente al productivo
Cluster local con **Kind** (Kubernetes in Docker) Infraestructura instalada con Helmfile: | Componente | Rol | |---|---| | `ingress-nginx` | Controlador de Ingress, expone servicios en `*.localhost` | | `KEDA` | Operador de escalado basado en métricas externas | | `lgtm-distributed` | Stack de observabilidad: Loki + Grafana + Tempo + Mimir | ## Helm Chart genérico Un único chart en `helm/` despliega cualquiera de las tres aplicaciones. Recursos generados por release: - **Deployment** — pod con dos contenedores: app + sidecar Nginx - **ConfigMap / Secret** — variables de entorno (credenciales, endpoints OTel) - **Ingress** — expone el servicio en `
.localhost` - **ScaledObject** — reglas de escalado horizontal con KEDA - **PersistentVolumeClaim** — almacenamiento para archivos de usuario (WordPress, Wagtail) ## Escalado automático con KEDA **KEDA** (Kubernetes Event-Driven Autoscaling) escala Deployments en respuesta a métricas externas al clúster. ```yaml triggers: - type: prometheus metadata: serverAddress: http://lgtm-distributed-mimir-nginx.observability.svc/prometheus metricName: wagtail_request_rate threshold: "10" query: > sum(rate( traces_spanmetrics_calls_total{service_name="wagtail"}[1m] )) ``` Escala entre **1 y 5 réplicas** cuando supera **10 req/s por réplica** ### La métrica `traces_spanmetrics_calls_total` Generada automáticamente por el conector `spanmetrics` del OTel Collector **a partir de las trazas**, sin instrumentación adicional.
flowchart TD A["Trazas de la app"] --> B["OTel Collector
spanmetrics connector"] B --> C["traces_spanmetrics_calls_total"] C --> D[("Mimir")] D --> E["KEDA"]
> _La misma telemetría que se consulta en Grafana alimenta la lógica de escalado_ ## Prueba de carga con Locust Scripts de Locust simulan usuarios navegando las tres aplicaciones: ```bash locust -f locust/all.py \ --users 50 --spawn-rate 5 \ --headless --run-time 3m ``` Pesos: WordPress 3x · Redmine 2x · Wagtail 2x ### El ciclo observable en Grafana 1. **Tasa de peticiones en ascenso** → panel RED muestra el incremento en req/s 2. **KEDA evalúa el umbral** → consulta Mimir cada pocos segundos 3. **Scale-up** → nuevos pods pasan por `Pending → ContainerCreating → Running` 4. **Latencia se estabiliza** → el balanceador distribuye el tráfico entre réplicas 5. **Scale-down** → tras el `cooldownPeriod`, KEDA reduce a una réplica
> _Este ciclo cierra el argumento central: la observabilidad no solo describe > el sistema, sino que puede **actuar sobre él** de forma autónoma_ ## 6. Conclusiones ### Objetivo cumplido Se instrumentaron tres aplicaciones en lenguajes distintos **sin modificar su código fuente**, centralizando la telemetría en un stack open-source unificado. Desplegado en dos entornos complementarios: - **Docker Compose** — desarrollo local e inspección de trazas - **Kubernetes (Kind)** — escalado automático impulsado por telemetría ## Sobre los pilares de la observabilidad Las tres señales son **complementarias**, no redundantes: | Señal | Valor | |---|---| | **Métricas** | Estado general del sistema · insumo para alertas y escalado | | **Logs** | Detalle de eventos en la aplicación | | **Trazas** | Flujo completo de un requerimiento entre todos los componentes |
El valor emergente aparece al **correlacionarlas**: navegar de una alerta en métricas hasta la traza del requerimiento afectado y sus logs exactos reduce significativamente el **MTTR** _(Mean Time To Resolution)_. ## Sobre OpenTelemetry Resuelve la fragmentación histórica de herramientas propietarias: - Protocolo de transporte unificado **OTLP** para las tres señales - SDKs para los principales lenguajes de programación - Auto-instrumentación que elimina la necesidad de modificar el código - El **OTel Collector** desacopla la recolección del almacenamiento
Su adopción por **GrafanaLabs, New Relic y Datadog** confirma que OTLP se proyecta como la interfaz universal de la telemetría en la industria. ## Sobre el stack LGTM Alternativa open-source viable a soluciones comerciales: | Componente | Diferenciador | |---|---| | **Mimir** | Alta cardinalidad · API Prometheus compatible · OTLP | | **Loki** | Indexado solo por labels → menor costo de almacenamiento | | **Tempo** | Multi-formato · integración nativa con Grafana | | **Grafana** | Correlación interactiva entre las tres señales en un solo panel |
El conector `spanmetrics` del OTel Collector deriva métricas RED directamente desde trazas, **sin agentes adicionales**. ## Sobre la observabilidad y la resiliencia > _La adaptación automática solo es posible si el sistema > tiene visibilidad sobre su propio estado._
En la PoC, KEDA escala cada Deployment en función de `traces_spanmetrics_calls_total`, derivada de las trazas: - Sin cambios adicionales en la instrumentación - La misma telemetría que diagnostica un problema **lo resuelve** - Demuestra que la observabilidad es una **base para la resiliencia** ## Trabajos futuros - **Rollbacks automatizados** impulsados por telemetría post-despliegue - **Service mesh** (Istio / Linkerd) para trazas y métricas a nivel de red - **Profiling** como cuarta señal (en desarrollo activo en OTel) - Extensión a arquitecturas **multi-cluster** y entornos cloud ## Gracias
**Lucas Di Cunzolo** Director: Miguel Angel Luengo Codirector: Christian Adrian Rodriguez Asesor profesional: Leandro Di Tommaso
_Licenciatura en Informática — UNLP, Facultad de Informática_ _Marzo 2026_