OpenTelemetry 中文教程
1. 项目概述
OpenTelemetry 是一个开源的可观测性框架,由 Cloud Native Computing Foundation (CNCF) 孵化和维护。它提供了一套统一的标准和工具,用于生成、收集和导出遥测数据(追踪、指标和日志),帮助开发者更好地理解和监控软件系统的行为。
主要功能
- 统一的遥测数据采集:支持追踪、指标和日志
- 厂商中立:提供标准化的遥测数据格式和API
- 多语言支持:支持多种编程语言
- 灵活的导出:支持导出到多种后端系统
- 自动和手动 instrumentation:支持自动和手动添加遥测代码
- 上下文传播:在分布式系统中自动传播追踪上下文
- 与现有系统集成:与 Prometheus、Jaeger、Zipkin 等集成
技术栈特点
- 基于开放标准:由 CNCF 维护的开放标准
- 模块化设计:核心组件和语言特定实现分离
- 可扩展架构:支持插件和扩展
- 性能优先:设计注重低开销
- 云原生友好:与 Kubernetes 等云原生技术深度集成
适用环境
- 微服务架构
- 分布式系统
- 云原生环境
- 容器化应用
- 传统单体应用
2. 核心概念
2.1 遥测数据类型
OpenTelemetry 支持三种主要的遥测数据类型:
- **追踪 (Traces)**:记录请求在系统中的完整调用路径,包含多个跨度 (Spans)
- **指标 (Metrics)**:记录可聚合的数值数据,如计数器、仪表盘、直方图等
- **日志 (Logs)**:记录离散的事件和消息
2.2 核心组件
- API:提供编程语言特定的接口,用于生成和处理遥测数据
- SDK:提供API的具体实现,包含配置、采样、处理和导出功能
- Instrumentation:自动或手动添加遥测代码的组件
- Collector:集中处理和导出遥测数据的独立服务
2.3 数据模型
- Trace:由多个相关的 Spans 组成,表示一个完整的请求处理过程
- Span:表示一个操作或工作单元,包含开始时间、结束时间、属性、事件等
- Metric:表示一个可测量的值,包含名称、标签、值等
- Log:表示一个离散的事件记录,包含时间戳、 severity、消息等
- Resource:表示遥测数据的来源,如服务名称、主机名、环境等
3. 安装与配置
3.1 安装 OpenTelemetry SDK
3.1.1 Node.js 应用
npm install @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node @opentelemetry/exporter-jaeger @opentelemetry/exporter-prometheus3.1.2 Java 应用
<!-- pom.xml -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
<version>1.30.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
<version>1.30.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-instrumentation-runtime-telemetry</artifactId>
<version>1.30.0</version>
</dependency>3.1.3 Python 应用
pip install opentelemetry-sdk opentelemetry-exporter-otlp opentelemetry-instrumentation-requests3.2 配置 OpenTelemetry Collector
3.2.1 安装 Collector
使用 Docker 运行 Collector:
docker run -d \n --name otel-collector \n -p 4317:4317 \n -p 4318:4318 \n -p 8888:8888 \n -v ./collector-config.yaml:/etc/otelcol/config.yaml \n otel/opentelemetry-collector-contrib:0.86.03.2.2 Collector 配置
# collector-config.yaml
receivers:
otlp:
protocols:
grpc:
http:
exporters:
otlp:
endpoint: jaeger:4317
tls:
insecure: true
prometheus:
endpoint: 0.0.0.0:8889
logging:
loglevel: debug
processors:
batch:
memory_limiter:
limit_mib: 4000
spike_limit_mib: 800
check_interval: 5s
extensions:
health_check:
pprof:
zpages:
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp, logging]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus, logging]
logs:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [logging]
extensions: [health_check, pprof, zpages]3.3 环境变量配置
OpenTelemetry 支持通过环境变量进行配置:
# 服务名称
OTEL_SERVICE_NAME=my-service
# 导出器配置
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
# 采样配置
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=0.1
# 资源属性
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production,service.version=1.0.04. 基本使用
4.1 自动 Instrumentation
4.1.1 Node.js 示例
// tracing.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metric-otlp-grpc');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
// 创建追踪导出器
const traceExporter = new OTLPTraceExporter({
url: 'http://localhost:4317'
});
// 创建指标导出器
const metricExporter = new OTLPMetricExporter({
url: 'http://localhost:4317'
});
// 创建指标读取器
const metricReader = new PeriodicExportingMetricReader({
exporter: metricExporter,
exportIntervalMillis: 10000
});
// 创建 SDK 实例
const sdk = new NodeSDK({
traceExporter,
metricReader,
instrumentations: [getNodeAutoInstrumentations()],
serviceName: 'my-service'
});
// 启动 SDK
sdk.start();
// 处理进程结束
process.on('SIGTERM', () => {
sdk.shutdown()
.then(() => console.log('OpenTelemetry SDK shutdown'))
.catch((error) => console.error('Error shutting down SDK', error))
.finally(() => process.exit(0));
});
module.exports = sdk;// app.js
// 在应用代码之前导入追踪配置
require('./tracing');
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.get('/api/users', async (req, res) => {
// 模拟数据库查询
await new Promise(resolve => setTimeout(resolve, 100));
res.json([{ id: 1, name: 'User 1' }, { id: 2, name: 'User 2' }]);
});
app.listen(3000, () => {
console.log('Server listening on port 3000');
});4.1.2 Java 示例
使用 Java Agent 进行自动 instrumentation:
java -javaagent:opentelemetry-javaagent.jar \n -Dotel.service.name=my-service \n -Dotel.exporter.otlp.endpoint=http://localhost:4317 \n -jar my-application.jar4.2 手动 Instrumentation
4.2.1 Node.js 示例
const { trace } = require('@opentelemetry/api');
// 获取当前追踪器
const tracer = trace.getTracer('my-service');
// 创建新的跨度
function processOrder(orderId) {
// 创建根跨度
const span = tracer.startSpan('process-order');
try {
// 设置跨度属性
span.setAttribute('order.id', orderId);
// 执行操作
validateOrder(orderId, span);
prepareShipment(orderId, span);
// 设置跨度状态为成功
span.setStatus({ code: 0 });
} catch (error) {
// 设置跨度状态为错误
span.setStatus({ code: 2, message: error.message });
span.recordException(error);
throw error;
} finally {
// 结束跨度
span.end();
}
}
// 创建子跨度
function validateOrder(orderId, parentSpan) {
const span = tracer.startSpan('validate-order', {
parent: parentSpan
});
try {
// 执行验证操作
console.log(`Validating order ${orderId}`);
span.setAttribute('order.status', 'validating');
// 模拟验证过程
return new Promise(resolve => {
setTimeout(() => {
span.setAttribute('order.status', 'valid');
resolve(true);
}, 50);
});
} finally {
span.end();
}
}
function prepareShipment(orderId, parentSpan) {
const span = tracer.startSpan('prepare-shipment', {
parent: parentSpan
});
try {
// 执行发货准备操作
console.log(`Preparing shipment for order ${orderId}`);
span.setAttribute('shipment.status', 'preparing');
// 模拟准备过程
return new Promise(resolve => {
setTimeout(() => {
span.setAttribute('shipment.status', 'prepared');
resolve(true);
}, 100);
});
} finally {
span.end();
}
}
// 调用函数
processOrder('12345');4.3 指标示例
const { metrics } = require('@opentelemetry/api');
// 获取指标记录器
const meter = metrics.getMeter('my-service');
// 创建计数器
const httpRequestsCounter = meter.createCounter('http_requests_total', {
description: 'Total number of HTTP requests'
});
// 创建仪表盘
const activeUsersGauge = meter.createObservableGauge('active_users', {
description: 'Number of active users'
});
// 创建直方图
const responseTimeHistogram = meter.createHistogram('http_response_time_ms', {
description: 'HTTP response time in milliseconds',
boundaries: [10, 50, 100, 200, 500, 1000]
});
// 记录指标
function handleRequest(req, res) {
const startTime = Date.now();
// 增加请求计数器
httpRequestsCounter.add(1, {
method: req.method,
path: req.path,
status_code: res.statusCode
});
// 模拟处理时间
setTimeout(() => {
const responseTime = Date.now() - startTime;
// 记录响应时间
responseTimeHistogram.record(responseTime, {
method: req.method,
path: req.path,
status_code: res.statusCode
});
res.end('Hello World!');
}, Math.random() * 100);
}
// 注册仪表盘回调
activeUsersGauge.addCallback((observableResult) => {
// 模拟获取活跃用户数
const activeUsers = Math.floor(Math.random() * 1000);
observableResult.observe(activeUsers);
});4.4 日志示例
const { logs } = require('@opentelemetry/api');
// 获取日志记录器
const logger = logs.getLogger('my-service');
// 记录日志
function processPayment(paymentId, amount) {
// 记录信息日志
logger.info('Processing payment', {
payment_id: paymentId,
amount: amount,
status: 'processing'
});
try {
// 模拟支付处理
if (amount > 1000) {
throw new Error('Amount exceeds limit');
}
// 记录成功日志
logger.info('Payment processed successfully', {
payment_id: paymentId,
amount: amount,
status: 'success'
});
return { success: true, paymentId };
} catch (error) {
// 记录错误日志
logger.error('Payment processing failed', {
payment_id: paymentId,
amount: amount,
status: 'failed',
error: error.message
});
return { success: false, paymentId, error: error.message };
}
}
// 调用函数
processPayment('pay_123', 500);
processPayment('pay_456', 1500);5. 高级特性
5.1 上下文传播
OpenTelemetry 自动处理分布式系统中的上下文传播:
- HTTP 传播:通过 HTTP 头传播追踪上下文
- gRPC 传播:通过 gRPC 元数据传播追踪上下文
- 消息队列传播:通过消息属性传播追踪上下文
5.1.1 自定义传播
const { propagation } = require('@opentelemetry/api');
const { W3CTraceContextPropagator } = require('@opentelemetry/core');
// 设置全局传播器
propagation.setGlobalPropagator(new W3CTraceContextPropagator());
// 手动注入上下文
function injectContext(headers) {
const context = propagation.createContext();
propagation.inject(context, headers, {
set: (carrier, key, value) => {
carrier[key] = value;
}
});
return headers;
}
// 手动提取上下文
function extractContext(headers) {
return propagation.extract(undefined, headers, {
get: (carrier, key) => carrier[key]
});
}
// 使用示例
const headers = {};
injectContext(headers);
console.log('Headers with context:', headers);
const extractedContext = extractContext(headers);
console.log('Extracted context:', extractedContext);5.2 采样策略
OpenTelemetry 支持多种采样策略:
- AlwaysOnSampler:总是采样
- AlwaysOffSampler:从不采样
- TraceIdRatioBasedSampler:基于追踪 ID 比率采样
- ParentBasedSampler:基于父跨度的采样决策
- CustomSampler:自定义采样策略
5.2.1 配置采样策略
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { TraceIdRatioBasedSampler } = require('@opentelemetry/sdk-trace-node');
const sdk = new NodeSDK({
// 配置 10% 采样率
sampler: new TraceIdRatioBasedSampler(0.1),
// 其他配置...
});5.3 资源检测
OpenTelemetry 自动检测和添加资源属性:
- 环境变量:从 OTEL_RESOURCE_ATTRIBUTES 环境变量读取
- 系统信息:检测主机名、操作系统等
- 容器信息:检测容器 ID、镜像等
- 云提供商信息:检测云提供商、区域等
5.3.1 自定义资源
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const resource = new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'my-service',
[SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0',
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: 'production',
'custom.attribute': 'custom-value'
});
const sdk = new NodeSDK({
resource,
// 其他配置...
});5.4 与现有系统集成
OpenTelemetry 可以与多种现有系统集成:
5.4.1 与 Prometheus 集成
# collector-config.yaml
exporters:
prometheus:
endpoint: 0.0.0.0:8889
service:
pipelines:
metrics:
receivers: [otlp]
processors: [batch]
exporters: [prometheus]5.4.2 与 Jaeger 集成
# collector-config.yaml
exporters:
otlp:
endpoint: jaeger:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp]5.4.3 与 Zipkin 集成
# collector-config.yaml
exporters:
zipkin:
endpoint: http://zipkin:9411/api/v2/spans
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [zipkin]6. 最佳实践
6.1 Instrumentation 策略
- 优先使用自动 Instrumentation:减少手动代码,提高覆盖率
- 补充手动 Instrumentation:对关键业务逻辑添加手动追踪
- 标准化命名:使用一致的跨度和指标命名约定
- 添加有意义的属性:为跨度和指标添加有意义的标签
- 保持适当的粒度:不要创建过多或过少的跨度
6.2 性能优化
- 合理采样:根据系统规模和性能需求设置适当的采样率
- 批量导出:配置适当的批处理大小和间隔
- 异步处理:使用异步方式处理和导出遥测数据
- 资源限制:为 Collector 设置适当的资源限制
- 监控遥测开销:监控遥测本身的开销
6.3 数据管理
- 设置合理的保留期:根据业务需求和存储成本设置
- 数据聚合:在导出前适当聚合数据
- 敏感数据处理:避免在遥测数据中存储敏感信息
- 数据压缩:启用数据压缩以减少网络传输
- 备份重要数据:对重要的遥测数据进行备份
6.4 告警和监控
- 基于遥测数据设置告警:如错误率、响应时间等
- 监控遥测系统本身:确保遥测系统正常运行
- 分级告警:根据严重程度设置不同级别的告警
- 与现有监控系统集成:如 Grafana、Prometheus Alertmanager
6.5 安全最佳实践
- 加密传输:使用 TLS 加密遥测数据传输
- 访问控制:限制对遥测数据的访问
- 认证和授权:为遥测系统添加认证和授权
- 审计日志:记录对遥测系统的访问和修改
- 合规性:确保遥测数据处理符合相关法规
7. 实际应用场景
7.1 微服务架构监控
场景:一个由多个微服务组成的电商系统
实施步骤:
- 为每个微服务添加 OpenTelemetry Instrumentation
- 部署 OpenTelemetry Collector 集群
- 配置导出到 Jaeger 和 Prometheus
- 使用 Grafana 创建统一仪表盘
- 设置基于遥测数据的告警
查询与分析:
- 追踪分析:查看请求在微服务间的完整调用链
- 性能分析:识别性能瓶颈和慢服务
- 错误分析:快速定位错误源头
- 依赖分析:分析服务间的依赖关系
7.2 云原生应用监控
场景:部署在 Kubernetes 上的应用
实施步骤:
- 使用 OpenTelemetry Operator 部署 Collector
- 为应用添加自动 Instrumentation
- 配置与 Kubernetes 集成
- 导出到云提供商的可观测性服务
优势:
- 自动发现:自动发现和监控新部署的服务
- 统一视图:在 Kubernetes 上下文中查看遥测数据
- 弹性扩展:根据负载自动扩展 Collector
- 云集成:与云提供商的可观测性服务无缝集成
7.3 传统应用现代化
场景:将传统单体应用迁移到微服务架构
实施步骤:
- 为单体应用添加 OpenTelemetry Instrumentation
- 监控迁移过程中的性能变化
- 为新的微服务添加 Instrumentation
- 比较迁移前后的性能和可靠性
优势:
- 基线建立:建立迁移前的性能基线
- 渐进式迁移:监控每个迁移步骤的影响
- 问题早发现:在迁移过程中及早发现问题
- 验证成功:验证迁移后的性能改进
8. 总结
OpenTelemetry 是一个功能强大、灵活可扩展的可观测性框架,它为现代软件系统提供了统一的遥测数据采集、处理和导出解决方案。通过本文的介绍,您应该已经了解了 OpenTelemetry 的核心概念、安装配置、基本使用和高级特性。
关键优势
- 开放标准:由 CNCF 维护的开放标准,避免厂商锁定
- 统一解决方案:同时支持追踪、指标和日志
- 多语言支持:支持多种编程语言
- 灵活导出:支持导出到多种后端系统
- 低开销设计:注重性能和低开销
- 云原生友好:与 Kubernetes 等云原生技术深度集成
应用场景
- 微服务架构监控和故障排查
- 分布式系统性能分析
- 云原生应用可观测性
- 传统应用现代化
- DevOps 持续监控
OpenTelemetry 正在成为可观测性领域的事实标准,它不仅提供了强大的工具和库,还建立了一套开放的标准和最佳实践。通过采用 OpenTelemetry,您可以构建更加可靠、可维护的软件系统,同时获得对系统行为的深入洞察。