OpenSearch Service 统一可观测性实测:Prometheus Direct Query + AI Agent Tracing 全解析¶
Lab 信息
- 难度: ⭐⭐ 中级
- 预估时间: 60 分钟
- 预估费用: $5-10(含清理)
- Region: us-east-1
- 最后验证: 2026-04-10
背景¶
可观测性领域长期面临"数据孤岛"问题:Prometheus 指标在 Grafana,Traces 在 Jaeger/X-Ray,Logs 在 CloudWatch。切换工具 + 手动关联 trace ID 是家常便饭。
2026 年 4 月,OpenSearch Service 发布了统一可观测性三件套:
- Prometheus Direct Query — 不复制数据,通过 live-call 直接在 OpenSearch UI 中用 PromQL 查 AMP 指标
- Application Monitoring — 从 OTel traces 自动生成 RED 指标 + 服务拓扑图
- Agent Tracing — 基于 OTel GenAI 语义约定,追踪 AI Agent 的完整执行链
这三者结合,意味着 SRE 团队可以在同一个 UI 里看指标、查 trace、关联日志——连 GenAI Agent 的 LLM 调用链也能追踪。本文实测验证这套组合的真实表现。
前置条件¶
- AWS 账号(需要 OpenSearch、AMP、OSIS、IAM 权限)
- AWS CLI v2 已配置
awscurl(用于 SigV4 签名请求):pip install awscurl- Python 3.x(用于生成测试数据)
curl(用于查询 OpenSearch)
核心概念¶
三大功能架构总览¶
┌──────────────┐
│ OpenSearch │
│ UI │ ← 统一入口
└──┬──┬──┬─────┘
│ │ │
PromQL ─────┘ │ └───── PPL/SQL
(live call) │ (indexed data)
│
┌─────────┴──────────┐
│ │
┌────────▼────────┐ ┌───────▼────────┐
│ Amazon │ │ OpenSearch │
│ Managed │ │ Domain │
│ Prometheus │ │ (traces/logs) │
└─────────────────┘ └────────────────┘
▲ ▲
│ │
remote_write ┌──────┴──────┐
│ │ OpenSearch │
│ │ Ingestion │
│ └──────┬───────┘
│ │
└───────────────────┘
▲
│ OTLP
┌──────┴──────┐
│ OTel │
│ Collector │
└──────┬───────┘
│
┌───────┴────────┐
│ Applications │
│ + AI Agents │
└────────────────┘
关键参数对比¶
| 特性 | Prometheus Direct Query | 传统数据复制方案 |
|---|---|---|
| 数据位置 | 留在 AMP | 复制到 OpenSearch |
| 查询方式 | PromQL (live call) | PPL/SQL (indexed) |
| OCU 费用 | 无 | 有(计算资源) |
| 延迟 | 略高(网络往返) | 低(本地索引) |
| 查询超时 | 30 秒(不可覆盖) | 取决于集群配置 |
| 数据源上限 | 20/账户/Region | N/A |
OTel GenAI 语义约定(Agent Tracing)¶
| Span 类型 | 属性 | 说明 |
|---|---|---|
| Agent | gen_ai.agent.name/id/version |
Agent 身份标识 |
| LLM 调用 | gen_ai.request.model, gen_ai.provider.name |
模型和供应商 |
| Token 计量 | gen_ai.usage.prompt_tokens/completion_tokens |
Token 用量追踪 |
| 工具执行 | gen_ai.tool.name, tool.result.status |
工具调用结果 |
| 操作类型 | gen_ai.operation.name |
chat/embeddings 等 |
动手实践¶
Step 1: 创建 AMP Workspace¶
实测输出:
Step 2: 创建 OpenSearch 域¶
cat > /tmp/os-domain.json << 'EOF'
{
"DomainName": "obs-lab",
"EngineVersion": "OpenSearch_2.19",
"ClusterConfig": {
"InstanceType": "t3.small.search",
"InstanceCount": 1
},
"EBSOptions": {
"EBSEnabled": true,
"VolumeType": "gp3",
"VolumeSize": 10
},
"AccessPolicies": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":\"es:*\",\"Resource\":\"arn:aws:es:us-east-1:*:domain/obs-lab/*\"}]}",
"AdvancedSecurityOptions": {
"Enabled": true,
"InternalUserDatabaseEnabled": true,
"MasterUserOptions": {
"MasterUserName": "admin",
"MasterUserPassword": "YourSecurePassword1!"
}
},
"NodeToNodeEncryptionOptions": { "Enabled": true },
"EncryptionAtRestOptions": { "Enabled": true },
"DomainEndpointOptions": { "EnforceHTTPS": true }
}
EOF
aws opensearch create-domain \
--cli-input-json file:///tmp/os-domain.json \
--region us-east-1
等待约 10-15 分钟域变为 Active。
Step 3: 配置 Prometheus Direct Query¶
创建 IAM Role(信任 directquery.opensearchservice.amazonaws.com):
# 信任策略
cat > /tmp/dq-trust.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "directquery.opensearchservice.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
EOF
aws iam create-role \
--role-name obs-lab-prometheus-dq \
--assume-role-policy-document file:///tmp/dq-trust.json
# 权限策略(AMP 查询权限)
cat > /tmp/dq-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["aps:QueryMetrics", "aps:GetLabels", "aps:GetSeries", "aps:GetMetricMetadata"],
"Resource": "arn:aws:aps:us-east-1:ACCOUNT_ID:workspace/WORKSPACE_ID"
}]
}
EOF
aws iam put-role-policy \
--role-name obs-lab-prometheus-dq \
--policy-name aps-query \
--policy-document file:///tmp/dq-policy.json
创建 OpenSearch UI Application + Direct Query 数据源:
# 创建 OpenSearch UI Application
aws opensearch create-application \
--name obs-lab-app \
--region us-east-1
# 创建 Prometheus 数据源
aws opensearch add-direct-query-data-source \
--data-source-name obs_lab_prometheus \
--data-source-type '{"Prometheus":{"RoleArn":"arn:aws:iam::ACCOUNT_ID:role/obs-lab-prometheus-dq","WorkspaceArn":"arn:aws:aps:us-east-1:ACCOUNT_ID:workspace/WORKSPACE_ID"}}' \
--description "Prometheus metrics for obs-lab" \
--region us-east-1
Step 4: 写入 Prometheus 测试指标¶
使用 awscurl 通过 remote_write 协议写入测试数据(需要 protobuf + snappy 编码,完整脚本见 GitHub repo)。
写入 4 个模拟服务的 metrics:
- http_requests_total — 请求计数
- http_request_duration_milliseconds — 请求延迟
- container_cpu_usage_seconds_total — CPU 使用率
验证 AMP 数据:
awscurl --service aps --region us-east-1 \
"https://aps-workspaces.us-east-1.amazonaws.com/workspaces/WORKSPACE_ID/api/v1/label/__name__/values"
实测输出:
{
"status": "success",
"data": [
"container_cpu_usage_seconds_total",
"http_request_duration_milliseconds",
"http_requests_total"
]
}
Step 5: 创建 OSIS Trace Pipeline¶
创建 Pipeline IAM Role:
创建 Pipeline(使用 AWS-TraceAnalyticsPipeline blueprint):
version: "2"
entry-pipeline:
source:
otel_trace_source:
path: "/entry-pipeline/v1/traces"
processor:
- trace_peer_forwarder:
sink:
- pipeline:
name: "span-pipeline"
- pipeline:
name: "service-map-pipeline"
span-pipeline:
source:
pipeline:
name: "entry-pipeline"
processor:
- otel_traces:
sink:
- opensearch:
hosts: ["https://YOUR_DOMAIN_ENDPOINT"]
aws:
sts_role_arn: "arn:aws:iam::ACCOUNT_ID:role/obs-lab-osis-pipeline"
region: "us-east-1"
serverless: false
index_type: "trace-analytics-raw"
service-map-pipeline:
source:
pipeline:
name: "entry-pipeline"
processor:
- otel_apm_service_map:
sink:
- opensearch:
hosts: ["https://YOUR_DOMAIN_ENDPOINT"]
aws:
sts_role_arn: "arn:aws:iam::ACCOUNT_ID:role/obs-lab-osis-pipeline"
region: "us-east-1"
serverless: false
index_type: "trace-analytics-service-map"
aws osis create-pipeline \
--pipeline-name obs-lab-traces \
--min-units 1 --max-units 1 \
--pipeline-configuration-body file:///tmp/osis-pipeline.yaml \
--region us-east-1
等待约 5 分钟变为 ACTIVE。
Step 6: 发送 AI Agent Traces¶
用 Python 脚本生成模拟 AI Agent 的 OTel traces(OTLP JSON 格式),模拟以下调用链:
user_request (SERVER)
└── execute_agent (INTERNAL)
├── chat [planning] (CLIENT) — gen_ai.request.model=anthropic.claude-3-sonnet
├── execute_tool [web_search] (CLIENT) — gen_ai.tool.name=web_search
└── chat [response] (CLIENT) — gen_ai.usage.prompt_tokens=2100
同时生成:
- 5 条 Agent traces(成功场景)
- 5 条微服务 traces(api-gateway → user-service → order-service → payment-service)
- 1 条 Error trace(Agent 工具调用超时)
# 生成 traces
python3 gen_traces.py # 输出 /tmp/otlp_traces.json
# 发送到 OSIS endpoint
awscurl --service osis --region us-east-1 \
-X POST \
-H "Content-Type: application/json" \
-d @/tmp/otlp_traces.json \
"https://PIPELINE_ENDPOINT/trace-pipeline/v1/traces"
实测输出:HTTP 200,空 body(成功)。
Step 7: 验证 OpenSearch 中的 Trace 数据¶
实测输出:
96 个 spans 全部索引成功。
查看 GenAI Agent Trace 详情:
curl -s -u admin:PASSWORD \
"https://DOMAIN_ENDPOINT/otel-v1-apm-span/_search?pretty" \
-H "Content-Type: application/json" \
-d '{"size":1,"query":{"term":{"name":"execute_agent"}}}'
实测输出(关键字段):
{
"serviceName": "ai-agent-service",
"name": "execute_agent",
"durationInNanos": 3350000000,
"span.attributes.gen_ai@agent@name": "research-assistant",
"span.attributes.gen_ai@agent@id": "agent-001",
"status.code": 1
}
属性名转换规则
OTel 属性中的 . 在 OpenSearch 索引中被转换为 @。例如 gen_ai.agent.name → span.attributes.gen_ai@agent@name。PPL 查询时需要使用转换后的字段名。
查看 Error Trace:
{
"serviceName": "ai-agent-service",
"name": "execute_tool",
"span.attributes.gen_ai@tool@name": "sql_query",
"span.attributes.error@type": "TimeoutError",
"status.code": 2,
"status.message": "SQL query timed out after 3s"
}
PPL 查询示例:
-- 统计各服务的 span 数量
source = otel-v1-apm-span-*
| stats count() as spanCount by serviceName
| sort - spanCount
-- 查找所有 Agent 执行超过 3 秒的 trace
source = otel-v1-apm-span-*
| where name = 'execute_agent' AND durationInNanos > 3000000000
| fields traceId, serviceName, durationInNanos
Step 8: 验证服务分布¶
curl -s -u admin:PASSWORD \
"https://DOMAIN_ENDPOINT/otel-v1-apm-span/_search" \
-H "Content-Type: application/json" \
-d '{"size":0,"aggs":{"services":{"terms":{"field":"serviceName.keyword","size":20}}}}'
实测输出:
| 服务名 | Span 数量 |
|---|---|
| ai-agent-service | 56 |
| api-gateway | 10 |
| order-service | 10 |
| payment-service | 10 |
| user-service | 10 |
5 个服务全部正确识别,Agent 服务包含最多 spans(每个 agent trace 有 5 个 spans)。
测试结果¶
| # | 测试场景 | 结果 | 关键数据 | 备注 |
|---|---|---|---|---|
| 1 | Prometheus Direct Query 配置 | ✅ 通过 | 3 metrics, 8 时间序列 | AMP → OpenSearch 数据源链路完整 |
| 2 | Agent Tracing 端到端 | ✅ 通过 | 96 spans, 5 services | GenAI 属性完整保留 |
| 3 | Application Map (otel_apm_service_map) | ⚠️ 受限 | — | OSIS 上有 db_path 权限 bug |
| 4 | PromQL 查询超时边界 | ⏭️ 未测 | — | 需通过 OpenSearch UI 操作 |
| 5 | Trace-Log 关联 | ⏭️ 未测 | — | 需额外 log pipeline |
| 6 | Error Trace 追踪 | ✅ 通过 | status.code=2 | error.type + message 完整保留 |
踩坑记录¶
踩坑 1: otel_apm_service_map 在 OSIS 上的 db_path 权限问题
在 OpenSearch Ingestion (OSIS) 托管环境中,otel_apm_service_map processor 默认尝试在 data/otel-apm-service-map/ 目录创建数据库文件,但 OSIS 的文件系统权限不允许在该路径创建目录。
ValidationException: service-map-pipeline.processor.otel_apm_service_map:
caused by: Unable to create the directory at the provided path: otel-apm-service-map
即使指定 db_path: /tmp/otel-apm-service-map 也会遇到 InternalFailure (HTTP 500)。
影响:使用完整的 TraceAnalyticsPipeline blueprint(含 service-map-pipeline)时可能失败。Workaround 是使用简化 pipeline(仅 span 存储,不生成 service map)。
来源:实测发现,AWS-TraceAnalyticsPipeline blueprint 中使用了该 processor 但未说明此限制。
踩坑 2: OSIS CreatePipeline 的 log-publishing-options 隐藏依赖
--log-publishing-options 参数引用的 CloudWatch Log Group 如果不存在,OSIS 不会返回明确的验证错误,而是直接返回 HTTP 500 InternalFailure。
Workaround:先创建 Log Group,或者干脆不指定 --log-publishing-options(Pipeline 仍能正常工作)。
踩坑 3: trace-analytics-raw index_type 不允许同时指定 index 字段
使用 index_type: trace-analytics-raw 或 trace-analytics-service-map 时,不能同时指定 index 字段,否则报 ValidationException。这是因为这些 index_type 自动管理索引命名。
费用明细¶
| 资源 | 单价 | 用量 | 费用 |
|---|---|---|---|
| OpenSearch 域 (t3.small.search) | $0.036/hr | ~12 hr | ~$0.43 |
| OpenSearch Ingestion (1 OCU) | $0.24/hr | ~2 hr | ~$0.48 |
| AMP Workspace (metrics ingestion) | $0.003/10K samples | ~480 samples | < $0.01 |
| AMP Query | $0.10/billion samples | 几次查询 | < $0.01 |
| Prometheus Direct Query (OCU) | $0 | — | $0 |
| 合计 | ~$1-2 |
Prometheus Direct Query 零 OCU 成本
这是此功能的核心卖点。与 S3/CloudWatch Logs 的 Direct Query 不同,Prometheus Direct Query 使用 live-call 架构,不会启动临时计算资源,因此不产生 OCU 费用。你只承担 AMP 标准查询费用。
清理资源¶
# 1. 删除 OSIS Pipeline
aws osis delete-pipeline --pipeline-name obs-lab-traces --region us-east-1
# 2. 等待 Pipeline 删除完成(约 3 分钟)
aws osis list-pipelines --region us-east-1 \
--query "Pipelines[?PipelineName=='obs-lab-traces'].Status"
# 3. 删除 Prometheus Direct Query 数据源
aws opensearch delete-direct-query-data-source \
--data-source-name obs_lab_prometheus --region us-east-1
# 4. 删除 OpenSearch UI Application
aws opensearch delete-application --id YOUR_APP_ID --region us-east-1
# 5. 删除 OpenSearch 域
aws opensearch delete-domain --domain-name obs-lab --region us-east-1
# 6. 删除 AMP Workspace
aws amp delete-workspace \
--workspace-id ws-XXXXX --region us-east-1
# 7. 删除 IAM Roles
aws iam delete-role-policy --role-name obs-lab-prometheus-dq --policy-name aps-query
aws iam delete-role --role-name obs-lab-prometheus-dq
aws iam delete-role-policy --role-name obs-lab-osis-pipeline --policy-name osis-access
aws iam delete-role --role-name obs-lab-osis-pipeline
# 8. 删除 CloudWatch Log Groups(如有)
aws logs delete-log-group --log-group-name /aws/vendedlogs/obs-lab-traces --region us-east-1
务必清理
OpenSearch 域 (t3.small.search) 持续费用约 $0.036/hr($0.86/天)。OSIS Pipeline 1 OCU 约 $0.24/hr($5.76/天)。请务必在实验结束后清理。
结论与建议¶
统一可观测性成熟度评估¶
| 能力 | 成熟度 | 评估 |
|---|---|---|
| Prometheus Direct Query | ⭐⭐⭐⭐ 可用于生产 | 配置简单、零 OCU、PromQL 原生支持 |
| Trace Ingestion (OSIS) | ⭐⭐⭐ 基本可用 | 端到端工作,但 service-map processor 有 bug |
| Agent Tracing (GenAI) | ⭐⭐⭐ 新兴阶段 | OTel GenAI 语义约定仍在 Development,但已可用 |
| Application Map (RED) | ⭐⭐ 受限 | otel_apm_service_map 在 OSIS 上有路径权限问题 |
| Trace-Log 关联 | ⭐⭐⭐ 文档确认 | 需配置 correlation,本次未实测 |
场景化推荐¶
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 已有 AMP,想在 OpenSearch UI 查 metrics | Prometheus Direct Query | 零额外成本,live-call 架构 |
| GenAI Agent 可观测性 | OSIS + OpenSearch + OTel GenAI SDK | 端到端 trace 链路完整 |
| 全栈统一可观测性(metrics + traces + logs) | 等 service-map bug 修复 | 目前 Application Map 受限 |
| 短期查询(< 1 小时窗口) | Direct Query 优先 | 无需数据复制,延迟可接受 |
| 长期趋势分析(> 30 天) | AMP Recording Rules + Direct Query | 避免 30s 查询超时 |
生产注意事项¶
- 30 秒查询超时不可覆盖 — 复杂 PromQL 必须控制查询范围,配合 recording rules
- GenAI 语义约定是 Development 状态 — 属性名可能在后续版本变化,做好向后兼容准备
- 属性名
.→@转换 — PPL/SQL 查询时注意字段名映射 - OSIS Pipeline 最小 1 OCU — 即使空闲也按 $0.24/hr 计费,测试完立即删除