Trazas con las que depuras de verdad, no trazas que recoges
Casi todos los despliegues de trazado producen tramos sueltos desconectados y una factura. La propagación del contexto, el muestreo por cola y un puñado de atributos útiles son lo que convierte un almacén de trazas en la herramienta que abres durante un incidente.
El proyecto de trazado termina, los paneles impresionan, y seis meses después nadie ha abierto la interfaz de trazas durante un incidente. Cuando miras por qué, casi siempre es una de dos cosas. O las trazas se rompen en el tercer salto y nunca puedes ver la petición entera, o la única traza que necesitabas no se muestreó.
Las dos tienen arreglo, y ninguna se arregla recogiendo más. El trazado es la única señal que responde a "por qué fue lenta esta petición concreta", y se gana su coste solo cuando alguien puede seguir una petición real de punta a punta en el momento en que lo necesita.
La propagación del contexto es donde se rompe
Una traza es un árbol de tramos unidos por un identificador de traza que viaja en la petición. La instrumentación automática se encarga de esto para llamadas HTTP y gRPC normales entre servicios instrumentados, y por eso la primera demostración siempre funciona.
Se rompe en los sitios que la instrumentación automática no ve:
- Colas e intermediarios de mensajes. El productor tiene que escribir el contexto de traza en las cabeceras del mensaje y el consumidor tiene que volver a leerlo. Sin eso, el trabajo hecho en el consumidor se convierte en una traza huérfana y el vínculo causal con la petición que lo desencadenó desaparece. Es la rotura más común que encontramos y la más dañina, porque el trabajo asíncrono es justo donde se esconde la latencia.
- Trabajos por lotes y tareas programadas. No hay petición entrante, así que decide a conciencia: una traza por ejecución, o una por elemento procesado, con un enlace de vuelta a la petición de origen cuando la haya.
- Clientes y librerías de terceros. La librería de un proveedor que construye su propia petición HTTP no llevará tus cabeceras salvo que la enganches.
- Todo lo que cruza una frontera de lenguaje o de marco donde un lado usa el formato estándar del consorcio web y el otro un formato heredado de un proveedor. Estandariza en el contexto de traza del consorcio web y configura los propagadores explícitamente en vez de fiarte de los valores por defecto.
- Los frontales. Si la traza empieza en tu balanceador, no puedes ver el tiempo que el usuario esperó de verdad. Empezarla en el navegador o en el cliente móvil da más trabajo y es la diferencia entre "la API tardó ochenta milisegundos" y "la página tardó cuatro segundos".
Prueba la propagación a propósito. Manda una petición por el camino más largo que tengas, incluido un salto por cola, y confirma que la traza tiene el número de tramos que esperas. Hazlo en integración continua si puedes.
El muestreo por cola es lo que quieres, y cuesta más
El muestreo por cabeza decide al principio de la petición, antes de que haya pasado nada, normalmente conservando un porcentaje fijo. Al uno por ciento te quedas con una centésima del tráfico y no vas a tener la traza del incidente, porque los errores son raros por definición y descartar trazas al azar los descarta en la misma proporción.
El muestreo por cola decide cuando la traza está completa, cuando ya sabes si fue lenta o falló. Eso te permite escribir la política que de verdad quieres: conserva todo lo que dio error, conserva todo lo que superó un umbral de latencia, conserva un pequeño porcentaje base de peticiones correctas para comparar, y conserva todo lo de un cliente o un extremo concreto que estés investigando.
El coste es que el colector tiene que retener en memoria todos los tramos de una traza hasta que esté completa, lo que necesita memoria y, sobre todo, que todos los tramos de una traza lleguen a la misma instancia del colector. Eso implica un exportador con reparto de carga delante de una capa de colectores que muestrean por cola, repartiendo por identificador de traza. Es genuinamente más infraestructura. Y es también la diferencia entre un almacén de trazas que usas y uno que pagas.
Un camino intermedio razonable, si el muestreo por cola es demasiado por ahora: conserva el cien por cien de los errores con muestreo por cabeza en el camino de error donde tu marco lo permita, más un porcentaje base más alto del que crees que necesitas, y revísalo cuando el volumen duela. El colector de OpenTelemetry es el sitio de todo esto, no el código de la aplicación.
Los atributos son lo que hace buscable una traza
Una traza con estructura perfecta y sin atributos te dice que una petición fue lenta pero no cuál. Los atributos del tramo son la diferencia entre navegar y encontrar.
Pon en el tramo raíz, como mínimo: la plantilla de la ruta en vez de la URL resuelta, el método y el estado HTTP, el identificador de inquilino o de cliente, el identificador de usuario, la versión desplegada y la región o celda. En los tramos descendentes: la sentencia de base de datos en su forma parametrizada, jamás con los valores interpolados, la tabla o colección de destino, el nombre de la cola o el tema, el nombre del servicio remoto y el número de intento del reintento.
Sigue las convenciones semánticas de OpenTelemetry en todo lo que cubran. La recompensa es que tu backend puede construir mapas de servicios y desgloses de latencia automáticamente en vez de que escribas consultas a medida por servicio.
Este es además el sitio correcto para los identificadores de alta cardinalidad. Un identificador de usuario en una etiqueta de Prometheus es la forma de matar Prometheus, como se cuenta en Prometheus a escala. El mismo identificador en un atributo de tramo es exactamente correcto, porque los backends de trazas están construidos para indexar campos de alta cardinalidad y los almacenes de métricas no. Esa división es la razón entera de operar los dos.
Nunca metas secretos, cuerpos completos de petición, cabeceras de autorización ni datos personales en los atributos. Los tramos van a un almacén con otra retención y otro control de acceso que tu base de datos de aplicación, y la carga útil de una traza es una forma muy fácil de filtrar un token al sistema de un proveedor.
El camino de la alerta al tramo
La razón de cablear las tres señales juntas es un único flujo de incidente que lleva minutos en vez de una tarde.
Una alerta se dispara sobre un síntoma, normalmente tasa de error o latencia en un extremo de cara al usuario. La alerta enlaza a un panel acotado a ese servicio. Desde el panel saltas a los ejemplares, que son identificadores de traza adjuntos a muestras concretas de la métrica, así que aterrizas en una traza que produjo el número que estás mirando y no en una petición lenta al azar. En la traza encuentras el tramo que se comió el tiempo. Desde ese tramo saltas a los registros de ese identificador de traza, filtrados a esa petición, y lees el error.
Cada uno de esos saltos tiene que ser un enlace, no un copiar y pegar. Los ejemplares hay que activarlos en el pipeline de métricas. Los registros necesitan el identificador de traza como campo estructurado, que es el campo más valioso de una línea de log y no cuesta nada, como se argumenta en el coste y la estructura de los logs.
Si tu equipo no puede hacer esa secuencia hoy, practícala en un día de juego antes de necesitarla a las tres de la madrugada.
Lo que se olvida
- La deriva de reloj hace que los tramos parezcan imposibles. Tramos hijos que empiezan antes que su padre, o duraciones negativas, son casi siempre desincronización horaria en una máquina.
- La decisión de muestreo tiene que ser coherente en toda la traza. Si un servicio muestrea por su cuenta, obtienes media traza, que es peor que ninguna.
- El almacenamiento de trazas no es barato y suele cobrarse por tramo. Una instrumentación parlanchina que emite un tramo por llamada al mapeador de objetos produce miles de tramos por petición. Agrégalos.
- Una traza sin error también sirve. Las investigaciones de latencia son la mayor parte del uso real, no las de error.
- La retención debería ser corta. De siete a treinta días cubre casi cualquier investigación. Guardar trazas un año es pagar por almacenar datos que nadie va a consultar.
Qué hacer esta semana
Coge una petición que pase por una cola, ejecútala de punta a punta y abre la traza. Cuenta los tramos y compáralos con el número de servicios que tocó de verdad. Si la traza se corta en la cola, has encontrado tu arreglo de mayor valor, y suele ser una docena de líneas entre productor y consumidor.