Introdução

A maior vantagem do OpenTelemetry está na sua integração com diferentes backends, tornando possível o envio das informações para vários locais diferentes sem a necessidade de alteração do código da aplicação, portanto, dando continuidade ao assunto do meu post anterior, nesse post vou abordar o envio dos traces coletados na aplicação python para o Datadog.

Integração do Datadog com o OpenTelemetry

O Datadog, assim como outros provedores de APM já se adaptou completamente ao uso do OpenTelemetry como ponto de coleta das informações, portanto, existem algumas formas de configurar essa integração.

Esteja ciente de que o foco do post é no envio apenas dos traces, assim, as configurações apresentadas funcionarão apenas para isso.

Atualmente existem 3 formas de enviar os dados de trace para o Datadog:

  1. Através do coletor oficial do OpenTelemetry;
  2. Através do Agente do Datadog;
  3. Através do cliente ddtrace.

OpenTelemetry Collector

Junto com o surgimento do OpenTelemetry também surgiu o OpenTelemetry Collector (ou Otel Collector). A ideia principal desse coletor é que ele seja uma forma de receber, processar e exportar os dados completamente agnostica aos provedores contratados, sendo possível o envio dos dados para mais de um provedor de forma simultânea.

Segundo a documentação oficial, é possível utiliza o coletor de três formas:

  • local: cada aplicação terá uma instância do coletor localmente;
  • centralizado: existirá apenas um instância central do coletor que todas as aplicações utilizarão;
  • combinado: nesse formato cada aplicação pode, ou não, ter uma instância do coletor, mas existirá outras intâncias, ou seja, um coletor enviará os dados para outros coletores, fazendo uma espécie de roteamento dos dados coletados.

local

Essa forma de utilização possui a vantagem de ser menos propenso a falhas, já que se um coletor apresentar problemas, apenas uma aplicação será impactada, porém, apresenta uma maior complexidade de gerencia de tokens de acesso, já que cada aplicação necessitará de um token para enviar os dados.

centralizado

Essa forma de utilização é contrária a anterior. Possui uma configuração simplificada, porém, centraliza todos os dados em uma instância, criando um ponto de falha.

combinado

Essa forma de utilização tenta unir as vantagens das anteriores, criando um modelo mais robusto. Como o coletor possui a capacidade de envio dos dados para multiplos locais de forma simultânea, é possível centralizar os dados em mais de um coletor, assim, diminuindo o ponto de falha. Por ainda apresentar os coletores centrais, apenas eles possuem os tokens para envio dos dados, o que torna o gerenciamento dos tokens tão simples quanto o modelo centralizado.

Sua desvantagem está na complexidade da solução. Como agora sua arquitetura possui mais componentes, isso demanda mais arquivos de configuração e mais pontos de atenção para segurança dos dados, tornando toda a coisa mais complexa.


Por questões de simplicidade, vou abordar apenas o modelo centralizado.

A configuração do coletor é feita através de um arquivo YAML. Para a criação desse arquivo vamos seguir duas documentações: a oficial e a do Datadog. Com isso, chegamos ao seguinte arquivo:

otel-collector-config.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Nesse seção configuramos como nosso coletor irá
# receber os dados. No nosso caso, configuramos o
# otlp para ser nossa entrada de dados através
# do protocolo grpc na porta padrão (4317).
receivers:
  otlp:
    protocols:
      grpc: {}

# Nessa seção configuramos o processamento dos dados
# recebidos. No nosso caso, vamos fazer o processamento
# em batch usando as configurações padrões.
processors:
  batch:
    # Datadog APM tem o limite de envio de 3.2MB.
    # Configura para que não envie mais do que isso.
    send_batch_max_size: 1000
    send_batch_size: 100
    timeout: 10s

# Nessa seção configuramos como o coletor exportará
# os dados. No nosso caso, enviaremos para o Datadog.
exporters:
  datadog:
    api:
      site: ${env:DD_SITE}
      key: ${env:DD_API_KEY}

# Nessa seção configuramos quais componentes do
# coletor serão habilitados. No nosso caso, apenas
# os traces serão habilitados.
service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [datadog]

Agora que temos a configuração feita podemos iniciar o coletor. Vamos utilizar o docker para fazer isso com o comando:

1
2
3
4
5
6
7
docker run --rm -d --name otel-collector \
    -e DD_SITE=datadoghq.com \
    -e DD_API_KEY='<api-key>' \
    -v ./otel-collector-config.yaml:/etc/otel-collector-config.yaml:ro \
    -p 4317:4317 \
    otel/opentelemetry-collector-contrib:latest \
        --config=/etc/otel-collector-config.yaml

No caso do Datadog, não podemos esquecer de definir os seguites parâmetros antes de iniciar nossa aplicação:

1
2
3
export OTEL_TRACES_EXPORTER=otlp
export OTEL_SERVICE_NAME='<service>'
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment='<environment>',service.version='<version>'

Datadog Agent

Atualmente o agente do Datadog possui a funcionalidade de trabalhar também como um coletor do OpenTelemetry. Segundo a documentação, para habilitar essa funcionalidade basta definir algumas variáveis de ambiente e apontar nossa aplicação para utilizar o agente. Como existem duas formas de rodar o agente, serverless e isolado, vou apontar as diferenças na sua configuração.

Assim como no Otel Collector, não podemos esquecer de definir as variáveis de ambiente OTEL_TRACES_EXPORTER, OTEL_SERVICE_NAME e OTEL_RESOURCE_ATTRIBUTES.

Serverless

Segundo a documentação, esse modo consiste em subir o agente junto a aplicação no container. Para configurar o agente serverless a habilitar o OpenTelementry, basta adicionar as seguintes linhas no Dockerfile:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Copia o agente para sua imagem docker.
COPY --from=datadog/serverless-init:1 /datadog-init /usr/bin/datadog-init

# Define o agente como ponto de entrada da sua imagem.
# Obs: o agente que irá inicializar sua aplicação, assim, basta
# definir os comandos de inicialização da aplicação como "CMDS".
ENTRYPOINT ["/usr/bin/datadog-init"]

# Configura o DD_SITE para onde o agente irá enviar os dados.
ENV DD_SITE=datadoghq.com

# Habilita a coleta de dados de APM.
ENV DD_APM_ENABLED=true

# Habilita o agente a funcionar como coletor do OpenTelemetry
# utilizando o protocolo GRPC.
ENV DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_GRPC_ENDPOINT=127.0.0.1:4317

# Habilita a coletas de traces utilizando o OpenTelemetry.
ENV DD_OTLP_CONFIG_TRACES_ENABLED=true

Por questões de segurança, nunca adicione o DD_API_KEY na imagem, assim, seu token não ficará exposto na imagem docker.

Isolado

O agente do Datadog possui várias formas de ser inicializado. Eu separei esse modo como “isolado” pelo fato de todas essas formas inicializarem o agente separado da aplicação. A configuração para esse modo consiste na definição das mesmas variáveis de ambiente, apenas alterando o valor de uma delas e definindo uma a mais, ficando:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Configura o token que será utilizado para enviar os dados.
DD_API_KEY='<api-key>'

# Configura o DD_SITE para onde o agente irá enviar os dados.
DD_SITE=datadoghq.com

# Habilita a coleta de dados de APM.
DD_APM_ENABLED=true

# Habilita o agente a receber dados não locais, ou seja, de
# outras máquinas.
#
# Obs: utilizar apenas se o agente for coletar dados de aplicações
# externas a máquina/container em que o agente se encontra.
DD_APM_NON_LOCAL_TRAFFIC=true

# Habilita o agente a funcionar como coletor do OpenTelemetry
# utilizando o protocolo GRPC.
DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_GRPC_ENDPOINT=0.0.0.0:4317

# Habilita a coletas de traces utilizando o OpenTelemetry.
DD_OTLP_CONFIG_TRACES_ENABLED=true

Datadog Python APM Client (ddtrace)

Quando escrevemos uma aplicação em python, uma forma fácil de coletar e enviar dados de trace para o Datadog é através do cliente ddtrace. Ele é responsável por fazer uma instrumentação automática, mas também possibilita o envio de dados customizados pelo código, porém, estamos fugindo do acoplamento do código a apenas um provider de APM.

Pensando na facilidade de se utilizar o ddtrace, foi adicionado uma funcionalidade nova para realizar a configuração do OpenTelemetry de forma automática, assim, utilizando o ddtrace como proxy para que os dados sejam enviados para o agente do Datadog. Para habilitar essa funcionalidade precisamos apenas definir uma variável de ambiente antes de iniciar o código. Usando o comando de inicialização da aplicação apresentada no post anterior, fica:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Comando de inicialização usando a instrumentação
# automática do OpenTelemetry.
export OTEL_SERVICE_NAME='<service>'
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment='<environment>',service.version='<version>'
opentelemetry-instrument uvicorn app:app --port 8080

# Comando de inicialização usando o ddtrace.
export DD_SERVICE='<service>'
export DD_ENV='<environment>'
export DD_VERSION='<version>'
export DD_TRACE_OTEL_ENABLED=true
ddtrace-run uvicorn app:app --port 8080

🚨 Atenção: o ddtrace coleta apenas dados de trace, portanto, caso queira que métricas e logs também sejam coletados, terá que mudar para uma das outras soluções apresentadas acima.

Conclusão

Como vimos acima, existem várias formas de integrar seu código escrito com OpenTelemetry no Datadog, cada uma com suas vantagens e complexidade diferente. Isso é importante porque fazendo dessa forma, conseguimos desacoplhar todo o nosso código de uma implementação fechada de uma ferramenta, o que torna a mudança para outra ferramenta, caso necessário, mais simples e rápida.