Já falamos um pouco sobre o Linkerd no último post, agora, para podermos estender ainda mais o nosso conhecimento sobre service mesh, vamos falar de um conceito interessante que vamos explorar com mais calma em outros artigos. Mas aqui vamos ter um exemplo prático! O Tracing.

Tracing

Tracing é um dos elementos da tríade que compõe o que vamos tratar mais a frente como Observabilidade, porém, no momento vamos focar nesse aspecto.

O tracing é a capacidade de poder acompanhar uma request de ponta a ponta verificando a ordem das chamadas dos serviços, o payload enviado e recebido por cada um, as respostas de cada serviço e também o tempo que cada request demorou.

Implementar um tracing simples em uma aplicação não é uma tarefa complexa, basta fazermos um log de tudo que recebemos e enviamos. Porém, existe um outro conceito chamado deep tracing ou distributed tracing que é justamente voltado à aplicações distribuídas.

E é ai que as coisas começam a ficar complicadas...

Deep Tracing

Deep Tracing é o nome que se dá à técnica de ligar logs de uma chamada a outra, formando uma linha do tempo do que foi feito. A cada log, damos o nome de um span e uma request pode desencadear múltiplos spans, ainda mais quando estamos tratando com microsserviços que chamam uns aos outros em sequencia.

Sendo muito simplista, o que o Deep Tracing faz é adicionar um header na request inicial que é chamado de Request ID – cada implementação da técnica tem seu próprio nome para isso. A cada nova request, esse ID é passado para frente e, combinada com a data da requisição, essas informações são utilizadas para construir um histórico de tudo que foi feito.

O problema é que, para fazer a implementação manual disso tudo, temos que:

  • Ter uma noção muito boa da nossa aplicação
  • Interceptar todas as chamadas HTTP e colocar um header em cada uma delas

O que não é uma tarefa fácil, por isso existem ferramentas como o Jaeger.

Jaeger

O Jaeger é uma ferramenta open source hospedada pela CNCF que serve justamente para evitar a complicação de ter que faz o deploy e implementação de todas as partes móveis que compõem um sistema de tracing distribuído.

Ele também utiliza o Open Telemetry um projeto que visa simplificar a telemetria de aplicações através de um conjunto padrão de ferramentas para obtenção de métricas.

O Jaeger é escrito em Golang, o que torna ele super rápido e muito útil para sistemas que estão distribuídos ou então possuem uma malha complexa de serviços. Sabe o que mais vai bem com essa arquitetura? O Linkerd!

Linkerd e Jaeger

Service Mesh e Observabilidade são conceitos que andam lado a lado, porém nem sempre você tem a capacidade de implementar ambos de forma simples. E é ai que tanto o Linkerd quanto o Jaeger brilham individualmente, mas, além de serem incríveis por si só, eles são ainda melhores juntos.

Usar o Linkerd com o Jaeger é uma excelente forma de obter todas as informações e métricas possíveis de sua aplicação de forma rápida e prática, até porque o próprio Linkerd suporta add-ons que incluem tanto o Jaeger como o OpenCensus Collector, uma ferramenta de coleta de métricas.

Mas chega de conceitos! A melhor forma de explicar o que é o Jaeger é através da prática! Então vamos colocar a mão na massa!

Aplicando Tracing

Para começar, vou assumir que você já está com o cluster criado e com o Linkerd Instalado, se você ainda não instalou tudo, volta para o artigo anterior e siga o tutorial até o final

Com o nosso cluster configurado e o Linkerd já instalado, vamos começar instalando a configuração all-in-one, que provê uma única imagem que contém todos os elementos necessários para que o tracing do Jaeger funcione tranquilamente.

Para instalar essa configuração, vamos criar um novo arquivo chamado config.yaml e colocar o seguinte conteúdo nele:

tracing:
  enabled: true

Então, vamos executar um comando do Linkerd para adicionar o novo add-on, na pasta aonde você criou o novo arquivo execute o seguinte comando:

linkerd upgrade --addon-config config.yaml | kubectl apply -f -

Assim que terminarmos, devemos ter dois novos deploys, um chamado linkerd-collector e outro chamado linkerd-jaeger no namespace linkerd:

Temos dois novos deployments dentro do namespace Linkerd

Anotações

Para detectar as mudanças e começar a realizar o tracing, o Linkerd utiliza duas anotações novas nos nossos deployments, sempre que precisarmos fazer essa modificação vamos incluir estas linhas juntamente com a anotação linkerd.io/inject: enabled, ficando assim:

spec:
  template:
    metadata:
      annotations:
      	linkerd.io/inject: enabled
        config.linkerd.io/trace-collector: linkerd-collector.linkerd:55678
        config.alpha.linkerd.io/trace-collector-service-account: linkerd-collector

Instrumentação

O tracing, diferentemente da maioria das técnicas do DevOps, exige uma instrumentação na aplicação, ou seja, temos que manualmente inserir o código do coletor de métricas no nosso sistema para que ele possa identificar e adicionar os headers e o ID para o trace.

Isso pode ser feito de forma manual na nossa aplicação usando Node através deste pacote, porém, para mantermos o processo mais simples, vamos utilizar a aplicação padrão do Linkerd para testar.

Primeiramente instalamos a aplicação com o comando

kubectl apply -f https://run.linkerd.io/emojivoto.yml

Depois executamos o seguinte comando para que possamos incluir a anotação que mostramos anteriormente

kubectl -n emojivoto patch -f https://run.linkerd.io/emojivoto.yml -p '
spec:
  template:
    metadata:
      annotations:
        linkerd.io/inject: enabled
        config.linkerd.io/trace-collector: linkerd-collector.linkerd:55678
        config.alpha.linkerd.io/trace-collector-service-account: linkerd-collector
'

Esperamos o deployment terminar de ser finalizado. Você pode executar o comando a seguir para acompanhar:

kubectl -n emojivoto rollout status deploy/web

Para finalizar, vamos ativar o tracing setando uma nova variável de ambiente no deployment com o seguinte comando

kubectl -n emojivoto set env --all deploy OC_AGENT_HOST=linkerd-collector.linkerd:55678

Explorando o Jaeger

Vamos ter certeza de que nossa implementação funcionou quando rodarmos o comando linkerd dashboard e virmos o ícone do Jaeger ao lado dos nossos namespaces

Veja o ícone do logo do Jaeger ao lado do Grafana

Se clicarmos nele, teremos todas as chamadas feitas dentro da aplicação e poderemos acompanhar o histórico de tudo que foi chamado via HTTP

Cada círculo é uma chamada

Se clicarmos em uma das linhas, conseguiremos ver por onde todas as requisições passaram e quais serviços foram chamados, bem como seus payloads e tempos

Outra funcionalidade interessante é a capacidade do Jaeger identificar a possível arquitetura do sistema. Basta clicar na guia "System Architecture" e selecionar entre o grafo direcionado e o DAG:

Diagrama de número de requisições
Grafo direcionado

Podemos também explorar cada uma das requisições individualmente através do mesmo ícone do Jaeger em cada uma das linhas quando iniciamos o "Live View" de uma rota.

Conclusão

Vamos falar mais de observabilidade por aqui no futuro, mas tenha em mente que o tracing é uma das principais razões pelo qual o service mesh é tão cobiçado. O poder que é possível extrair quando você sabe exatamente o que está acontecendo em seu sistema permite que você resolva bugs e trate erros de forma muito mais simples e rápida.

Espero que tenham gostado do artigo, deixe seu comentário, curta e compartilhe! Vamos ajudar todo mundo a entender o que é tracing! Não se esqueça de se inscrever na newsletter para mais conteúdo exclusivo!

Até mais!