Minilab: Integración de OpenTelemetry con Dynatrace usando PHP y Collector

Introducción

En este minilab aprenderás a implementar trazabilidad distribuida (distributed tracing) usando OpenTelemetry con Dynatrace como backend de observabilidad. Crearemos una aplicación PHP instrumentada que envía trazas personalizadas a través del OpenTelemetry Collector hacia Dynatrace.

¿Qué es OpenTelemetry?

OpenTelemetry (OTel) es un estándar abierto y vendor-neutral para la observabilidad de aplicaciones. Permite instrumentar, generar, recolectar y exportar datos de telemetría (trazas, métricas y logs) sin depender de un proveedor específico.

¿Por qué usar OpenTelemetry con Dynatrace?

  • Portabilidad: Código independiente del vendor
  • Flexibilidad: Puedes cambiar de backend sin modificar tu aplicación
  • Estándar de industria: Soportado por CNCF (Cloud Native Computing Foundation)
  • Integración nativa: Dynatrace soporta OTLP (OpenTelemetry Protocol) de forma nativa

Arquitectura del minilab

┌─────────────────┐
│   Navegador     │
│    (Cliente)    │
└────────┬────────┘
         │ HTTP
         ▼
┌─────────────────┐
│  Nginx (Puerto  │
│      80)        │
└────────┬────────┘
         │
         ▼
┌─────────────────┐      OTLP/HTTP        ┌──────────────────┐
│   PHP 8.2-FPM   │─────────────────────│  OpenTelemetry   │
│  + OpenTelemetry│      Port 4318       │    Collector     │
└─────────────────┘                      └─────────┬────────┘
                                                   │
                                                   │ OTLP + API Token
                                                   ▼
                                         ┌──────────────────┐
                                         │    Dynatrace     │
                                         │   (SaaS Tenant)  │
                                         └──────────────────┘


Requisitos previos

Antes de comenzar, asegúrate de tener:

VirtualBox con Ubuntu Server 24.04 instalado
Docker y Docker Compose instalados
✅ Acceso SSH a tu servidor Ubuntu
Cuenta de Dynatrace (trial de 15 días gratuito)
✅ Conexión a internet desde el servidor


Fase 1: Preparar Dynatrace

Paso 1.1: Crear Access Token

  1. Accede a tu tenant de Dynatrace
  2. Ve a AppsManageAccess tokens
  3. Click en Generate new token
  4. Configura el token:
    • Nombre: opentelemetry-lab
    • Permisos necesarios:
      • metrics.ingest – Ingest metrics
      • logs.ingest – Ingest logs
      • openTelemetryTrace.ingest – Ingest OpenTelemetry traces
  5. Genera el token y cópialo (solo se muestra una vez)

Paso 1.2: Anotar el endpoint OTLP

Tu endpoint OTLP de Dynatrace tiene este formato:

https://{tu-environment-id}.live.dynatrace.com/api/v2/otlp

Por ejemplo: https://abc12345.live.dynatrace.com/api/v2/otlp

Anota tu environment ID y el token para usarlos más adelante.


Fase 2: Instalar OpenTelemetry Collector

El OpenTelemetry Collector es un componente que recibe, procesa y exporta datos de telemetría. Lo instalaremos directamente en el host (fuera de Docker).

Paso 2.1: Conectar al servidor

ssh usuario@ip-de-tu-servidor

Paso 2.2: Descargar e instalar el Collector

# Crear directorio para el Collector
sudo mkdir -p /opt/otelcol

# Descargar el OpenTelemetry Collector Contrib
cd /opt/otelcol
sudo wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.115.1/otelcol-contrib_0.115.1_linux_amd64.tar.gz

# Descomprimir
sudo tar -xvzf otelcol-contrib_0.115.1_linux_amd64.tar.gz

# Dar permisos de ejecución
sudo chmod +x otelcol-contrib

Paso 2.3: Crear archivo de configuración

Crea el archivo de configuración del Collector:

sudo nano /opt/otelcol/config.yaml

Pega este contenido (reemplaza {TU_ENVIRONMENT_ID} y {TU_TOKEN}):

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:
    timeout: 10s
    send_batch_size: 1024

exporters:
  otlphttp:
    endpoint: https://{TU_ENVIRONMENT_ID}.live.dynatrace.com/api/v2/otlp
    headers:
      Authorization: "Api-Token {TU_TOKEN}"

  debug:
    verbosity: detailed

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp, debug]
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp, debug]
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp, debug]

Ejemplo real:

exporters:
  otlphttp:
    endpoint: https://jdn3490734.live.dynatrace.com/api/v2/otlp
    headers:
      Authorization: "Api-Token dt0c01.BCY2IAH34T7VMZF2OLJN7NBT.UJNENUKNO5SEUHNDCBZMPBUP2HTOXOS3QHAA3DGEEELC5EDUHN"

Guarda el archivo: Ctrl + O, Enter, Ctrl + X

Paso 2.4: Ejecutar el Collector

Inicia el Collector en una terminal:

cd /opt/otelcol
sudo ./otelcol-contrib --config=config.yaml

Deberías ver:

2025-12-25T15:13:59.398Z  info  service@v0.115.0/service.go:166  Setting up own telemetry...
2025-12-25T15:13:59.404Z  info  service@v0.115.0/service.go:238  Starting otelcol-contrib...
2025-12-25T15:13:59.404Z  info  extensions/extensions.go:39      Starting extensions...
2025-12-25T15:13:59.406Z  info  service@v0.115.0/service.go:261  Everything is ready. Begin running and processing data.

Deja esta terminal abierta con el Collector corriendo. Abre una nueva terminal SSH para continuar con los siguientes pasos.


Fase 3: Configurar la aplicación PHP con OpenTelemetry

Paso 3.1: Crear la estructura del proyecto

En la nueva terminal:

# Crear directorio del proyecto
mkdir -p ~/lab-dynatrace/html
cd ~/lab-dynatrace

Paso 3.2: Crear Dockerfile para PHP

nano Dockerfile.php

Contenido:

FROM php:8.2-fpm

# Instalar dependencias necesarias
RUN apt-get update && apt-get install -y \
    git \
    unzip \
    libzip-dev \
    && docker-php-ext-install zip

# Instalar la extensión OpenTelemetry para PHP
RUN pecl install opentelemetry-1.1.0 \
    && docker-php-ext-enable opentelemetry

# Instalar Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Directorio de trabajo
WORKDIR /var/www/html

CMD ["php-fpm"]

Paso 3.3: Crear composer.json

nano composer.json

Contenido:

{
    "require": {
        "open-telemetry/sdk": "^1.0",
        "open-telemetry/exporter-otlp": "^1.0",
        "open-telemetry/opentelemetry-auto-slim": "^1.0"
    }
}

Paso 3.4: Crear docker-compose.yml

nano docker-compose.yml

Contenido:

version: '3'
services:
  web:
    image: nginx:latest
    container_name: lab-dynatrace_web_1
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - ./html:/usr/share/nginx/html
    depends_on:
      - app
    networks:
      - dynatrace-net

  app:
    build:
      context: .
      dockerfile: Dockerfile.php
    container_name: lab-dynatrace_app_1
    volumes:
      - ./html:/var/www/html
    environment:
      - OTEL_PHP_AUTOLOAD_ENABLED=true
      - OTEL_SERVICE_NAME=lab-dynatrace-php
      - OTEL_TRACES_EXPORTER=otlp
      - OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
      - OTEL_EXPORTER_OTLP_ENDPOINT=http://host.docker.internal:4318
      - OTEL_PROPAGATORS=tracecontext,baggage
    extra_hosts:
      - "host.docker.internal:host-gateway"
    networks:
      - dynatrace-net

networks:
  dynatrace-net:
    driver: bridge

Paso 3.5: Crear configuración de Nginx

nano nginx.conf

Contenido:

server {
    listen 80;
    server_name localhost;
    root /usr/share/nginx/html;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Paso 3.6: Crear la aplicación PHP con instrumentación OpenTelemetry

nano html/index.php

Contenido completo:

<?php
require_once '/var/www/html/vendor/autoload.php';

use OpenTelemetry\API\Globals;
use OpenTelemetry\API\Trace\Span;
use OpenTelemetry\API\Trace\SpanKind;
use OpenTelemetry\API\Trace\StatusCode;

// Obtener el tracer
$tracer = Globals::tracerProvider()->getTracer('lab-dynatrace-php');

// Crear un span para la petición principal
$rootSpan = $tracer->spanBuilder('http_request')
    ->setSpanKind(SpanKind::KIND_SERVER)
    ->setAttribute('http.method', $_SERVER['REQUEST_METHOD'])
    ->setAttribute('http.url', $_SERVER['REQUEST_URI'])
    ->setAttribute('http.host', $_SERVER['HTTP_HOST'])
    ->startSpan();

$scope = $rootSpan->activate();

try {
    // Simular una operación de base de datos
    $dbSpan = $tracer->spanBuilder('database_query')
        ->setSpanKind(SpanKind::KIND_CLIENT)
        ->setAttribute('db.system', 'mysql')
        ->setAttribute('db.operation', 'SELECT')
        ->startSpan();
    
    usleep(50000); // Simular 50ms de latencia
    $serverTime = date('Y-m-d H:i:s');
    
    $dbSpan->setStatus(StatusCode::STATUS_OK);
    $dbSpan->end();

    // Simular procesamiento de negocio
    $businessSpan = $tracer->spanBuilder('business_logic')
        ->setAttribute('operation', 'calculate_server_status')
        ->startSpan();
    
    usleep(30000); // Simular 30ms de procesamiento
    $phpVersion = phpversion();
    
    $businessSpan->addEvent('calculation_complete', [
        'result' => 'success',
        'php_version' => $phpVersion
    ]);
    $businessSpan->end();

    $rootSpan->setStatus(StatusCode::STATUS_OK);

} catch (Exception $e) {
    $rootSpan->recordException($e);
    $rootSpan->setStatus(StatusCode::STATUS_ERROR, $e->getMessage());
} finally {
    $scope->detach();
    $rootSpan->end();
}
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Laboratorio Dynatrace + OpenTelemetry</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 50px auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            background: white;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        h1 {
            color: #1496ff;
        }
        .badge {
            display: inline-block;
            background: #4caf50;
            color: white;
            padding: 5px 10px;
            border-radius: 3px;
            font-size: 12px;
            margin-left: 10px;
        }
        button {
            background-color: #1496ff;
            color: white;
            border: none;
            padding: 10px 20px;
            font-size: 16px;
            border-radius: 4px;
            cursor: pointer;
            margin: 10px 5px;
        }
        button:hover {
            background-color: #0d7cd6;
        }
        .status {
            margin-top: 20px;
            padding: 15px;
            background-color: #e8f5e9;
            border-left: 4px solid #4caf50;
            border-radius: 4px;
        }
        .otel-info {
            margin-top: 20px;
            padding: 15px;
            background-color: #e3f2fd;
            border-left: 4px solid #2196f3;
            border-radius: 4px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🚀 Laboratorio Dynatrace - Sistema Activo<span class="badge">OpenTelemetry</span></h1>
        
        <div class="status">
            <strong>✅ Estado del Backend:</strong> PHP <?php echo $phpVersion; ?> funcionando correctamente
        </div>

        <div class="otel-info">
            <strong>📊 OpenTelemetry Activo:</strong> Esta página está enviando trazas distribuidas a Dynatrace
        </div>
        
        <p>Este es un laboratorio de pruebas para explorar las capacidades de monitorización Full-Stack de Dynatrace.</p>
        <p><strong>Tiempo del servidor:</strong> <?php echo $serverTime; ?></p>
        
        <h3>Prueba de Real User Monitoring (RUM)</h3>
        <p>Haz clic en el botón para generar un error JavaScript que será capturado por Dynatrace:</p>
        <button id="btnError">⚠️ Generar Error JavaScript</button>

        <h3>Prueba de OpenTelemetry</h3>
        <p>Genera tráfico para ver las trazas en Dynatrace:</p>
        <button id="btnTrace">🔍 Generar Trazas OTLP</button>
        
        <script>
            document.getElementById('btnError').addEventListener('click', function() {
                console.error("Error forzado para pruebas de RUM en Dynatrace");
                throw new Error("Error de prueba generado desde el laboratorio Dynatrace");
            });

            document.getElementById('btnTrace').addEventListener('click', function() {
                // Recargar la página para generar nuevas trazas
                for(let i = 0; i < 5; i++) {
                    fetch(window.location.href)
                        .then(response => console.log('Traza generada ' + (i+1)))
                        .catch(error => console.error('Error:', error));
                }
                alert('5 peticiones enviadas. Revisa Dynatrace en unos minutos para ver las trazas distribuidas.');
            });
        </script>
    </div>
</body>
</html>

Paso 3.7: Construir y levantar los contenedores

# Construir la imagen de PHP con OpenTelemetry
docker-compose build

# Levantar los servicios
docker-compose up -d

Este proceso puede tardar unos minutos la primera vez.

Paso 3.8: Instalar dependencias de Composer dentro del contenedor

# Entrar al contenedor
docker exec -it lab-dynatrace_app_1 bash

# Dentro del contenedor, crear composer.json
cat > /var/www/html/composer.json << 'EOF'
{
    "require": {
        "open-telemetry/sdk": "^1.0",
        "open-telemetry/exporter-otlp": "^1.0",
        "open-telemetry/opentelemetry-auto-slim": "^1.0"
    }
}
EOF

# Instalar dependencias
cd /var/www/html
composer install --no-dev --optimize-autoloader

# Salir del contenedor
exit

Paso 3.9: Reiniciar el contenedor

docker-compose restart app


Fase 4: Verificar y validar

Paso 4.1: Verificar que los contenedores están corriendo

docker-compose ps

Deberías ver ambos contenedores en estado «Up»:

        Name                      Command               State         Ports
----------------------------------------------------------------------------------
lab-dynatrace_app_1   docker-php-entrypoint php-fpm   Up      9000/tcp
lab-dynatrace_web_1   /docker-entrypoint.sh ngin...   Up      0.0.0.0:80->80/tcp

Paso 4.2: Verificar logs de PHP

docker-compose logs -f app

Deberías ver:

app_1  | [25-Dec-2025 15:50:38] NOTICE: fpm is running, pid 1
app_1  | [25-Dec-2025 15:50:38] NOTICE: ready to handle connections

Sin errores de OpenTelemetry. Presiona Ctrl + C para salir.

Paso 4.3: Acceder a la aplicación

Abre tu navegador y accede a:

http://localhost:8080

Deberías ver la página del laboratorio con el badge «OpenTelemetry».

Paso 4.4: Generar tráfico

Haz clic varias veces en el botón «🔍 Generar Trazas OTLP» para generar peticiones.

Paso 4.5: Verificar el Collector

Ve a la terminal donde está corriendo el OpenTelemetry Collector. Deberías empezar a ver logs detallados mostrando las trazas que está recibiendo y exportando:

ScopeSpans #0
ScopeSpans SchemaURL: 
InstrumentationScope lab-dynatrace-php
Span #0
    Trace ID       : f96608e0b9c26fd2b5c747b7e2556b81
    Parent ID      : c9e90f32737efcce
    ID             : 35c5c642bcecfd00
    Name           : database_query
    Kind           : Client
    Start time     : 2025-12-25 15:54:27.137092923 +0000 UTC
    End time       : 2025-12-25 15:54:27.187654329 +0000 UTC
    Status code    : Ok
Attributes:
    -> db.system: Str(mysql)
    -> db.operation: Str(SELECT)
Span #1
    Trace ID       : f96608e0b9c26fd2b5c747b7e2556b81
    Parent ID      : c9e90f32737efcce
    ID             : 2234cf58edd677094
    Name           : business_logic
    Kind           : Internal
    ...

Esto confirma que: ✅ La aplicación PHP está generando trazas ✅ El Collector las está recibiendo ✅ El Collector las está exportando a Dynatrace

Paso 4.6: Verificar en Dynatrace

  1. Accede a tu tenant de Dynatrace
  2. Ve a ServicesServices Classic (en el menú de Application Observability)
  3. Busca el servicio lab-dynatrace-php
  4. Haz clic en él

Deberías ver:

Service Metrics:

  • Response time: ~81-92 ms
  • Throughput: peticiones por minuto
  • Failed requests: 0%

Distributed Traces:

  • Lista de trazas http_request
  • Haz clic en cualquier traza

Trace Detail: Verás la traza distribuida completa con los 3 spans:

  1. http_request – El span principal (servidor)
  2. database_query – La consulta simulada a MySQL
  3. business_logic – El procesamiento de negocio

Cada span muestra:

  • Duración
  • Atributos personalizados (db.system, db.operation, etc.)
  • Eventos (calculation_complete)
  • Timeline visual

Explicación de los conceptos

¿Qué es un Span?

Un span representa una unidad de trabajo en un sistema distribuido. Contiene:

  • Nombre de la operación
  • Timestamp de inicio y fin
  • Atributos (metadata)
  • Eventos
  • Relación con otros spans (parent/child)

¿Qué es un Trace?

Un trace (traza) es una colección de spans que representan el flujo completo de una petición a través de un sistema distribuido.

Tipos de Spans que creamos

// 1. Span de tipo SERVER (entrada HTTP)
$rootSpan = $tracer->spanBuilder('http_request')
    ->setSpanKind(SpanKind::KIND_SERVER)
    ->setAttribute('http.method', 'GET')
    ->startSpan();

// 2. Span de tipo CLIENT (llamada externa - BD)
$dbSpan = $tracer->spanBuilder('database_query')
    ->setSpanKind(SpanKind::KIND_CLIENT)
    ->setAttribute('db.system', 'mysql')
    ->startSpan();

// 3. Span INTERNAL (procesamiento interno)
$businessSpan = $tracer->spanBuilder('business_logic')
    ->setAttribute('operation', 'calculate_server_status')
    ->startSpan();

Flujo de datos

┌──────────────┐
│  PHP App     │  1. Genera spans con OpenTelemetry SDK
└──────┬───────┘
       │ OTLP/HTTP (puerto 4318)
       ▼
┌──────────────┐
│  Collector   │  2. Recibe, procesa (batch), exporta
└──────┬───────┘
       │ OTLP/HTTP + API Token
       ▼
┌──────────────┐
│  Dynatrace   │  3. Almacena y visualiza
└──────────────┘


Conceptos clave de OpenTelemetry

1. Tracer Provider

Es la fábrica que crea tracers. En nuestro código:

$tracer = Globals::tracerProvider()->getTracer('lab-dynatrace-php');

2. Context Propagation

OpenTelemetry propaga el contexto (trace ID, span ID) automáticamente entre componentes para mantener la trazabilidad.

3. Exporters

Componentes que envían la telemetría a backends. Usamos:

  • OTLP HTTP Exporter: Para enviar a Dynatrace
  • Debug Exporter: Para ver logs en consola

4. Processors

Transforman o filtran los datos antes de exportarlos. Usamos:

  • Batch Processor: Agrupa múltiples spans antes de enviarlos (más eficiente)

Ventajas de este enfoque

✅ Vendor Neutral

El código de instrumentación es estándar OpenTelemetry. Puedes cambiar de Dynatrace a Jaeger, Zipkin, o cualquier otro backend compatible simplemente modificando la configuración del Collector.

✅ Separation of Concerns

  • Aplicación: Solo se preocupa de generar telemetría estándar
  • Collector: Maneja el enrutamiento, procesamiento y exportación
  • Backend: Visualiza y analiza

✅ Flexibilidad

El Collector puede:

  • Filtrar datos sensibles
  • Enriquecer con metadata adicional
  • Enviar a múltiples backends simultáneamente
  • Aplicar sampling (muestreo)

✅ Control de costes

Puedes controlar qué datos envías y a dónde, optimizando costes de ingesta.


Troubleshooting

Problema: Errores «Transport factory not defined for protocol: grpc»

Solución: Asegúrate de usar http/protobuf en lugar de grpc:

- OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
- OTEL_EXPORTER_OTLP_ENDPOINT=http://host.docker.internal:4318

Problema: No veo trazas en Dynatrace

Verificar:

  1. El Collector está corriendo y no muestra errores
  2. El token tiene los permisos correctos
  3. El endpoint de Dynatrace es correcto
  4. Hay tráfico generado (recargar la página varias veces)
# Ver logs del Collector
# Deberías ver "ScopeSpans" en los logs

# Ver logs de PHP
docker-compose logs app
# No debe haber errores de OpenTelemetry

Problema: vendor/autoload.php not found

Solución: Instalar dependencias de Composer dentro del contenedor:

docker exec -it lab-dynatrace_app_1 bash
cd /var/www/html
composer install
exit
docker-compose restart app


Limpieza (Cleanup)

Para detener y eliminar todos los recursos:

# Detener contenedores
docker-compose down

# Eliminar imágenes (opcional)
docker-compose down --rmi all

# Detener el Collector
# En la terminal del Collector: Ctrl + C


Recursos adicionales


Conclusiones

En este minilab has aprendido a:

✅ Instalar y configurar el OpenTelemetry Collector
✅ Instrumentar una aplicación PHP con OpenTelemetry SDK
✅ Crear trazas personalizadas (custom spans) con atributos y eventos
✅ Enviar telemetría a Dynatrace usando OTLP
✅ Visualizar trazas distribuidas en Dynatrace
✅ Entender la arquitectura de observabilidad con OpenTelemetry

Este conocimiento es fundamental para:

  • Examen Dynatrace Associate: OpenTelemetry es parte del temario oficial
  • Observabilidad moderna: Estándar de industria adoptado por empresas cloud-native
  • Troubleshooting: Identificar cuellos de botella y problemas de rendimiento en sistemas distribuidos

¡Felicidades por completar el minilab!


Autor: Observa Sistemas
Fecha: Diciembre 2025
Versión: 1.0
Tags: #Dynatrace #OpenTelemetry #ObservabilityAsCode #DistributedTracing #PHP #Docker

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.

ACEPTAR
Aviso de cookies