OpenTelemetry(简称OTel)是一个开源可观测性框架,用于仪表化、生成、收集和导出诸如跟踪、度量、日志等遥测数据
Collector

部署参考文档:
https://opentelemetry.io/zh/docs
https://www.zhihu.com/question/545762884/answer/3323681028

Instrumentation

Instrumentation 是 OpenTelemetry 中用于对应用程序进行监控和数据收集。它包括手动仪表化(Manual Instrumentation)和自动仪表化(Automatic Instrumentation)两种方式。通过 Instrumentation,开发人员可以将应用程序的运行数据(如追踪、指标、日志)收集并发送到后端系统进行分析和可视化。

  1. 手动仪表化:
    • 开发者通过在代码中显式地添加 OpenTelemetry API 调用,来创建和管理追踪、指标等遥测数据。
    • 可以对应用程序的特定部分进行详细的监控和分析,获取更深入的性能和行为信息。
    • 在关键的业务逻辑处手动创建 span 来记录操作的开始和结束时间,从而精确地监控特定代码段的执行情况。
  2. 自动仪表化:
    • 无需修改应用程序源代码,通过特定的机制(如字节码注入、代理等)自动对应用程序中的各种库、框架等进行监控,收集遥测数据。
    • 减少手动添加监控代码的工作量,快速为应用程序添加基本的监控能力。例如,OpenTelemetry 的 Java 自动仪表化可以通过一个 agent 自动收集日志、指标、trace 并上报。

Collector

Collector 是一个开源的、可扩展的代理,用于接收、处理和导出遥测数据(如 traces、metrics 和 logs)。它提供了一种厂商中立的方式来收集、处理和导出遥测数据,消除了运行、操作和维护多个代理/收集器的需要。

Deployment

在不修改应用程序代码的情况下,收集和处理遥测数据。

  1. Agent:
    • 部署方式:每个客户端SDK或下游采集器都配置一个collector。收集器实例与应用程序在同一主机上运行,例如sidecar或者daemonset方式。
    • 工作方式:应用程序中的SDK以OTLP协议发送遥测数据给collector。然后collector处理遥测数据并对接到可观测性后端。Agent形态
  2. Gateway:
    • 部署方式:使用开箱即用的负载均衡器(比如nginx)在otel-collector之间分配负载。
    • 工作方式:应用程序中的SDK以OTLP协议发送遥测数据给负载均衡器,负载均衡器根据算法选择其中一个collector实例进行处理并对接到可观测性后端Gateway形态

Pipeline components

  • receiver:负责按照对应的协议格式监听接收遥测数据,并把数据转给一个或者多个processor。

  • processor:负责做遥测数据加工处理,如丢弃数据,增加信息,转批处理等,并把数据传递给下一个processor或者传递给一个或多个exporter。

  • exporter:负责把数据往下一个接收端发送(一般是遥测后端),exporter可以定义同时从多个不同的processor中获取遥测数据。

Collector 除了提供让遥测数据收集与业务逻辑处理正交的能力,还充当了遥测数据对接遥测后端的适配器。Collector可以接收 Otlp、Zipkin、Jaeger等任意格式的数据,然后以 Otlp、Zipkin、Jaeger等任意格式的数据转发出去。这一切只取决于你需要输入或输出的格式是否有对应的 receiver 和 exporter 实现

最佳实践

Jaeger

为了便于演示这里使用 jaegertracing/all-in-one 镜像来部署 Jaeger,这个镜像包含了 Jaeger 收集器、内存存储、查询服务和 UI 等组件,非常适合开发和测试使用。通过环境变量 COLLECTOR_OTLP_ENABLED 启动对 OTLP(OpenTelemetry Protocol) 的支持。

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: jaeger
spec:
replicas: 1
selector:
matchLabels:
app: jaeger
template:
metadata:
labels:
app: jaeger
spec:
containers:
- name: jaeger
image: jaegertracing/all-in-one:latest
env:
- name: COLLECTOR_OTLP_ENABLED
value: "true"
ports:
- containerPort: 16686
- containerPort: 14268
---
apiVersion: v1
kind: Service
metadata:
name: jaeger
spec:
selector:
app: jaeger
type: ClusterIP
ports:
- name: ui
port: 16686
targetPort: 16686
- name: collector
port: 14268
targetPort: 14268
- name: http
protocol: TCP
port: 4318
targetPort: 4318
- name: grpc
protocol: TCP
port: 4317
targetPort: 4317
EOF

​​OpenTelemetry Operator

OpenTelemetry Operator用于管理OpenTelemetry 收集器(OpenTelemetry Collectors)和工作负载的自动检测(auto-instrumentation)
安装OpenTelemetry Operator

helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts

helm install opentelemetry-operator open-telemetry/opentelemetry-operator \
--set "manager.collectorImage.repository=otel/opentelemetry-collector-k8s" \
--set admissionWebhooks.certManager.enabled=false \
--set admissionWebhooks.autoGenerateCert.enabled=true

可以看到对应的 Pod 已经正常运行:

➜  ~ kubectl --namespace default get pods -l "app.kubernetes.io/name=opentelemetry-operator"
NAME READY STATUS RESTARTS AGE
opentelemetry-operator-f78f45ffc-sgns7 2/2 Running 0 2m44s

还会自动为我们添加OpenTelemetry 相关的 CRD:

➜  ~ kubectl get crd |grep opentelemetry
instrumentations.opentelemetry.io 2025-01-14T07:32:04Z
opampbridges.opentelemetry.io 2025-01-14T07:32:04Z
opentelemetrycollectors.opentelemetry.io 2025-01-14T07:32:04Z

OpenTelemetry Collector

kubectl apply -f - <<EOF
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
name: otel
spec:
config: |
receivers:
otlp:
protocols:
grpc:
http:
processors:
memory_limiter:
check_interval: 1s
limit_percentage: 75
spike_limit_percentage: 15
batch:
send_batch_size: 10000
timeout: 10s

exporters:
debug:
otlp/jaeger:
endpoint: "jaeger.default:4317"
tls:
insecure: true

service:
pipelines:
traces:
receivers: [otlp]
processors: []
exporters: [debug,otlp/jaeger]
EOF

创建 CR OpenTelemetryCollector 后,Otel Operator 会创建一个 deployment 和 多个 service。

➜  ~
kubectl get deployment,service -l app.kubernetes.io/component=opentelemetry-collector

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/otel-collector 1/1 1 1 3m37s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/otel-collector ClusterIP 10.111.0.124 <none> 4317/TCP,4318/TCP 3m36s
service/otel-collector-headless ClusterIP None <none> 4317/TCP,4318/TCP 3m36s
service/otel-collector-monitoring ClusterIP 10.111.151.111 <none> 8888/TCP 3m36s

Auto-instrumentation

创建一个Java 服务的基本检测资源

kubectl apply -f - <<EOF
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
name: instrumentation-sample
spec:
propagators:
- tracecontext
- baggage
- b3
sampler:
type: parentbased_traceidratio
argument: "1"
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: otel-collector.default:4318
java:
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: http://otel-collector.default:4317
EOF

​Instrumentation​​ 由以下属性组成:
​​exporter.endpoint​​ -(可选)将遥测数据发送到 OTLP 格式的地址。
​​propagators​​ - 使所有数据源能够共享底层上下文机制,用于在事务的整个生命周期中存储状态和访问数据。
​sampler​​​ - 通过减少收集和发送到后端的跟踪样本数量来引入的噪音和开销的机制。 OpenTelemetry 提供两种类型:​​StaticSampler​​​ 和 ​TraceIDRatioBased​​。
语言属性,即​​java​​​、​​nodejs​​​、​​python​​​ 和​​dotnet​​ - 根据 pod 注解中设置的语言,使用自动插桩的自定义镜像

默认情况下,自动检测 Java 服务的 Instrumentation 资源与协议otlp一起使用http/protobuf。这意味着配置的端点必须能够通过http有效protobuf负载接收 OTLP。因此,示例使用otel-collector.default:4318,它连接到 http上一步中创建的 Collector 的 otlpreceiver 的端口。

创建java示例应用

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-sample
spec:
replicas: 1
selector:
matchLabels:
app: java-sample
template:
metadata:
labels:
app: java-sample
annotations:
#spec.template.metadata.annotations特定于语言的注解来完成自动检测
instrumentation.opentelemetry.io/inject-java: "true"
spec:
containers:
- name: java-sample
image: pinakispecial/spring-boot-rest
ports:
- containerPort: 8080
EOF

可用注解:
​true​​ - 从命名空间注入和埋点资源。
​my-instrumentation​​​ - 当前命名空间中注入​​Instrumentation​​ CR 实例的名称。
​my-other-namespace/my-instrumentation​​​ - 另一个命名空间中​​Instrumentation​​ CR 实例的名称和命名空间。
​​false​​ -不注入。

可以看到 Otel Operator 向 Pod 中注入了一个 otel 的初始化容器

➜  ~ kubectl get pod java-sample-6f688ffb4f-gq95j -o jsonpath='{.spec.initContainers[*].image} {.spec.containers[*].image}'
ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:1.33.6 pinakispecial/spring-boot-rest%

以及在 java 容器中注入了一系列的环境变量进行配置

spec:
containers:
- env:
- name: OTEL_NODE_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.hostIP
- name: OTEL_POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: http://otel-collector.default:4317
- name: JAVA_TOOL_OPTIONS
value: ' -javaagent:/otel-auto-instrumentation-java/javaagent.jar'
- name: OTEL_SERVICE_NAME
value: java-sample
- name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
- name: OTEL_PROPAGATORS
value: tracecontext,baggage,b3
- name: OTEL_TRACES_SAMPLER
value: parentbased_traceidratio
- name: OTEL_TRACES_SAMPLER_ARG
value: "1"
- name: OTEL_RESOURCE_ATTRIBUTES
value: k8s.container.name=java-sample,k8s.deployment.name=java-sample,k8s.namespace.name=default,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),k8s.replicaset.name=java-sample-6f688ffb4f,service.instance.id=default.$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME).java-sample

请求服务接口

#开放Java服务的端口访问
➜ ~ kubectl port-forward java-sample-6f688ffb4f-gq95j 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
#请求接口
➜ ~ curl localhost:8080
OK%

#开放Jaeger UI的端口访问
➜ ~ kubectl port-forward svc/jaeger 16686:16686
Forwarding from 127.0.0.1:16686 -> 16686
Forwarding from [::1]:16686 -> 16686

访问 Jaeger UI 查看访问的链路信息