OpenTelemetry is a collection of APIs, SDKs, and tools. Use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) to help you analyze your software’s performance and behavior.
Source: https://opentelemetry.io/
Otel Diagram
Source: https://opentelemetry.io/docs/
Cenários para auto-instrumentação (Zero-code Instrumentation)
Alguns cenários podem nos levar a recorrer à instrumentação automática de aplicações. São eles:
- Não ter acesso ao código fonte;
- Falta de sponsors para um refatoramento, por não ser viável ao negócio;
- Não ser o "dono" da aplicação;
- A aplicação já executada há tempos, ninguém tem mais o contexto e o desenvolvimento foi descontinuado.
Mesmo se você se encontrar em um dos motivadores acima (ou mais de um), ainda pode haver a necessidade de observar essas aplicações.
É importante lembrar que essa abordagem de auto-instrumentação traz algumas ressalvas, como, por exemplo, o excesso de telemetria gerado pela aplicação instrumentada.
Quando temos um cenário de uma aplicação greenfield (do zero), será mais interessante já pensar na instrumentação como parte do desenvolvimento, sendo chamada de instrumentação manual e não auto-instrumentação.
Arquitetura do Lab
Nesse lab, vamos explorar a auto-instrumentação com OpenTelemetry Operator para Kubernetes.
Repositórios:
- Terraform: https://github.com/paulofponciano/EKS-Istio-Karpenter
- Stack observabilidade: https://github.com/paulofponciano/o11y-lab-prom-otel-loki-tempo
O provisionamento da infraestrutura e seus componentes, como: Cluster EKS, Istio ingress, karpenter e também a stack de observabilidade que contempla o Grafana Tempo, pode ser feito seguindo esse outro Lab - o11y: OpenTelemetry, Prometheus, Loki e Tempo no EKS [Lab Session]. Portanto, partiremos do ponto em que o EKS já está configurado e com o Grafana Tempo em execução.
Deploy do OpenTelemetry Operator
Componentes já em execução no cluster:
- Kube-prometheus-stack
- Promtail
- Grafana Loki (distributed)
- Grafana Tempo (distributed)
O que vamos usar aqui é o Grafana Tempo e o Grafana que faz parte da stack do Prometheus. No Grafana, já há um datasource do Tempo configurado.
Deploy do operator usando Helm:
git clone https://github.com/paulofponciano/o11y-lab-prom-otel-loki-tempo.git
cd o11y-lab-prom-otel-loki-tempo/opentelemetry/auto-instrumentation/operator
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update
helm install opentelemetry-operator open-telemetry/opentelemetry-operator \
--namespace opentelemetry-operator-system \
--create-namespace \
--set "manager.collectorImage.repository=otel/opentelemetry-collector-k8s" \
--set admissionWebhooks.certManager.enabled=false \
--set admissionWebhooks.autoGenerateCert.enabled=true
Logs iniciais do OpenTelemetry Operator:
Deploy Otel collector
Podemos agora fazer o deploy de um custom resource que é o OpenTelemetryCollector. Basicamente, ele receberá os traces (receivers) e os enviará (exporters) para o Grafana Tempo, onde poderão ser visualizados.
kubectl apply -f otel-collector.yaml
Manifesto do collector:
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
name: otelcol
namespace: o11y
spec:
config:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
memory_limiter:
check_interval: 1s
limit_percentage: 75
spike_limit_percentage: 15
batch:
send_batch_size: 10000
timeout: 10s
exporters:
debug: {}
otlp:
endpoint: tempo-distributor.o11y.svc.cluster.local:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [debug, otlp]
Logs iniciais do otel collector:
Exemplo Java e Otel Instrumentation
Mais um custom resource que faremos o deploy é o Instrumentation. Dentre outras configurações, nele definiremos quem será o otel collector, no caso, será o que acabamos de criar.
kubectl apply -f java-auto-instrumentation.yaml
Manifesto de instrumentation:
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: java-auto-instrumentation
namespace: o11y
spec:
exporter:
endpoint: http://otelcol-collector.o11y.svc.cluster.local:4318
propagators:
- tracecontext
- baggage
sampler:
type: parentbased_traceidratio
argument: "1"
kubectl get instrumentation -n o11y
Agora podemos fazer o deploy de uma aplicação de exemplo em java (vertx-create-span):
kubectl create ns sample-api-java
kubectl apply -f java-service-with-auto-instrumentation.yaml
Importante notar a annotation que devemos informar no manifesto, onde é definida a instrumentation que usaremos:
# Part of the complete manifest:
template:
metadata:
labels:
app: operator-e2e-tests
annotations:
instrumentation.opentelemetry.io/inject-java: "o11y/java-auto-instrumentation"
# ...
Executando kubectl describe pod
no pod da aplicação que acabou de subir, vemos que foi injetado um initContainer para auto-instrumentação:
Dentro do container:
Simulando tráfego de entrada na aplicação java:
Logs da aplicação e do otel collector:
No Grafana, visualizamos os traces recebidos através do datasource plugin do Grafana Tempo:
Exemplo Python e Otel Instrumentation
Faremos o mesmo processo de deploy, mas agora para uma app demo que simula a comunicação de microsserviços. Primeiro o apply do manifesto de instrumentation - Embora fosse possível reutilizar a Instrumentation já criada, optamos por criar uma específica para cada caso:
kubectl apply -f python-auto-instrumentation.yaml
kubectl get instrumentation -n o11y
Deploy app demo python:
kubectl create ns sample-api-python
kubectl apply -f python-service-with-auto-instrumentation.yaml
Annotation referenciando a Instrumentation:
# Part of the complete manifest:
template:
metadata:
annotations:
instrumentation.opentelemetry.io/inject-python: "o11y/python-auto-instrumentation"
labels:
app: microservice-1
# ...
Com kubectl describe pod
novamente vemos o initContainer da auto-instrumentação, agora para python:
Gerando tráfego de entrada:
No Grafana:
Podemos simular uma latência maior entre as chamadas, aumentando o 'RESPONSE_TIME' no manifesto de deploy, e reaplicar:
# Part of the complete manifest:
spec:
containers:
- name: app
image: paulofponciano/demo-ms-intercomunicacao:latest
ports:
- containerPort: 5000
env:
- name: TITLE
value: "Microservice 2"
- name: RESPONSE_TIME
value: "3500" # bottleneck
- name: EXTERNAL_CALL_URL
value: "http://microservice-3.sample-api-python.svc.cluster.local:5000"
- name: EXTERNAL_CALL_METHOD
value: "GET"
# ...
Comparando com o trace anterior (antes da alteração no RESPONSE_TIME), o ponto de gargalo fica visível:
Referências:
Dose ao vivo: aula prática de OpenTelemetry Operator
OpenTelemetry - Injecting Auto-instrumentation
OpenTelemetry Operator for Kubernetes
Keep shipping! 🐳
Top comments (0)