BLOG | NGINX

Tutorial do NGINX: Como usar o OpenTelemetry Tracing para entender seus microsserviços

NGINX-Parte-de-F5-horiz-preto-tipo-RGB
Javier Evans Miniatura
Javier Evans
Publicado em 28 de março de 2023

Este post é um dos quatro tutoriais que ajudam você a colocar em prática os conceitos do Microservices de março de 2023: Comece a fornecer microsserviços :

 

Uma arquitetura de microsserviços traz muitos benefícios, incluindo maior autonomia da equipe e maior flexibilidade em dimensionamento e implantação. Por outro lado, quanto mais serviços houver em um sistema (e um aplicativo de microsserviços pode ter dezenas ou até centenas), mais difícil será manter uma imagem clara da operação geral do sistema. Como escritores e mantenedores de sistemas de software complexos, sabemos da importância vital de ter essa imagem clara. Observabilidade as ferramentas nos dão o poder de construir essa imagem em vários serviços e infraestrutura de suporte.

Neste tutorial, destacamos um tipo muito importante de observabilidade para aplicativos de microsserviços: rastreamento. Antes de começar, vamos definir alguns termos comumente usados ao discutir observabilidade:

  • Observabilidade – A capacidade de entender o estado ou condição interna de um sistema complexo (como um aplicativo de microsserviços) com base apenas no conhecimento de suas saídas externas (como rastros, logs e métricas).
  • Monitoramento – Capacidade de observar e verificar o progresso ou status de um objeto ao longo de um período de tempo. Por exemplo, você pode monitorar o tráfego que chega ao seu aplicativo durante os horários de pico e usar essa informação para dimensioná-lo adequadamente em resposta.
  • Telemetria – O ato de coletar métricas, rastros e registros e transferi-los de seu ponto de origem para outro sistema para armazenamento e análise. Além disso, os dados em si.
  • Rastreamento/rastros – Um relato da jornada de uma solicitação ou ação à medida que ela se move por todos os nós de um sistema distribuído.
  • Span – Um registro dentro de um rastreamento de uma operação e seus metadados associados. Os traços são compostos de muitos intervalos aninhados.
  • Registro/logs de eventos – Um registro de texto com carimbo de data/hora e metadados.
  • Métrica – Uma medição capturada em tempo de execução. Por exemplo, a quantidade de memória usada por um aplicativo em um determinado momento.

Podemos usar todos esses conceitos para obter insights sobre o desempenho dos nossos microsserviços. O rastreamento é uma parte particularmente útil de uma estratégia de observabilidade porque os rastros oferecem uma “visão geral” do que está acontecendo em vários componentes, geralmente fracamente acoplados, quando uma solicitação é feita. Também é uma maneira particularmente eficaz de identificar gargalos de desempenho.

Este tutorial usa o kit de ferramentas de rastreamento do OpenTelemetry (OTel), um padrão de código aberto independente de fornecedor para coleta, processamento e exportação de telemetria que está rapidamente ganhando popularidade. Na concepção da OTel, um rastreamento divide um fluxo de dados, que pode envolver vários serviços, em uma série de “pedaços” organizados cronologicamente que podem ajudar você a entender facilmente:

  • Todos os passos que aconteceram em um “pedaço”
  • Quanto tempo todas essas etapas levaram
  • Metadados sobre cada etapa

Se você não conhece o OTel, consulte O que é OpenTelemetry? para uma introdução completa ao padrão e considerações para implementá-lo.

Visão geral do tutorial

Este tutorial se concentra no rastreamento das operações de um aplicativo de microsserviços com OTel. Nos quatro desafios deste tutorial, você aprenderá a rastrear uma solicitação por meio do seu sistema e responder a perguntas sobre seus microsserviços:

Esses desafios ilustram nosso processo recomendado ao configurar o rastreamento pela primeira vez. Os passos são:

  1. Entenda o sistema e também a operação específica que você está instrumentando.
  2. Decida o que você precisa saber sobre o sistema em execução.
  3. Instrumente o sistema “ingenuamente” – ou seja, use uma configuração padrão sem tentar eliminar informações que você não precisa ou reunir pontos de dados personalizados – e avalie se a instrumentação ajuda você a responder às suas perguntas.
  4. Ajuste as informações relatadas para que você possa responder a essas perguntas mais rapidamente.

Observação:  Nossa intenção neste tutorial é ilustrar alguns conceitos básicos sobre telemetria, não mostrar a maneira correta de implantar microsserviços em produção. Embora use uma arquitetura real de “microsserviços”, há algumas ressalvas importantes:

  • O tutorial não usa uma estrutura de orquestração de contêineres, como Kubernetes ou Nomad. Isso é para que você possa aprender sobre conceitos de microsserviços sem se prender às especificidades de uma determinada estrutura. Os padrões apresentados aqui são portáteis para um sistema que executa uma dessas estruturas.
  • Os serviços são otimizados para facilitar a compreensão e não para o rigor da engenharia de software. O objetivo é analisar o papel de um serviço no sistema e seus padrões de comunicação, não as especificidades do código. Para mais informações, consulte os arquivos README dos serviços individuais.

Arquitetura do tutorial e objetivos de telemetria

Arquitetura e Fluxo do Usuário

Este diagrama ilustra a arquitetura geral e o fluxo de dados entre os microsserviços e outros elementos usados no tutorial.

Diagrama mostrando a topologia usada no tutorial, com rastreamento OpenTelemetry de um sistema de mensagens com dois microsserviços, NGINX e RabbitMQ

Os dois microsserviços são:

Três peças de infraestrutura de suporte são:

  • NGINX Open Source – Um ponto de entrada para o serviço de mensagens e o sistema em geral
  • RabbitMQ – Um popular corretor de mensagens de código aberto que permite que os serviços se comuniquem de forma assíncrona
  • Jaeger – Um sistema de rastreamento distribuído de ponta a ponta de código aberto para coleta e visualização de telemetria de componentes do sistema que a produzem

Tirando o OTel de cena por um momento, podemos nos concentrar na sequência de eventos que estamos rastreando: o que acontece quando um usuário envia uma nova mensagem de bate-papo e o destinatário é notificado sobre isso.

Diagrama mostrando o fluxo de informações no sistema de mensagens usado no tutorial

O fluxo se divide assim:

  1. Um usuário envia uma mensagem para o serviço de mensagens . O proxy reverso NGINX intercepta a mensagem e a encaminha para uma das muitas instâncias do serviço de mensagens .
  2. O serviço de mensagens grava a nova mensagem em seu banco de dados.
  3. O serviço de mensagens produz um evento em uma fila de mensagens do RabbitMQ chamada chat_queue para indicar que uma mensagem foi enviada. O evento é genérico e não tem um alvo específico.
  4. Ao mesmo tempo:

    • 4a. O serviço de mensagens retorna uma resposta ao remetente informando que a mensagem foi enviada com sucesso.
    • 4b. O serviço notificador percebe o novo evento no chat_queue e o consome.
  5. O serviço notificador verifica seu banco de dados para as preferências de notificação do destinatário da nova mensagem.
  6. O serviço de notificação usa o método preferido do destinatário para enviar uma ou muitas notificações (neste tutorial, as opções de método são SMS e e-mail).

Metas de telemetria

Ao configurar a instrumentação de telemetria, é melhor começar com um conjunto de objetivos para instrumentação mais definidos do que “enviar tudo e esperar por insights”. Temos três objetivos principais de telemetria para este tutorial:

  1. Entenda todas as etapas pelas quais uma solicitação passa durante o fluxo de novas mensagens
  2. Tenha certeza de que o fluxo está sendo executado de ponta a ponta em cinco segundos em condições normais
  3. Veja quanto tempo leva para o serviço de notificação começar a processar o evento despachado pelo serviço de mensagens (atraso excessivo pode significar que o serviço de notificação está tendo problemas para ler a fila de eventos e os eventos estão sendo armazenados)

Observe que essas metas estão relacionadas tanto à operação técnica do sistema quanto à experiência do usuário.

Pré-requisitos e configuração do tutorial

Pré-requisitos

Para concluir o tutorial em seu próprio ambiente, você precisa:

  • Um ambiente compatível com Linux/Unix

    Observação:  As atividades neste tutorial que envolvem o rastreamento do NGINX não funcionam em processadores baseados em ARM porque o módulo OpenTelemetry para NGINX não é compatível. (Isso inclui arquiteturas Linux aarch64 e máquinas Apple com o chip M1 ou M2.) As atividades que envolvem os serviços de mensageiro e notificador funcionam em todas as arquiteturas.

  • Familiaridade básica com a linha de comando do Linux, JavaScript e bash (mas todo o código e comandos são fornecidos e explicados, para que você ainda possa ter sucesso com conhecimento limitado)
  • Docker e Docker Compose
  • Node.js 19.x ou posterior

    • Testamos a versão 19.x, mas esperamos que versões mais recentes do Node.js também funcionem.
    • Para obter informações detalhadas sobre a instalação do Node.js, consulte o README no repositório do serviço de mensagens . Você também pode instalar o asdf para obter exatamente a mesma versão do Node.js usada no tutorial.
  • curl (já instalado na maioria dos sistemas)
  • As tecnologias listadas em Arquitetura e Fluxo do Usuário : messenger e notifier (você fará o download delas na próxima seção), NGINX Open Source , Jaeger e RabbitMQ .

Observação:  O tutorial usa o JavaScript SDK porque os serviços de mensagens e notificações são escritos em Node.js. Você também configura o recurso de instrumentação automática do OTel (também chamado de autoinstrumentação ) para ter uma ideia do tipo de informação disponível no OTel. O tutorial explica tudo o que você precisa saber sobre o SDK OTel Node.js, mas para mais detalhes, consulte a documentação do OTel .

Configurar

  1. Inicie uma sessão de terminal.
  2. No seu diretório inicial, crie o diretório microservices-march e clone os repositórios do GitHub para este tutorial nele. (Você também pode usar um nome de diretório diferente e adaptar as instruções adequadamente.)

    Observação:  Ao longo do tutorial, o prompt na linha de comando do Linux é omitido para facilitar a cópia e colagem dos comandos no seu terminal. O til ( ~ ) representa seu diretório inicial.

    mkdir ~/microservices-marchcd ~/microservices-march
    git clone https://github.com/microservices-march/messenger --branch mm23-metrics-start
    git clone https://github.com/microservices-march/notifier --branch mm23-metrics-start
    git clone https://github.com/microservices-march/platform --branch mm23-metrics-start
    

Desafio 1: Configurar instrumentação básica OTel

Neste desafio, você inicia o serviço de mensagens e configura a autoinstrumentação OTel para enviar telemetria ao console.

Inicie o serviço de mensagens

  1. Mude para o repositório da plataforma e inicie o Docker Compose:

    cd ~/microservices-march/platformdocker compose up -d --build
    

    Isso inicia o RabbitMQ e o Jaeger, que serão usados em desafios subsequentes.

    • O sinalizador ‑d instrui o Docker Compose a se desvincular dos contêineres quando eles forem iniciados (caso contrário, os contêineres permanecerão anexados ao seu terminal).
    • O sinalizador --build instrui o Docker Compose a reconstruir todas as imagens na inicialização. Isso garante que as imagens que você está executando permaneçam atualizadas mesmo com quaisquer alterações potenciais nos arquivos.
  2. Mude para o diretório do aplicativo no repositório do messenger e instale o Node.js (você pode substituir por um método diferente, se desejar):

    cd ~/microservices-march/messenger/appasdf instalar
    
  3. Instalar dependências:

    instalação npm
    
  4. Inicie o banco de dados PostgreSQL para o serviço de mensagens :

    docker compose up -d
    
  5. Crie o esquema e as tabelas do banco de dados e insira alguns dados iniciais:

    npm executar refresh-db
    

Configurar a Auto-Instrumentação OTel enviada ao console

Com a autoinstrumentação OTel, você não precisa modificar nada na base de código do messenger para configurar o rastreamento. Em vez de ser importado para o próprio código do aplicativo, toda a configuração de rastreamento é definida em um script que é importado para o processo do Node.js em tempo de execução.

Aqui você configura a autoinstrumentação do serviço de mensagens com o destino mais básico para rastreamentos, o console. No Desafio 2 , você alterará a configuração para enviar rastros ao Jaeger como um coletor externo.

  1. Ainda trabalhando no diretório do aplicativo do repositório do messenger , instale os pacotes principais do OTel Node.js:

    npm install @opentelemetry/sdk-node@0.36.0 \
    @opentelemetry/auto-instrumentations-node@0.36.4
    

    Essas bibliotecas fornecem a seguinte funcionalidade:

    • @opentelemetry/sdk-node – Geração e exportação de dados OTel
    • @opentelemetry/auto-instrumentations-node – Configuração automática com configuração padrão de todas as instrumentações Node.js mais comuns

    Observação:  Uma peculiaridade da OTel é que seus SDKs JavaScript são divididos em partes muito, muito pequenas. Então você instalará mais alguns pacotes apenas para o exemplo básico neste tutorial. Para entender quais pacotes você pode precisar para realizar tarefas de instrumentação além daquelas abordadas neste tutorial, leia os (muito bons) guias de introdução do OTel e dê uma olhada no repositório GitHub do OTel.

  2. Crie um novo arquivo chamado tracing.mjs para conter o código de instalação e configuração para o rastreamento OTel:

    rastreamento de toque.mjs
    
  3. No seu editor de texto preferido, abra tracing.mjs e adicione este código:

    //1
    importar opentelemetry de "@opentelemetry/sdk-node";
    importar { getNodeAutoInstrumentations } de "@opentelemetry/auto-instrumentations-node";
    
    //2
    const sdk = new opentelemetry.NodeSDK({
    traceExporter: new opentelemetry.tracing.ConsoleSpanExporter(),
    instrumentações: [getNodeAutoInstrumentations()],
    });
    
    //3
    sdk.start();
    

    O código faz o seguinte:

    1. Importa as funções e objetos necessários do OTel SDK.
    2. Cria uma nova instância do NodeSDK e a configura para:

      • Enviar spans para o console ( ConsoleSpanExporter ).
      • Use o autoinstrumentador como conjunto básico de instrumentação. Esta instrumentação carrega todas as bibliotecas de autoinstrumentação mais comuns. No tutorial os relevantes são:

        • @opentelemetry/instrumentation-pg para a biblioteca de banco de dados Postgres ( pg )
        • @opentelemetry/instrumentation-express para a estrutura Node.js Express
        • @opentelemetry/instrumentation-amqplib para a biblioteca RabbitMQ ( amqplib )
    3. Inicia o SDK.
  4. Inicie o serviço de mensagens , importando o script de autoinstrumentação que você criou na Etapa 3.

    nó --import ./tracing.mjs index.mjs
    

    Depois de um momento, muitas saídas relacionadas ao rastreamento começam a aparecer no console (seu terminal):

    ...
    {
    traceId: '9c1801593a9d3b773e5cbd314a8ea89c',
    parentId: indefinido,
    traceState: indefinido,
    nome: 'fs statSync',
    id: '2ddf082c1d609fbe',
    tipo: 0,
    carimbo de data e hora: 1676076410782000,
    duração: 3,
    atributos: {},
    status: { código: 0 },
    eventos: [],
    links: []
    }
    ...
    

Observação:  Deixe a sessão do terminal aberta para reutilização no Desafio 2.

Desafio 2: Configurar instrumentação OTel e visualização de rastreamento para todos os serviços

Existem muitas ferramentas que você pode usar para visualizar e analisar rastros, mas este tutorial usa o Jaeger . Jaeger é uma estrutura de rastreamento distribuída de ponta a ponta , simples e de código aberto, com uma interface de usuário baseada na web integrada para visualizar intervalos e outros dados de rastreamento. A infraestrutura fornecida no repositório da plataforma inclui o Jaeger (você o iniciou na Etapa 1 do Desafio 1), para que você possa se concentrar na análise de dados em vez de lidar com ferramentas complexas.

O Jaeger pode ser acessado no endpoint http://localhost:16686 no seu navegador, mas se você acessar o endpoint agora, não há nada para ver sobre seu sistema. Isso ocorre porque os rastros que você está coletando no momento estão sendo enviados para o console! Para visualizar dados de rastreamento no Jaeger, você precisa exportar os rastreamentos usando o formato do protocolo OpenTelemetry (OTLP).

Neste desafio, você instrumentará o fluxo do usuário principal configurando a instrumentação para:

Configurar a autoinstrumentação OTel enviada para um coletor externo

Como lembrete, usar a autoinstrumentação do OTel significa que você não modifica nada na base de código do messenger para configurar o rastreamento. Em vez disso, toda a configuração de rastreamento está em um script que é importado para o processo Node.js em tempo de execução. Aqui você altera o destino dos rastros gerados pelo serviço de mensagens do console para um coletor externo (Jaeger neste tutorial).

  1. Ainda trabalhando no mesmo terminal do Desafio 1, e no diretório do aplicativo do repositório do messenger , instale o pacote Node.js do exportador OTLP:

    npm install @opentelemetry/exporter-trace-otlp-http@0.36.0
    

    A biblioteca @opentelemetry/exporter-trace-otlp-http exporta informações de rastreamento no formato OTLP via HTTP. É usado ao enviar telemetria para um coletor externo OTel.

  2. Abra o tracing.mjs (que você criou e editou no Desafio 1) e faça estas alterações:

    • Adicione esta linha ao conjunto de instruções de importação no topo do arquivo:

      importar { OTLPTraceExporter } de "@opentelemetry/exporter-trace-otlp-http";
      
    • Altere o “exportador” que você fornece ao SDK OTel do exportador de console usado no Desafio 1 para um que possa enviar dados OTLP por HTTP para um coletor compatível com OTLP. Substituir:

      traceExporter:novo opentelemetry.tracing.ConsoleSpanExporter(),
      

      com:

      traceExporter: novo OTLPTraceExporter({ cabeçalhos: {} }),
      

    Observação:  Para simplificar, o tutorial assume que o coletor reside no local padrão, http://localhost:4318/v1/traces . Em um sistema real, é uma boa ideia definir o local explicitamente.

  3. Pressione Ctrl+c para interromper o serviço de mensagens , que você iniciou neste terminal na Etapa 4 de Configurar a instrumentação automática do OTel enviada ao console . Em seguida, reinicie-o para usar o novo exportador configurado na Etapa 2:

    ^cnode --import ./tracing.mjs index.mjs
    
  4. Inicie uma segunda sessão de terminal separada. (As instruções subsequentes chamam este de terminal do cliente e o terminal original – usado nas etapas 1 e 3 – de terminal do mensageiro .) Aguarde cerca de dez segundos e envie uma solicitação de verificação de integridade ao serviço de mensagens (você pode executar isso algumas vezes se quiser ver vários rastros):

    curl -X OBTER http://localhost:4000/health
    

    Esperar dez segundos antes de enviar a solicitação ajuda a tornar seu rastro mais fácil de encontrar, porque ele vem depois dos muitos rastros que a autoinstrumentação gera quando o serviço é iniciado.

  5. Em um navegador, acesse a interface do usuário do Jaeger em http://localhost:16686 e verifique se o exportador OTLP está funcionando conforme o esperado. Clique em Pesquisar na barra de título e, no menu suspenso no campo Serviço , selecione o serviço cujo nome começa com unknown_service . Clique no botão Encontrar rastros :

  6. Clique em um traço no lado direito da janela para exibir uma lista dos intervalos nele contidos. Cada intervalo descreve as operações, às vezes envolvendo vários serviços, que foram executadas como parte do rastreamento. O intervalo jsonParser na captura de tela mostra quanto tempo levou para executar a parte jsonParser do código de tratamento de solicitações do serviço de mensagens .

    Captura de tela da interface gráfica do usuário do Jaeger mostrando a lista de intervalos para unknown_service, antes que a autoinstrumentação seja alterada para mostrar os nomes de serviço corretos

  7. Conforme observado na Etapa 5, o nome do serviço exportado pelo OTel SDK ( unknown_service ) não é significativo. Para corrigir isso, no terminal do Messenger pressione Ctrl+c para interromper o serviço de mensagens . Em seguida, instale mais alguns pacotes do Node.js:

    ^c 
    npm install @opentelemetry/semantic-conventions@1.10.0 \
    @opentelemetry/resources@1.10.0
    

    Essas duas bibliotecas fornecem a seguinte funcionalidade:

    • @opentelemetry/semantic-conventions – Define os atributos padrão para rastros conforme definido na especificação OTel.
    • @opentelemetry/resources – Define um objeto (recurso) que representa a fonte que gera os dados OTel (neste tutorial, o serviço de mensagens ).
  8. Abra tracing.mjs em um editor de texto e faça estas alterações:

    • Adicione estas linhas ao conjunto de instruções de importação no topo do arquivo:

      importar { Recurso } de "@opentelemetry/resources"; importar { AtributosDeRecursosSemânticos } de "@opentelemetry/convenções-semanticas";
      
    • Crie um recurso chamado messenger na chave correta na especificação OTel adicionando a seguinte linha após a última instrução de importação :

      const resource = novo Recurso({ [SemanticResourceAttributes.SERVICE_NAME]: "mensageiro",
      });
      
    • Passe o objeto de recurso para o construtor NodeSDK adicionando a linha destacada em laranja entre as linhas em preto:

      const sdk = new opentelemetry.NodeSDK({ resource, traceExporter: new OTLPTraceExporter({ headers: {} }), instrumentações: [getNodeAutoInstrumentations()], });
      
  9. Reinicie o serviço de mensagens :

    nó --import ./tracing.mjs index.mjs
    
  10. Aguarde cerca de dez segundos e, no terminal do cliente (que você abriu na Etapa 4), envie outra solicitação de verificação de integridade ao servidor (você pode executar o comando algumas vezes se quiser ver vários rastros):

    curl -X OBTER http://localhost:4000/health
    

    Observação:  Deixe o terminal do cliente aberto para reutilização na próxima seção e o terminal do mensageiro aberto para reutilização no Desafio 3.

  11. Confirme se um novo serviço chamado messenger aparece na interface do usuário do Jaeger no navegador (isso pode levar alguns segundos e talvez seja necessário atualizar a interface do usuário do Jaeger):

    Captura de tela da interface gráfica do usuário do Jaeger mostrando o messenger na lista de serviços disponíveis para inspeção aprofundada de psans

  12. Selecione messenger no menu suspenso Serviço e clique no botão Encontrar rastros para ver todos os rastros recentes originados do serviço de messenger (a captura de tela mostra os 2 mais recentes de 20):

    Captura de tela da interface gráfica do usuário do Jaeger mostrando os 2 rastros mais recentes do serviço de mensagens

  13. Clique em um traço para exibir os intervalos nele contidos. Cada intervalo é devidamente marcado como originário do serviço de mensagens :

    Captura de tela da interface gráfica do usuário do Jaeger mostrando detalhes de um único intervalo de mensageiro

Configurar a Auto-Instrumentação OTel do Serviço Notificador

Agora inicie e configure a autoinstrumentação para o serviço notificador , executando basicamente os mesmos comandos das duas seções anteriores para o serviço de mensagens .

  1. Abra uma nova sessão de terminal (chamada de terminal notificador nas etapas subsequentes). Mude para o diretório do aplicativo no repositório do notificador e instale o Node.js (você pode substituir por um método diferente, se desejar):

    cd ~/microservices-march/notifier/appasdf install
    
  2. Instalar dependências:

    instalação npm
    
  3. Inicie o banco de dados PostgreSQL para o serviço notificador :

    docker compose up -d
    
  4. Crie o esquema e as tabelas do banco de dados e insira alguns dados iniciais:

    npm executar refresh-db
    
  5. Instale os pacotes OTel Node.js (para uma descrição do que os pacotes fazem, consulte as Etapas 1 e 3 em Configurar a instrumentação automática do OTel enviada ao console ):

    npm install @opentelemetry/auto-instrumentations-node@0.36.4 \
    @opentelemetry/exporter-trace-otlp-http@0.36.0 \
    @opentelemetry/resources@1.10.0 \
    @opentelemetry/sdk-node@0.36.0 \
    @opentelemetry/semantic-conventions@1.10.0
    
  6. Crie um novo arquivo chamado tracing.mjs :

    rastreamento de toque.mjs
    
  7. No seu editor de texto preferido, abra tracing.mjs e adicione o seguinte script para colocar o OTel SDK em funcionamento:

    importar opentelemetry de "@opentelemetry/sdk-node";
    importar { getNodeAutoInstrumentations } de "@opentelemetry/auto-instrumentations-node";
    importar { OTLPTraceExporter } de "@opentelemetry/exporter-trace-otlp-http";
    importar { Resource } de "@opentelemetry/resources";
    importar { SemanticResourceAttributes } de "@opentelemetry/semantic-conventions";
    
    const resource = new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: "notifier",
    });
    
    const sdk = new opentelemetry.NodeSDK({
    resource,
    traceExporter: new OTLPTraceExporter({ headers: {} }),
    instrumentations: [getNodeAutoInstrumentations()],
    });
    
    sdk.iniciar();
    

    Observação:  Este script é exatamente o mesmo que o do serviço de mensagens , exceto que o valor no campo SemanticResourceAttributes.SERVICE_NAME é notifier .

  8. Inicie o serviço de notificação com a autoinstrumentação OTel:

    nó --import ./tracing.mjs index.mjs
    
  9. Aguarde cerca de dez segundos e, no terminal do cliente, envie uma solicitação de verificação de integridade ao serviço notificador . Este serviço está escutando na porta 5000 para evitar conflito com o serviço de mensagens escutando na porta 4000:

    curl http://localhost:5000/saúde
    

    Observação:  Deixe os terminais do cliente e do notificador abertos para reutilização no Desafio 3.

  10. Confirme se um novo serviço chamado notificador aparece na interface do usuário do Jaeger no navegador:

    Captura de tela da interface gráfica do usuário do Jaeger mostrando o notificador na lista de serviços disponíveis para inspeção aprofundada de vãos

Configurar a instrumentação OTel do NGINX

Para o NGINX, você configura o rastreamento manualmente em vez de usar o método de autoinstrumentação OTel. Atualmente, a maneira mais comum de instrumentar o NGINX usando o OTel é usar um módulo escrito em C. Módulos de terceiros são uma parte importante do ecossistema NGINX, mas exigem algum trabalho para serem configurados. Este tutorial faz a configuração para você. Para obter informações básicas, consulte Compilando módulos dinâmicos de terceiros para NGINX e NGINX Plus em nosso blog.

  1. Inicie uma nova sessão de terminal (o terminal NGINX ), altere o diretório para a raiz do repositório do messenger e crie um novo diretório chamado load-balancer , além de novos arquivos chamados Dockerfile , nginx.conf e opentelemetry_module.conf :

    cd ~/microservices-march/messenger/mkdir balanceador de carga
    cd balanceador de carga
    toque Dockerfile
    toque nginx.conf
    toque opentelemetry_module.conf
    
  2. No seu editor de texto preferido, abra o Dockerfile e adicione o seguinte (os comentários explicam o que cada linha faz, mas você pode criar e executar o contêiner do Docker sem entender tudo):

    DE --platform=amd64 nginx:1.23.1 # Substitua o arquivo nginx.conf pelo nosso próprio COPY nginx.conf /etc/nginx/nginx.conf # Defina a versão do módulo NGINX OTel ARG OPENTELEMETRY_CPP_VERSION=1.0.3 # Defina o caminho de pesquisa para bibliotecas compartilhadas usadas ao compilar e executar o NGINX ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/opentelemetry-webserver-sdk/sdk_lib/lib # 1. Baixe a versão mais recente do modelo Consul e do módulo do servidor web OTel C++, otel-webserver-module ADD https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/download/webserver%2Fv${OPENTELEMETRY_CPP_VERSION} /opentelemetry-webserver-sdk-x64-linux.tgz /tmp EXECUTE apt-get update \ && apt-get install -y --no-install-recommends dumb-init unzip \ # 2. Extraia os arquivos do módulo && tar xvfz /tmp/opentelemetry-webserver-sdk-x64-linux.tgz -C /opt \ && rm -rf /tmp/opentelemetry-webserver-sdk-x64-linux.tgz \ # 3. Instale e adicione a diretiva 'load_module' no topo do arquivo de configuração principal do NGINX && /opt/opentelemetry-webserver-sdk/install.sh \ && echo "load_module /opt/opentelemetry-webserver-sdk/WebServerModule/Nginx/1.23.1/ngx_http_opentelemetry_module.so;\n$(cat /etc/nginx/nginx.conf)" > /etc/nginx/nginx.conf # 4. Copiar no arquivo de configuração para o módulo NGINX OTel COPY opentelemetry_module.conf /etc/nginx/conf.d/opentelemetry_module.conf EXPOSE 8085 STOPSIGNAL SIGQUIT
    
  3. Abra nginx.conf e adicione o seguinte:

    eventos {}
    http {
    include /etc/nginx/conf.d/opentelemetry_module.conf;
    
    mensageiro upstream {
    servidor localhost:4000;
    }
    
    servidor {
    listen 8085;
    
    localização / {
    proxy_pass http://messenger;
    }
    }
    }
    

    Este arquivo de configuração NGINX extremamente básico informa ao NGINX para:

    • Configure um grupo upstream chamado messenger que representa o grupo de instâncias do serviço de mensagens
    • Ouça solicitações HTTP na porta 8085
    • Encaminhe todas as solicitações de entrada para caminhos que começam com / (ou seja, todas as solicitações de entrada) para o mensageiro upstream

    Observação:  Isso é bem próximo da configuração real do NGINX como um proxy reverso e balanceador de carga em um ambiente de produção. A única grande diferença é que o argumento para a diretiva server no bloco upstream geralmente é um nome de domínio ou endereço IP em vez de localhost .

  4. Abra opentelemetry_module.conf e adicione o seguinte:

    NginxModuleEnabled LIGADO;NginxModuleOtelSpanExporter otlp;
    NginxModuleOtelExporterEndpoint localhost:4317;
    NginxModuleServiceName messenger-lb;
    NginxModuleServiceNamespace MicroservicesMarchDemoArchitecture;
    NginxModuleServiceInstanceId DemoInstanceId;
    NginxModuleResolveBackends LIGADO;
    NginxModuleTraceAsError LIGADO;
    
  5. Crie uma imagem Docker contendo o NGINX e o módulo NGINX OTel:

    docker build -t messenger-lb .
    
  6. Inicie o contêiner Docker para o proxy reverso NGINX e o balanceador de carga:

    docker run --rm --name messenger-lb -p 8085:8085 --network="host" messenger-lb
    
  7. No terminal do cliente, envie uma solicitação de verificação de integridade ao serviço de mensagens por meio do proxy reverso NGINX e do balanceador de carga (não há necessidade de esperar antes de enviar esta solicitação):

    curl http://localhost:8085/saúde
    

    Observação:  Deixe os terminais NGINX e do cliente abertos para reutilização no Desafio 3.

  8. No navegador, confirme se o novo serviço messenger-lb está listado na interface do usuário do Jaeger junto com os serviços que você iniciou anteriormente. Pode ser necessário recarregar a interface do usuário do Jaeger no seu navegador.

    Captura de tela da interface gráfica do usuário do Jaeger mostrando a lista de serviços disponíveis para inspeção aprofundada de vãos, agora incluindo messenger-lb

Desafio 3: Aprenda a ler rastros OTel

Em Arquitetura e Fluxo do Usuário , descrevemos os estágios do fluxo do usuário, mas para recapitular:

  1. Um usuário inicia uma conversa enviando uma mensagem para outro usuário.
  2. O proxy reverso NGINX intercepta a mensagem e a encaminha para o serviço de mensagens .
  3. O serviço de mensagens grava a mensagem em seu banco de dados e, em seguida, envia um evento por meio do RabbitMQ.
  4. O serviço notificador consome esse evento, consulta as preferências de notificação do destinatário (segundo usuário) e envia uma notificação ao destinatário pelo método preferido.

E os objetivos da implementação da telemetria são:

  1. Entenda todas as etapas pelas quais uma solicitação passa para concluir o novo fluxo de mensagens.
  2. Tenha certeza de que o fluxo será executado de ponta a ponta em cinco segundos, em condições normais.
  3. Veja quanto tempo leva para o serviço notificador começar a processar o evento enviado pelo serviço de mensagens .

Neste desafio, você aprenderá a avaliar se os traços gerados pela instrumentação OTel atendem aos objetivos mencionados acima. Primeiro, você testa o sistema e cria alguns rastros . Em seguida, você inspeciona o rastreamento de um fluxo de mensagens e as seções dele geradas pelo NGINX , o serviço de mensagens e o serviço de notificação .

Criar dados de rastreamento

No terminal do cliente, configure uma conversa e envie algumas mensagens entre dois usuários:

curl -X POST \
-H "Tipo de conteúdo: application/json" \
-d '{"participant_ids": [1, 2]}' \
'http://localhost:8085/conversations'

curl -X POST \
-H "ID do usuário: 1" \
-H "Tipo de conteúdo: application/json" \
-d '{"content": "Esta é a primeira mensagem"}' \
'http://localhost:8085/conversations/1/messages'

curl -X POST \
-H "User-Id: 2" \
-H "Tipo de conteúdo: application/json" \
-d '{"content": "Esta é a segunda mensagem"}' \
'http://localhost:8085/conversations/1/messages'

Uma saída como a seguinte é gerada pelo serviço notificador e aparece no terminal do notificador:

Nova_mensagem recebida: {"type":"new_message","channel_id":1,"user_id":1,"index":1,"participant_ids":[1,2]}Enviando notificação de nova mensagem via sms para 12027621401

Nova_mensagem recebida: {"type":"new_message","channel_id":1,"user_id":2,"index":2,"participant_ids":[1,2]}

Enviando notificação de nova mensagem via e-mail para the_hotstepper@kamo.ze

Enviando notificação de nova mensagem via sms para 19147379938

Prepare-se para ler os rastros

Abra a interface do usuário do Jaeger no navegador, selecione messenger-lb no menu suspenso Serviço e clique no botão Encontrar rastros . Uma lista de rastros é exibida, começando no início do fluxo. Clique em qualquer traço para exibir detalhes sobre ele, como nesta captura de tela:

Captura de tela da interface gráfica do usuário do Jaeger mostrando todo o conjunto de intervalos no fluxo

Clique e explore um pouco. Antes de prosseguir, considere como as informações nos rastros apoiam seus objetivos de instrumentação, conforme listados na introdução do Desafio 3. Perguntas pertinentes incluem:

  • Quais informações ajudam a atingir as metas?
  • Que informações estão faltando?
  • Quais informações não são relevantes?

Examine a seção NGINX (messenger-lb) do Trace

Objetivo 1: Veja todas as etapas pelas quais uma solicitação passa no novo fluxo de mensagens

Comece com o intervalo NGINX, que tem 11 intervalos filho dentro do intervalo pai. Como a configuração atual do NGINX é muito simples, os intervalos filho não são muito interessantes e simplesmente mostram o tempo gasto em cada etapa do ciclo de vida de processamento de solicitações do NGINX. No entanto, o intervalo pai (o primeiro) contém alguns insights interessantes:

Captura de tela da interface gráfica do usuário do Jaeger mostrando o intervalo pai na seção NGINX (messenger-lb) do rastreamento

  • Em Tags você vê os seguintes atributos:

    • Campo http.methodPOST (em termos REST significa criação)
    • Campo http.status_code201 (indicando criação bem-sucedida)
    • Campo http.targetconversations/1/messages (o ponto final da mensagem)

    Juntas, essas três informações se combinam para dizer: “Uma solicitação POST foi enviada para /conversations/1/messages e a resposta foi201 (criado com sucesso)”. Isso corresponde às etapas 1 e 4a em Arquitetura e fluxo do usuário ).

  • Em Processo , o campo webengine.name mostra que esta é a parte NGINX da solicitação.

Além disso, como os intervalos para messenger e notifier estão aninhados dentro do intervalo messenger-lb conversations/1 (conforme mostrado na captura de tela em Prepare-se para ler os rastros ), você pode dizer que a solicitação enviada ao serviço de messenger por meio do proxy reverso NGINX atingiu todos os componentes esperados no fluxo.

Essas informações atendem ao objetivo porque você pode ver que o proxy reverso NGINX fazia parte do fluxo.

Objetivo 2: Verifique se o fluxo é executado em cinco segundos

Na lista de intervalos rotulados como messenger-lb , observe o intervalo mais recente (está no final da lista) para ver quanto tempo levou a parte NGINX da solicitação. Na captura de tela, o intervalo começou em 589 microssegundos (µs) e durou 24µs, o que significa que a operação completa do proxy reverso levou apenas 613µs – cerca de 0,6 milissegundos (ms). (Os valores exatos serão diferentes quando você executar o tutorial.)

Captura de tela da interface gráfica do usuário do Jaeger mostrando intervalos na seção NGINX (messenger-lb) do rastreamento

Em uma configuração como essa, na maioria das vezes os valores são úteis apenas em relação a outras medições e variam entre os sistemas. Neste caso, porém, esta operação claramente não corre o risco de se aproximar dos cinco segundos de duração.

Essas informações satisfazem o objetivo porque você pode ver que as operações do NGINX não levaram nem perto de cinco segundos. Se houver uma operação muito lenta no fluxo, ela deve estar acontecendo mais tarde.

Objetivo 3: Veja quanto tempo leva para o serviço de notificação ler o evento enviado pelo serviço de mensagens

A camada de proxy reverso do NGINX não inclui nenhuma informação sobre isso, então você pode passar para os intervalos do messenger .

Examine a seção do mensageiro do Trace

Objetivo 1: Veja todas as etapas pelas quais uma solicitação passa no novo fluxo de mensagens

A seção de serviço de mensagens do rastreamento contém outros 11 intervalos. Novamente, a maioria dos intervalos filho diz respeito às etapas básicas que o framework Express usa ao processar uma solicitação e não são muito interessantes. No entanto, o intervalo pai (o primeiro) novamente contém alguns insights interessantes:

Captura de tela da interface gráfica do usuário do Jaeger mostrando o intervalo pai na seção do mensageiro do rastreamento

Em Tags você vê os seguintes atributos:

  • Campo http.methodPOST (novamente, em termos REST, isso significa criação)
  • Campo http.route/conversations/:conversationId/messages (a rota da mensagem)
  • Campo http.target/conversations/1/messages (o ponto final da mensagem)

Essas informações satisfazem o objetivo porque nos mostram que o serviço de mensagens fazia parte do fluxo e que o ponto final atingido era o novo ponto final da mensagem.

Objetivo 2: Verifique se o fluxo é executado em cinco segundos

Conforme indicado na captura de tela a seguir, a parte do mensageiro do rastreamento começou em 1,28 ms e terminou em 36,28 ms, para um tempo total de 35 ms. A maior parte desse tempo foi gasto analisando JSON ( middleware - jsonParser ) e, em maior extensão, conectando-se ao banco de dados ( pg-pool.connect e tcp.connect ).

Isso faz sentido, já que diversas consultas SQL também são feitas no processo de escrita da mensagem. Isso, por sua vez, sugere que você pode querer aumentar a configuração de autoinstrumentação para capturar o tempo dessas consultas. (O tutorial não mostra essa instrumentação adicional, mas no Desafio 4 você cria manualmente intervalos que podem ser usados para encapsular consultas de banco de dados.)

Captura de tela da interface gráfica do usuário do Jaeger mostrando intervalos na seção do mensageiro do rastreamento e quanto tempo eles levaram

Essa informação satisfaz o objetivo porque mostra que as operações do mensageiro não levam nem perto de cinco segundos. Se houver uma operação muito lenta no fluxo, ela deve estar acontecendo mais tarde.

Objetivo 3: Veja quanto tempo leva para o serviço de notificação ler o evento enviado pelo serviço de mensagens

Assim como os intervalos do NGINX, os intervalos do mensageiro não incluem essas informações, então você pode passar para os intervalos do notificador .

Examine a seção do notificador do Trace

Objetivo 1: Veja todas as etapas pelas quais uma solicitação passa no novo fluxo de mensagens

A seção do notificador do rastreamento contém apenas dois intervalos:

Captura de tela da interface gráfica do usuário do Jaeger mostrando os dois intervalos na seção do notificador do rastreamento

  • O intervalo do processo chat_queue – Confirma que o serviço notificador processou um evento da fila de mensagens chat_queue
  • O intervalo pg-pool.connect – Mostra que após processar o evento o serviço notificador fez algum tipo de conexão com seu banco de dados

As informações disponíveis nesses períodos cumprem apenas parcialmente o objetivo de compreender cada etapa. Você pode ver que o serviço notificador chegou ao ponto de consumir o evento da fila, mas não sabe se:

  • A notificação de mensagem enviada por este serviço corresponde ao evento despachado pelo serviço de mensagens
  • As notificações de mensagens relevantes foram enviadas corretamente ao destinatário da mensagem

Isso indica que você precisa fazer o seguinte para entender completamente o fluxo do serviço do notificador :

  • Instrumentar manualmente os intervalos que mostram uma notificação sendo enviada
  • Garanta que haja uma conexão explícita entre o evento despachado pelo serviço de mensagens e o evento consumido pelo serviço de notificação , na forma de um ID de rastreamento

Objetivo 2: Verifique se o fluxo é executado em cinco segundos

Observando o tempo geral dos intervalos do serviço do notificador , você vê que a solicitação passou 30,77 ms na seção do notificador do fluxo. No entanto, como não há intervalos que sinalizem o “fim” de todo o fluxo (o envio de notificações ao destinatário), não é possível determinar o tempo total desta seção do fluxo ou o tempo geral de conclusão da operação.

Objetivo 3: Veja quanto tempo leva para o serviço de notificação ler o evento enviado pelo serviço de mensagens

No entanto, você pode ver que um intervalo de processo chat_queue para o serviço notificador começou em 6,12 ms, 2 ms depois que um intervalo de envio chat_queue começou para o serviço de mensagens em 4,12 ms.

Captura de tela da interface gráfica do usuário do Jaeger mostrando o serviço de notificação consumindo um evento despachado pelo serviço de mensagens

Essa meta é atingida porque você sabe que o notificador consumiu um evento 2 ms depois que ele foi despachado pelo serviço de mensagens . Diferentemente da meta 2, para atingir essa meta você não precisa saber se o evento foi totalmente processado ou quanto tempo isso levou.

Conclusões

Com base na nossa análise dos traços produzidos pela atual autoinstrumentação OTel, fica claro que:

  • Muitos desses vãos não são úteis em sua forma atual:

    • O NGINX está produzindo intervalos relacionados a funções – como verificação de autorização e serviço de arquivos – que não são relevantes para a função com a qual você se importa, proxy reverso. Entretanto, neste ponto, a instrumentação OTel para NGINX não permite que você omita intervalos irrelevantes, então nada pode ser feito.
    • Entre os spans para os serviços do Node.js (os serviços de mensageiro e notificador ), alguns parecem relevantes para os objetivos: os spans para análise JSON, manipulador de solicitações e todas as operações de banco de dados. Alguns dos intervalos de middleware (como expressInit e corsMiddleware ) não parecem relevantes e podem ser removidos.
  • Faltam vãos críticos para o seguinte:

    • Notificações enviadas pelo serviço notificador
    • Um mapeamento claro entre o evento RabbitMQ despachado do serviço de mensagens e o processado pelo serviço notificador

Isto significa que a instrumentação básica cumpre o último objetivo:

  • Veja quanto tempo leva para o serviço de notificação começar a processar o evento despachado pelo serviço de mensagens

Entretanto, não há informações suficientes para cumprir os dois primeiros objetivos:

  • Entenda todas as etapas pelas quais uma solicitação passa durante o fluxo de novas mensagens
  • Tenha certeza de que o fluxo está sendo executado de ponta a ponta em menos de cinco segundos em circunstâncias normais

Desafio 4: Otimize a instrumentação com base em leituras de rastreamento

Neste desafio, você otimiza a instrumentação OTel com base nas análises de traços feitas no Desafio 3. Isso inclui remover spans desnecessários , criar novos spans personalizados e confirmar que o evento consumido pelo serviço notificador é o mesmo gerado pelo serviço de mensagens.

Remova vãos desnecessários

  1. No seu editor de texto preferido, abra o arquivo tracing.mjs no diretório do aplicativo do repositório do messenger e adicione o seguinte no final da lista de instruções de importação na parte superior:

    const IGNORED_EXPRESS_SPANS = novo Conjunto([ "middleware - expressInit",
    "middleware - corsMiddleware",
    ]);
    

    Isso define um conjunto de nomes de intervalos, derivados da lista de intervalos mostrada na captura de tela a seguir da interface do usuário do Jaeger, a serem omitidos do rastreamento porque não fornecem informações úteis para esse fluxo. Você pode decidir que outros intervalos listados na captura de tela também não são necessários e adicioná-los à lista de IGNORED_EXPRESS_SPANS .

    Captura de tela da interface gráfica do usuário do Jaeger mostrando uma lista de vários intervalos do serviço de mensagens que podem fornecer informações relevantes e, portanto, podem ser omitidos do rastreamento

  2. Adicione filtros à configuração de autoinstrumentação para omitir intervalos que você não deseja, alterando a linha destacada em laranja:

    const sdk = new opentelemetry.NodeSDK({ resource, traceExporter: new OTLPTraceExporter({ headers: {} }), instrumentações: [getNodeAutoInstrumentations()], });
    

    para isso:

    const sdk = new opentelemetry.NodeSDK({ resource, traceExporter: new OTLPTraceExporter({ headers: {} }), instrumentações: [ getNodeAutoInstrumentations({ "@opentelemetry/instrumentation-express": { ignoreLayers: [ (name) => { return IGNORED_EXPRESS_SPANS.has(name); }, ], }, }), ], });
    

    A função getNodeAutoInstrumentations faz referência ao conjunto de intervalos definidos na Etapa 1 para filtrá-los do rastreamento gerado por @opentelemetry/instrumentation-express . Em outras palavras, a instrução return é resolvida como verdadeira para um intervalo que pertence a IGNORED_EXPRESS_SPANS , e a instrução ignoreLayers remove esse intervalo do rastreamento.

  3. No terminal do messenger, pressione Ctrl+c para interromper o serviço de messenger . Em seguida, reinicie-o:

    ^cnode --import ./tracing.mjs index.mjs
    
  4. Aguarde cerca de dez segundos e, no terminal do cliente, envie uma nova mensagem:

    curl -X POST \ -H "ID do usuário: 2" \
    -H "Tipo de conteúdo: application/json" \
    -d '{"content": "Esta é a segunda mensagem"}' \
    'http://localhost:8085/conversations/1/messages'
    
  5. Verifique novamente os spans do messenger na IU do Jaeger. Os dois spans do middleware , expressInit e corsMiddleware , não aparecem mais (você pode comparar com a captura de tela do Objetivo 2 de Examine a Seção do messenger do Trace no Desafio 3.

    Captura de tela da interface gráfica do usuário do Jaeger mostrando que o rastreamento não inclui mais dois intervalos após você alterar a instrumentação para filtrá-los do rastreamento

Configurar intervalos personalizados

Nesta seção, você toca no código do aplicativo pela primeira vez. A autoinstrumentação gera uma grande quantidade de informações sem exigir alterações no aplicativo, mas alguns insights só são possíveis pela instrumentação de partes específicas da lógica de negócios.

Para o novo fluxo de mensagens que você está instrumentando, um exemplo disso é rastrear o envio de notificações para o destinatário da mensagem.

  1. Abra index.mjs no diretório do aplicativo do repositório do notificador . Este arquivo contém toda a lógica de negócios do serviço. Adicione a seguinte linha no final da lista de instruções de importação no topo do arquivo:

    importar { trace } de "@opentelemetry/api";
    
  2. Substitua este código (aproximadamente na linha 91 do arquivo):

    para (deixe pref de preferências) { console.log( `Enviando notificação de nova mensagem via${pref.address_type} para${pref.address} ` ); }
    

    com:

    const tracer = trace.getTracer("notifier"); // 1tracer.startActiveSpan( // 2 "notification.send_all", { attributes: { user_id: msg.user_id, }, }, (parentSpan) => { for (let pref of preferences) { tracer.startActiveSpan( // 3 "notification.send", { attributes: { // 4 notification_type: pref.address_type, user_id: pref.user_id, }, }, (span) => { console.log( `Enviando notificação de nova mensagem via${pref.address_type} para${pref.address} ` ); span.end(); // 5 } ); } parentSpan.end(); // 6 } );
    

    O novo código faz o seguinte:

    1. Obtém o rastreador , que é um objeto global para interagir com rastros OTel.
    2. Inicia um novo intervalo pai chamado notification.send_all e define o atributo user_id para identificar o remetente da mensagem.
    3. Entra em um loop onde as preferências de notificação do destinatário são enumeradas e um novo intervalo filho chamado notification.send é criado em notification.send_all . Cada notificação gera um novo intervalo.
    4. Define mais atributos para o intervalo filho:

      • notification_type – Um de sms ou e-mail
      • user_id – O ID do usuário que recebe a notificação
    5. Fecha cada período de notificação filho.
    6. Fecha o intervalo pai notification.send_all .

    Ter um intervalo pai garante que cada operação de “enviar notificação” seja relatada, mesmo que nenhuma preferência de notificação seja descoberta para o usuário.

  3. No terminal do notificador, pressione Ctrl+c para interromper o serviço do notificador . Em seguida, reinicie-o:

    ^cnode --import ./tracing.mjs index.mjs
    
  4. Aguarde cerca de dez segundos e, no terminal do cliente, envie uma nova mensagem:

    curl -X POST \ -H "ID do usuário: 2" \
    -H "Tipo de conteúdo: application/json" \
    -d '{"content": "Esta é a segunda mensagem"}' \
    'http://localhost:8085/conversations/1/messages'
    
  5. Verifique novamente os spans do notificador na IU do Jaeger. Você vê o span pai e dois spans filhos, cada um com uma operação de “enviar notificação”:

    Captura de tela da interface gráfica do usuário do Jaeger mostrando o resultado da definição de três novos intervalos no código para o serviço de notificação

Agora você pode atingir totalmente o primeiro e o segundo objetivos, porque pode ver todas as etapas pelas quais uma solicitação passa durante o novo fluxo de mensagens. Os tempos em cada intervalo expõem qualquer atraso entre qualquer uma dessas etapas.

Confirme se o mensageiro e o notificador estão lidando com o mesmo evento

Há mais uma coisa que você precisa para ter uma visão completa do fluxo. O evento que está sendo processado pelo serviço notificador é, na verdade, o mesmo que foi despachado pelo serviço de mensagens ?

Você não precisa fazer nenhuma alteração explícita para conectar os dois traços – mas também não quer confiar cegamente na mágica da autoinstrumentação.

Com isso em mente, adicione algum código de depuração rápido para verificar se o rastreamento que começa no serviço NGINX é realmente o mesmo (tem o mesmo ID de rastreamento) que aquele consumido pelo serviço notificador .

  1. Abra o arquivo index.mjs no diretório do aplicativo do repositório do messenger e faça estas alterações:

    • Adicione a seguinte linha no final da lista de instruções de importação na parte superior:

      importar { trace } de "@opentelemetry/api";
      
    • Adicione as linhas destacadas em laranja abaixo da linha existente em preto:

      função assíncrona createMessageInConversation(req, res) { const tracer = trace.getActiveSpan(); console.log("TRACE_ID: ", tracer.spanContext().traceId);
      

      As novas linhas imprimem o TRACE_ID de dentro da função no messenger que manipula a criação de uma nova mensagem.

  2. Abra o arquivo index.mjs no diretório do aplicativo do repositório do notificador e adicione a linha destacada em laranja abaixo da linha existente em preto:

    exportar função assíncrona handleMessageConsume(canal, msg, manipuladores) { console.log("RABBIT_MQ_MESSAGE: ", msg);
    

    A nova linha imprime o conteúdo completo do evento AMQP recebido pelo serviço notificador .

  3. Pare e reinicie os serviços do messenger e do notifier executando estes comandos nos terminais do messenger e do notifier:

    ^cnode --import ./tracing.mjs index.mjs
    
  4. Aguarde cerca de dez segundos e, no terminal do cliente, envie uma mensagem novamente:

    curl -X POST \ -H "ID do usuário: 2" \
    -H "Tipo de conteúdo: application/json" \
    -d '{"content": "Esta é a segunda mensagem"}' \
    'http://localhost:8085/conversations/1/messages'
    
  5. Veja os logs dos serviços de mensagens e notificações . O log do serviço de mensagens inclui uma linha como esta que relata o ID de rastreamento de uma mensagem (o ID real será diferente quando você executar o tutorial):

    ID_DO_RASTREIO:  29377a9b546c50be629c8e64409bbfb5
    
  6. Da mesma forma, o log do serviço notificador relata um ID de rastreamento na saída como este:

    _spanContext: {
    traceId: '29377a9b546c50be629c8e64409bbfb5', spanId: 'a94e9462a39e6dbf', traceFlags: 1,
    traceState: indefinido
    },
    
  7. Os IDs de rastreamento correspondem no console, mas como uma etapa final, você pode compará-los ao ID de rastreamento na IU do Jaeger. Abra a IU no endpoint de ID de rastreamento relevante (o seu será diferente, mas neste exemplo é http://localhost:16686/trace/29377a9b546c50be629c8e64409bbfb5 ) para ver o rastreamento inteiro. O traço de Jaeger confirma que:

    • A autoinstrumentação AMQP no serviço de mensagens adiciona esse ID de rastreamento como parte dos metadados quando o evento é despachado.
    • A autoinstrumentação AMQP no serviço notificador espera esses metadados e define o contexto de rastreamento adequadamente.

Observação:  Em um sistema de produção real, você removeria o código adicionado nesta seção depois de confirmar que o fluxo está funcionando conforme o esperado.

Limpeza de recursos

Você criou alguns contêineres e imagens ao longo deste tutorial! Use estas instruções para removê-los.

  • Para remover qualquer contêiner Docker em execução:

    docker rm $(docker stop messenger-lb)
    
  • Para remover o serviço de plataforma e os serviços de banco de dados do messenger e do notifier :

    cd ~/microservices-march/plataforma && docker compose down
    cd ~/microservices-march/notificador && docker compose down
    cd ~/microservices-march/mensageiro && docker compose down
    

Próximos passos

Parabéns, você concluiu o tutorial!

  • Você configura a instrumentação OTel em um proxy reverso NGINX e dois serviços Node.js.
  • Você analisou os dados fornecidos pela autoinstrumentação OTel com um olhar crítico e adicionou alguma telemetria que estava faltando para atingir os objetivos do laboratório OTel:
    • Você obteve uma visão razoável do fluxo de uma solicitação específica por meio de um sistema de mensagens sem alterar diretamente nenhum código do aplicativo.
    • Você confirmou que o fluxo estava sendo executado de ponta a ponta em menos de cinco segundos em circunstâncias normais.

E, no entanto, você mal arranhou a superfície de como seria uma configuração de rastreamento ideal! Em um ambiente de produção, você pode querer adicionar coisas como spans personalizados para cada consulta de banco de dados e metadados adicionais em todos os spans descrevendo detalhes de tempo de execução, como o ID do contêiner de cada serviço. Você também pode implementar os outros dois tipos de dados OTel (métricas e registro) para dar a você uma imagem completa da saúde do seu sistema.

Para continuar sua educação em microsserviços, confira Microservices março de 2023. Na Unidade 4: Gerencie o caos e a complexidade dos microsserviços com observabilidade , você aprenderá sobre as três principais classes de dados de observabilidade, a importância da infraestrutura e do alinhamento de aplicativos e maneiras de começar a analisar dados profundos.


"Esta postagem do blog pode fazer referência a produtos que não estão mais disponíveis e/ou não têm mais suporte. Para obter as informações mais atualizadas sobre os produtos e soluções F5 NGINX disponíveis, explore nossa família de produtos NGINX . O NGINX agora faz parte do F5. Todos os links anteriores do NGINX.com redirecionarão para conteúdo semelhante do NGINX no F5.com."