Rastreamento distribuído em um aplicativo de microsserviços

Refresh_date: 24-10-2023

Este é o quarto de uma série de quatro partes sobre como projetar, criar e implantar microsserviços. Esta série descreve os vários elementos de uma arquitetura de microsserviços. A série inclui informações sobre os benefícios e as desvantagens do padrão de arquitetura de microsserviços e como aplicá-lo.

  1. Introdução a microsserviços
  2. Como refatorar um monolítico em microsserviços
  3. Comunicação entre serviços em uma configuração de microsserviços
  4. Rastreamento distribuído em um aplicativo de microsserviços (este documento)

Esta série é destinada a desenvolvedores e arquitetos de aplicativos que projetam e implementam a migração para refatorar um aplicativo monolítico em um aplicativo de microsserviços.

Em um sistema distribuído, é importante saber como uma solicitação flui de um serviço para outro e quanto tempo leva para executar uma tarefa em cada serviço. Pense no aplicativo Online Boutique baseado em microsserviços que você implantou no documento anterior, Como refatorar um monolítico em microsserviços. O aplicativo é composto por vários serviços. Por exemplo, a captura de tela a seguir mostra a página de detalhes do produto, que busca informações dos serviços de front-end, de recomendação e de anúncios.

A página de detalhes do produto.

Para renderizar a página de detalhes do produto, o serviço de front-end se comunica com os serviços de recomendação e de anúncios, conforme mostrado no diagrama a seguir:

O serviço de front-end se comunica com o serviço de recomendação, o catálogo de produtos e o serviço de anúncios.

Figura 1. Serviços escritos em idiomas diferentes.

Na figura 1, o serviço de front-end é escrito em Go. O serviço de recomendação, escrito em Python, usa o gRPC para se comunicar com o serviço de front-end. O serviço de anúncios, escrito em Java, também usa o gRPC para se comunicar com o serviço de front-end. Além do gRPC, o método de comunicação entre serviços também pode ser usado em HTTP REST.

Ao criar esse sistema distribuído, você quer que suas ferramentas de observabilidade forneçam os seguintes insights:

  • Os serviços que uma solicitação passou.
  • Onde ocorreram atrasos se uma solicitação era lenta.
  • Onde ocorreu um erro se a solicitação falhar.
  • As diferenças entre a execução da solicitação e o comportamento normal do sistema
  • Se as diferenças na execução da solicitação estão relacionadas ao desempenho (se algumas chamadas de serviço demoraram mais ou menos que o normal).

Objetivos

  • Use os arquivos de manifesto personalizados para configurar a infraestrutura.
  • Implante o aplicativo de exemplo do Online Boutique no Google Kubernetes Engine (GKE).
  • Use o Cloud Trace para analisar a jornada de um usuário no aplicativo de exemplo.

Custos

Neste documento, você usará os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Ao concluir este documento, você evitará o faturamento contínuo excluindo os recursos criados. Para mais informações, consulte Como fazer a limpeza.

Antes de começar

Se você já configurou um projeto concluindo o documento anterior desta série, comunique a comunicação entre serviços em uma configuração de microsserviços, poderá reutilizá-lo. Conclua as etapas a seguir para ativar outras APIs e definir variáveis de ambiente.

  1. No console do Google Cloud, na página do seletor de projetos, selecione ou crie um projeto do Google Cloud.

    Acessar o seletor de projetos

  2. Verifique se a cobrança está ativada para o seu projeto do Google Cloud.

  3. No Console do Google Cloud, ative o Cloud Shell.

    Ativar o Cloud Shell

  4. Ative as APIs do Compute Engine, GKE, Cloud SQL, Artifact Analysis, Trace e Container Registry:

     gcloud services enable \
       compute.googleapis.com \
       sql-component.googleapis.com \
       servicenetworking.googleapis.com\
       container.googleapis.com \
       containeranalysis.googleapis.com \
       containerregistry.googleapis.com \
       sqladmin.googleapis.com
    

Rastreamento distribuído

O rastreamento distribuído anexa metadados contextuais a cada solicitação e garante que os metadados sejam compartilhados entre as solicitações. Você usa pontos de rastreamento para instrumentar o rastreamento distribuído. Por exemplo, é possível instrumentar seus serviços (front-end, recomendação e anúncios) com dois pontos de rastreamento para processar uma solicitação de cliente e visualizar os detalhes de um produto: um ponto de rastreamento para enviar a solicitação e outro para ponto de recebimento para receber a resposta. O diagrama a seguir mostra como essa instrumentação de ponto de trace funciona:

Uma instrumentação de ponto de rastreamento que tem dois pontos de rastreamento.

Figura 2. Cada chamada entre serviços tem dois pontos de rastreamento que consistem em um par de solicitação-resposta.

Para que os pontos de rastreamento entendam qual solicitação executar quando o serviço é invocado, o serviço de origem transmite um ID de trace ao longo do fluxo de execução. O processo que transmite o ID de trace é chamado de propagação de metadados ou propagação de contexto distribuído. A propagação de contexto transfere metadados por meio de chamadas de rede quando os serviços de um aplicativo distribuído se comunicam durante a execução de uma determinada solicitação. O diagrama a seguir mostra a propagação de metadados:

A propagação de metadados transmite o ID de trace.

Figura 3. Os metadados de trace são transmitidos entre serviços. Os metadados incluem informações como quais serviços chamam quais e seus carimbos de data/hora.

No exemplo do Online Boutique, um trace começa quando um usuário envia uma solicitação inicial para buscar detalhes do produto. Um novo ID de trace é gerado, e cada solicitação sucessiva é decorada com cabeçalhos que contêm metadados contextuais de volta para a solicitação original.

Cada operação individual que é invocada como parte do preenchimento da solicitação do usuário final é chamada de período. Cada tag de serviço de origem inclui cada período com o próprio ID exclusivo e o código de trace do período pai. O diagrama a seguir mostra uma visualização de um gráfico de Gantt de um trace:

As operações individuais são marcadas como períodos.

Figura 4. Um período pai inclui o tempo de resposta dos períodos filhos.

A Figura 4 mostra uma árvore de rastreamento em que o serviço de front-end chama o serviço de recomendação e o serviço de anúncios. O serviço de front-end é o período pai, que descreve o tempo de resposta observado pelo usuário final. Os períodos filhos descrevem como o serviço de recomendação e o serviço de anúncios foram chamados e respondidos, incluindo as informações do tempo de resposta.

Uma malha de serviço como o Istio permite o rastreamento distribuído do tráfego de serviço a serviço sem a necessidade de qualquer instrumentação dedicada. No entanto, pode haver situações em que se queira ter mais controle sobre os traces ou que seja necessário rastrear o código que não esteja sendo executado em uma malha de serviço.

Neste documento, usamos o OpenCensus para permitir a instrumentação de aplicativos de microsserviço distribuídos para coletar traces e métricas. Com o OpenCensus, é possível coletar métricas e rastreamentos e exportá-los para back-ends, como o Prometheus, o Cloud Monitoring, o Datadog, o Graphite, o Zipkin e o Jaeger.

Instrumentação com o OpenTelemetry

As seções a seguir mostram como usar a propagação de contexto para permitir que períodos de várias solicitações sejam anexados a um único rastreamento pai.

Este exemplo usa as bibliotecas JavaScript, Python e Go do OpenTelemetry para instrumentar e trace a implementação dos serviços de pagamento, recomendação e front-end. Dependendo do nível de detalhes da instrumentação, os dados de rastreamento podem afetar o custo do projeto (faturamento do Cloud Trace). Para reduzir a latência, a maioria dos sistemas de rastreamento usa várias formas de amostragem para capturar apenas uma determinada porcentagem dos traces observados. Nos ambientes de produção, é possível que sua organização tenha motivos para fazer as amostras e por quê. Talvez você queira personalizar sua estratégia de amostragem com base no gerenciamento de custos, concentrando-se em rastros interessantes ou filtrando ruídos. Para saber mais sobre amostragem, consulte Amostragem do OpenTelemetry.

Neste documento, usamos o Cloud Trace para visualizar traces distribuídos. Use um exportador do OpenTelemetry para enviar traces ao Trace.

Registrar exportadores de rastreamento

Nesta seção, mostramos como registrar o exportador de rastreamento em cada serviço adicionando linhas ao código do microsserviço.

Para o serviço de front-end (escrito em Go), o exemplo de código a seguir registra o exportador:

[...]
exporter, err := otlptracegrpc.New(
        ctx,
        otlptracegrpc.WithGRPCConn(svc.collectorConn))
    if err != nil {
        log.Warnf("warn: Failed to create trace exporter: %v", err)
    }
tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithSampler(sdktrace.AlwaysSample()))
    otel.SetTracerProvider(tp)

Para o serviço de recomendação (escrito em Python), a amostra de código a seguir registra o exportador:

if os.environ["ENABLE_TRACING"] == "1":
    trace.set_tracer_provider(TracerProvider())
    otel_endpoint = os.getenv("COLLECTOR_SERVICE_ADDR", "localhost:4317")
    trace.get_tracer_provider().add_span_processor(
        BatchSpanProcessor(
            OTLPSpanExporter(
            endpoint = otel_endpoint,
            insecure = True
            )
        )
    )

Para o serviço de pagamento (escrito em JavaScript), o exemplo de código a seguir registra o exportador:

provider.addSpanProcessor(new SimpleSpanProcessor(new OTLPTraceExporter({url: collectorUrl})));
provider.register();

Configurar a propagação de contexto

O sistema de rastreamento precisa seguir uma especificação de contexto de rastreamento que defina o formato para propagar o contexto de rastreamento entre os serviços. Os exemplos de formato de propagação incluem o formato B3 do Zipkin e o X-Google-Cloud-Trace.

O OpenTelemetry propaga o contexto usando o TextMapPropagator global. Este exemplo usa o propagador do contexto de trace, que usa o formato traceparent do W3C. As bibliotecas de instrumentação, como as bibliotecas HTTP e gRPC do OpenTelemetry, usam o propagador global para adicionar contexto de trace como metadados a solicitações HTTP ou gRPC. Para que a propagação de contexto seja bem-sucedida, o cliente e o servidor precisam usar o mesmo formato de propagação.

Propagação de contexto sobre HTTP

O serviço de front-end injeta um contexto de trace nos cabeçalhos de solicitação HTTP. Os serviços de back-end extraem o contexto do trace. O exemplo de código a seguir mostra como o serviço de front-end está instrumentado para configurar o contexto de rastreamento:

otel.SetTextMapPropagator(
    propagation.NewCompositeTextMapPropagator(
        propagation.TraceContext{}, propagation.Baggage{}))

if os.Getenv("ENABLE_TRACING") == "1" {
    log.Info("Tracing enabled.")
    initTracing(log, ctx, svc)
} else {
    log.Info("Tracing disabled.")
}

...

var handler http.Handler = r
handler = &logHandler{log: log, next: handler}     // add logging
handler = ensureSessionID(handler)                 // add session ID
handler = otelhttp.NewHandler(handler, "frontend") // add OpenTelemetry tracing

Propagação de contexto sobre gRPC

Considere o fluxo em que o serviço de finalização de compra faz o pedido com base no produto selecionado pelo usuário. Esses serviços se comunicam por meio do gRPC.

O exemplo de código a seguir usa um interceptador de chamadas gRPC que intercepta as chamadas enviadas e injeta o contexto de rastreamento:

var srv *grpc.Server

// Propagate trace context always
otel.SetTextMapPropagator(
    propagation.NewCompositeTextMapPropagator(
        propagation.TraceContext{}, propagation.Baggage{}))
srv = grpc.NewServer(
    grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
    grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor()),
)

Depois de receber a solicitação, o serviço de pagamento ou de catálogo de produtos (ListProducts) extrai o contexto dos cabeçalhos da solicitação e usa os metadados de trace pai para gerar um período filho.

As seções a seguir fornecem detalhes sobre como configurar e revisar a distribuição distribuída para o exemplo de aplicativo Online Boutique.

Como implantar o aplicativo

Se você já tiver um aplicativo em execução para concluir o documento anterior desta série, Comunicação entre serviços em uma configuração de microsserviços, pule para a próxima seção. Como revisar traces. Caso contrário, conclua as seguintes etapas para implantar o exemplo do exemplo Online Boutique:

  1. Para configurar a infraestrutura, clone o repositório do GitHub no Cloud Shell:

    git clone https://github.com/GoogleCloudPlatform/microservices-demo.git
    
  2. Para a nova implantação, redefina as variáveis de ambiente:

    PROJECT_ID=PROJECT_ID
    REGION=us-central1
    GSA_NAME=microservices-sa
    GSA_EMAIL=$GSA_NAME@$PROJECT_ID.iam.gserviceaccount.com
    

    Substitua:

    • PROJECT_ID: o identificador do ID do projeto.
  3. Opcional: crie um cluster novo ou reutilize um cluster atual, se houver:

    gcloud container clusters create-auto online-boutique --project=${PROJECT_ID}
      --region=${REGION}
    
  4. Crie uma conta de serviço do Google:

    gcloud iam service-accounts create $GSA_NAME \
      --project=$PROJECT_ID
    
  5. Ative as APIs:

    gcloud services enable \
    monitoring.googleapis.com \
    cloudtrace.googleapis.com \
    cloudprofiler.googleapis.com \
      --project ${PROJECT_ID}
    
  6. Conceda os papéis necessários para o Cloud Tracing para o GCS:

    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member "serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role roles/cloudtrace.agent
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member "serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role roles/monitoring.metricWriter
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member "serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role roles/cloudprofiler.agent
    
    gcloud iam service-accounts add-iam-policy-binding ${GSA_EMAIL} \
    --role roles/iam.workloadIdentityUser \
    --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/default]"
    
  7. Anote sua conta de serviço do Kubernetes (default/default para o namespace padrão) para usar a conta de serviço do Google IAM:

    kubectl annotate serviceaccount default \
        iam.gke.io/gcp-service-account=${GSA_EMAIL}
    
  8. Ativar a configuração das Operações do Cloud para GKE que permite o rastreamento:

    cd ~/microservices-demo/kustomize && \
    kustomize edit add component components/google-cloud-operations
    
  9. Isso vai atualizar o arquivo kustomize/kustomization.yaml, que pode ser semelhante a:

    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    resources:
    - base
    components:
    - components/google-cloud-operations
    [...]
    
  10. Implantar os microsserviços:

    kubectl apply -k .
    
  11. Verifique o status da implantação:

    kubectl rollout status deployment/frontend
    kubectl rollout status deployment/paymentservice
    kubectl rollout status deployment/recommendationservice
    kubectl rollout status deployment/adservice
    

    A saída de cada comando é semelhante a esta:

    Waiting for deployment "" rollout to finish: 0 of 1 updated replicas are available...
    deployment "" successfully rolled out
    
  12. Consiga o endereço IP do aplicativo implantado:

    kubectl get service frontend-external | awk '{print $4}'
    

    Aguarde o endereço IP do balanceador de carga ser publicado. Para sair do comando, pressione Ctrl+C. Anote o endereço IP do balanceador de carga e acesse o aplicativo no URL http://IP_ADDRESS. Pode levar algum tempo para o balanceador de carga se tornar íntegro e começar a transmitir tráfego.

Como revisar rastreamentos usando o Cloud Trace

A jornada de compra de um usuário no aplicativo Online Boutique tem o seguinte fluxo:

  • O usuário vê um catálogo de produtos na página de destino.
  • Para fazer uma compra, o usuário clica em Comprar.
  • O usuário é redirecionado para uma página de detalhes do produto onde adiciona o item ao carrinho.
  • O usuário é redirecionado para uma página de finalização de compra, onde pode fazer um pagamento para concluir o pedido.

Considere um cenário em que você precisa solucionar problemas de tempos de resposta altos ao carregar a página de detalhes do produto. Conforme descrito anteriormente, a página de detalhes do produto é composta de vários microsserviços. Para determinar onde e porque a alta latência está ocorrendo, você pode ver gráficos de rastreamento distribuídos para analisar o desempenho de toda a solicitação nos diferentes serviços.

Para revisar os gráficos de rastreamento distribuídos, faça o seguinte:

  1. Acesse o aplicativo e clique em qualquer produto. A página de detalhes do produto é exibida.
  2. No console do Google Cloud, acesse a página Lista de traces e revise a linha do tempo.
  3. Para ver os resultados do trace distribuído, clique em Front-end na coluna do URI.
  4. A Visualização da hierarquia do Trace exibe os períodos associados ao URI:

    A visualização de cascata de trace exibe períodos.

    Na captura de tela, o trace de um produto contém os seguintes períodos:

    • O período de front-end captura a latência de ponta a ponta (150,349 ms) que o cliente observa ao carregar a página de detalhes do produto.
    • O período do Serviço de recomendação captura a latência das chamadas de back-end na busca de recomendações (4,246 ms) relacionadas ao produto.
    • O período Serviço de anúncios captura a latência das chamadas de back-end ao buscar anúncios (4,511 ms) relevantes para a página do produto.

Para solucionar problemas de tempos de resposta altos, você recebe insights centrados nos serviços que incluem gráficos de distribuição de latência de todas as solicitações de outliers quando as dependências do serviço não atendem aos respectivosobjetivos de nível de serviço (SLOs). Também é possível usar o Cloud Trace para ver insights de desempenho e criar relatórios de análise com base nos dados de amostra.

Solução de problemas

Se os traces no Gerenciamento do desempenho de aplicativos não aparecerem, verifique se há um erro de permissão negada na Análise de registros. A permissão negada ocorre quando a conta de serviço não tem acesso para exportar os traces. Revise as etapas sobre como conceder os papéis necessários para o Cloud Trace e anote a conta de serviço com o namespace correto. Depois disso, reinicie o opentelemetrycollector:

```
kubectl rollout restart deployment opentelemetrycollector
```

Limpar

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados no tutorial, exclua o projeto que os contém ou mantenha o projeto e exclua os recursos individuais.

Exclua o projeto

  1. No Console do Google Cloud, acesse a página Gerenciar recursos.

    Acessar "Gerenciar recursos"

  2. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
  3. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.

Excluir os recursos

Se você quiser manter o projeto do Google Cloud usado neste documento, exclua os recursos individuais:

  • No Cloud Shell, exclua os recursos:

    gcloud container clusters delete online-boutique --project=${PROJECT_ID} --region=${REGION}
    

A seguir